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.
 
 
 
 
 
 

307 lines
8.0 KiB

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