2D Run-and-gun shooter inspired by One Man's Doomsday, Counter-Strike, and Metal Slug.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

313 lines
8.1 KiB

  1. #include "IZ_app.h"
  2. static IZ_App* global_app;
  3. void IZ_AppHandleSignal(i32 _signal) {
  4. global_app->net_state.ws.interrupted = true;
  5. IZ_WSServerCancelService(&global_app->net_state.ws);
  6. }
  7. IZ_ProcedureResult IZ_AppInitialize(IZ_App *app, u8 argc, const char **argv) {
  8. global_app = app;
  9. memset(app, 0, sizeof(IZ_App));
  10. signal(SIGINT, IZ_AppHandleSignal);
  11. // IZ_LogInterceptWSMessages(app->config.log_level);
  12. IZ_LogInterceptWSMessages(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE);
  13. const char* cmdline_buffer;
  14. char config_path[128];
  15. // TODO abstract command line args parsing
  16. if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, "-c"))) {
  17. memcpy_s(config_path, 128, cmdline_buffer, 128);
  18. } else {
  19. IZ_ConfigGetDefaultPath(config_path, 128);
  20. }
  21. if (IZ_NetInitialize(&app->net_state, app, config_path, argc, argv)) {
  22. return -1;
  23. }
  24. return 0;
  25. }
  26. IZ_ProcedureResult IZ_AppRun(IZ_App *app, u8 argc, const char **argv) {
  27. if (IZ_AppInitialize(app, argc, argv)) {
  28. return -1;
  29. }
  30. if (IZ_WSServerInitialize(&app->net_state.ws, (IZ_WSServerInitializeParams) {
  31. .port = app->net_state.config.port,
  32. })) {
  33. return -1;
  34. }
  35. i32 result = 0;
  36. while (true) {
  37. if (IZ_WSServerHandle(&app->net_state.ws)) {
  38. result = -1;
  39. break;
  40. }
  41. if (app->net_state.ws.interrupted) {
  42. break;
  43. }
  44. }
  45. IZ_WSServerTeardown(&app->net_state.ws);
  46. lwsl_user("Server closed. Bye!\n");
  47. return result;
  48. }
  49. void IZ_WSServerCullLaggingClients(IZ_WSServerVHostData *vhd) {
  50. u32 oldest_tail = lws_ring_get_oldest_tail(vhd->ring);
  51. IZ_WSServerSessionData *old_pss = NULL;
  52. i32 most = 0;
  53. i32 before = (i32) lws_ring_get_count_waiting_elements(vhd->ring, &oldest_tail);
  54. i32 m;
  55. /*
  56. * At least one guy with the oldest tail has lagged too far, filling
  57. * the ringbuffer with stuff waiting for them, while new stuff is
  58. * coming in, and they must close, freeing up ringbuffer entries.
  59. */
  60. lws_start_foreach_llp_safe(
  61. IZ_WSServerSessionData**,
  62. ppss,
  63. vhd->pss_list,
  64. pss_list
  65. ) {
  66. if ((*ppss)->tail == oldest_tail) {
  67. old_pss = *ppss;
  68. lwsl_user("Killing lagging client %p\n", (*ppss)->wsi);
  69. lws_set_timeout((*ppss)->wsi, PENDING_TIMEOUT_LAGGING,
  70. /*
  71. * we may kill the wsi we came in on,
  72. * so the actual close is deferred
  73. */
  74. LWS_TO_KILL_ASYNC);
  75. /*
  76. * We might try to write something before we get a
  77. * chance to close. But this pss is now detached
  78. * from the ring buffer. Mark this pss as culled so we
  79. * don't try to do anything more with it.
  80. */
  81. (*ppss)->culled = true;
  82. /*
  83. * Because we can't kill it synchronously, but we
  84. * know it's closing momentarily and don't want its
  85. * participation any more, remove its pss from the
  86. * vhd pss list early. (This is safe to repeat
  87. * uselessly later in the close flow).
  88. *
  89. * Notice this changes *ppss!
  90. */
  91. lws_ll_fwd_remove(IZ_WSServerSessionData, pss_list, (*ppss), vhd->pss_list);
  92. /* use the changed *ppss so we won't skip anything */
  93. continue;
  94. }
  95. /*
  96. * so this guy is a survivor of the cull. Let's track
  97. * what is the largest number of pending ring elements
  98. * for any survivor.
  99. */
  100. m = (i32) lws_ring_get_count_waiting_elements(vhd->ring, &((*ppss)->tail));
  101. if (m > most) {
  102. most = m;
  103. }
  104. } lws_end_foreach_llp_safe(ppss);
  105. /* it would mean we lost track of oldest... but Coverity insists */
  106. if (!old_pss) {
  107. return;
  108. }
  109. /*
  110. * Let's recover (ie, free up) all the ring slots between the
  111. * original oldest's last one and the "worst" survivor.
  112. */
  113. lws_ring_consume_and_update_oldest_tail(
  114. vhd->ring,
  115. IZ_WSServerSessionData,
  116. &old_pss->tail,
  117. (size_t) (before - most),
  118. vhd->pss_list,
  119. tail,
  120. pss_list
  121. );
  122. lwsl_user("%s: shrunk ring from %d to %d\n", __func__, before, most);
  123. }
  124. /* destroys the message when everyone has had a copy of it */
  125. IZ_ProcedureResult IZ_WSServerProtocolInitialize(struct lws* wsi, void* in) {
  126. IZ_WSServerVHostData* vhd_instance = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get(
  127. lws_get_vhost(wsi),
  128. lws_get_protocol(wsi)
  129. );
  130. IZ_WSServerVHostData** vhd = &vhd_instance;
  131. *vhd = lws_protocol_vh_priv_zalloc(
  132. lws_get_vhost(wsi),
  133. lws_get_protocol(wsi),
  134. sizeof(IZ_WSServerVHostData)
  135. );
  136. (*vhd)->context = lws_get_context(wsi);
  137. (*vhd)->protocol = lws_get_protocol(wsi);
  138. (*vhd)->vhost = lws_get_vhost(wsi);
  139. (*vhd)->port = (u16*) lws_pvo_search(
  140. (const struct lws_protocol_vhost_options *)in,
  141. "port"
  142. )->value;
  143. (*vhd)->app = lws_pvo_search(
  144. (const struct lws_protocol_vhost_options *)in,
  145. "app"
  146. )->value;
  147. (*vhd)->ring = lws_ring_create(
  148. sizeof(IZ_WebsocketMessage),
  149. RING_COUNT,
  150. IZ_WebsocketDestroyMessage
  151. );
  152. if (!(*vhd)->ring) {
  153. return -1;
  154. }
  155. return 0;
  156. }
  157. void IZ_WSServerProtocolTeardown(struct lws* wsi) {
  158. IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get(
  159. lws_get_vhost(wsi),
  160. lws_get_protocol(wsi)
  161. );
  162. lws_ring_destroy(vhd->ring);
  163. }
  164. void IZ_WSServerOnOpen(struct lws* wsi, IZ_WSServerSessionData* pss) {
  165. IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get(
  166. lws_get_vhost(wsi),
  167. lws_get_protocol(wsi)
  168. );
  169. /* add ourselves to the list of live pss held in the vhd */
  170. lwsl_user("LWS_CALLBACK_ESTABLISHED: wsi %p\n", wsi);
  171. lws_ll_fwd_insert(pss, pss_list, vhd->pss_list);
  172. pss->tail = lws_ring_get_oldest_tail(vhd->ring);
  173. pss->wsi = wsi;
  174. }
  175. void IZ_WSServerOnClose(struct lws* wsi, IZ_WSServerSessionData* pss) {
  176. IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get(
  177. lws_get_vhost(wsi),
  178. lws_get_protocol(wsi)
  179. );
  180. lwsl_user("LWS_CALLBACK_CLOSED: wsi %p\n", wsi);
  181. /* remove our closing pss from the list of live pss */
  182. lws_ll_fwd_remove(IZ_WSServerSessionData, pss_list, pss, vhd->pss_list);
  183. }
  184. IZ_ProcedureResult IZ_WSServerWritable(struct lws* wsi, IZ_WSServerSessionData* pss) {
  185. IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get(
  186. lws_get_vhost(wsi),
  187. lws_get_protocol(wsi)
  188. );
  189. if (pss->culled) {
  190. return 0;
  191. }
  192. const IZ_WebsocketMessage* pmsg = lws_ring_get_element(vhd->ring, &pss->tail);
  193. if (!pmsg) {
  194. return 0;
  195. }
  196. /* notice we allowed for LWS_PRE in the payload already */
  197. i32 m = lws_write(
  198. wsi,
  199. ((unsigned char*) pmsg->payload) + LWS_PRE,
  200. pmsg->len,
  201. pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT
  202. );
  203. if (m < (i32) pmsg->len) {
  204. lwsl_err("ERROR %d writing to ws socket\n", m);
  205. return -1;
  206. }
  207. lws_ring_consume_and_update_oldest_tail(
  208. vhd->ring, /* lws_ring object */
  209. IZ_WSServerSessionData, /* type of objects with tails */
  210. &pss->tail, /* tail of guy doing the consuming */
  211. 1, /* number of payload objects being consumed */
  212. vhd->pss_list, /* head of list of objects with tails */
  213. tail, /* member name of tail in objects with tails */
  214. pss_list /* member name of next object in objects with tails */
  215. );
  216. /* more to do for us? */
  217. if (lws_ring_get_element(vhd->ring, &pss->tail)) {
  218. /* come back as soon as we can write more */
  219. lws_callback_on_writable(pss->wsi);
  220. }
  221. return 0;
  222. }
  223. IZ_ProcedureResult IZ_WSServerOnReceive(struct lws* wsi, void* in, size_t len) {
  224. IZ_WSServerVHostData* vhd = (IZ_WSServerVHostData*) lws_protocol_vh_priv_get(
  225. lws_get_vhost(wsi),
  226. lws_get_protocol(wsi)
  227. );
  228. i32 n = (i32) lws_ring_get_count_free_elements(vhd->ring);
  229. if (!n) {
  230. /* forcibly make space */
  231. IZ_WSServerCullLaggingClients(vhd);
  232. n = (i32) lws_ring_get_count_free_elements(vhd->ring);
  233. }
  234. if (!n) {
  235. return 0;
  236. }
  237. lwsl_user("LWS_CALLBACK_RECEIVE: free space %d\n", n);
  238. IZ_WebsocketMessage amsg;
  239. const u8 result = (
  240. lws_frame_is_binary(wsi)
  241. ? IZ_WebsocketCreateBinaryMessage(wsi, &amsg, in, len)
  242. : IZ_WebsocketCreateTextMessage(wsi, &amsg, in, len)
  243. );
  244. if (result) {
  245. lwsl_user("OOM: dropping\n");
  246. return 1;
  247. }
  248. if (!lws_ring_insert(vhd->ring, &amsg, 1)) {
  249. IZ_WebsocketDestroyMessage(&amsg);
  250. lwsl_user("dropping!\n");
  251. return 1;
  252. }
  253. /*
  254. * let everybody know we want to write something on them
  255. * as soon as they are ready
  256. */
  257. lws_start_foreach_llp(IZ_WSServerSessionData**, ppss, vhd->pss_list) {
  258. lws_callback_on_writable((*ppss)->wsi);
  259. }
  260. lws_end_foreach_llp(ppss, pss_list);
  261. return 0;
  262. }