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.
 
 
 
 
 
 

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