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.protocol = NETWORK_PROTOCOL; | ||||
vhd->i.pwsi = &vhd->client_wsi; | 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)) { | if (lws_client_connect_via_info(&vhd->i)) { | ||||
return; | return; | ||||
} | } | ||||
@@ -120,38 +123,60 @@ void IZ_WSClientProtocolTeardown(struct lws* wsi) { | |||||
} | } | ||||
lws_sul_cancel(&vhd->sul); | 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)"); | lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); | ||||
IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( | IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( | ||||
lws_get_vhost(wsi), | lws_get_vhost(wsi), | ||||
lws_get_protocol(wsi) | lws_get_protocol(wsi) | ||||
); | ); | ||||
if (!vhd) { | |||||
return -1; | |||||
} | |||||
struct IZ_App* app = (struct IZ_App*) vhd->app; | struct IZ_App* app = (struct IZ_App*) vhd->app; | ||||
IZ_AppBindConnection(app, NULL); | IZ_AppBindConnection(app, NULL); | ||||
vhd->client_wsi = 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( | lws_sul_schedule( | ||||
vhd->context, | vhd->context, | ||||
0, | 0, | ||||
&vhd->sul, | &vhd->sul, | ||||
IZ_WSClientAttemptConnect, | 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) { | 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( | IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( | ||||
lws_get_vhost(wsi), | lws_get_vhost(wsi), | ||||
lws_get_protocol(wsi) | lws_get_protocol(wsi) | ||||
); | ); | ||||
struct IZ_App* app = (struct IZ_App*) vhd->app; | 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); | IZ_AppBindConnection(app, wsi); | ||||
net_state->status = IZ_NET_STATUS_CONNECTED; | |||||
net_state->retries = 0; | |||||
pss->tail = 0; | pss->tail = 0; | ||||
return 0; | return 0; | ||||
} | } | ||||
@@ -251,6 +276,8 @@ void IZ_WSClientOnReceive(struct lws* wsi, IZ_WSClientSessionData* pss, void* in | |||||
lwsl_user("dropping!\n"); | lwsl_user("dropping!\n"); | ||||
return; | return; | ||||
} | } | ||||
lws_ring_consume_single_tail(pss->ring, &pss->tail, 1); | |||||
lws_callback_on_writable(wsi); | lws_callback_on_writable(wsi); | ||||
if (!pss->flow_controlled && n < 3) { | if (!pss->flow_controlled && n < 3) { | ||||
@@ -1,25 +1,25 @@ | |||||
#include "IZ_net.h" | #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) { | void IZ_NetLoadConfig(IZ_NetState* state, const char* config_path) { | ||||
char buffer[32]; | char buffer[32]; | ||||
ini_gets("Network", "Username", IZ_NET_DEFAULT_STATE.config.username, buffer, 32, config_path); | ini_gets("Network", "Username", IZ_NET_DEFAULT_STATE.config.username, buffer, 32, config_path); | ||||
memcpy_s(state->config.username, 32, buffer, 32); | 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) { | 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; | 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; | 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[]) { | void IZ_NetOverrideConfig(IZ_NetState* state, u8 argc, const char* argv[]) { | ||||
const char* cmdline_buffer; | const char* cmdline_buffer; | ||||
if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, "-i"))) { | 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->ws.user_data = user_data; | ||||
state->callback = callback; | state->callback = callback; | ||||
state->retries = state->config.max_reconnect_retries; | |||||
u8 player_index; | u8 player_index; | ||||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | ||||
state->action[player_index] = 0; | state->action[player_index] = 0; | ||||
@@ -69,28 +78,47 @@ IZ_ProcedureResult IZ_NetInitialize( | |||||
} | } | ||||
void IZ_NetConnect(IZ_NetState* state, IZ_WSClientInitializeParams params) { | void IZ_NetConnect(IZ_NetState* state, IZ_WSClientInitializeParams params) { | ||||
if (IZ_NetIsConnected(state)) { | |||||
if (!state->callback) { | |||||
return; | 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; | return; | ||||
} | } | ||||
state->retries = 0; | |||||
state->params = params; | state->params = params; | ||||
state->client_thread = SDL_CreateThread(state->callback, "networking", state->ws.user_data); | state->client_thread = SDL_CreateThread(state->callback, "networking", state->ws.user_data); | ||||
SDL_DetachThread(state->client_thread); | SDL_DetachThread(state->client_thread); | ||||
} | } | ||||
void IZ_NetDisconnect(IZ_NetState* state) { | void IZ_NetDisconnect(IZ_NetState* state) { | ||||
if (!IZ_NetIsConnected(state)) { | |||||
if (state->status == IZ_NET_STATUS_PRISTINE) { | |||||
return; | 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); | IZ_WSClientCancelService(&state->ws); | ||||
} | } | ||||
void IZ_NetSendBinaryMessage(IZ_NetState* state, void* in, size_t len) { | void IZ_NetSendBinaryMessage(IZ_NetState* state, void* in, size_t len) { | ||||
if (!IZ_NetIsConnected(state)) { | |||||
if (state->status != IZ_NET_STATUS_CONNECTED) { | |||||
return; | 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) { | void IZ_NetSendTextMessage(IZ_NetState* state, char* in, size_t len) { | ||||
if (!IZ_NetIsConnected(state)) { | |||||
if (state->status != IZ_NET_STATUS_CONNECTED) { | |||||
return; | return; | ||||
} | } | ||||
@@ -9,8 +9,17 @@ | |||||
#include "core/IZ_websocket.h" | #include "core/IZ_websocket.h" | ||||
#include "svc/IZ_wsclient.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 { | typedef struct { | ||||
u16 interval; | |||||
u16 packet_interval_ms; | |||||
u8 max_reconnect_retries; | |||||
u8 reconnect_interval_secs; | |||||
char username[32]; | char username[32]; | ||||
} IZ_NetConfig; | } IZ_NetConfig; | ||||
@@ -21,13 +30,17 @@ typedef struct { | |||||
IZ_WSClientInitializeParams params; | IZ_WSClientInitializeParams params; | ||||
void* callback; | void* callback; | ||||
IZ_Action action[IZ_PLAYERS]; | IZ_Action action[IZ_PLAYERS]; | ||||
u8 retries; | |||||
IZ_NetStatus status; | |||||
// TODO add message queue | // TODO add message queue | ||||
} IZ_NetState; | } IZ_NetState; | ||||
static IZ_NetState IZ_NET_DEFAULT_STATE = { | static IZ_NetState IZ_NET_DEFAULT_STATE = { | ||||
.client_thread = NULL, | .client_thread = NULL, | ||||
.config = { | .config = { | ||||
.interval = 200, | |||||
.packet_interval_ms = 200, | |||||
.max_reconnect_retries = 3, | |||||
.reconnect_interval_secs = 3, | |||||
.username = "Player", | .username = "Player", | ||||
}, | }, | ||||
.ws = { | .ws = { | ||||
@@ -43,6 +56,8 @@ static IZ_NetState IZ_NET_DEFAULT_STATE = { | |||||
}, | }, | ||||
.callback = NULL, | .callback = NULL, | ||||
.action = {}, | .action = {}, | ||||
.retries = 3, | |||||
.status = IZ_NET_STATUS_PRISTINE, | |||||
}; | }; | ||||
IZ_ProcedureResult IZ_NetInitialize(IZ_NetState*, void*, void*, const char*, u8, const char**); | IZ_ProcedureResult IZ_NetInitialize(IZ_NetState*, void*, void*, const char*, u8, const char**); | ||||
@@ -14,8 +14,7 @@ IZ_ProcedureResult IZ_WSClientCallback( | |||||
IZ_WSClientProtocolTeardown(wsi); | IZ_WSClientProtocolTeardown(wsi); | ||||
break; | break; | ||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: | case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: | ||||
IZ_WSClientConnectionError(wsi, in); | |||||
break; | |||||
return IZ_WSClientConnectionError(wsi, in); | |||||
case LWS_CALLBACK_CLIENT_ESTABLISHED: | case LWS_CALLBACK_CLIENT_ESTABLISHED: | ||||
return IZ_WSClientOnOpen(wsi, user); | return IZ_WSClientOnOpen(wsi, user); | ||||
case LWS_CALLBACK_CLIENT_CLOSED: | case LWS_CALLBACK_CLIENT_CLOSED: | ||||
@@ -47,7 +47,7 @@ IZ_ProcedureResult IZ_WSClientProtocolInitialize(struct lws*, void*); | |||||
void IZ_WSClientProtocolTeardown(struct lws*); | 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*); | 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) { | void IZ_VideoUpdateForDebugNet(IZ_VideoState* video_state, IZ_NetState* net_state) { | ||||
if (!net_state->ws.connection) { | |||||
return; | |||||
} | |||||
const u8 size = 4; | 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) { | SDL_RenderFillRectF(video_state->renderer, &(SDL_FRect) { | ||||
0, | 0, | ||||
(f32) (video_state->config.height - size), | (f32) (video_state->config.height - size), | ||||
@@ -66,6 +76,10 @@ void IZ_VideoUpdateForDebugNet(IZ_VideoState* video_state, IZ_NetState* net_stat | |||||
size, | size, | ||||
}); | }); | ||||
if (!net_state->ws.connection) { | |||||
return; | |||||
} | |||||
u8 column; | u8 column; | ||||
u8 row; | u8 row; | ||||