From fec7105c0346100500a3828d328e9fc006c468d8 Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Sat, 18 Jun 2022 07:25:14 +0800 Subject: [PATCH] Refactor project Split the network events to the net directory to keep the app code implementation clean. --- CMakeLists.txt | 2 +- src/packages/game/IZ_app.c | 388 +++++++++------------------- src/packages/game/IZ_app.h | 47 +--- src/packages/game/net/IZ_app.c | 284 ++++++++++++++++++++ src/packages/game/net/IZ_app.h | 60 +++++ src/packages/game/output/IZ_video.c | 76 +----- src/packages/game/output/IZ_video.h | 7 +- 7 files changed, 484 insertions(+), 380 deletions(-) create mode 100644 src/packages/game/net/IZ_app.c create mode 100644 src/packages/game/net/IZ_app.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5731770..e84548d 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) + src/packages/game/net/IZ_net.c src/packages/game/net/IZ_net.h src/packages/game/net/IZ_app.c src/packages/game/net/IZ_app.h) target_link_libraries( game diff --git a/src/packages/game/IZ_app.c b/src/packages/game/IZ_app.c index 38c67d0..4d7e06a 100644 --- a/src/packages/game/IZ_app.c +++ b/src/packages/game/IZ_app.c @@ -1,29 +1,23 @@ #include "IZ_app.h" -IZ_ProcedureResult IZ_AppConnect(void* app_raw) { - IZ_App* app = app_raw; - if (IZ_WSClientInitialize(&app->net_state.ws, app->net_state.params)) { - return -1; - } +u64 IZ_AppGetTicks(struct IZ_App* app) { + return app->ticks; +} - i32 result = 0; - while (true) { - if (IZ_WSClientHandle(&app->net_state.ws)) { - result = -1; - break; - } +void IZ_AppBindConnection(struct IZ_App* app, struct lws* wsi) { + app->net_state.ws.connection = wsi; +} - if (app->net_state.ws.interrupted) { - break; - } - } +IZ_NetState* IZ_AppGetNetState(struct IZ_App* app) { + return &app->net_state; +} - IZ_WSClientTeardown(&app->net_state.ws); - return result; +IZ_InputState* IZ_AppGetInputState(struct IZ_App* app) { + return &app->input_state; } -IZ_ProcedureResult IZ_AppInitialize(IZ_App* app, u8 argc, const char* argv[]) { - memset(app, 0, sizeof(IZ_App)); +IZ_ProcedureResult IZ_AppInitialize(struct IZ_App* app, u8 argc, const char* argv[]) { + memset(app, 0, sizeof(struct IZ_App)); u32 flags = ( SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER @@ -44,7 +38,7 @@ IZ_ProcedureResult IZ_AppInitialize(IZ_App* app, u8 argc, const char* argv[]) { IZ_ConfigGetDefaultPath(config_path, 128); } - if (IZ_VideoInitialize(&app->video_state, config_path, argc, argv)) { + if (IZ_VideoInitialize(&app->video_state, app, config_path, argc, argv)) { return IZ_APP_RUN_VIDEO_INIT_ERROR; } @@ -63,7 +57,7 @@ IZ_ProcedureResult IZ_AppInitialize(IZ_App* app, u8 argc, const char* argv[]) { return IZ_APP_RUN_RESULT_OK; } -void IZ_AppTeardown(IZ_App* app) { +void IZ_AppTeardown(struct IZ_App* app) { IZ_NetDisconnect(&app->net_state); IZ_PoolTeardown(&app->pool); IZ_InputTeardown(&app->input_state); @@ -71,7 +65,7 @@ void IZ_AppTeardown(IZ_App* app) { SDL_Quit(); } -IZ_ProcedureResult IZ_AppHandleSDLEvents(IZ_App* app) { +IZ_ProcedureResult IZ_AppHandleSDLEvents(struct IZ_App* app) { SDL_Event e; while (SDL_PollEvent(&e) != 0) { if (e.type == SDL_QUIT) { @@ -100,7 +94,7 @@ IZ_ProcedureResult IZ_AppHandleSDLEvents(IZ_App* app) { return 0; } -void IZ_AppHandlePortMIDIEvents(IZ_App* app) { +void IZ_AppHandlePortMIDIEvents(struct IZ_App* app) { u8 player_index; i32* midi_events_count; u32 midi_event_index; @@ -130,21 +124,7 @@ void IZ_AppHandlePortMIDIEvents(IZ_App* app) { } } -void IZ_AppHandleNetworkingInboundBinaryEvents(IZ_App* app, void* binary_raw, size_t len) { - u8* binary = binary_raw; - size_t i; - printf("%llu: Binary", app->ticks); - for (i = 0; i < len; i += 1) { - printf("%c%02x", i == 0 ? '(' : ' ', binary[i]); - } - printf(")\n"); -} - -void IZ_AppHandleNetworkingInboundTextEvents(IZ_App* app, const char* text, size_t len) { - printf("%llu: String(%s)\n", app->ticks, text); -} - -IZ_ProcedureResult IZ_AppHandleInputEvents(IZ_App* app) { +IZ_ProcedureResult IZ_AppHandleInputEvents(struct IZ_App* app) { i32 sdl_events_result = IZ_AppHandleSDLEvents(app); if (sdl_events_result) { return sdl_events_result; @@ -154,252 +134,140 @@ IZ_ProcedureResult IZ_AppHandleInputEvents(IZ_App* app) { return 0; } -IZ_ProcedureResult IZ_AppRun(IZ_App* app, u8 argc, const char* argv[]) { - IZ_ProcedureResult init_result = IZ_AppInitialize(app, argc, argv); - if (init_result) { - return init_result; - } - - while (true) { - app->ticks = SDL_GetTicks64(); - - // TODO do audio processing - // TODO do networking - - if (IZ_AppHandleInputEvents(app)) { - break; +void IZ_VideoUpdateForDebugTicks(IZ_VideoState* video_state, uint64_t ticks) { + SDL_SetRenderDrawColor(video_state->renderer, 0x00, 0xff, 0xff, 0xff); + u64 the_ticks = ticks; + u8 column; + u8 row; + const u8 size = 4; + + u8 i; + for (i = 0; i < 64; i += 1) { + column = i % 32; + row = i / 32; + + if (the_ticks & 0x1) { + SDL_RenderFillRectF(video_state->renderer, &(SDL_FRect) { + (f32) (video_state->config.width - ((column + 1) * size)), + (f32) (video_state->config.height - ((row + 1) * size)), + size, + size + }); } + the_ticks >>= 1; + } +} - // 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] - ); +void IZ_VideoUpdateForDebugInput(IZ_VideoState* video_state, IZ_InputState* input_state) { + SDL_SetRenderDrawColor(video_state->renderer, 0xff, 0xff, 0x00, 0xff); + const u8 size = 4; + + u8 column; + u8 row; + + u8 p; + u8 i; + for (p = 0; p < IZ_PLAYERS; p += 1) { + IZ_Action the_action = input_state->action[p]; + for (i = 0; i < CONTROLS; i += 1) { + column = (i % 4) + (p * 4); + row = i / 4; + + if (the_action & 0x1) { + SDL_RenderFillRectF(video_state->renderer, &(SDL_FRect) { + (f32) (column * size), + (f32) (row * size), + size, + size + }); } - app->net_state.action[player_index] = app->input_state.action[player_index]; + the_action >>= 1; } - - IZ_VideoUpdate(&app->video_state, app->ticks, &app->input_state); } - - IZ_AppTeardown(app); - return IZ_APP_RUN_RESULT_OK; } -void IZ_WSClientAttemptConnect(struct lws_sorted_usec_list *sul) { - IZ_WSClientVHostData* vhd = lws_container_of(sul, IZ_WSClientVHostData, sul); - - vhd->i.context = vhd->context; - vhd->i.port = *vhd->port; - vhd->i.address = vhd->address; - vhd->i.path = vhd->path; - vhd->i.host = vhd->i.address; - vhd->i.origin = vhd->i.address; - vhd->i.ssl_connection = 0; - - vhd->i.protocol = NETWORK_PROTOCOL; - vhd->i.pwsi = &vhd->client_wsi; - - if (lws_client_connect_via_info(&vhd->i)) { +void IZ_VideoUpdateForDebugNet(IZ_VideoState* video_state, IZ_NetState* net_state) { + if (!net_state->ws.connection) { return; } + const u8 size = 4; - lws_sul_schedule( - vhd->context, - 0, - &vhd->sul, - IZ_WSClientAttemptConnect, - 10 * LWS_US_PER_SEC - ); -} - -IZ_ProcedureResult IZ_WSClientProtocolInitialize(struct lws* wsi, void* in) { - const struct lws_protocols* protocols = lws_get_protocol(wsi); - struct lws_vhost* vhost = lws_get_vhost(wsi); - IZ_WSClientVHostData* vhd_instance = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get(vhost,protocols); - IZ_WSClientVHostData** vhd = &vhd_instance; - *vhd = lws_protocol_vh_priv_zalloc(vhost, protocols, sizeof(IZ_WSClientVHostData)); - (*vhd)->ring = lws_ring_create( - sizeof(IZ_WebsocketMessage), - RING_COUNT, - IZ_WebsocketDestroyMessage - ); - if (!(*vhd)->ring) { - return -1; - } - (*vhd)->context = lws_get_context(wsi); - (*vhd)->protocol = protocols; - (*vhd)->vhost = vhost; - (*vhd)->port = (u16*) lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "port" - )->value; - (*vhd)->address = lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "address" - )->value; - (*vhd)->path = lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "path" - )->value; - (*vhd)->app = lws_pvo_search( - (const struct lws_protocol_vhost_options *)in, - "app" - )->value; - IZ_WSClientAttemptConnect(&(*vhd)->sul); - return 0; -} - -void IZ_WSClientProtocolTeardown(struct lws* wsi) { - IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( - lws_get_vhost(wsi), - lws_get_protocol(wsi) - ); - - if (vhd->ring) { - lws_ring_destroy(vhd->ring); - } - - lws_sul_cancel(&vhd->sul); -} - -void 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) - ); - IZ_App* app = (IZ_App*) vhd->app; - app->net_state.ws.connection = NULL; - vhd->client_wsi = NULL; - lws_sul_schedule( - vhd->context, + SDL_SetRenderDrawColor(video_state->renderer, 0x00, 0xff, 0x00, 0xff); + SDL_RenderFillRectF(video_state->renderer, &(SDL_FRect) { 0, - &vhd->sul, - IZ_WSClientAttemptConnect, - LWS_US_PER_SEC - ); -} - -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; + (f32) (video_state->config.height - size), + size, + size, + }); + + u8 column; + u8 row; + + u8 p; + u8 i; + for (p = 0; p < IZ_PLAYERS; p += 1) { + IZ_Action the_action = net_state->action[p]; + for (i = 0; i < CONTROLS; i += 1) { + column = (i % 4) + (p * 4); + row = i / 4; + + if (the_action & 0x1) { + SDL_RenderFillRectF(video_state->renderer, &(SDL_FRect) { + (f32) (column * size), + (f32) ((row * size) + (video_state->config.height - (size * 5))), + size, + size + }); + } + the_action >>= 1; + } } - - IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( - lws_get_vhost(wsi), - lws_get_protocol(wsi) - ); - IZ_App* app = (IZ_App*) vhd->app; - app->net_state.ws.connection = wsi; - pss->tail = 0; - return 0; } -void IZ_WSClientOnClose(struct lws* wsi) { - IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( - lws_get_vhost(wsi), - lws_get_protocol(wsi) - ); - IZ_App* app = (IZ_App*) vhd->app; - app->net_state.ws.connection = NULL; - vhd->client_wsi = NULL; - lws_sul_schedule( - vhd->context, - 0, - &vhd->sul, - IZ_WSClientAttemptConnect, - LWS_US_PER_SEC - ); +void IZ_VideoUpdateForDebug(IZ_VideoState* video_state, u64 ticks, IZ_InputState* input_state, IZ_NetState* net_state) { + IZ_VideoUpdateForDebugTicks(video_state, ticks); + IZ_VideoUpdateForDebugInput(video_state, input_state); + IZ_VideoUpdateForDebugNet(video_state, net_state); } -IZ_ProcedureResult IZ_WSClientWritable(struct lws* wsi) { - IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( - lws_get_vhost(wsi), - lws_get_protocol(wsi) - ); - - const IZ_WebsocketMessage* pmsg = lws_ring_get_element(vhd->ring, &vhd->tail); - if (!pmsg) { - return 0; - } - - /* notice we allowed for LWS_PRE in the payload already */ - i32 m = lws_write( - wsi, - ((unsigned char*) pmsg->payload) + LWS_PRE, - pmsg->len, - pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT - ); - - if (m < (i32)pmsg->len) { - lwsl_err("ERROR %d writing to ws socket\n", m); - return -1; - } - - lws_ring_consume_single_tail(vhd->ring, &vhd->tail, 1); - - /* more to do for us? */ - if (lws_ring_get_element(vhd->ring, &vhd->tail)) { - /* come back as soon as we can write more */ - lws_callback_on_writable(wsi); +void IZ_VideoUpdate(IZ_VideoState* video_state) { + struct IZ_App* app = video_state->user_data; + if (app->ticks - video_state->last_update_at > 1000 / video_state->config.max_fps) { + // Update window + SDL_SetRenderDrawColor(video_state->renderer, 0x00, 0x00, 0x00, 0xff); + SDL_RenderClear(video_state->renderer); + for (u8 i = 0; i < MAX_ACTIVE_SPRITES; i += 1) { + if (!video_state->active_sprites[i]) { + continue; + } + // TODO draw sprites + } + IZ_VideoUpdateForDebug(video_state, app->ticks, &app->input_state, &app->net_state); + SDL_RenderPresent(video_state->renderer); + video_state->last_update_at = app->ticks; } - - return 0; } -void IZ_WSClientOnReceive(struct lws* wsi, IZ_WSClientSessionData* pss, void* in, size_t len) { - i32 n = (i32) lws_ring_get_count_free_elements(pss->ring); - if (!n) { - lwsl_user("dropping!\n"); - return; +IZ_ProcedureResult IZ_AppRun(struct IZ_App* app, u8 argc, const char* argv[]) { + IZ_ProcedureResult init_result = IZ_AppInitialize(app, argc, argv); + if (init_result) { + return init_result; } - lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, first %d, last %d, bin %d)\n", - (int)len, (int)lws_remaining_packet_payload(wsi), - lws_is_first_fragment(wsi), - lws_is_final_fragment(wsi), - lws_frame_is_binary(wsi)); - - // lwsl_hexdump_notice(in, len); - - IZ_WebsocketMessage amsg; - i32 result = ( - lws_frame_is_binary(wsi) - ? IZ_WebsocketCreateBinaryMessage(wsi, &amsg, in, len) - : IZ_WebsocketCreateTextMessage(wsi, &amsg, in, len) - ); - if (result < 0) { - lwsl_user("OOM: dropping\n"); - return; - } + while (true) { + app->ticks = SDL_GetTicks64(); - IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( - lws_get_vhost(wsi), - lws_get_protocol(wsi) - ); + // TODO do audio processing + // TODO do networking - IZ_App* app = (IZ_App*) vhd->app; - if (amsg.binary) { - IZ_AppHandleNetworkingInboundBinaryEvents(app, in, len); - } else { - IZ_AppHandleNetworkingInboundTextEvents(app, in, len); - } + if (IZ_AppHandleInputEvents(app)) { + break; + } - if (!lws_ring_insert(pss->ring, &amsg, 1)) { - IZ_WebsocketDestroyMessage(&amsg); - lwsl_user("dropping!\n"); - return; + IZ_AppHandleOutboundNetworking(app); + IZ_VideoUpdate(&app->video_state); } - lws_callback_on_writable(wsi); - if (!pss->flow_controlled && n < 3) { - pss->flow_controlled = true; - lws_rx_flow_control(wsi, 0); - } + IZ_AppTeardown(app); + return IZ_APP_RUN_RESULT_OK; } diff --git a/src/packages/game/IZ_app.h b/src/packages/game/IZ_app.h index 558fa94..4cae2dc 100644 --- a/src/packages/game/IZ_app.h +++ b/src/packages/game/IZ_app.h @@ -6,8 +6,7 @@ #include "input/IZ_input.h" #include "output/IZ_video.h" #include "memory/IZ_pool.h" -#include "net/svc/IZ_wsclient.h" -#include "net/IZ_net.h" +#include "net/IZ_app.h" typedef enum { IZ_APP_RUN_RESULT_OK, @@ -18,7 +17,7 @@ typedef enum { IZ_APP_RUN_NETWORKING_ERROR, } IZ_AppRunResult; -typedef struct { +typedef struct IZ_App { IZ_InputState input_state; IZ_VideoState video_state; IZ_Pool pool; @@ -27,46 +26,8 @@ typedef struct { IZ_NetState net_state; } IZ_App; -typedef struct { - u8 player_index: 3; - u8 player_state: 5; - u16 action_set; -} IZ_AppPlayerActionSyncMessage; +IZ_ProcedureResult IZ_AppRun(struct IZ_App*, u8, const char**); -typedef struct { - u8 player_index: 3; - f32 x; - f32 y; - f32 right; - f32 up; -} IZ_AppPlayerState; - -typedef enum { - IZ_MESSAGE_KIND_ACTION_SYNC = 0, - IZ_MESSAGE_KIND_STATE_SYNC = 1, -} IZ_MessageKind; - -typedef struct { - u8 message_kind; // player - u64 client_elapsed_time; // for synchronization -} IZ_AppMessageHeader; - -typedef struct { - u8 player_actions_count; - IZ_AppPlayerActionSyncMessage player_actions[]; -} IZ_AppPlayerActionSection; - -typedef struct { - IZ_AppMessageHeader header; - IZ_AppPlayerActionSection player_actions; -} IZ_AppActionSyncMessage; - -typedef struct { - IZ_AppMessageHeader header; - IZ_AppPlayerState player_state[IZ_PLAYERS]; - IZ_AppPlayerActionSection player_actions; -} IZ_AppStateSyncMessage; - -IZ_ProcedureResult IZ_AppRun(IZ_App*, u8, const char**); +IZ_ProcedureResult IZ_AppConnect(void*); #endif diff --git a/src/packages/game/net/IZ_app.c b/src/packages/game/net/IZ_app.c new file mode 100644 index 0000000..77ebe69 --- /dev/null +++ b/src/packages/game/net/IZ_app.c @@ -0,0 +1,284 @@ +#include "IZ_app.h" + +void IZ_AppHandleNetworkingInboundBinaryEvents(struct IZ_App* app, void* binary_raw, size_t len) { + u8* binary = binary_raw; + size_t i; + printf("%llu: Binary", IZ_AppGetTicks(app)); + for (i = 0; i < len; i += 1) { + printf("%c%02x", i == 0 ? '(' : ' ', binary[i]); + } + printf(")\n"); +} + +void IZ_AppHandleNetworkingInboundTextEvents(struct IZ_App* app, const char* text, size_t len) { + printf("%llu: String(%s)\n", IZ_AppGetTicks(app), text); +} + +void IZ_AppHandleOutboundNetworking(struct IZ_App* app) { + // TODO implement queueing of messages + IZ_NetState* net_state = IZ_AppGetNetState(app); + IZ_InputState* input_state = IZ_AppGetInputState(app); + + u8 player_index; + for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { + if (net_state->action[player_index] != input_state->action[player_index]) { + u8 player_actions_count = 1; + IZ_AppActionSyncMessage* msg = SDL_malloc( + sizeof(IZ_AppMessageHeader) + + sizeof(u8) + + (sizeof(IZ_AppPlayerActionSyncMessage) * player_actions_count) + ); + msg->header.client_elapsed_time = IZ_AppGetTicks(app); + msg->header.message_kind = IZ_MESSAGE_KIND_ACTION_SYNC; + msg->action.player_actions_count = player_actions_count; + msg->action.player[0].index = player_index; + msg->action.player[0].value = input_state->action[player_index]; + msg->action.player[0].state = 0; + IZ_NetSendBinaryMessage( + net_state, + msg, + sizeof(*msg) + ); + SDL_free(msg); + } + net_state->action[player_index] = input_state->action[player_index]; + } +} + +void IZ_WSClientAttemptConnect(struct lws_sorted_usec_list *sul) { + IZ_WSClientVHostData* vhd = lws_container_of(sul, IZ_WSClientVHostData, sul); + + vhd->i.context = vhd->context; + vhd->i.port = *vhd->port; + vhd->i.address = vhd->address; + vhd->i.path = vhd->path; + vhd->i.host = vhd->i.address; + vhd->i.origin = vhd->i.address; + vhd->i.ssl_connection = 0; + + vhd->i.protocol = NETWORK_PROTOCOL; + vhd->i.pwsi = &vhd->client_wsi; + + if (lws_client_connect_via_info(&vhd->i)) { + return; + } + + lws_sul_schedule( + vhd->context, + 0, + &vhd->sul, + IZ_WSClientAttemptConnect, + 10 * LWS_US_PER_SEC + ); +} + +IZ_ProcedureResult IZ_WSClientProtocolInitialize(struct lws* wsi, void* in) { + const struct lws_protocols* protocols = lws_get_protocol(wsi); + struct lws_vhost* vhost = lws_get_vhost(wsi); + IZ_WSClientVHostData* vhd_instance = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get(vhost,protocols); + IZ_WSClientVHostData** vhd = &vhd_instance; + *vhd = lws_protocol_vh_priv_zalloc(vhost, protocols, sizeof(IZ_WSClientVHostData)); + (*vhd)->ring = lws_ring_create( + sizeof(IZ_WebsocketMessage), + RING_COUNT, + IZ_WebsocketDestroyMessage + ); + if (!(*vhd)->ring) { + return -1; + } + (*vhd)->context = lws_get_context(wsi); + (*vhd)->protocol = protocols; + (*vhd)->vhost = vhost; + (*vhd)->port = (u16*) lws_pvo_search( + (const struct lws_protocol_vhost_options *)in, + "port" + )->value; + (*vhd)->address = lws_pvo_search( + (const struct lws_protocol_vhost_options *)in, + "address" + )->value; + (*vhd)->path = lws_pvo_search( + (const struct lws_protocol_vhost_options *)in, + "path" + )->value; + (*vhd)->app = lws_pvo_search( + (const struct lws_protocol_vhost_options *)in, + "app" + )->value; + IZ_WSClientAttemptConnect(&(*vhd)->sul); + return 0; +} + +void IZ_WSClientProtocolTeardown(struct lws* wsi) { + IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( + lws_get_vhost(wsi), + lws_get_protocol(wsi) + ); + + if (vhd->ring) { + lws_ring_destroy(vhd->ring); + } + + lws_sul_cancel(&vhd->sul); +} + +void 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) + ); + struct IZ_App* app = (struct IZ_App*) vhd->app; + IZ_AppBindConnection(app, NULL); + vhd->client_wsi = NULL; + lws_sul_schedule( + vhd->context, + 0, + &vhd->sul, + IZ_WSClientAttemptConnect, + LWS_US_PER_SEC + ); +} + +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_AppBindConnection(app, wsi); + pss->tail = 0; + return 0; +} + +void IZ_WSClientOnClose(struct lws* wsi) { + 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_AppBindConnection(app, NULL); + vhd->client_wsi = NULL; + lws_sul_schedule( + vhd->context, + 0, + &vhd->sul, + IZ_WSClientAttemptConnect, + LWS_US_PER_SEC + ); +} + +IZ_ProcedureResult IZ_WSClientWritable(struct lws* wsi) { + IZ_WSClientVHostData* vhd = (IZ_WSClientVHostData*) lws_protocol_vh_priv_get( + lws_get_vhost(wsi), + lws_get_protocol(wsi) + ); + + const IZ_WebsocketMessage* pmsg = lws_ring_get_element(vhd->ring, &vhd->tail); + if (!pmsg) { + return 0; + } + + /* notice we allowed for LWS_PRE in the payload already */ + i32 m = lws_write( + wsi, + ((unsigned char*) pmsg->payload) + LWS_PRE, + pmsg->len, + pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT + ); + + if (m < (i32)pmsg->len) { + lwsl_err("ERROR %d writing to ws socket\n", m); + return -1; + } + + lws_ring_consume_single_tail(vhd->ring, &vhd->tail, 1); + + /* more to do for us? */ + if (lws_ring_get_element(vhd->ring, &vhd->tail)) { + /* come back as soon as we can write more */ + lws_callback_on_writable(wsi); + } + + return 0; +} + +void IZ_WSClientOnReceive(struct lws* wsi, IZ_WSClientSessionData* pss, void* in, size_t len) { + i32 n = (i32) lws_ring_get_count_free_elements(pss->ring); + if (!n) { + lwsl_user("dropping!\n"); + return; + } + + lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, first %d, last %d, bin %d)\n", + (int)len, (int)lws_remaining_packet_payload(wsi), + lws_is_first_fragment(wsi), + lws_is_final_fragment(wsi), + lws_frame_is_binary(wsi)); + + // lwsl_hexdump_notice(in, len); + + IZ_WebsocketMessage amsg; + i32 result = ( + lws_frame_is_binary(wsi) + ? IZ_WebsocketCreateBinaryMessage(wsi, &amsg, in, len) + : IZ_WebsocketCreateTextMessage(wsi, &amsg, in, len) + ); + if (result < 0) { + lwsl_user("OOM: dropping\n"); + return; + } + + 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; + if (amsg.binary) { + IZ_AppHandleNetworkingInboundBinaryEvents(app, in, len); + } else { + IZ_AppHandleNetworkingInboundTextEvents(app, in, len); + } + + if (!lws_ring_insert(pss->ring, &amsg, 1)) { + IZ_WebsocketDestroyMessage(&amsg); + lwsl_user("dropping!\n"); + return; + } + lws_callback_on_writable(wsi); + + if (!pss->flow_controlled && n < 3) { + pss->flow_controlled = true; + lws_rx_flow_control(wsi, 0); + } +} + +IZ_ProcedureResult IZ_AppConnect(void* app_raw) { + struct IZ_App* app = app_raw; + IZ_NetState* net_state = IZ_AppGetNetState(app); + + if (IZ_WSClientInitialize(&net_state->ws, net_state->params)) { + return -1; + } + + i32 result = 0; + while (true) { + if (IZ_WSClientHandle(&net_state->ws)) { + result = -1; + break; + } + + if (net_state->ws.interrupted) { + break; + } + } + + IZ_WSClientTeardown(&net_state->ws); + return result; +} diff --git a/src/packages/game/net/IZ_app.h b/src/packages/game/net/IZ_app.h new file mode 100644 index 0000000..dedd8c5 --- /dev/null +++ b/src/packages/game/net/IZ_app.h @@ -0,0 +1,60 @@ +#ifndef IZ_NET_APP_H +#define IZ_NET_APP_H + +#include "../IZ_common.h" +#include "../input/IZ_input.h" +#include "IZ_net.h" + +typedef enum { + IZ_MESSAGE_KIND_ACTION_SYNC = 0, + IZ_MESSAGE_KIND_STATE_SYNC = 1, +} IZ_MessageKind; + +typedef struct { + u8 index: 3; + u8 state: 5; + u16 value; +} IZ_AppPlayerActionSyncMessage; + +typedef struct { + u8 player_index: 3; + f32 x; + f32 y; + f32 right; + f32 up; +} IZ_AppPlayerState; + +typedef struct { + u8 message_kind; // player + u64 client_elapsed_time; // for synchronization +} IZ_AppMessageHeader; + +typedef struct { + u8 player_actions_count; + IZ_AppPlayerActionSyncMessage player[]; +} IZ_AppPlayerActionSection; + +typedef struct { + IZ_AppMessageHeader header; + IZ_AppPlayerActionSection action; +} IZ_AppActionSyncMessage; + +typedef struct { + IZ_AppMessageHeader header; + IZ_AppPlayerState player_state[IZ_PLAYERS]; + IZ_AppPlayerActionSection player_actions; +} IZ_AppStateSyncMessage; + +struct IZ_App; + +u64 IZ_AppGetTicks(struct IZ_App*); + +void IZ_AppBindConnection(struct IZ_App*, struct lws*); + +IZ_NetState* IZ_AppGetNetState(struct IZ_App*); + +IZ_InputState* IZ_AppGetInputState(struct IZ_App*); + +void IZ_AppHandleOutboundNetworking(struct IZ_App*); + +#endif diff --git a/src/packages/game/output/IZ_video.c b/src/packages/game/output/IZ_video.c index 394cd84..5a332b7 100644 --- a/src/packages/game/output/IZ_video.c +++ b/src/packages/game/output/IZ_video.c @@ -27,7 +27,7 @@ void IZ_VideoOverrideConfig(IZ_VideoState* state, u8 argc, const char* argv[]) { } } -IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, const char* config_path, u8 argc, const char* argv[]) { +IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, void* user_data, const char* config_path, u8 argc, const char* argv[]) { SDL_memcpy(state, &IZ_DEFAULT_VIDEO_STATE, sizeof(IZ_VideoState)); IZ_VideoLoadConfig(state, config_path); @@ -36,6 +36,7 @@ IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, const char* config_p } IZ_VideoOverrideConfig(state, argc, argv); state->last_update_at = 0u; + state->user_data = user_data; SDL_Window* window = SDL_CreateWindow( IZ_APP_NAME, @@ -55,79 +56,6 @@ IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, const char* config_p return 0; } -void IZ_VideoUpdateForDebugTicks(IZ_VideoState* video_state, uint64_t ticks) { - SDL_SetRenderDrawColor(video_state->renderer, 0x00, 0xff, 0xff, 0xff); - u64 the_ticks = ticks; - u8 column; - u8 row; - const u8 size = 4; - - u8 i; - for (i = 0; i < 64; i += 1) { - column = i % 32; - row = i / 32; - - if (the_ticks & 0x1) { - SDL_RenderFillRectF(video_state->renderer, &(SDL_FRect) { - (f32) (video_state->config.width - ((column + 1) * size)), - (f32) (video_state->config.height - ((row + 1) * size)), - size, - size - }); - } - the_ticks >>= 1; - } -} - -void IZ_VideoUpdateForDebugInput(IZ_VideoState* video_state, IZ_InputState* input_state) { - SDL_SetRenderDrawColor(video_state->renderer, 0xff, 0xff, 0x00, 0xff); - u8 column; - u8 row; - const u8 size = 4; - - u8 p; - u8 i; - for (p = 0; p < IZ_PLAYERS; p += 1) { - IZ_Action the_action = input_state->action[p]; - for (i = 0; i < CONTROLS; i += 1) { - column = (i % 4) + (p * 4); - row = i / 4; - - if (the_action & 0x1) { - SDL_RenderFillRectF(video_state->renderer, &(SDL_FRect) { - (f32) (column * size), - (f32) (row * size), - size, - size - }); - } - the_action >>= 1; - } - } -} - -void IZ_VideoUpdateForDebug(IZ_VideoState* video_state, IZ_InputState* input_state, u64 ticks) { - IZ_VideoUpdateForDebugTicks(video_state, ticks); - IZ_VideoUpdateForDebugInput(video_state, input_state); -} - -void IZ_VideoUpdate(IZ_VideoState* video_state, u64 ticks, IZ_InputState* input_state) { - if (ticks - video_state->last_update_at > 1000 / video_state->config.max_fps) { - // Update window - SDL_SetRenderDrawColor(video_state->renderer, 0x00, 0x00, 0x00, 0xff); - SDL_RenderClear(video_state->renderer); - for (u8 i = 0; i < MAX_ACTIVE_SPRITES; i += 1) { - if (!video_state->active_sprites[i]) { - continue; - } - // TODO draw sprites - } - IZ_VideoUpdateForDebug(video_state, input_state, ticks); - SDL_RenderPresent(video_state->renderer); - video_state->last_update_at = ticks; - } -} - void IZ_VideoTeardown(IZ_VideoState* state) { SDL_DestroyWindow(state->window); } diff --git a/src/packages/game/output/IZ_video.h b/src/packages/game/output/IZ_video.h index 4cdffef..05c031e 100644 --- a/src/packages/game/output/IZ_video.h +++ b/src/packages/game/output/IZ_video.h @@ -6,6 +6,7 @@ #include #include "../input/IZ_input.h" +#include "../net/IZ_net.h" #include "../IZ_common.h" #include "../IZ_config.h" @@ -21,6 +22,7 @@ typedef struct { } IZ_VideoConfig; typedef struct { + void* user_data; IZ_VideoConfig config; u64 last_update_at; SDL_Window* window; @@ -29,6 +31,7 @@ typedef struct { } IZ_VideoState; static const IZ_VideoState IZ_DEFAULT_VIDEO_STATE = { + .user_data = NULL, .config = { .width = 320u, .height = 240u, @@ -40,11 +43,11 @@ static const IZ_VideoState IZ_DEFAULT_VIDEO_STATE = { .active_sprites = {}, }; -IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState*, const char*, u8, const char**); +IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState*, void*, const char*, u8, const char**); IZ_ProcedureResult IZ_VideoSaveConfig(IZ_VideoState*, const char*); -void IZ_VideoUpdate(IZ_VideoState*, u64, IZ_InputState*); +void IZ_VideoUpdate(IZ_VideoState*); void IZ_VideoTeardown(IZ_VideoState*);