From a345474f03551f82d3aca5bf1ce9e0d0cf532bb2 Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Thu, 16 Jun 2022 14:53:41 +0800 Subject: [PATCH] Implement sending events Implement the sending of input events to the websocket server. --- CMakeLists.txt | 2 +- src/packages/game/IZ_app.c | 101 ++++++---------- src/packages/game/IZ_app.h | 4 +- src/packages/game/net/IZ_net.c | 134 ++++++++++++++++++++++ src/packages/game/net/IZ_net.h | 61 ++++++++++ src/packages/game/net/core/IZ_websocket.c | 2 + src/packages/game/net/svc/IZ_wsclient.c | 28 ++--- src/packages/game/net/svc/IZ_wsclient.h | 17 +-- src/packages/server/net/IZ_net.c | 5 +- src/packages/server/net/IZ_net.h | 8 +- src/packages/server/net/svc/IZ_wsserver.h | 2 - 11 files changed, 263 insertions(+), 101 deletions(-) create mode 100644 src/packages/game/net/IZ_net.c create mode 100644 src/packages/game/net/IZ_net.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 86a9f7d..52800b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,7 @@ add_executable( src/packages/game/util/IZ_midi.h src/packages/game/net/core/IZ_websocket.h src/packages/game/net/core/IZ_websocket.c -) + src/packages/game/net/IZ_net.c src/packages/game/net/IZ_net.h) target_link_libraries( game diff --git a/src/packages/game/IZ_app.c b/src/packages/game/IZ_app.c index 070d72e..4eda132 100644 --- a/src/packages/game/IZ_app.c +++ b/src/packages/game/IZ_app.c @@ -1,77 +1,27 @@ #include "IZ_app.h" -IZ_ProcedureResult IZ_AppWSClientInitialize(IZ_App* app) { - if (IZ_WSClientInitialize(&app->client)) { - printf("error\n"); - return -1; - } - return 0; -} - IZ_ProcedureResult IZ_AppConnect(void* app_raw) { IZ_App* app = app_raw; - if (IZ_AppWSClientInitialize(app)) { + if (IZ_WSClientInitialize(&app->net_state.ws, app->net_state.params)) { return -1; } i32 result = 0; while (true) { - if (IZ_WSClientHandle(&app->client)) { + if (IZ_WSClientHandle(&app->net_state.ws)) { result = -1; break; } - if (app->client.ws.interrupted) { + if (app->net_state.ws.interrupted) { break; } } - IZ_WSClientTeardown(&app->client); + IZ_WSClientTeardown(&app->net_state.ws); return result; } -void IZ_AppHandleNetworkingOutboundEvents(IZ_App* app) { - struct lws* wsi = app->client.ws.connection; - printf("%p\n", wsi); - if (!wsi) { - return; - } - IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( - lws_get_vhost(wsi), - lws_get_protocol(wsi) - ); - - IZ_WebsocketMessage amsg; - i32 result; - result = IZ_WebsocketCreateTextMessage(wsi, &amsg, "hello", 5); - - if (result < 0) { - return; - } - - lws_ring_insert(vhd->ring, &amsg, 1); - lws_callback_on_writable(wsi); -} - -void IZ_AppEstablishConnection(IZ_App* app, IZ_WSClientInitializeParams params) { - if (app->client.ws.context) { - return; - } - - app->client.params = params; - app->client.userdata = app; - app->client_thread = SDL_CreateThread(IZ_AppConnect, "networking", app); - SDL_DetachThread(app->client_thread); -} - -void IZ_AppCloseConnection(IZ_App* app) { - if (!app->client.ws.context) { - return; - } - app->client.ws.interrupted = true; - IZ_WSClientCancelService(&app->client); -} - IZ_ProcedureResult IZ_AppInitialize(IZ_App* app, u8 argc, const char* argv[]) { memset(app, 0, sizeof(IZ_App)); u32 flags = ( @@ -102,6 +52,10 @@ IZ_ProcedureResult IZ_AppInitialize(IZ_App* app, u8 argc, const char* argv[]) { return IZ_APP_RUN_INPUT_INIT_ERROR; } + if (IZ_NetInitialize(&app->net_state, app, IZ_AppConnect, config_path, argc, argv)) { + return IZ_APP_RUN_NETWORKING_ERROR; + } + IZ_PoolInitialize(&app->pool, POOL_MAX_SIZE); // TODO put into its timer module @@ -110,7 +64,7 @@ IZ_ProcedureResult IZ_AppInitialize(IZ_App* app, u8 argc, const char* argv[]) { } void IZ_AppTeardown(IZ_App* app) { - IZ_AppCloseConnection(app); + IZ_NetDisconnect(&app->net_state); IZ_PoolTeardown(&app->pool); IZ_InputTeardown(&app->input_state); IZ_VideoTeardown(&app->video_state); @@ -126,15 +80,18 @@ IZ_ProcedureResult IZ_AppHandleSDLEvents(IZ_App* app) { if (e.type == SDL_KEYDOWN) { if (e.key.keysym.sym == SDLK_PAGEUP) { - IZ_AppEstablishConnection(app, (IZ_WSClientInitializeParams){ - .address = "127.0.0.1", - .path = "/", - .port = 42069, - }); + IZ_NetConnect( + &app->net_state, + (IZ_WSClientInitializeParams) { + .host = "127.0.0.1", + .path = "/", + .port = 42069, + } + ); } else if (e.key.keysym.sym == SDLK_PAGEDOWN) { - IZ_AppCloseConnection(app); + IZ_NetDisconnect(&app->net_state); } else if (e.key.keysym.sym == SDLK_INSERT) { - IZ_AppHandleNetworkingOutboundEvents(app); + IZ_NetSendTextMessage(&app->net_state, "hello", 5); } } @@ -178,7 +135,7 @@ void IZ_AppHandleNetworkingInboundBinaryEvents(IZ_App* app, void* binary_raw, si size_t i; printf("%llu: Binary", app->ticks); for (i = 0; i < len; i += 1) { - printf("%c%x", i == 0 ? '(' : ' ', binary[i]); + printf("%c%02x", i == 0 ? '(' : ' ', binary[i]); } printf(")\n"); } @@ -213,6 +170,18 @@ IZ_ProcedureResult IZ_AppRun(IZ_App* app, u8 argc, const char* argv[]) { break; } + // TODO update this + u8 player_index; + for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { + if (app->net_state.action[player_index] != app->input_state.action[player_index]) { + IZ_NetSendBinaryMessage( + &app->net_state, + &(app->input_state.action[player_index]), + sizeof app->input_state.action[player_index] + ); + } + app->net_state.action[player_index] = app->input_state.action[player_index]; + } IZ_VideoUpdate(&app->video_state, app->ticks, &app->input_state); } @@ -311,7 +280,7 @@ void IZ_WSClientConnectionError(struct lws* wsi, void* in) { lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); IZ_App* app = (IZ_App*) vhd->app; - app->client.ws.connection = NULL; + app->net_state.ws.connection = NULL; vhd->client_wsi = NULL; lws_sul_schedule( vhd->context, @@ -329,7 +298,7 @@ IZ_ProcedureResult IZ_WSClientOnOpen(struct lws* wsi, IZ_WSClientSessionData* ps ); IZ_App* app = (IZ_App*) vhd->app; - app->client.ws.connection = wsi; + app->net_state.ws.connection = wsi; pss->ring = lws_ring_create(sizeof(IZ_WebsocketMessage), RING_COUNT,IZ_WebsocketDestroyMessage); if (!pss->ring) { return -1; @@ -345,7 +314,7 @@ void IZ_WSClientOnClose(struct lws* wsi) { ); IZ_App* app = (IZ_App*) vhd->app; - app->client.ws.connection = NULL; + app->net_state.ws.connection = NULL; vhd->client_wsi = NULL; lws_sul_schedule( vhd->context, diff --git a/src/packages/game/IZ_app.h b/src/packages/game/IZ_app.h index c95c198..558fa94 100644 --- a/src/packages/game/IZ_app.h +++ b/src/packages/game/IZ_app.h @@ -7,6 +7,7 @@ #include "output/IZ_video.h" #include "memory/IZ_pool.h" #include "net/svc/IZ_wsclient.h" +#include "net/IZ_net.h" typedef enum { IZ_APP_RUN_RESULT_OK, @@ -22,9 +23,8 @@ typedef struct { IZ_VideoState video_state; IZ_Pool pool; - IZ_WSClientState client; - SDL_Thread* client_thread; u64 ticks; + IZ_NetState net_state; } IZ_App; typedef struct { diff --git a/src/packages/game/net/IZ_net.c b/src/packages/game/net/IZ_net.c new file mode 100644 index 0000000..ea7e546 --- /dev/null +++ b/src/packages/game/net/IZ_net.c @@ -0,0 +1,134 @@ +#include "IZ_net.h" + +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); +} + +IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetState* state, const char* config_path) { + if (!ini_puts("Network", "Username", state->config.username, config_path)) { + return -1; + } + + if (!ini_putl("Network", "Interval", state->config.interval, config_path)) { + return -1; + } + + return 0; +} + +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); + } +} + +IZ_ProcedureResult IZ_NetInitialize( + IZ_NetState* state, + void* user_data, + void* callback, + const char* config_path, + u8 argc, + const char* argv[] +) { + memcpy_s(state, sizeof(IZ_NetState), &IZ_NET_DEFAULT_STATE, sizeof(IZ_NetState)); + IZ_NetLoadConfig(state, config_path); + if (IZ_NetSaveConfig(state, config_path) < 0) { + return -1; + } + IZ_NetOverrideConfig(state, argc, argv); + if (!user_data) { + return -2; + } + state->ws.user_data = user_data; + state->callback = callback; + u8 player_index; + for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { + state->action[player_index] = 0; + } + + return 0; +} + +void IZ_NetConnect(IZ_NetState* state, IZ_WSClientInitializeParams params) { + if (IZ_NetIsConnected(state)) { + return; + } + + if (!state->callback) { + return; + } + + 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)) { + return; + } + IZ_WSClientCancelService(&state->ws); +} + +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_NetSendBinaryMessage(IZ_NetState* state, void* in, size_t len) { + if (!IZ_NetIsConnected(state)) { + return; + } + + IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( + lws_get_vhost(state->ws.connection), + lws_get_protocol(state->ws.connection) + ); + + IZ_WebsocketMessage amsg; + i32 result; + result = IZ_WebsocketCreateBinaryMessage(state->ws.connection, &amsg, in, len); + + if (result < 0) { + return; + } + + lws_ring_insert(vhd->ring, &amsg, 1); + lws_callback_on_writable(state->ws.connection); +} + +void IZ_NetSendTextMessage(IZ_NetState* state, char* in, size_t len) { + if (!IZ_NetIsConnected(state)) { + return; + } + + IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( + lws_get_vhost(state->ws.connection), + lws_get_protocol(state->ws.connection) + ); + + IZ_WebsocketMessage amsg; + i32 result; + result = IZ_WebsocketCreateTextMessage(state->ws.connection, &amsg, in, len); + + if (result < 0) { + return; + } + + lws_ring_insert(vhd->ring, &amsg, 1); + lws_callback_on_writable(state->ws.connection); +} diff --git a/src/packages/game/net/IZ_net.h b/src/packages/game/net/IZ_net.h new file mode 100644 index 0000000..84e3138 --- /dev/null +++ b/src/packages/game/net/IZ_net.h @@ -0,0 +1,61 @@ +#ifndef IZ_NET_H +#define IZ_NET_H + +#include +#include +#include "../IZ_common.h" +#include "../IZ_config.h" +#include "../input/IZ_action.h" +#include "core/IZ_websocket.h" +#include "svc/IZ_wsclient.h" + +typedef struct { + u16 interval; + char username[32]; +} IZ_NetConfig; + +typedef struct { + SDL_Thread* client_thread; + IZ_NetConfig config; + IZ_Websocket ws; + IZ_WSClientInitializeParams params; + void* callback; + IZ_Action action[IZ_PLAYERS]; +} IZ_NetState; + +static IZ_NetState IZ_NET_DEFAULT_STATE = { + .client_thread = NULL, + .config = { + .interval = 200, + .username = "Player", + }, + .ws = { + .interrupted = false, + .context = NULL, + .connection = NULL, + .user_data = NULL, + }, + .params = { + .port = 42069, + .path = "/", + .host = "localhost", + }, + .callback = NULL, + .action = {}, +}; + +IZ_ProcedureResult IZ_NetInitialize(IZ_NetState*, void*, void*, const char*, u8, const char**); + +void IZ_NetConnect(IZ_NetState*, IZ_WSClientInitializeParams); + +void IZ_NetDisconnect(IZ_NetState*); + +IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetState*, const char*); + +void IZ_NetSendBinaryMessage(IZ_NetState*, void*, size_t); + +void IZ_NetSendTextMessage(IZ_NetState*, char*, size_t); + +bool IZ_NetIsConnected(IZ_NetState*); + +#endif diff --git a/src/packages/game/net/core/IZ_websocket.c b/src/packages/game/net/core/IZ_websocket.c index cecc301..f37d928 100644 --- a/src/packages/game/net/core/IZ_websocket.c +++ b/src/packages/game/net/core/IZ_websocket.c @@ -2,6 +2,7 @@ void IZ_WebsocketInitialize(IZ_Websocket* ws) { ws->context = NULL; + ws->connection = NULL; ws->interrupted = false; } @@ -11,6 +12,7 @@ IZ_ProcedureResult IZ_WebsocketHandle(IZ_Websocket* ws) { void IZ_WebsocketCancelService(IZ_Websocket* ws) { ws->interrupted = true; + ws->connection = NULL; lws_cancel_service(ws->context); } diff --git a/src/packages/game/net/svc/IZ_wsclient.c b/src/packages/game/net/svc/IZ_wsclient.c index 99ff27a..913f915 100644 --- a/src/packages/game/net/svc/IZ_wsclient.c +++ b/src/packages/game/net/svc/IZ_wsclient.c @@ -33,7 +33,7 @@ IZ_ProcedureResult IZ_WSClientCallback( return lws_callback_http_dummy(wsi, reason, user, in, len); } -IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state) { +IZ_ProcedureResult IZ_WSClientInitialize(IZ_Websocket* state, IZ_WSClientInitializeParams params) { struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = CONTEXT_PORT_NO_LISTEN; @@ -58,7 +58,7 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state) { "address", /* pvo name */ "localhost" /* pvo value */ }; - pvo_address.value = state->params.address; + pvo_address.value = params.host; static struct lws_protocol_vhost_options pvo_path = { &pvo_address, @@ -66,7 +66,7 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state) { "path", /* pvo name */ "/" /* pvo value */ }; - pvo_path.value = state->params.path; + pvo_path.value = params.path; static struct lws_protocol_vhost_options pvo_port = { &pvo_path, @@ -74,7 +74,7 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state) { "port", /* pvo name */ NULL /* pvo value */ }; - pvo_port.value = (void*) &state->params.port; + pvo_port.value = (void*) ¶ms.port; static struct lws_protocol_vhost_options pvo_app = { &pvo_port, @@ -82,7 +82,7 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state) { "app", NULL, }; - pvo_app.value = state->userdata; + pvo_app.value = state->user_data; static const struct lws_protocol_vhost_options pvo = { NULL, /* "next" pvo linked-list */ @@ -93,22 +93,22 @@ IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState* state) { info.pvo = &pvo; info.fd_limit_per_thread = 1 + 1 + 1; - IZ_WebsocketInitialize(&state->ws); - state->ws.context = lws_create_context(&info); - if (!state->ws.context) { + IZ_WebsocketInitialize(state); + state->context = lws_create_context(&info); + if (!state->context) { return -1; } return 0; } -IZ_ProcedureResult IZ_WSClientHandle(IZ_WSClientState* state) { - return IZ_WebsocketHandle(&state->ws); +IZ_ProcedureResult IZ_WSClientHandle(IZ_Websocket* state) { + return IZ_WebsocketHandle(state); } -void IZ_WSClientTeardown(IZ_WSClientState* state) { - IZ_WebsocketTeardown(&state->ws); +void IZ_WSClientTeardown(IZ_Websocket* state) { + IZ_WebsocketTeardown(state); } -void IZ_WSClientCancelService(IZ_WSClientState* state) { - IZ_WebsocketCancelService(&state->ws); +void IZ_WSClientCancelService(IZ_Websocket* state) { + IZ_WebsocketCancelService(state); } diff --git a/src/packages/game/net/svc/IZ_wsclient.h b/src/packages/game/net/svc/IZ_wsclient.h index 9064aaf..5fa79d7 100644 --- a/src/packages/game/net/svc/IZ_wsclient.h +++ b/src/packages/game/net/svc/IZ_wsclient.h @@ -1,7 +1,6 @@ #ifndef IZ_WSCLIENT_H #define IZ_WSCLIENT_H -#include "libwebsockets.h" #include "../../IZ_common.h" #include "../core/IZ_websocket.h" @@ -31,24 +30,18 @@ typedef struct { } IZ_WSClientSessionData; typedef struct { - const char* address; + const char* host; const char* path; u16 port; } IZ_WSClientInitializeParams; -typedef struct { - IZ_Websocket ws; - void* userdata; - IZ_WSClientInitializeParams params; -} IZ_WSClientState; - -IZ_ProcedureResult IZ_WSClientInitialize(IZ_WSClientState*); +IZ_ProcedureResult IZ_WSClientInitialize(IZ_Websocket*, IZ_WSClientInitializeParams); -IZ_ProcedureResult IZ_WSClientHandle(IZ_WSClientState*); +IZ_ProcedureResult IZ_WSClientHandle(IZ_Websocket*); -void IZ_WSClientTeardown(IZ_WSClientState*); +void IZ_WSClientTeardown(IZ_Websocket*); -void IZ_WSClientCancelService(IZ_WSClientState*); +void IZ_WSClientCancelService(IZ_Websocket*); IZ_ProcedureResult IZ_WSClientProtocolInitialize(struct lws*, void*); diff --git a/src/packages/server/net/IZ_net.c b/src/packages/server/net/IZ_net.c index 5909f90..1f26da8 100644 --- a/src/packages/server/net/IZ_net.c +++ b/src/packages/server/net/IZ_net.c @@ -3,10 +3,10 @@ void IZ_NetLoadConfig(IZ_NetState* state, const char* config_path) { char buffer[128]; - ini_gets("Network", "Name", IZ_APP_NAME, buffer, 128, config_path); + ini_gets("Network", "Name", IZ_NET_DEFAULT_STATE.config.name, buffer, 128, config_path); memcpy_s(state->config.name, 64, buffer, 64); - ini_gets("Network", "Motd", "", buffer, 128, config_path); + ini_gets("Network", "Motd", IZ_NET_DEFAULT_STATE.config.motd, buffer, 128, config_path); memcpy_s(state->config.motd, 128, buffer, 128); state->config.port = ini_getl("Network", "Port", IZ_NET_DEFAULT_STATE.config.port, config_path); @@ -62,3 +62,4 @@ IZ_ProcedureResult IZ_NetInitialize( state->ws.user_data = user_data; return 0; } + diff --git a/src/packages/server/net/IZ_net.h b/src/packages/server/net/IZ_net.h index 0de3e48..76bcd4a 100644 --- a/src/packages/server/net/IZ_net.h +++ b/src/packages/server/net/IZ_net.h @@ -6,6 +6,9 @@ #include "../IZ_config.h" #include "core/IZ_websocket.h" +#define IZ_DEFAULT_MOTD "" + + typedef struct { u16 port; char name[64]; @@ -15,13 +18,14 @@ typedef struct { typedef struct { IZ_NetConfig config; IZ_Websocket ws; + // TODO add message queue } IZ_NetState; static IZ_NetState IZ_NET_DEFAULT_STATE = { .config = { .port = 42069, - .name = IZ_APP_NAME, - .motd = "", + .name = IZ_APP_NAME " Server", + .motd = IZ_DEFAULT_MOTD, }, .ws = { .interrupted = false, diff --git a/src/packages/server/net/svc/IZ_wsserver.h b/src/packages/server/net/svc/IZ_wsserver.h index 4da8fd5..312abbf 100644 --- a/src/packages/server/net/svc/IZ_wsserver.h +++ b/src/packages/server/net/svc/IZ_wsserver.h @@ -2,10 +2,8 @@ #define IZ_WSSERVER_H #include -#include "libwebsockets.h" #include #include "../../IZ_common.h" -#include "../../IZ_config.h" #include "../core/IZ_websocket.h" #ifndef S_ISDIR