Provide the facility for the player to retry a failed connection a certain number of times.feature/data-structs
@@ -59,6 +59,9 @@ void IZ_WSClientAttemptConnect(struct lws_sorted_usec_list *sul) { | |||
vhd->i.protocol = NETWORK_PROTOCOL; | |||
vhd->i.pwsi = &vhd->client_wsi; | |||
struct IZ_App* app = (struct IZ_App*) vhd->app; | |||
IZ_NetState* net_state = IZ_AppGetNetState(app); | |||
net_state->status = IZ_NET_STATUS_CONNECTING; | |||
if (lws_client_connect_via_info(&vhd->i)) { | |||
return; | |||
} | |||
@@ -120,38 +123,60 @@ void IZ_WSClientProtocolTeardown(struct lws* wsi) { | |||
} | |||
lws_sul_cancel(&vhd->sul); | |||
struct IZ_App* app = (struct IZ_App*) vhd->app; | |||
IZ_NetState* net_state = IZ_AppGetNetState(app); | |||
net_state->status = IZ_NET_STATUS_PRISTINE; | |||
} | |||
void IZ_WSClientConnectionError(struct lws* wsi, void* in) { | |||
IZ_ProcedureResult IZ_WSClientConnectionError(struct lws* wsi, void* in) { | |||
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); | |||
IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( | |||
lws_get_vhost(wsi), | |||
lws_get_protocol(wsi) | |||
); | |||
if (!vhd) { | |||
return -1; | |||
} | |||
struct IZ_App* app = (struct IZ_App*) vhd->app; | |||
IZ_AppBindConnection(app, NULL); | |||
vhd->client_wsi = NULL; | |||
IZ_NetState* net_state = IZ_AppGetNetState(app); | |||
if (net_state->retries == net_state->config.max_reconnect_retries) { | |||
lwsl_err("Max number of retries reached!\n"); | |||
net_state->status = IZ_NET_STATUS_PRISTINE; | |||
return -1; | |||
} | |||
net_state->status = IZ_NET_STATUS_ERROR; | |||
net_state->retries += 1; | |||
lws_sul_schedule( | |||
vhd->context, | |||
0, | |||
&vhd->sul, | |||
IZ_WSClientAttemptConnect, | |||
LWS_US_PER_SEC | |||
net_state->config.reconnect_interval_secs * LWS_US_PER_SEC | |||
); | |||
return 0; | |||
} | |||
IZ_ProcedureResult IZ_WSClientOnOpen(struct lws* wsi, IZ_WSClientSessionData* pss) { | |||
pss->ring = lws_ring_create(sizeof(IZ_WebsocketMessage), RING_COUNT,IZ_WebsocketDestroyMessage); | |||
if (!pss->ring) { | |||
return -1; | |||
} | |||
IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( | |||
lws_get_vhost(wsi), | |||
lws_get_protocol(wsi) | |||
); | |||
struct IZ_App* app = (struct IZ_App*) vhd->app; | |||
IZ_NetState* net_state = IZ_AppGetNetState(app); | |||
pss->ring = lws_ring_create(sizeof(IZ_WebsocketMessage), RING_COUNT,IZ_WebsocketDestroyMessage); | |||
if (!pss->ring) { | |||
net_state->status = IZ_NET_STATUS_ERROR; | |||
return -1; | |||
} | |||
IZ_AppBindConnection(app, wsi); | |||
net_state->status = IZ_NET_STATUS_CONNECTED; | |||
net_state->retries = 0; | |||
pss->tail = 0; | |||
return 0; | |||
} | |||
@@ -251,6 +276,8 @@ void IZ_WSClientOnReceive(struct lws* wsi, IZ_WSClientSessionData* pss, void* in | |||
lwsl_user("dropping!\n"); | |||
return; | |||
} | |||
lws_ring_consume_single_tail(pss->ring, &pss->tail, 1); | |||
lws_callback_on_writable(wsi); | |||
if (!pss->flow_controlled && n < 3) { | |||
@@ -1,25 +1,25 @@ | |||
#include "IZ_net.h" | |||
bool IZ_NetIsConnected(IZ_NetState* state) { | |||
if (state->ws.interrupted) { | |||
return false; | |||
} | |||
if (!state->ws.context) { | |||
return false; | |||
} | |||
if (!state->ws.connection) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
void IZ_NetLoadConfig(IZ_NetState* state, const char* config_path) { | |||
char buffer[32]; | |||
ini_gets("Network", "Username", IZ_NET_DEFAULT_STATE.config.username, buffer, 32, config_path); | |||
memcpy_s(state->config.username, 32, buffer, 32); | |||
state->config.interval = ini_getl("Network", "Interval", IZ_NET_DEFAULT_STATE.config.interval, config_path); | |||
state->config.packet_interval_ms = ini_getl("Network", "PacketIntervalMs", IZ_NET_DEFAULT_STATE.config.packet_interval_ms, config_path); | |||
if (!(100 <= state->config.packet_interval_ms && state->config.packet_interval_ms <= 500)) { | |||
state->config.packet_interval_ms = IZ_NET_DEFAULT_STATE.config.packet_interval_ms; | |||
} | |||
state->config.max_reconnect_retries = ini_getl("Network", "MaxReconnectRetries", IZ_NET_DEFAULT_STATE.config.max_reconnect_retries, config_path); | |||
if (!(0 <= state->config.max_reconnect_retries && state->config.max_reconnect_retries <= 8)) { | |||
state->config.max_reconnect_retries = IZ_NET_DEFAULT_STATE.config.max_reconnect_retries; | |||
} | |||
state->config.reconnect_interval_secs = ini_getl("Network", "ReconnectIntervalSeconds", IZ_NET_DEFAULT_STATE.config.reconnect_interval_secs, config_path); | |||
if (!(3 <= state->config.reconnect_interval_secs && state->config.reconnect_interval_secs <= 10)) { | |||
state->config.reconnect_interval_secs = IZ_NET_DEFAULT_STATE.config.reconnect_interval_secs; | |||
} | |||
} | |||
IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetState* state, const char* config_path) { | |||
@@ -27,7 +27,15 @@ IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetState* state, const char* config_path) | |||
return -1; | |||
} | |||
if (!ini_putl("Network", "Interval", state->config.interval, config_path)) { | |||
if (!ini_putl("Network", "PacketIntervalMs", state->config.packet_interval_ms, config_path)) { | |||
return -1; | |||
} | |||
if (!ini_putl("Network", "MaxReconnectRetries", state->config.max_reconnect_retries, config_path)) { | |||
return -1; | |||
} | |||
if (!ini_putl("Network", "ReconnectIntervalSeconds", state->config.reconnect_interval_secs, config_path)) { | |||
return -1; | |||
} | |||
@@ -37,7 +45,7 @@ IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetState* state, const char* config_path) | |||
void IZ_NetOverrideConfig(IZ_NetState* state, u8 argc, const char* argv[]) { | |||
const char* cmdline_buffer; | |||
if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, "-i"))) { | |||
state->config.interval = atoi(cmdline_buffer); | |||
state->config.packet_interval_ms = atoi(cmdline_buffer); | |||
} | |||
} | |||
@@ -60,6 +68,7 @@ IZ_ProcedureResult IZ_NetInitialize( | |||
} | |||
state->ws.user_data = user_data; | |||
state->callback = callback; | |||
state->retries = state->config.max_reconnect_retries; | |||
u8 player_index; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
state->action[player_index] = 0; | |||
@@ -69,28 +78,47 @@ IZ_ProcedureResult IZ_NetInitialize( | |||
} | |||
void IZ_NetConnect(IZ_NetState* state, IZ_WSClientInitializeParams params) { | |||
if (IZ_NetIsConnected(state)) { | |||
if (!state->callback) { | |||
return; | |||
} | |||
if (!state->callback) { | |||
if (state->status == IZ_NET_STATUS_CONNECTED) { | |||
return; | |||
} | |||
if (state->status == IZ_NET_STATUS_CONNECTING) { | |||
return; | |||
} | |||
if (state->retries < state->config.max_reconnect_retries) { | |||
return; | |||
} | |||
state->retries = 0; | |||
state->params = params; | |||
state->client_thread = SDL_CreateThread(state->callback, "networking", state->ws.user_data); | |||
SDL_DetachThread(state->client_thread); | |||
} | |||
void IZ_NetDisconnect(IZ_NetState* state) { | |||
if (!IZ_NetIsConnected(state)) { | |||
if (state->status == IZ_NET_STATUS_PRISTINE) { | |||
return; | |||
} | |||
if (state->ws.connection) { | |||
IZ_WSClientVHostData *vhd = (IZ_WSClientVHostData *) lws_protocol_vh_priv_get( | |||
lws_get_vhost(state->ws.connection), | |||
lws_get_protocol(state->ws.connection) | |||
); | |||
if (vhd) { | |||
lws_sul_cancel(&vhd->sul); | |||
} | |||
} | |||
state->retries = state->config.max_reconnect_retries; | |||
IZ_WSClientCancelService(&state->ws); | |||
} | |||
void IZ_NetSendBinaryMessage(IZ_NetState* state, void* in, size_t len) { | |||
if (!IZ_NetIsConnected(state)) { | |||
if (state->status != IZ_NET_STATUS_CONNECTED) { | |||
return; | |||
} | |||
@@ -112,7 +140,7 @@ void IZ_NetSendBinaryMessage(IZ_NetState* state, void* in, size_t len) { | |||
} | |||
void IZ_NetSendTextMessage(IZ_NetState* state, char* in, size_t len) { | |||
if (!IZ_NetIsConnected(state)) { | |||
if (state->status != IZ_NET_STATUS_CONNECTED) { | |||
return; | |||
} | |||
@@ -9,8 +9,17 @@ | |||
#include "core/IZ_websocket.h" | |||
#include "svc/IZ_wsclient.h" | |||
typedef enum { | |||
IZ_NET_STATUS_PRISTINE, | |||
IZ_NET_STATUS_CONNECTING, | |||
IZ_NET_STATUS_ERROR, | |||
IZ_NET_STATUS_CONNECTED, | |||
} IZ_NetStatus; | |||
typedef struct { | |||
u16 interval; | |||
u16 packet_interval_ms; | |||
u8 max_reconnect_retries; | |||
u8 reconnect_interval_secs; | |||
char username[32]; | |||
} IZ_NetConfig; | |||
@@ -21,13 +30,17 @@ typedef struct { | |||
IZ_WSClientInitializeParams params; | |||
void* callback; | |||
IZ_Action action[IZ_PLAYERS]; | |||
u8 retries; | |||
IZ_NetStatus status; | |||
// TODO add message queue | |||
} IZ_NetState; | |||
static IZ_NetState IZ_NET_DEFAULT_STATE = { | |||
.client_thread = NULL, | |||
.config = { | |||
.interval = 200, | |||
.packet_interval_ms = 200, | |||
.max_reconnect_retries = 3, | |||
.reconnect_interval_secs = 3, | |||
.username = "Player", | |||
}, | |||
.ws = { | |||
@@ -43,6 +56,8 @@ static IZ_NetState IZ_NET_DEFAULT_STATE = { | |||
}, | |||
.callback = NULL, | |||
.action = {}, | |||
.retries = 3, | |||
.status = IZ_NET_STATUS_PRISTINE, | |||
}; | |||
IZ_ProcedureResult IZ_NetInitialize(IZ_NetState*, void*, void*, const char*, u8, const char**); | |||
@@ -14,8 +14,7 @@ IZ_ProcedureResult IZ_WSClientCallback( | |||
IZ_WSClientProtocolTeardown(wsi); | |||
break; | |||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: | |||
IZ_WSClientConnectionError(wsi, in); | |||
break; | |||
return IZ_WSClientConnectionError(wsi, in); | |||
case LWS_CALLBACK_CLIENT_ESTABLISHED: | |||
return IZ_WSClientOnOpen(wsi, user); | |||
case LWS_CALLBACK_CLIENT_CLOSED: | |||
@@ -47,7 +47,7 @@ IZ_ProcedureResult IZ_WSClientProtocolInitialize(struct lws*, void*); | |||
void IZ_WSClientProtocolTeardown(struct lws*); | |||
void IZ_WSClientConnectionError(struct lws*, void*); | |||
IZ_ProcedureResult IZ_WSClientConnectionError(struct lws*, void*); | |||
IZ_ProcedureResult IZ_WSClientOnOpen(struct lws*, IZ_WSClientSessionData*); | |||
@@ -53,12 +53,22 @@ void IZ_VideoUpdateForDebugInput(IZ_VideoState* video_state, IZ_InputState* inpu | |||
} | |||
void IZ_VideoUpdateForDebugNet(IZ_VideoState* video_state, IZ_NetState* net_state) { | |||
if (!net_state->ws.connection) { | |||
return; | |||
} | |||
const u8 size = 4; | |||
SDL_SetRenderDrawColor(video_state->renderer, 0x00, 0xff, 0x00, 0xff); | |||
switch (net_state->status) { | |||
default: | |||
return; | |||
case IZ_NET_STATUS_ERROR: | |||
SDL_SetRenderDrawColor(video_state->renderer, 0xff, 0x00, 0x00, 0xff); | |||
break; | |||
case IZ_NET_STATUS_CONNECTING: | |||
SDL_SetRenderDrawColor(video_state->renderer, 0xff, 0xff, 0x00, 0xff); | |||
break; | |||
case IZ_NET_STATUS_CONNECTED: | |||
SDL_SetRenderDrawColor(video_state->renderer, 0x00, 0xff, 0x00, 0xff); | |||
break; | |||
} | |||
SDL_RenderFillRectF(video_state->renderer, &(SDL_FRect) { | |||
0, | |||
(f32) (video_state->config.height - size), | |||
@@ -66,6 +76,10 @@ void IZ_VideoUpdateForDebugNet(IZ_VideoState* video_state, IZ_NetState* net_stat | |||
size, | |||
}); | |||
if (!net_state->ws.connection) { | |||
return; | |||
} | |||
u8 column; | |||
u8 row; | |||