1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2022 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
20 // ============================================================================
21 // generic helper functions
22 // ============================================================================
24 static void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data,
27 #if defined(PLATFORM_EMSCRIPTEN)
28 // threads currently not fully supported by Emscripten/SDL and some browsers
31 SDL_Thread *thread = SDL_CreateThread(function, name, data);
34 SDL_DetachThread(thread);
36 Error("Cannot create thread to %s!", error);
38 // nasty kludge to lower probability of intermingled thread error messages
43 static char *getPasswordJSON(char *password)
45 static char password_json[MAX_FILENAME_LEN] = "";
46 static boolean initialized = FALSE;
50 if (password != NULL &&
51 !strEqual(password, "") &&
52 !strEqual(password, UNDEFINED_PASSWORD))
53 snprintf(password_json, MAX_FILENAME_LEN,
54 " \"password\": \"%s\",\n",
55 setup.api_server_password);
63 static char *getFileBase64(char *filename)
65 struct stat file_status;
67 if (stat(filename, &file_status) != 0)
69 Error("cannot stat file '%s'", filename);
74 int buffer_size = file_status.st_size;
75 byte *buffer = checked_malloc(buffer_size);
79 if (!(file = fopen(filename, MODE_READ)))
81 Error("cannot open file '%s'", filename);
88 for (i = 0; i < buffer_size; i++)
94 Error("cannot read from input file '%s'", filename);
107 int buffer_encoded_size = base64_encoded_size(buffer_size);
108 char *buffer_encoded = checked_malloc(buffer_encoded_size);
110 base64_encode(buffer_encoded, buffer, buffer_size);
112 checked_free(buffer);
114 return buffer_encoded;
118 // ============================================================================
119 // add score API functions
120 // ============================================================================
122 struct ApiAddScoreThreadData
126 char *leveldir_subdir;
127 char *score_tape_filename;
128 struct ScoreEntry score_entry;
131 static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
132 char *score_tape_filename)
134 struct ApiAddScoreThreadData *data =
135 checked_malloc(sizeof(struct ApiAddScoreThreadData));
136 struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
138 if (score_tape_filename == NULL)
139 score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
142 data->tape_saved = tape_saved;
143 data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
144 data->score_tape_filename = getStringCopy(score_tape_filename);
145 data->score_entry = *score_entry;
150 static void FreeThreadData_ApiAddScore(void *data_raw)
152 struct ApiAddScoreThreadData *data = data_raw;
154 checked_free(data->leveldir_subdir);
155 checked_free(data->score_tape_filename);
159 static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
162 struct ApiAddScoreThreadData *data = data_raw;
163 struct ScoreEntry *score_entry = &data->score_entry;
164 char *score_tape_filename = data->score_tape_filename;
165 boolean tape_saved = data->tape_saved;
166 int level_nr = data->level_nr;
168 request->hostname = setup.api_server_hostname;
169 request->port = API_SERVER_PORT;
170 request->method = API_SERVER_METHOD;
171 request->uri = API_SERVER_URI_ADD;
173 char *tape_base64 = getFileBase64(score_tape_filename);
175 if (tape_base64 == NULL)
177 Error("loading and base64 encoding score tape file failed");
182 char *player_name_raw = score_entry->name;
183 char *player_uuid_raw = setup.player_uuid;
185 if (options.player_name != NULL && global.autoplay_leveldir != NULL)
187 player_name_raw = options.player_name;
188 player_uuid_raw = "";
191 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
192 char *levelset_name = getEscapedJSON(leveldir_current->name);
193 char *levelset_author = getEscapedJSON(leveldir_current->author);
194 char *level_name = getEscapedJSON(level.name);
195 char *level_author = getEscapedJSON(level.author);
196 char *player_name = getEscapedJSON(player_name_raw);
197 char *player_uuid = getEscapedJSON(player_uuid_raw);
199 snprintf(request->body, MAX_HTTP_BODY_SIZE,
202 " \"game_version\": \"%s\",\n"
203 " \"game_platform\": \"%s\",\n"
204 " \"batch_time\": \"%d\",\n"
205 " \"levelset_identifier\": \"%s\",\n"
206 " \"levelset_name\": \"%s\",\n"
207 " \"levelset_author\": \"%s\",\n"
208 " \"levelset_num_levels\": \"%d\",\n"
209 " \"levelset_first_level\": \"%d\",\n"
210 " \"level_nr\": \"%d\",\n"
211 " \"level_name\": \"%s\",\n"
212 " \"level_author\": \"%s\",\n"
213 " \"use_step_counter\": \"%d\",\n"
214 " \"rate_time_over_score\": \"%d\",\n"
215 " \"player_name\": \"%s\",\n"
216 " \"player_uuid\": \"%s\",\n"
217 " \"score\": \"%d\",\n"
218 " \"time\": \"%d\",\n"
219 " \"tape_basename\": \"%s\",\n"
220 " \"tape_saved\": \"%d\",\n"
221 " \"tape\": \"%s\"\n"
223 getPasswordJSON(setup.api_server_password),
224 getProgramRealVersionString(),
225 getProgramPlatformString(),
226 (int)global.autoplay_time,
230 leveldir_current->levels,
231 leveldir_current->first_level,
235 level.use_step_counter,
236 level.rate_time_over_score,
241 score_entry->tape_basename,
245 checked_free(tape_base64);
247 checked_free(levelset_identifier);
248 checked_free(levelset_name);
249 checked_free(levelset_author);
250 checked_free(level_name);
251 checked_free(level_author);
252 checked_free(player_name);
253 checked_free(player_uuid);
255 ConvertHttpRequestBodyToServerEncoding(request);
260 static void HandleResponse_ApiAddScore(struct HttpResponse *response,
263 server_scores.uploaded = TRUE;
266 static void HandleFailure_ApiAddScore(void *data_raw)
268 struct ApiAddScoreThreadData *data = data_raw;
270 PrepareScoreTapesForUpload(data->leveldir_subdir);
273 #if defined(PLATFORM_EMSCRIPTEN)
274 static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
275 void *buffer, unsigned int size)
277 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
279 if (response != NULL)
281 HandleResponse_ApiAddScore(response, data_raw);
283 checked_free(response);
287 Error("server response too large to handle (%d bytes)", size);
289 HandleFailure_ApiAddScore(data_raw);
292 FreeThreadData_ApiAddScore(data_raw);
295 static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
296 int code, const char *status)
298 Error("server failed to handle request: %d %s", code, status);
300 HandleFailure_ApiAddScore(data_raw);
302 FreeThreadData_ApiAddScore(data_raw);
305 static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw,
308 // nothing to do here
311 static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request,
314 if (!SetRequest_ApiAddScore(request, data_raw))
316 FreeThreadData_ApiAddScore(data_raw);
321 emscripten_async_wget2_data(request->uri,
326 Emscripten_ApiAddScore_Loaded,
327 Emscripten_ApiAddScore_Failed,
328 Emscripten_ApiAddScore_Progress);
333 static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
334 struct HttpResponse *response,
337 if (!SetRequest_ApiAddScore(request, data_raw))
340 if (!DoHttpRequest(request, response))
342 Error("HTTP request failed: %s", GetHttpError());
344 HandleFailure_ApiAddScore(data_raw);
349 if (!HTTP_SUCCESS(response->status_code))
351 Error("server failed to handle request: %d %s",
352 response->status_code,
353 response->status_text);
355 HandleFailure_ApiAddScore(data_raw);
360 HandleResponse_ApiAddScore(response, data_raw);
363 static void ApiAddScore_HttpRequest(struct HttpRequest *request,
364 struct HttpResponse *response,
367 ApiAddScore_HttpRequestExt(request, response, data_raw);
369 FreeThreadData_ApiAddScore(data_raw);
373 static int ApiAddScoreThread(void *data_raw)
375 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
376 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
378 program.api_thread_count++;
380 #if defined(PLATFORM_EMSCRIPTEN)
381 Emscripten_ApiAddScore_HttpRequest(request, data_raw);
383 ApiAddScore_HttpRequest(request, response, data_raw);
386 program.api_thread_count--;
388 checked_free(request);
389 checked_free(response);
394 void ApiAddScoreAsThread(int nr, boolean tape_saved, char *score_tape_filename)
396 struct ApiAddScoreThreadData *data =
397 CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
399 ExecuteAsThread(ApiAddScoreThread,
401 "upload score to server");
405 // ============================================================================
406 // get score API functions
407 // ============================================================================
409 struct ApiGetScoreThreadData
412 char *score_cache_filename;
415 static void *CreateThreadData_ApiGetScore(int nr)
417 struct ApiGetScoreThreadData *data =
418 checked_malloc(sizeof(struct ApiGetScoreThreadData));
419 char *score_cache_filename = getScoreCacheFilename(nr);
422 data->score_cache_filename = getStringCopy(score_cache_filename);
427 static void FreeThreadData_ApiGetScore(void *data_raw)
429 struct ApiGetScoreThreadData *data = data_raw;
431 checked_free(data->score_cache_filename);
435 static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
438 struct ApiGetScoreThreadData *data = data_raw;
439 int level_nr = data->level_nr;
441 request->hostname = setup.api_server_hostname;
442 request->port = API_SERVER_PORT;
443 request->method = API_SERVER_METHOD;
444 request->uri = API_SERVER_URI_GET;
446 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
447 char *levelset_name = getEscapedJSON(leveldir_current->name);
449 snprintf(request->body, MAX_HTTP_BODY_SIZE,
452 " \"game_version\": \"%s\",\n"
453 " \"game_platform\": \"%s\",\n"
454 " \"levelset_identifier\": \"%s\",\n"
455 " \"levelset_name\": \"%s\",\n"
456 " \"level_nr\": \"%d\"\n"
458 getPasswordJSON(setup.api_server_password),
459 getProgramRealVersionString(),
460 getProgramPlatformString(),
465 checked_free(levelset_identifier);
466 checked_free(levelset_name);
468 ConvertHttpRequestBodyToServerEncoding(request);
473 static void HandleResponse_ApiGetScore(struct HttpResponse *response,
476 struct ApiGetScoreThreadData *data = data_raw;
478 if (response->body_size == 0)
480 // no scores available for this level
485 ConvertHttpResponseBodyToClientEncoding(response);
487 char *filename = data->score_cache_filename;
491 // used instead of "leveldir_current->subdir" (for network games)
492 InitScoreCacheDirectory(levelset.identifier);
494 if (!(file = fopen(filename, MODE_WRITE)))
496 Warn("cannot save score cache file '%s'", filename);
501 for (i = 0; i < response->body_size; i++)
502 fputc(response->body[i], file);
506 SetFilePermissions(filename, PERMS_PRIVATE);
508 server_scores.updated = TRUE;
511 #if defined(PLATFORM_EMSCRIPTEN)
512 static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw,
513 void *buffer, unsigned int size)
515 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
517 if (response != NULL)
519 HandleResponse_ApiGetScore(response, data_raw);
521 checked_free(response);
525 Error("server response too large to handle (%d bytes)", size);
528 FreeThreadData_ApiGetScore(data_raw);
531 static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw,
532 int code, const char *status)
534 Error("server failed to handle request: %d %s", code, status);
536 FreeThreadData_ApiGetScore(data_raw);
539 static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw,
542 // nothing to do here
545 static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request,
548 if (!SetRequest_ApiGetScore(request, data_raw))
550 FreeThreadData_ApiGetScore(data_raw);
555 emscripten_async_wget2_data(request->uri,
560 Emscripten_ApiGetScore_Loaded,
561 Emscripten_ApiGetScore_Failed,
562 Emscripten_ApiGetScore_Progress);
567 static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
568 struct HttpResponse *response,
571 if (!SetRequest_ApiGetScore(request, data_raw))
574 if (!DoHttpRequest(request, response))
576 Error("HTTP request failed: %s", GetHttpError());
581 if (!HTTP_SUCCESS(response->status_code))
583 // do not show error message if no scores found for this level set
584 if (response->status_code == 404)
587 Error("server failed to handle request: %d %s",
588 response->status_code,
589 response->status_text);
594 HandleResponse_ApiGetScore(response, data_raw);
597 static void ApiGetScore_HttpRequest(struct HttpRequest *request,
598 struct HttpResponse *response,
601 ApiGetScore_HttpRequestExt(request, response, data_raw);
603 FreeThreadData_ApiGetScore(data_raw);
607 static int ApiGetScoreThread(void *data_raw)
609 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
610 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
612 program.api_thread_count++;
614 #if defined(PLATFORM_EMSCRIPTEN)
615 Emscripten_ApiGetScore_HttpRequest(request, data_raw);
617 ApiGetScore_HttpRequest(request, response, data_raw);
620 program.api_thread_count--;
622 checked_free(request);
623 checked_free(response);
628 void ApiGetScoreAsThread(int nr)
630 struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr);
632 ExecuteAsThread(ApiGetScoreThread,
634 "download scores from server");
638 // ============================================================================
639 // get score tape API functions
640 // ============================================================================
642 struct ApiGetScoreTapeThreadData
646 char *score_tape_filename;
649 static void *CreateThreadData_ApiGetScoreTape(int nr, int id,
650 char *score_tape_basename)
652 struct ApiGetScoreTapeThreadData *data =
653 checked_malloc(sizeof(struct ApiGetScoreTapeThreadData));
654 char *score_tape_filename = getScoreCacheTapeFilename(score_tape_basename, nr);
658 data->score_tape_filename = getStringCopy(score_tape_filename);
663 static void FreeThreadData_ApiGetScoreTape(void *data_raw)
665 struct ApiGetScoreTapeThreadData *data = data_raw;
667 checked_free(data->score_tape_filename);
671 static boolean SetRequest_ApiGetScoreTape(struct HttpRequest *request,
674 struct ApiGetScoreTapeThreadData *data = data_raw;
675 int score_id = data->score_id;
677 request->hostname = setup.api_server_hostname;
678 request->port = API_SERVER_PORT;
679 request->method = API_SERVER_METHOD;
680 request->uri = API_SERVER_URI_GETTAPE;
682 snprintf(request->body, MAX_HTTP_BODY_SIZE,
685 " \"game_version\": \"%s\",\n"
686 " \"game_platform\": \"%s\",\n"
689 getPasswordJSON(setup.api_server_password),
690 getProgramRealVersionString(),
691 getProgramPlatformString(),
694 ConvertHttpRequestBodyToServerEncoding(request);
699 static void HandleResponse_ApiGetScoreTape(struct HttpResponse *response,
702 struct ApiGetScoreTapeThreadData *data = data_raw;
704 if (response->body_size == 0)
706 // no score tape available for this level
711 // (do not convert HTTP response body, as it contains binary data here)
713 int level_nr = data->level_nr;
714 char *filename = data->score_tape_filename;
718 // used instead of "leveldir_current->subdir" (for network games)
719 InitScoreCacheTapeDirectory(levelset.identifier, level_nr);
721 if (!(file = fopen(filename, MODE_WRITE)))
723 Warn("cannot save score tape file '%s'", filename);
728 for (i = 0; i < response->body_size; i++)
729 fputc(response->body[i], file);
733 SetFilePermissions(filename, PERMS_PRIVATE);
735 server_scores.tape_downloaded = TRUE;
738 #if defined(PLATFORM_EMSCRIPTEN)
739 static void Emscripten_ApiGetScoreTape_Loaded(unsigned handle, void *data_raw,
740 void *buffer, unsigned int size)
742 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
744 if (response != NULL)
746 HandleResponse_ApiGetScoreTape(response, data_raw);
748 checked_free(response);
752 Error("server response too large to handle (%d bytes)", size);
755 FreeThreadData_ApiGetScoreTape(data_raw);
758 static void Emscripten_ApiGetScoreTape_Failed(unsigned handle, void *data_raw,
759 int code, const char *status)
761 Error("server failed to handle request: %d %s", code, status);
763 FreeThreadData_ApiGetScoreTape(data_raw);
766 static void Emscripten_ApiGetScoreTape_Progress(unsigned handle, void *data_raw,
769 // nothing to do here
772 static void Emscripten_ApiGetScoreTape_HttpRequest(struct HttpRequest *request,
775 if (!SetRequest_ApiGetScoreTape(request, data_raw))
777 FreeThreadData_ApiGetScoreTape(data_raw);
782 emscripten_async_wget2_data(request->uri,
787 Emscripten_ApiGetScoreTape_Loaded,
788 Emscripten_ApiGetScoreTape_Failed,
789 Emscripten_ApiGetScoreTape_Progress);
794 static void ApiGetScoreTape_HttpRequestExt(struct HttpRequest *request,
795 struct HttpResponse *response,
798 if (!SetRequest_ApiGetScoreTape(request, data_raw))
801 if (!DoHttpRequest(request, response))
803 Error("HTTP request failed: %s", GetHttpError());
808 if (!HTTP_SUCCESS(response->status_code))
810 // do not show error message if no scores found for this level set
811 if (response->status_code == 404)
814 Error("server failed to handle request: %d %s",
815 response->status_code,
816 response->status_text);
821 HandleResponse_ApiGetScoreTape(response, data_raw);
824 static void ApiGetScoreTape_HttpRequest(struct HttpRequest *request,
825 struct HttpResponse *response,
828 ApiGetScoreTape_HttpRequestExt(request, response, data_raw);
830 FreeThreadData_ApiGetScoreTape(data_raw);
834 static int ApiGetScoreTapeThread(void *data_raw)
836 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
837 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
839 program.api_thread_count++;
841 #if defined(PLATFORM_EMSCRIPTEN)
842 Emscripten_ApiGetScoreTape_HttpRequest(request, data_raw);
844 ApiGetScoreTape_HttpRequest(request, response, data_raw);
847 program.api_thread_count--;
849 checked_free(request);
850 checked_free(response);
855 void ApiGetScoreTapeAsThread(int nr, int id, char *score_tape_basename)
857 struct ApiGetScoreTapeThreadData *data =
858 CreateThreadData_ApiGetScoreTape(nr, id, score_tape_basename);
860 ExecuteAsThread(ApiGetScoreTapeThread,
861 "ApiGetScoreTape", data,
862 "download score tape from server");
866 // ============================================================================
867 // rename player API functions
868 // ============================================================================
870 struct ApiRenamePlayerThreadData
876 static void *CreateThreadData_ApiRenamePlayer(void)
878 struct ApiRenamePlayerThreadData *data =
879 checked_malloc(sizeof(struct ApiRenamePlayerThreadData));
881 data->player_name = getStringCopy(setup.player_name);
882 data->player_uuid = getStringCopy(setup.player_uuid);
887 static void FreeThreadData_ApiRenamePlayer(void *data_raw)
889 struct ApiRenamePlayerThreadData *data = data_raw;
891 checked_free(data->player_name);
892 checked_free(data->player_uuid);
896 static boolean SetRequest_ApiRenamePlayer(struct HttpRequest *request,
899 struct ApiRenamePlayerThreadData *data = data_raw;
900 char *player_name_raw = data->player_name;
901 char *player_uuid_raw = data->player_uuid;
903 request->hostname = setup.api_server_hostname;
904 request->port = API_SERVER_PORT;
905 request->method = API_SERVER_METHOD;
906 request->uri = API_SERVER_URI_RENAME;
908 char *player_name = getEscapedJSON(player_name_raw);
909 char *player_uuid = getEscapedJSON(player_uuid_raw);
911 snprintf(request->body, MAX_HTTP_BODY_SIZE,
914 " \"game_version\": \"%s\",\n"
915 " \"game_platform\": \"%s\",\n"
916 " \"name\": \"%s\",\n"
917 " \"uuid\": \"%s\"\n"
919 getPasswordJSON(setup.api_server_password),
920 getProgramRealVersionString(),
921 getProgramPlatformString(),
925 checked_free(player_name);
926 checked_free(player_uuid);
928 ConvertHttpRequestBodyToServerEncoding(request);
933 static void HandleResponse_ApiRenamePlayer(struct HttpResponse *response,
936 // nothing to do here
939 #if defined(PLATFORM_EMSCRIPTEN)
940 static void Emscripten_ApiRenamePlayer_Loaded(unsigned handle, void *data_raw,
941 void *buffer, unsigned int size)
943 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
945 if (response != NULL)
947 HandleResponse_ApiRenamePlayer(response, data_raw);
949 checked_free(response);
953 Error("server response too large to handle (%d bytes)", size);
956 FreeThreadData_ApiRenamePlayer(data_raw);
959 static void Emscripten_ApiRenamePlayer_Failed(unsigned handle, void *data_raw,
960 int code, const char *status)
962 Error("server failed to handle request: %d %s", code, status);
964 FreeThreadData_ApiRenamePlayer(data_raw);
967 static void Emscripten_ApiRenamePlayer_Progress(unsigned handle, void *data_raw,
970 // nothing to do here
973 static void Emscripten_ApiRenamePlayer_HttpRequest(struct HttpRequest *request,
976 if (!SetRequest_ApiRenamePlayer(request, data_raw))
978 FreeThreadData_ApiRenamePlayer(data_raw);
983 emscripten_async_wget2_data(request->uri,
988 Emscripten_ApiRenamePlayer_Loaded,
989 Emscripten_ApiRenamePlayer_Failed,
990 Emscripten_ApiRenamePlayer_Progress);
995 static void ApiRenamePlayer_HttpRequestExt(struct HttpRequest *request,
996 struct HttpResponse *response,
999 if (!SetRequest_ApiRenamePlayer(request, data_raw))
1002 if (!DoHttpRequest(request, response))
1004 Error("HTTP request failed: %s", GetHttpError());
1009 if (!HTTP_SUCCESS(response->status_code))
1011 Error("server failed to handle request: %d %s",
1012 response->status_code,
1013 response->status_text);
1018 HandleResponse_ApiRenamePlayer(response, data_raw);
1021 static void ApiRenamePlayer_HttpRequest(struct HttpRequest *request,
1022 struct HttpResponse *response,
1025 ApiRenamePlayer_HttpRequestExt(request, response, data_raw);
1027 FreeThreadData_ApiRenamePlayer(data_raw);
1031 static int ApiRenamePlayerThread(void *data_raw)
1033 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
1034 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
1036 program.api_thread_count++;
1038 #if defined(PLATFORM_EMSCRIPTEN)
1039 Emscripten_ApiRenamePlayer_HttpRequest(request, data_raw);
1041 ApiRenamePlayer_HttpRequest(request, response, data_raw);
1044 program.api_thread_count--;
1046 checked_free(request);
1047 checked_free(response);
1052 void ApiRenamePlayerAsThread(void)
1054 struct ApiRenamePlayerThreadData *data = CreateThreadData_ApiRenamePlayer();
1056 ExecuteAsThread(ApiRenamePlayerThread,
1057 "ApiRenamePlayer", data,
1058 "rename player on server");
1062 // ============================================================================
1063 // reset player UUID API functions
1064 // ============================================================================
1066 struct ApiResetUUIDThreadData
1069 char *player_uuid_old;
1070 char *player_uuid_new;
1073 static void *CreateThreadData_ApiResetUUID(char *uuid_new)
1075 struct ApiResetUUIDThreadData *data =
1076 checked_malloc(sizeof(struct ApiResetUUIDThreadData));
1078 data->player_name = getStringCopy(setup.player_name);
1079 data->player_uuid_old = getStringCopy(setup.player_uuid);
1080 data->player_uuid_new = getStringCopy(uuid_new);
1085 static void FreeThreadData_ApiResetUUID(void *data_raw)
1087 struct ApiResetUUIDThreadData *data = data_raw;
1089 checked_free(data->player_name);
1090 checked_free(data->player_uuid_old);
1091 checked_free(data->player_uuid_new);
1095 static boolean SetRequest_ApiResetUUID(struct HttpRequest *request,
1098 struct ApiResetUUIDThreadData *data = data_raw;
1099 char *player_name_raw = data->player_name;
1100 char *player_uuid_old_raw = data->player_uuid_old;
1101 char *player_uuid_new_raw = data->player_uuid_new;
1103 request->hostname = setup.api_server_hostname;
1104 request->port = API_SERVER_PORT;
1105 request->method = API_SERVER_METHOD;
1106 request->uri = API_SERVER_URI_RESETUUID;
1108 char *player_name = getEscapedJSON(player_name_raw);
1109 char *player_uuid_old = getEscapedJSON(player_uuid_old_raw);
1110 char *player_uuid_new = getEscapedJSON(player_uuid_new_raw);
1112 snprintf(request->body, MAX_HTTP_BODY_SIZE,
1115 " \"game_version\": \"%s\",\n"
1116 " \"game_platform\": \"%s\",\n"
1117 " \"name\": \"%s\",\n"
1118 " \"uuid_old\": \"%s\",\n"
1119 " \"uuid_new\": \"%s\"\n"
1121 getPasswordJSON(setup.api_server_password),
1122 getProgramRealVersionString(),
1123 getProgramPlatformString(),
1128 checked_free(player_name);
1129 checked_free(player_uuid_old);
1130 checked_free(player_uuid_new);
1132 ConvertHttpRequestBodyToServerEncoding(request);
1137 static void HandleResponse_ApiResetUUID(struct HttpResponse *response,
1140 struct ApiResetUUIDThreadData *data = data_raw;
1142 // upgrade player UUID in server setup file
1143 setup.player_uuid = getStringCopy(data->player_uuid_new);
1144 setup.player_version = 2;
1146 SaveSetup_ServerSetup();
1149 #if defined(PLATFORM_EMSCRIPTEN)
1150 static void Emscripten_ApiResetUUID_Loaded(unsigned handle, void *data_raw,
1151 void *buffer, unsigned int size)
1153 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
1155 if (response != NULL)
1157 HandleResponse_ApiResetUUID(response, data_raw);
1159 checked_free(response);
1163 Error("server response too large to handle (%d bytes)", size);
1166 FreeThreadData_ApiResetUUID(data_raw);
1169 static void Emscripten_ApiResetUUID_Failed(unsigned handle, void *data_raw,
1170 int code, const char *status)
1172 Error("server failed to handle request: %d %s", code, status);
1174 FreeThreadData_ApiResetUUID(data_raw);
1177 static void Emscripten_ApiResetUUID_Progress(unsigned handle, void *data_raw,
1178 int bytes, int size)
1180 // nothing to do here
1183 static void Emscripten_ApiResetUUID_HttpRequest(struct HttpRequest *request,
1186 if (!SetRequest_ApiResetUUID(request, data_raw))
1188 FreeThreadData_ApiResetUUID(data_raw);
1193 emscripten_async_wget2_data(request->uri,
1198 Emscripten_ApiResetUUID_Loaded,
1199 Emscripten_ApiResetUUID_Failed,
1200 Emscripten_ApiResetUUID_Progress);
1205 static void ApiResetUUID_HttpRequestExt(struct HttpRequest *request,
1206 struct HttpResponse *response,
1209 if (!SetRequest_ApiResetUUID(request, data_raw))
1212 if (!DoHttpRequest(request, response))
1214 Error("HTTP request failed: %s", GetHttpError());
1219 if (!HTTP_SUCCESS(response->status_code))
1221 Error("server failed to handle request: %d %s",
1222 response->status_code,
1223 response->status_text);
1228 HandleResponse_ApiResetUUID(response, data_raw);
1231 static void ApiResetUUID_HttpRequest(struct HttpRequest *request,
1232 struct HttpResponse *response,
1235 ApiResetUUID_HttpRequestExt(request, response, data_raw);
1237 FreeThreadData_ApiResetUUID(data_raw);
1241 static int ApiResetUUIDThread(void *data_raw)
1243 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
1244 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
1246 program.api_thread_count++;
1248 #if defined(PLATFORM_EMSCRIPTEN)
1249 Emscripten_ApiResetUUID_HttpRequest(request, data_raw);
1251 ApiResetUUID_HttpRequest(request, response, data_raw);
1254 program.api_thread_count--;
1256 checked_free(request);
1257 checked_free(response);
1262 void ApiResetUUIDAsThread(char *uuid_new)
1264 struct ApiResetUUIDThreadData *data = CreateThreadData_ApiResetUUID(uuid_new);
1266 ExecuteAsThread(ApiResetUUIDThread,
1267 "ApiResetUUID", data,
1268 "reset UUID on server");