diff --git a/.travis.yml b/.travis.yml index 4959e4d2..66349890 100644 --- a/.travis.yml +++ b/.travis.yml @@ -439,6 +439,30 @@ matrix: # List Ubuntu LTS, newest to oldest # Then list non-LTS, newest to oldest ################################ + - os: linux + addons: + apt: + packages: + - libsdl2-mixer-dev + - libpng-dev + - libgl1-mesa-dev + - libgme-dev + - libcurl4-openssl-dev + - p7zip-full + - gcc-4.8 + compiler: gcc-4.8 + dist: xenial + if: env(DPL_ENABLED) = "1" AND (env(_DPL_JOB_ENABLED) = "1" OR env(DPL_JOB_ENABLE_ALL) = "1") + AND (branch =~ /^.*deployer.*$/ OR (tag IS present AND env(DPL_TAG_ENABLED) = "1")) + AND env(DPL_TERMINATE_MAIN) != "1" + env: + - _DPL_JOB_ENABLED=1 + - _DPL_JOB_NAME=focal + - _DPL_DPUT_TARGET=1 + - _DPL_PACKAGE_SOURCE=1 + - PACKAGE_DISTRO=focal + - PACKAGE_SUBVERSION=~20.04focal + #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5 - os: linux addons: apt: @@ -463,6 +487,30 @@ matrix: - PACKAGE_DISTRO=bionic - PACKAGE_SUBVERSION=~18.04bionic #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5 + - os: linux + addons: + apt: + packages: + - libsdl2-mixer-dev + - libpng-dev + - libgl1-mesa-dev + - libgme-dev + - libcurl4-openssl-dev + - p7zip-full + - gcc-4.8 + compiler: gcc-4.8 + dist: xenial + if: env(DPL_ENABLED) = "1" AND (env(_DPL_JOB_ENABLED) = "1" OR env(DPL_JOB_ENABLE_ALL) = "1") + AND (branch =~ /^.*deployer.*$/ OR (tag IS present AND env(DPL_TAG_ENABLED) = "1")) + AND env(DPL_TERMINATE_MAIN) != "1" + env: + - _DPL_JOB_ENABLED=1 + - _DPL_JOB_NAME=xenial + - _DPL_DPUT_TARGET=1 + - _DPL_PACKAGE_SOURCE=1 + - PACKAGE_DISTRO=xenial + - PACKAGE_SUBVERSION=~16.04xenial + #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5 - os: linux addons: apt: @@ -505,59 +553,11 @@ matrix: AND env(DPL_TERMINATE_MAIN) != "1" env: - _DPL_JOB_ENABLED=1 - - _DPL_JOB_NAME=disco + - _DPL_JOB_NAME=eoan - _DPL_DPUT_TARGET=1 - _DPL_PACKAGE_SOURCE=1 - - PACKAGE_DISTRO=disco - - PACKAGE_SUBVERSION=~19.04disco - #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5 - - os: linux - addons: - apt: - packages: - - libsdl2-mixer-dev - - libpng-dev - - libgl1-mesa-dev - - libgme-dev - - libcurl4-openssl-dev - - p7zip-full - - gcc-4.8 - compiler: gcc-4.8 - dist: xenial - if: env(DPL_ENABLED) = "1" AND (env(_DPL_JOB_ENABLED) = "1" OR env(DPL_JOB_ENABLE_ALL) = "1") - AND (branch =~ /^.*deployer.*$/ OR (tag IS present AND env(DPL_TAG_ENABLED) = "1")) - AND env(DPL_TERMINATE_MAIN) != "1" - env: - - _DPL_JOB_ENABLED=1 - - _DPL_JOB_NAME=cosmic - - _DPL_DPUT_TARGET=1 - - _DPL_PACKAGE_SOURCE=1 - - PACKAGE_DISTRO=cosmic - - PACKAGE_SUBVERSION=~18.10cosmic - #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5 - - os: linux - addons: - apt: - packages: - - libsdl2-mixer-dev - - libpng-dev - - libgl1-mesa-dev - - libgme-dev - - libcurl4-openssl-dev - - p7zip-full - - gcc-4.8 - compiler: gcc-4.8 - dist: xenial - if: env(DPL_ENABLED) = "1" AND (env(_DPL_JOB_ENABLED) = "1" OR env(DPL_JOB_ENABLE_ALL) = "1") - AND (branch =~ /^.*deployer.*$/ OR (tag IS present AND env(DPL_TAG_ENABLED) = "1")) - AND env(DPL_TERMINATE_MAIN) != "1" - env: - - _DPL_JOB_ENABLED=1 - - _DPL_JOB_NAME=xenial - - _DPL_DPUT_TARGET=1 - - _DPL_PACKAGE_SOURCE=1 - - PACKAGE_DISTRO=xenial - - PACKAGE_SUBVERSION=~16.04xenial + - PACKAGE_DISTRO=eoan + - PACKAGE_SUBVERSION=~19.10eoan #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5 allow_failures: - compiler: clang-3.5 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c0d673a6..cbb0fa20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ set(SRB2_CORE_SOURCES m_random.c md5.c mserv.c + http-mserv.c s_sound.c screen.c sounds.c diff --git a/src/Makefile b/src/Makefile index 8eda8c40..dd678cb2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -548,6 +548,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/w_wad.o \ $(OBJDIR)/filesrch.o \ $(OBJDIR)/mserv.o \ + $(OBJDIR)/http-mserv.o\ $(OBJDIR)/i_tcp.o \ $(OBJDIR)/lzf.o \ $(OBJDIR)/vid_copy.o \ diff --git a/src/command.c b/src/command.c index bd3a2243..95fd5ef4 100644 --- a/src/command.c +++ b/src/command.c @@ -543,10 +543,7 @@ static void COM_ExecuteString(char *ptext) if (!stricmp(com_argv[0], a->name)) { if (recursion > MAX_ALIAS_RECURSION) - { CONS_Alert(CONS_WARNING, M_GetText("Alias recursion cycle detected!\n")); - recursion = 0; - } else { char buf[1024]; @@ -578,8 +575,10 @@ static void COM_ExecuteString(char *ptext) } WRITESTRING(write, read); + // Monster Iestyn: keep track of how many levels of recursion we're in recursion++; COM_BufInsertText(buf); + recursion--; } return; } diff --git a/src/console.c b/src/console.c index 28958089..11a7961e 100644 --- a/src/console.c +++ b/src/console.c @@ -31,6 +31,7 @@ #include "i_video.h" #include "z_zone.h" #include "i_system.h" +#include "i_threads.h" #include "d_main.h" #include "m_menu.h" #include "filesrch.h" @@ -45,6 +46,16 @@ #define MAXHUDLINES 20 +#ifdef HAVE_THREADS +I_mutex con_mutex; + +# define Lock_state() I_lock_mutex(&con_mutex) +# define Unlock_state() I_unlock_mutex(con_mutex) +#else/*HAVE_THREADS*/ +# define Lock_state() +# define Unlock_state() +#endif/*HAVE_THREADS*/ + static boolean con_started = false; // console has been initialised boolean con_startup = false; // true at game startup, screen need refreshing static boolean con_forcepic = true; // at startup toggle console translucency when first off @@ -170,6 +181,8 @@ static void CONS_hudlines_Change(void) { INT32 i; + Lock_state(); + // Clear the currently displayed lines for (i = 0; i < con_hudlines; i++) con_hudtime[i] = 0; @@ -181,6 +194,8 @@ static void CONS_hudlines_Change(void) con_hudlines = cons_hudlines.value; + Unlock_state(); + CONS_Printf(M_GetText("Number of console HUD lines is now %d\n"), con_hudlines); } @@ -188,12 +203,16 @@ static void CONS_hudlines_Change(void) // static void CONS_Clear_f(void) { + Lock_state(); + memset(con_buffer, 0, CON_BUFFERSIZE); con_cx = 0; con_cy = con_totallines-1; con_line = &con_buffer[con_cy*con_width]; con_scrollup = 0; + + Unlock_state(); } // Choose english keymap @@ -369,20 +388,29 @@ void CON_Init(void) for (i = 0; i < NUMINPUTS; i++) bindtable[i] = NULL; + Lock_state(); + // clear all lines memset(con_buffer, 0, CON_BUFFERSIZE); // make sure it is ready for the loading screen con_width = 0; + + Unlock_state(); + CON_RecalcSize(); CON_SetupColormaps(); + Lock_state(); + //note: CON_Ticker should always execute at least once before D_Display() con_clipviewtop = -1; // -1 does not clip con_hudlines = atoi(cons_hudlines.defaultvalue); + Unlock_state(); + // setup console input filtering CON_InputInit(); @@ -391,15 +419,23 @@ void CON_Init(void) COM_AddCommand("cls", CONS_Clear_f); //COM_AddCommand("english", CONS_English_f); // set console full screen for game startup MAKE SURE VID_Init() done !!! + Lock_state(); + con_destlines = vid.height; con_curlines = vid.height; + Unlock_state(); if (!dedicated) { + Lock_state(); + con_started = true; con_startup = true; // need explicit screen refresh until we are in Doom loop consoletoggle = false; + + Unlock_state(); + CV_RegisterVar(&cons_msgtimeout); CV_RegisterVar(&cons_hudlines); CV_RegisterVar(&cons_speed); @@ -411,19 +447,27 @@ void CON_Init(void) } else { + Lock_state(); + con_started = true; con_startup = false; // need explicit screen refresh until we are in Doom loop consoletoggle = true; + + Unlock_state(); } } // Console input initialization // static void CON_InputInit(void) { + Lock_state(); + // prepare the first prompt line memset(inputlines, 0, sizeof (inputlines)); inputline = 0; input_cur = input_sel = input_len = 0; + + Unlock_state(); } //====================================================================== @@ -439,6 +483,8 @@ static void CON_RecalcSize(void) char *tmp_buffer; char *string; + Lock_state(); + switch (cv_constextsize.value) { case V_NOSCALEPATCH: @@ -476,11 +522,18 @@ static void CON_RecalcSize(void) // check for change of video width if (conw == con_width) + { + Unlock_state(); return; // didn't change + } + + Unlock_state(); tmp_buffer = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL); string = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL); // BP: it is a line but who know + Lock_state(); + oldcon_width = con_width; oldnumlines = con_totallines; oldcon_cy = con_cy; @@ -501,6 +554,8 @@ static void CON_RecalcSize(void) con_line = &con_buffer[con_cy*con_width]; con_scrollup = 0; + Unlock_state(); + // re-arrange console text buffer to keep text if (oldcon_width) // not the first time { @@ -525,7 +580,11 @@ static void CON_RecalcSize(void) static void CON_ChangeHeight(void) { - INT32 minheight = 20 * con_scalefactor; // 20 = 8+8+4 + INT32 minheight; + + Lock_state(); + + minheight = 20 * con_scalefactor; // 20 = 8+8+4 // toggle console in con_destlines = (cons_height.value*vid.height)/100; @@ -535,13 +594,19 @@ static void CON_ChangeHeight(void) con_destlines = vid.height; con_destlines &= ~0x3; // multiple of text row height + + Unlock_state(); } // Handles Console moves in/out of screen (per frame) // static void CON_MoveConsole(void) { - const fixed_t conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT); + fixed_t conspeed; + + Lock_state(); + + conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT); // instant if (!cons_speed.value) @@ -563,6 +628,8 @@ static void CON_MoveConsole(void) if (con_curlines < con_destlines) con_curlines = con_destlines; } + + Unlock_state(); } INT32 CON_ShiftChar(INT32 ch) @@ -587,27 +654,44 @@ void CON_ClearHUD(void) { INT32 i; + Lock_state(); + for (i = 0; i < con_hudlines; i++) con_hudtime[i] = 0; + + Unlock_state(); } // Force console to move out immediately // note: con_ticker will set consoleready false void CON_ToggleOff(void) { + Lock_state(); + if (!con_destlines) + { + Unlock_state(); return; + } con_destlines = 0; con_curlines = 0; CON_ClearHUD(); con_forcepic = 0; con_clipviewtop = -1; // remove console clipping of view + + Unlock_state(); } boolean CON_Ready(void) { - return consoleready; + boolean ready; + Lock_state(); + { + ready = consoleready; + } + Unlock_state(); + return ready; } // Console ticker: handles console move in/out, cursor blinking @@ -615,7 +699,11 @@ boolean CON_Ready(void) void CON_Ticker(void) { INT32 i; - INT32 minheight = 20 * con_scalefactor; // 20 = 8+8+4 + INT32 minheight; + + Lock_state(); + + minheight = 20 * con_scalefactor; // 20 = 8+8+4 // cursor blinking con_tick++; @@ -673,6 +761,8 @@ void CON_Ticker(void) if (con_hudtime[i] < 0) con_hudtime[i] = 0; } + + Unlock_state(); } // @@ -684,32 +774,51 @@ void CON_Ticker(void) static void CON_InputClear(void) { + Lock_state(); + memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS); input_cur = input_sel = input_len = 0; + + Unlock_state(); } static void CON_InputSetString(const char *c) { + Lock_state(); + memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS); strcpy(inputlines[inputline], c); input_cur = input_sel = input_len = strlen(c); + + Unlock_state(); } static void CON_InputAddString(const char *c) { size_t csize = strlen(c); + + Lock_state(); + if (input_len + csize > CON_MAXPROMPTCHARS-1) + { + Unlock_state(); return; + } if (input_cur != input_len) memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur); memcpy(&inputlines[inputline][input_cur], c, csize); input_len += csize; input_sel = (input_cur += csize); + + Unlock_state(); } static void CON_InputDelSelection(void) { size_t start, end, len; + + Lock_state(); + if (input_cur > input_sel) { start = input_sel; @@ -728,27 +837,39 @@ static void CON_InputDelSelection(void) input_len -= len; input_sel = input_cur = start; + + Unlock_state(); } static void CON_InputAddChar(char c) { if (input_len >= CON_MAXPROMPTCHARS-1) return; + + Lock_state(); + if (input_cur != input_len) memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur); inputlines[inputline][input_cur++] = c; inputlines[inputline][++input_len] = 0; input_sel = input_cur; + + Unlock_state(); } static void CON_InputDelChar(void) { if (!input_cur) return; + + Lock_state(); + if (input_cur != input_len) memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur); inputlines[inputline][--input_len] = 0; input_sel = --input_cur; + + Unlock_state(); } // @@ -1174,6 +1295,8 @@ static void CON_Print(char *msg) S_StartSound(NULL, sfx_radio); } + Lock_state(); + if (!(*msg & 0x80)) { con_line[con_cx++] = '\x80'; @@ -1234,7 +1357,10 @@ static void CON_Print(char *msg) } if (*msg == '\0') + { + Unlock_state(); return; + } // printable character for (l = 0; l < (con_width-11) && msg[l] > ' '; l++) @@ -1252,6 +1378,8 @@ static void CON_Print(char *msg) for (; l > 0; l--) con_line[con_cx++] = *(msg++); } + + Unlock_state(); } void CON_LogMessage(const char *msg) @@ -1283,6 +1411,7 @@ void CONS_Printf(const char *fmt, ...) { va_list argptr; static char *txt = NULL; + boolean startup; if (txt == NULL) txt = malloc(8192); @@ -1315,11 +1444,16 @@ void CONS_Printf(const char *fmt, ...) CON_LogMessage(txt); #endif + Lock_state(); + // make sure new text is visible con_scrollup = 0; + startup = con_startup; + + Unlock_state(); // if not in display loop, force screen update - if (con_startup) + if (startup) { #if (defined (_WINDOWS)) || (defined (__OS2__) && !defined (HAVE_SDL)) patch_t *con_backpic = W_CachePatchName("KARTKREW", PU_CACHE); @@ -1633,8 +1767,13 @@ static void CON_DrawConsole(void) // void CON_Drawer(void) { + Lock_state(); + if (!con_started || !graphics_started) + { + Unlock_state(); return; + } if (con_recalc) CON_RecalcSize(); @@ -1644,4 +1783,6 @@ void CON_Drawer(void) else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_VOTING || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS) CON_DrawHudlines(); + + Unlock_state(); } diff --git a/src/console.h b/src/console.h index 11621746..ba0141a0 100644 --- a/src/console.h +++ b/src/console.h @@ -12,6 +12,7 @@ #include "d_event.h" #include "command.h" +#include "i_threads.h" #ifdef _WII void CON_InitWii(void); @@ -21,6 +22,10 @@ void CON_Init(void); boolean CON_Responder(event_t *ev); +#ifdef HAVE_THREADS +extern I_mutex con_mutex; +#endif + // set true when screen size has changed, to adapt console extern boolean con_recalc; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5b02fc5e..6b8c107b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -76,7 +76,7 @@ boolean server = true; // true or false but !server == client #define client (!server) boolean nodownload = false; -static boolean serverrunning = false; +boolean serverrunning = false; INT32 serverplayer = 0; char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) @@ -1351,7 +1351,7 @@ static boolean CL_AskFileList(INT32 firstfile) netbuffer->packettype = PT_TELLFILESNEEDED; netbuffer->u.filesneedednum = firstfile; - return HSendPacket(servernode, true, 0, sizeof (INT32)); + return HSendPacket(servernode, false, 0, sizeof (INT32)); } /** Sends a special packet to declare how many players in local @@ -1829,7 +1829,7 @@ static void CL_LoadReceivedSavegame(void) #endif #ifndef NONET -static void SendAskInfo(INT32 node, boolean viams) +static void SendAskInfo(INT32 node) { const tic_t asktime = I_GetTime(); netbuffer->packettype = PT_ASKINFO; @@ -1840,10 +1840,6 @@ static void SendAskInfo(INT32 node, boolean viams) // now allowed traffic from the host to us in, so once the MS relays // our address to the host, it'll be able to speak to us. HSendPacket(node, false, 0, sizeof (askinfo_pak)); - - // Also speak to the MS. - if (viams && node != 0 && node != BROADCASTADDR) - SendAskInfoViaMS(node, asktime); } serverelem_t serverlist[MAXSERVERLIST]; @@ -1909,13 +1905,96 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node) M_SortServerList(); } +#ifdef HAVE_THREADS +struct Fetch_servers_ctx +{ + int room; + int id; +}; + +static void +Fetch_servers_thread (struct Fetch_servers_ctx *ctx) +{ + msg_server_t *server_list; + + server_list = GetShortServersList(ctx->room, ctx->id); + + if (server_list) + { + I_lock_mutex(&ms_QueryId_mutex); + { + if (ctx->id != ms_QueryId) + { + free(server_list); + server_list = NULL; + } + } + I_unlock_mutex(ms_QueryId_mutex); + + if (server_list) + { + I_lock_mutex(&m_menu_mutex); + { + if (m_waiting_mode == M_WAITING_SERVERS) + m_waiting_mode = M_NOT_WAITING; + } + I_unlock_mutex(m_menu_mutex); + + I_lock_mutex(&ms_ServerList_mutex); + { + ms_ServerList = server_list; + } + I_unlock_mutex(ms_ServerList_mutex); + } + } + + free(ctx); +} +#endif/*HAVE_THREADS*/ + +void CL_QueryServerList (msg_server_t *server_list) +{ + INT32 i; + + for (i = 0; server_list[i].header.buffer[0]; i++) + { + // Make sure MS version matches our own, to + // thwart nefarious servers who lie to the MS. + + /* lol bruh, that version COMES from the servers */ + //if (strcmp(version, server_list[i].version) == 0) + { + INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); + if (node == -1) + break; // no more node free + SendAskInfo(node); + // Force close the connection so that servers can't eat + // up nodes forever if we never get a reply back from them + // (usually when they've not forwarded their ports). + // + // Don't worry, we'll get in contact with the working + // servers again when they send SERVERINFO to us later! + // + // (Note: as a side effect this probably means every + // server in the list will probably be using the same node (e.g. node 1), + // not that it matters which nodes they use when + // the connections are closed afterwards anyway) + // -- Monster Iestyn 12/11/18 + Net_CloseConnection(node|FORCECLOSE); + } + } +} + void CL_UpdateServerList(boolean internetsearch, INT32 room) { +#ifdef HAVE_THREADS + struct Fetch_servers_ctx *ctx; +#endif + SL_ClearServerList(0); if (!netgame && I_NetOpenSocket) { - MSCloseUDPSocket(); // Tidy up before wiping the slate. if (I_NetOpenSocket()) { netgame = true; @@ -1925,56 +2004,36 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) // search for local servers if (netgame) - SendAskInfo(BROADCASTADDR, false); + SendAskInfo(BROADCASTADDR); if (internetsearch) { - const msg_server_t *server_list; - INT32 i = -1; - server_list = GetShortServersList(room); +#ifdef HAVE_THREADS + ctx = malloc(sizeof *ctx); + + /* This called from M_Refresh so I don't use a mutex */ + m_waiting_mode = M_WAITING_SERVERS; + + I_lock_mutex(&ms_QueryId_mutex); + { + ctx->id = ms_QueryId; + } + I_unlock_mutex(ms_QueryId_mutex); + + ctx->room = room; + + I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx); +#else + msg_server_t *server_list; + + server_list = GetShortServersList(room, 0); + if (server_list) { - char version[8] = ""; -#if VERSION > 0 || SUBVERSION > 0 - snprintf(version, sizeof (version), "%d.%d", VERSION, SUBVERSION); -#else - strcpy(version, GetRevisionString()); + CL_QueryServerList(server_list); + free(server_list); + } #endif - version[sizeof (version) - 1] = '\0'; - - for (i = 0; server_list[i].header.buffer[0]; i++) - { - // Make sure MS version matches our own, to - // thwart nefarious servers who lie to the MS. - - if (strcmp(version, server_list[i].version) == 0) - { - INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); - if (node == -1) - break; // no more node free - SendAskInfo(node, true); - // Force close the connection so that servers can't eat - // up nodes forever if we never get a reply back from them - // (usually when they've not forwarded their ports). - // - // Don't worry, we'll get in contact with the working - // servers again when they send SERVERINFO to us later! - // - // (Note: as a side effect this probably means every - // server in the list will probably be using the same node (e.g. node 1), - // not that it matters which nodes they use when - // the connections are closed afterwards anyway) - // -- Monster Iestyn 12/11/18 - Net_CloseConnection(node|FORCECLOSE); - } - } - } - - //no server list?(-1) or no servers?(0) - if (!i) - { - ; /// TODO: display error or warning? - } } } @@ -2044,11 +2103,11 @@ static boolean CL_FinishedFileList(void) CL_Reset(); D_StartTitle(); M_StartMessage(M_GetText( - "You have WAD files loaded or have\n" - "modified the game in some way, and\n" - "your file list does not match\n" - "the server's file list.\n" - "Please restart SRB2Kart before connecting.\n\n" + "You have the wrong addons loaded.\n\n" + "To play on this server, restart\n" + "the game and don't load any addons.\n" + "SRB2Kart will automatically add\n" + "everything you need when you join.\n\n" "Press ESC\n" ), NULL, MM_NOTHING); return false; @@ -2083,11 +2142,12 @@ static boolean CL_FinishedFileList(void) CL_Reset(); D_StartTitle(); M_StartMessage(M_GetText( - "You cannot connect to this server\n" - "because you cannot download the files\n" - "that you are missing from the server.\n\n" - "See the console or log file for\n" - "more details.\n\n" + "An error occured when trying to\n" + "download missing addons.\n" + "(This is almost always a problem\n" + "with the server, not your game.)\n\n" + "See the console or log file\n" + "for additional details.\n\n" "Press ESC\n" ), NULL, MM_NOTHING); return false; @@ -2149,14 +2209,13 @@ static boolean CL_FinishedFileList(void) /** Called by CL_ServerConnectionTicker * - * \param viams ??? * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. * \return False if the connection was aborted * \sa CL_ServerConnectionTicker * \sa CL_ConnectToServer * */ -static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) +static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) { #ifndef NONET INT32 i; @@ -2214,7 +2273,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) // Ask the info to the server (askinfo packet) if (*asksent + NEWTICRATE < I_GetTime()) { - SendAskInfo(servernode, viams); + SendAskInfo(servernode); *asksent = I_GetTime(); } #else @@ -2229,7 +2288,6 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) /** Called by CL_ConnectToServer * - * \param viams ??? * \param tmpsave The name of the gamestate file??? * \param oldtic Used for knowing when to poll events and redraw * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. @@ -2238,7 +2296,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) * \sa CL_ConnectToServer * */ -static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic_t *oldtic, tic_t *asksent) +static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) { boolean waitmore; INT32 i; @@ -2250,7 +2308,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic switch (cl_mode) { case CL_SEARCHING: - if (!CL_ServerConnectionSearchTicker(viams, asksent)) + if (!CL_ServerConnectionSearchTicker(asksent)) return false; break; @@ -2444,11 +2502,10 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic /** Use adaptive send using net_bandwidth and stat.sendbytes * - * \param viams ??? * \todo Better description... * */ -static void CL_ConnectToServer(boolean viams) +static void CL_ConnectToServer(void) { INT32 pnumnodes, nodewaited = doomcom->numnodes, i; tic_t oldtic; @@ -2520,9 +2577,9 @@ static void CL_ConnectToServer(boolean viams) { // If the connection was aborted for some reason, leave #ifndef NONET - if (!CL_ServerConnectionTicker(viams, tmpsave, &oldtic, &asksent)) + if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) #else - if (!CL_ServerConnectionTicker(viams, (char*)NULL, &oldtic, (tic_t *)NULL)) + if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL)) #endif return; @@ -2648,6 +2705,7 @@ static void Command_ClearBans(void) return; I_ClearBans(); + D_SaveBan(); reasontail = NULL; while (reasonhead) { @@ -2702,9 +2760,6 @@ static void Command_ReloadBan(void) //recheck ban.txt static void Command_connect(void) { - // Assume we connect directly. - boolean viams = false; - if (COM_Argc() < 2 || *COM_Argv(1) == 0) { CONS_Printf(M_GetText( @@ -2738,9 +2793,6 @@ static void Command_connect(void) if (netgame && !stricmp(COM_Argv(1), "node")) { servernode = (SINT8)atoi(COM_Argv(2)); - - // Use MS to traverse NAT firewalls. - viams = true; } else if (netgame) { @@ -2749,7 +2801,6 @@ static void Command_connect(void) } else if (I_NetOpenSocket) { - MSCloseUDPSocket(); // Tidy up before wiping the slate. I_NetOpenSocket(); netgame = true; multiplayer = true; @@ -2778,7 +2829,7 @@ static void Command_connect(void) } botingame = false; botskin = 0; - CL_ConnectToServer(viams); + CL_ConnectToServer(); } #endif @@ -3829,7 +3880,6 @@ boolean SV_SpawnServer(void) SV_GenContext(); if (netgame && I_NetOpenSocket) { - MSCloseUDPSocket(); // Tidy up before wiping the slate. I_NetOpenSocket(); if (ms_RoomId > 0) RegisterServer(); @@ -3837,7 +3887,7 @@ boolean SV_SpawnServer(void) // non dedicated server just connect to itself if (!dedicated) - CL_ConnectToServer(false); + CL_ConnectToServer(); else doomcom->numslots = 1; } @@ -5674,7 +5724,13 @@ FILESTAMP if (nowtime > resptime) { resptime = nowtime; +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Ticker(); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif CON_Ticker(); } SV_FileSendTicker(); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index e9b180df..a872b02e 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -18,6 +18,7 @@ #include "d_netcmd.h" #include "tables.h" #include "d_player.h" +#include "mserv.h" /* The 'packet version' is used to distinguish packet formats. @@ -531,6 +532,7 @@ typedef enum } kickreason_t; extern boolean server; +extern boolean serverrunning; #define client (!server) extern boolean dedicated; // For dedicated server extern UINT16 software_MAXPACKETLENGTH; @@ -577,6 +579,7 @@ void CL_RemoveSplitscreenPlayer(UINT8 p); void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); void CL_RemovePlayer(INT32 playernum, INT32 reason); +void CL_QueryServerList(msg_server_t *list); void CL_UpdateServerList(boolean internetsearch, INT32 room); // Is there a game running boolean Playing(void); diff --git a/src/d_main.c b/src/d_main.c index 15116b53..4c496c8a 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -50,6 +50,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "hu_stuff.h" #include "i_sound.h" #include "i_system.h" +#include "i_threads.h" #include "i_video.h" #include "m_argv.h" #include "m_menu.h" @@ -143,6 +144,8 @@ boolean advancedemo; INT32 debugload = 0; #endif +char savegamename[256]; + #ifdef _arch_dreamcast char srb2home[256] = "/cd"; char srb2path[256] = "/cd"; @@ -185,35 +188,6 @@ UINT8 shiftdown = 0; // 0x1 left, 0x2 right UINT8 ctrldown = 0; // 0x1 left, 0x2 right UINT8 altdown = 0; // 0x1 left, 0x2 right boolean capslock = 0; // gee i wonder what this does. -// -// D_ModifierKeyResponder -// Sets global shift/ctrl/alt variables, never actually eats events -// -static inline void D_ModifierKeyResponder(event_t *ev) -{ - if (ev->type == ev_keydown || ev->type == ev_console) switch (ev->data1) - { - case KEY_LSHIFT: shiftdown |= 0x1; return; - case KEY_RSHIFT: shiftdown |= 0x2; return; - case KEY_LCTRL: ctrldown |= 0x1; return; - case KEY_RCTRL: ctrldown |= 0x2; return; - case KEY_LALT: altdown |= 0x1; return; - case KEY_RALT: altdown |= 0x2; return; - case KEY_CAPSLOCK: capslock = !capslock; return; - - default: return; - } - else if (ev->type == ev_keyup) switch (ev->data1) - { - case KEY_LSHIFT: shiftdown &= ~0x1; return; - case KEY_RSHIFT: shiftdown &= ~0x2; return; - case KEY_LCTRL: ctrldown &= ~0x1; return; - case KEY_RCTRL: ctrldown &= ~0x2; return; - case KEY_LALT: altdown &= ~0x1; return; - case KEY_RALT: altdown &= ~0x2; return; - default: return; - } -} // // D_ProcessEvents @@ -223,6 +197,8 @@ void D_ProcessEvents(void) { event_t *ev; + boolean eaten; + for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) { ev = &events[eventtail]; @@ -244,7 +220,17 @@ void D_ProcessEvents(void) } // Menu input - if (M_Responder(ev)) +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + { + eaten = M_Responder(ev); + } +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + + if (eaten) continue; // menu ate the event // Demo input: @@ -253,7 +239,17 @@ void D_ProcessEvents(void) continue; // demo ate the event // console input - if (CON_Responder(ev)) +#ifdef HAVE_THREADS + I_lock_mutex(&con_mutex); +#endif + { + eaten = CON_Responder(ev); + } +#ifdef HAVE_THREADS + I_unlock_mutex(con_mutex); +#endif + + if (eaten) continue; // ate the event G_Responder(ev); @@ -280,7 +276,7 @@ static void D_Display(void) { if (nodrawers) return; // for comparative timing/profiling - + // check for change of screen size (video mode) if (setmodeneeded && !wipe) SCR_SetMode(); // change video mode @@ -549,7 +545,13 @@ static void D_Display(void) if (gamestate != GS_TIMEATTACK) CON_Drawer(); +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Drawer(); // menu is drawn even on top of everything +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif // focus lost moved to M_Drawer // diff --git a/src/d_net.h b/src/d_net.h index 30de2991..cc80fcaa 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -19,7 +19,9 @@ #define __D_NET__ // Max computers in a game -#define MAXNETNODES 64 +// 127 is probably as high as this can go, because +// SINT8 is used for nodes sometimes >:( +#define MAXNETNODES 127 #define BROADCASTADDR MAXNETNODES #define NETSPLITSCREEN // Kart's splitscreen netgame feature diff --git a/src/d_netfil.c b/src/d_netfil.c index fd28413b..621ce295 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -784,7 +784,7 @@ void SV_FileSendTicker(void) if (ram) M_Memcpy(p->data, &f->id.ram[transfer[i].position], size); else if (fread(p->data, 1, size, transfer[i].currentfile) != size) - I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, strerror(ferror(transfer[i].currentfile))); + I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile)); p->position = LONG(transfer[i].position); // Put flag so receiver knows the total size if (transfer[i].position + size == f->size) @@ -863,7 +863,7 @@ void Got_Filetxpak(void) // We can receive packet in the wrong order, anyway all os support gaped file fseek(file->file, pos, SEEK_SET); if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1) - I_Error("Can't write to %s: %s\n",filename, strerror(ferror(file->file))); + I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file)); file->currentsize += size; // Finished? @@ -1106,7 +1106,7 @@ void CURLPrepareFile(const char* url, int dfilenum) // Only allow HTTP and HTTPS curl_easy_setopt(http_handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); - curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("SRB2Kart/v%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION)); // Set user agent as some servers won't accept invalid user agents. + curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("SRB2Kart/v%d.%d", VERSION, SUBVERSION)); // Set user agent as some servers won't accept invalid user agents. // Follow a redirect request, if sent by the server. curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1L); diff --git a/src/doomdef.h b/src/doomdef.h index ed41e346..9ee55d94 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -155,8 +155,8 @@ extern char logfilename[1024]; #else #define VERSION 1 // Game version #define SUBVERSION 2 // more precise version number -#define VERSIONSTRING "v1.2" -#define VERSIONSTRINGW L"v1.2" +#define VERSIONSTRING "v1.2 (HTTP MS)" +#define VERSIONSTRINGW L"v1.2 (HTTP MS)" // Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates! // And change CMakeLists.txt, for CMake users! // AND appveyor.yml, for the build bots! @@ -506,7 +506,7 @@ void CONS_Debug(INT32 debugflags, const char *fmt, ...) FUNCDEBUG; // Things that used to be in dstrings.h #define SAVEGAMENAME "srb2sav" -char savegamename[256]; +extern char savegamename[256]; // m_misc.h #ifdef GETTEXT diff --git a/src/f_finale.c b/src/f_finale.c index 909ca261..ade26bbe 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -24,6 +24,7 @@ #include "w_wad.h" #include "z_zone.h" #include "i_system.h" +#include "i_threads.h" #include "m_menu.h" #include "dehacked.h" #include "g_input.h" @@ -317,7 +318,13 @@ void F_IntroDrawer(void) { I_OsPolling(); I_UpdateNoBlit(); +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Drawer(); // menu is drawn even on top of wipes +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 } } diff --git a/src/f_wipe.c b/src/f_wipe.c index bb1397bc..8bcb1278 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -22,6 +22,7 @@ #include "z_zone.h" #include "i_system.h" +#include "i_threads.h" #include "m_menu.h" #include "console.h" #include "d_main.h" @@ -379,7 +380,15 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu) I_UpdateNoBlit(); if (drawMenu) + { +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Drawer(); // menu is drawn even on top of wipes +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + } I_FinishUpdate(); // page flip or blit buffer diff --git a/src/g_game.c b/src/g_game.c index bf0557d0..dc5874b6 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6368,7 +6368,9 @@ void G_BeginRecording(void) demoflags |= DF_ENCORE; #ifdef HAVE_BLUA - demoflags |= DF_LUAVARS; + if (!modeattacking) // Ghosts don't read luavars, and you shouldn't ever need to save Lua in replays, you doof! + // SERIOUSLY THOUGH WHY WOULD YOU LOAD HOSTMOD AND RECORD A GHOST WITH IT !???? + demoflags |= DF_LUAVARS; #endif // Setup header. @@ -6474,8 +6476,9 @@ void G_BeginRecording(void) WRITEUINT8(demo_p, 0xFF); // Denote the end of the player listing #ifdef HAVE_BLUA - // player lua vars, always saved even if empty - LUA_ArchiveDemo(); + // player lua vars, always saved even if empty... Unless it's record attack. + if (!modeattacking) + LUA_ArchiveDemo(); #endif memset(&oldcmd,0,sizeof(oldcmd)); @@ -7543,6 +7546,7 @@ void G_DoPlayDemo(char *defdemoname) if (!gL) // No Lua state! ...I guess we'll just start one... LUA_ClearState(); + // No modeattacking check, DF_LUAVARS won't be present here. LUA_UnArchiveDemo(); } #endif diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 1bad26ee..c7ff5a4a 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -407,7 +407,7 @@ void HWR_RenderPlane(extrasubsector_t *xsub, boolean isceiling, fixed_t fixedhei if (angle) // Only needs to be done if there's an altered angle { - angle = (InvAngle(angle)+ANGLE_180)>>ANGLETOFINESHIFT; + angle = InvAngle(angle)>>ANGLETOFINESHIFT; // This needs to be done so that it scrolls in a different direction after rotation like software /*tempxsow = FLOAT_TO_FIXED(scrollx); @@ -439,7 +439,7 @@ void HWR_RenderPlane(extrasubsector_t *xsub, boolean isceiling, fixed_t fixedhei tempxsow = FLOAT_TO_FIXED(v3d->s); tempytow = FLOAT_TO_FIXED(v3d->t); v3d->s = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); - v3d->t = (FIXED_TO_FLOAT(-FixedMul(tempxsow, FINESINE(angle)) - FixedMul(tempytow, FINECOSINE(angle)))); + v3d->t = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINESINE(angle)) + FixedMul(tempytow, FINECOSINE(angle)))); } //v3d->s = (float)(v3d->s - flatxref + scrollx); diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 5dc46099..eef723e5 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -143,7 +143,7 @@ static const GLfloat byte2float[256] = { // -----------------+ #ifdef DEBUG_TO_FILE -FILE *gllogstream; +FILE *gllogstream = NULL; #endif FUNCPRINTF void GL_DBG_Printf(const char *format, ...) @@ -152,14 +152,14 @@ FUNCPRINTF void GL_DBG_Printf(const char *format, ...) char str[4096] = ""; va_list arglist; - if (!gllogstream) - gllogstream = fopen("ogllog.txt", "w"); + if (gllogstream) + { + va_start(arglist, format); + vsnprintf(str, 4096, format, arglist); + va_end(arglist); - va_start(arglist, format); - vsnprintf(str, 4096, format, arglist); - va_end(arglist); - - fwrite(str, strlen(str), 1, gllogstream); + fwrite(str, strlen(str), 1, gllogstream); + } #else (void)format; #endif @@ -823,7 +823,7 @@ EXPORT boolean HWRAPI(LoadShaders) (void) #ifdef GL_SHADERS GLuint gl_vertShader, gl_fragShader; GLint i, result; - + if (!pglUseProgram) return false; gl_customvertexshaders[0] = NULL; @@ -2002,11 +2002,11 @@ EXPORT void HWRAPI(RenderBatches) (int *sNumPolys, int *sNumVerts, int *sNumCall boolean stopFlag = false; boolean changeState = false; boolean changeShader = false; - GLuint nextShader; + GLuint nextShader = 0U; boolean changeTexture = false; - GLuint nextTexture; + GLuint nextTexture = 0U; boolean changePolyFlags = false; - FBITFIELD nextPolyFlags; + FBITFIELD nextPolyFlags = 0U; boolean changeSurfaceInfo = false; FSurfaceInfo nextSurfaceInfo; @@ -2307,7 +2307,7 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUI pglColor4ubv((GLubyte*)&pSurf->PolyColor.s); } - + // Tint color tint.red = byte2float[pSurf->TintColor.s.red]; tint.green = byte2float[pSurf->TintColor.s.green]; diff --git a/src/http-mserv.c b/src/http-mserv.c new file mode 100644 index 00000000..9d05b80e --- /dev/null +++ b/src/http-mserv.c @@ -0,0 +1,675 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by James R. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +// \brief HTTP based master server + +/* +Documentation available here. + + +*/ + +#include + +#include "doomdef.h" +#include "d_clisrv.h" +#include "command.h" +#include "m_argv.h" +#include "m_menu.h" +#include "mserv.h" +#include "i_tcp.h"/* for current_port */ +#include "i_threads.h" + +/* reasonable default I guess?? */ +#define DEFAULT_BUFFER_SIZE (4096) + +/* I just stop myself from making macros anymore. */ +#define Blame( ... ) \ + CONS_Printf("\x85" __VA_ARGS__) + +static void MasterServer_Debug_OnChange (void); + +consvar_t cv_masterserver_timeout = { + "masterserver_timeout", "5", CV_SAVE, CV_Unsigned, + NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ +}; + +consvar_t cv_masterserver_debug = { + "masterserver_debug", "Off", CV_SAVE|CV_CALL, CV_OnOff, + MasterServer_Debug_OnChange, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ +}; + +consvar_t cv_masterserver_token = { + "masterserver_token", "", CV_SAVE, NULL, + NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ +}; + +static int hms_started; + +static char *hms_api; +#ifdef HAVE_THREADS +static I_mutex hms_api_mutex; +#endif + +static char *hms_server_token; + +struct HMS_buffer +{ + CURL *curl; + char *buffer; + int needle; + int end; +}; + +static void +Contact_error (void) +{ + CONS_Alert(CONS_ERROR, + "There was a problem contacting the master server...\n" + ); +} + +static size_t +HMS_on_read (char *s, size_t _1, size_t n, void *userdata) +{ + struct HMS_buffer *buffer; + size_t blocks; + + (void)_1; + + buffer = userdata; + + if (n >= (size_t)( buffer->end - buffer->needle )) + { + /* resize to next multiple of buffer size */ + blocks = ( n / DEFAULT_BUFFER_SIZE + 1 ); + buffer->end += ( blocks * DEFAULT_BUFFER_SIZE ); + + buffer->buffer = realloc(buffer->buffer, buffer->end); + } + + memcpy(&buffer->buffer[buffer->needle], s, n); + buffer->needle += n; + + return n; +} + +static struct HMS_buffer * +HMS_connect (const char *format, ...) +{ + va_list ap; + CURL *curl; + char *url; + char *quack_token; + size_t seek; + size_t token_length; + struct HMS_buffer *buffer; + + if (! hms_started) + { + if (curl_global_init(CURL_GLOBAL_ALL) != 0) + { + Contact_error(); + Blame("From curl_global_init.\n"); + return NULL; + } + else + { + atexit(curl_global_cleanup); + hms_started = 1; + } + } + + curl = curl_easy_init(); + + if (! curl) + { + Contact_error(); + Blame("From curl_easy_init.\n"); + return NULL; + } + + if (cv_masterserver_token.string[0]) + { + quack_token = curl_easy_escape(curl, cv_masterserver_token.string, 0); + token_length = ( sizeof "?token="-1 )+ strlen(quack_token); + } + else + { + quack_token = NULL; + token_length = 0; + } + +#ifdef HAVE_THREADS + I_lock_mutex(&hms_api_mutex); +#endif + + seek = strlen(hms_api) + 1;/* + '/' */ + + va_start (ap, format); + url = malloc(seek + vsnprintf(0, 0, format, ap) + token_length + 1); + va_end (ap); + + sprintf(url, "%s/", hms_api); + +#ifdef HAVE_THREADS + I_unlock_mutex(hms_api_mutex); +#endif + + va_start (ap, format); + seek += vsprintf(&url[seek], format, ap); + va_end (ap); + + if (quack_token) + sprintf(&url[seek], "?token=%s", quack_token); + + CONS_Printf("HMS: connecting '%s'...\n", url); + + buffer = malloc(sizeof *buffer); + buffer->curl = curl; + buffer->end = DEFAULT_BUFFER_SIZE; + buffer->buffer = malloc(buffer->end); + buffer->needle = 0; + + if (cv_masterserver_debug.value) + { + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_STDERR, logstream); + } + + if (M_CheckParm("-bindaddr") && M_IsNextParm()) + { + curl_easy_setopt(curl, CURLOPT_INTERFACE, M_GetNextParm()); + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + + curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); + + curl_free(quack_token); + free(url); + + return buffer; +} + +static int +HMS_do (struct HMS_buffer *buffer) +{ + CURLcode cc; + long status; + + char *p; + + cc = curl_easy_perform(buffer->curl); + + if (cc != CURLE_OK) + { + Contact_error(); + Blame( + "From curl_easy_perform: %s\n", + curl_easy_strerror(cc) + ); + return 0; + } + + buffer->buffer[buffer->needle] = '\0'; + + curl_easy_getinfo(buffer->curl, CURLINFO_RESPONSE_CODE, &status); + + if (status != 200) + { + p = strchr(buffer->buffer, '\n'); + + if (p) + *p = '\0'; + + Contact_error(); + Blame( + "Master server error %ld: %s%s\n", + status, + buffer->buffer, + ( (p) ? "" : " (malformed)" ) + ); + + return 0; + } + else + return 1; +} + +static void +HMS_end (struct HMS_buffer *buffer) +{ + curl_easy_cleanup(buffer->curl); + free(buffer->buffer); + free(buffer); +} + +int +HMS_fetch_rooms (int joining, int query_id) +{ + struct HMS_buffer *hms; + int ok; + + int doing_shit; + + char *id; + char *title; + char *room_motd; + + int id_no; + + char *p; + char *end; + + int i; + + (void)query_id; + + hms = HMS_connect("rooms"); + + if (! hms) + return 0; + + if (HMS_do(hms)) + { + doing_shit = 1; + + p = hms->buffer; + + for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") );) + { + *end = '\0'; + + id = strtok(p, "\n"); + title = strtok(0, "\n"); + room_motd = strtok(0, ""); + + if (id && title && room_motd) + { + id_no = atoi(id); + + /* + Don't show the 'All' room if hosting. And it's a hack like this + because I'm way too lazy to add another feature to the MS. + */ + if (joining || id_no != 0) + { +#ifdef HAVE_THREADS + I_lock_mutex(&ms_QueryId_mutex); + { + if (query_id != ms_QueryId) + doing_shit = 0; + } + I_unlock_mutex(ms_QueryId_mutex); + + if (! doing_shit) + break; +#endif + + room_list[i].header.buffer[0] = 1; + + room_list[i].id = id_no; + strlcpy(room_list[i].name, title, sizeof room_list[i].name); + strlcpy(room_list[i].motd, room_motd, sizeof room_list[i].motd); + + i++; + } + + p = ( end + 3 );/* skip the three linefeeds */ + } + else + break; + } + + if (doing_shit) + room_list[i].header.buffer[0] = 0; + + ok = 1; + + if (doing_shit) + { +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + { + for (i = 0; room_list[i].header.buffer[0]; i++) + { + if(*room_list[i].name != '\0') + { + MP_RoomMenu[i+1].text = room_list[i].name; + roomIds[i] = room_list[i].id; + MP_RoomMenu[i+1].status = IT_STRING|IT_CALL; + } + } + } +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + } + } + else + ok = 0; + + HMS_end(hms); + + return ok; +} + +int +HMS_register (void) +{ + struct HMS_buffer *hms; + int ok; + + char post[256]; + + char *title; + + hms = HMS_connect("rooms/%d/register", ms_RoomId); + + if (! hms) + return 0; + + title = curl_easy_escape(hms->curl, cv_servername.string, 0); + + snprintf(post, sizeof post, + "port=%d&" + "title=%s&" + "version=%d.%d", + + current_port, + + title, + + VERSION, + SUBVERSION + ); + + curl_free(title); + + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); + + ok = HMS_do(hms); + + if (ok) + { + hms_server_token = strdup(strtok(hms->buffer, "\n")); + } + + HMS_end(hms); + + return ok; +} + +int +HMS_unlist (void) +{ + struct HMS_buffer *hms; + int ok; + + hms = HMS_connect("servers/%s/unlist", hms_server_token); + + if (! hms) + return 0; + + curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); + + ok = HMS_do(hms); + HMS_end(hms); + + free(hms_server_token); + + return ok; +} + +int +HMS_update (void) +{ + struct HMS_buffer *hms; + int ok; + + char post[256]; + + char *title; + + hms = HMS_connect("servers/%s/update", hms_server_token); + + if (! hms) + return 0; + + title = curl_easy_escape(hms->curl, cv_servername.string, 0); + + snprintf(post, sizeof post, + "title=%s", + title + ); + + curl_free(title); + + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); + + ok = HMS_do(hms); + HMS_end(hms); + + return ok; +} + +void +HMS_list_servers (void) +{ + struct HMS_buffer *hms; + + char *p; + + hms = HMS_connect("servers"); + + if (! hms) + return; + + if (HMS_do(hms)) + { + p = &hms->buffer[strlen(hms->buffer)]; + while (*--p == '\n') + ; + + CONS_Printf("%s\n", hms->buffer); + } + + HMS_end(hms); +} + +msg_server_t * +HMS_fetch_servers (msg_server_t *list, int room_number, int query_id) +{ + struct HMS_buffer *hms; + + int doing_shit; + + char local_version[9]; + + char *room; + + char *address; + char *port; + char *title; + char *version; + + char *end; + char *section_end; + char *p; + + int i; + + (void)query_id; + + if (room_number > 0) + { + hms = HMS_connect("rooms/%d/servers", room_number); + } + else + hms = HMS_connect("servers"); + + if (! hms) + return NULL; + + if (HMS_do(hms)) + { + doing_shit = 1; + + snprintf(local_version, sizeof local_version, + "%d.%d", + VERSION, + SUBVERSION + ); + + p = hms->buffer; + i = 0; + + do + { + section_end = strstr(p, "\n\n"); + + room = strtok(p, "\n"); + + p = strtok(0, ""); + + if (! p) + break; + + while (i < MAXSERVERLIST && ( end = strchr(p, '\n') )) + { + *end = '\0'; + + address = strtok(p, " "); + port = strtok(0, " "); + title = strtok(0, " "); + version = strtok(0, ""); + + if (address && port && title && version) + { +#ifdef HAVE_THREADS + I_lock_mutex(&ms_QueryId_mutex); + { + if (query_id != ms_QueryId) + doing_shit = 0; + } + I_unlock_mutex(ms_QueryId_mutex); + + if (! doing_shit) + break; +#endif + + if (strcmp(version, local_version) == 0) + { + strlcpy(list[i].ip, address, sizeof list[i].ip); + strlcpy(list[i].port, port, sizeof list[i].port); + strlcpy(list[i].name, title, sizeof list[i].name); + strlcpy(list[i].version, version, sizeof list[i].version); + + list[i].room = atoi(room); + + list[i].header.buffer[0] = 1; + + i++; + } + + if (end == section_end)/* end of list for this room */ + break; + else + p = ( end + 1 );/* skip server delimiter */ + } + else + { + section_end = 0;/* malformed so quit the parsing */ + break; + } + } + + if (! doing_shit) + break; + + p = ( section_end + 2 ); + } + while (section_end) ; + + if (doing_shit) + list[i].header.buffer[0] = 0; + } + else + list = NULL; + + HMS_end(hms); + + return list; +} + +int +HMS_compare_mod_version (char *buffer, size_t buffer_size) +{ + struct HMS_buffer *hms; + int ok; + + char *version; + char *version_name; + + hms = HMS_connect("versions/%d", MODID); + + if (! hms) + return 0; + + ok = 0; + + if (HMS_do(hms)) + { + version = strtok(hms->buffer, " "); + version_name = strtok(0, "\n"); + + if (version && version_name) + { + if (atoi(version) != MODVERSION) + { + strlcpy(buffer, version_name, buffer_size); + ok = 1; + } + else + ok = -1; + } + } + + HMS_end(hms); + + return ok; +} + +void +HMS_set_api (char *api) +{ +#ifdef HAVE_THREADS + I_lock_mutex(&hms_api_mutex); +#endif + { + free(hms_api); + hms_api = api; + } +#ifdef HAVE_THREADS + I_unlock_mutex(hms_api_mutex); +#endif +} + +static void +MasterServer_Debug_OnChange (void) +{ + /* TODO: change to 'latest-log.txt' for log files revision. */ + if (cv_masterserver_debug.value) + CONS_Printf("Master server debug messages will appear in log.txt\n"); +} diff --git a/src/i_threads.h b/src/i_threads.h new file mode 100644 index 00000000..45a3dcc3 --- /dev/null +++ b/src/i_threads.h @@ -0,0 +1,39 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by James R. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file i_threads.h +/// \brief Multithreading abstraction + +#ifdef HAVE_THREADS + +#ifndef I_THREADS_H +#define I_THREADS_H + +typedef void (*I_thread_fn)(void *userdata); + +typedef void * I_mutex; +typedef void * I_cond; + +void I_start_threads (void); +void I_stop_threads (void); + +void I_spawn_thread (const char *name, I_thread_fn, void *userdata); + +/* check in your thread whether to return early */ +int I_thread_is_stopped (void); + +void I_lock_mutex (I_mutex *); +void I_unlock_mutex (I_mutex); + +void I_hold_cond (I_cond *, I_mutex); + +void I_wake_one_cond (I_cond *); +void I_wake_all_cond (I_cond *); + +#endif/*I_THREADS_H*/ +#endif/*HAVE_THREADS*/ diff --git a/src/k_kart.c b/src/k_kart.c index cc5504bb..b4c2992e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -898,6 +898,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp newodds = 0; else POWERITEMODDS(newodds); + break; case KITEM_HYUDORO: if ((hyubgone > 0) || COOLDOWNONSTART) newodds = 0; @@ -1061,6 +1062,10 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) bestbumper = players[i].kartstuff[k_bumper]; } + // No forced SPB in 1v1s, it has to be randomly rolled + if (pingame <= 2) + dontforcespb = true; + // This makes the roulette produce the random noises. if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsDisplayPlayer(player) && !demo.freecam) { @@ -3312,7 +3317,8 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter) if (!thismine || P_MobjWasRemoved(thismine)) return; - if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine + //This guarantees you hit a mine being dragged + if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine, and clean up the old one { mine = P_SpawnMobj(thismine->x, thismine->y, thismine->z, MT_SSMINE); P_SetTarget(&mine->target, thismine->target); @@ -3320,7 +3326,19 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter) mine->flags2 = thismine->flags2; mine->floorz = thismine->floorz; mine->ceilingz = thismine->ceilingz; + + //Since we aren't using P_KillMobj, we need to clean up the hnext reference + { + P_SetTarget(&thismine->target->hnext, NULL); //target is the player who owns the mine + thismine->target->player->kartstuff[k_bananadrag] = 0; + thismine->target->player->kartstuff[k_itemheld] = 0; + + if (--thismine->target->player->kartstuff[k_itemamount] <= 0) + thismine->target->player->kartstuff[k_itemtype] = KITEM_NONE; + } + P_RemoveMobj(thismine); + } else mine = thismine; @@ -3870,6 +3888,62 @@ void K_DropItems(player_t *player) K_StripItems(player); } +void K_DropRocketSneaker(player_t *player) +{ + mobj_t *shoe = player->mo; + fixed_t flingangle; + boolean leftshoe = true; //left shoe is first + + if (!(player->mo && !P_MobjWasRemoved(player->mo) && player->mo->hnext && !P_MobjWasRemoved(player->mo->hnext))) + return; + + while ((shoe = shoe->hnext) && !P_MobjWasRemoved(shoe)) + { + if (shoe->type != MT_ROCKETSNEAKER) + return; //woah, not a rocketsneaker, bail! safeguard in case this gets used when you're holding non-rocketsneakers + + shoe->flags2 &= ~MF2_DONTDRAW; + shoe->flags &= ~MF_NOGRAVITY; + shoe->angle += ANGLE_45; + + if (shoe->eflags & MFE_VERTICALFLIP) + shoe->z -= shoe->height; + else + shoe->z += shoe->height; + + //left shoe goes off tot eh left, right shoe off to the right + if (leftshoe) + flingangle = -(ANG60); + else + flingangle = ANG60; + + S_StartSound(shoe, shoe->info->deathsound); + P_SetObjectMomZ(shoe, 8*FRACUNIT, false); + P_InstaThrust(shoe, R_PointToAngle2(shoe->target->x, shoe->target->y, shoe->x, shoe->y)+flingangle, 16*FRACUNIT); + shoe->momx += shoe->target->momx; + shoe->momy += shoe->target->momy; + shoe->momz += shoe->target->momz; + shoe->extravalue2 = 1; + + leftshoe = false; + } + P_SetTarget(&player->mo->hnext, NULL); + player->kartstuff[k_rocketsneakertimer] = 0; +} + +void K_DropKitchenSink(player_t *player) +{ + if (!(player->mo && !P_MobjWasRemoved(player->mo) && player->mo->hnext && !P_MobjWasRemoved(player->mo->hnext))) + return; + + if (player->mo->hnext->type != MT_SINK_SHIELD) + return; //so we can just call this function regardless of what is being held + + P_KillMobj(player->mo->hnext, NULL, NULL); + + P_SetTarget(&player->mo->hnext, NULL); +} + // When an item in the hnext chain dies. void K_RepairOrbitChain(mobj_t *orbit) { @@ -4490,6 +4564,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) fast->momx = 3*player->mo->momx/4; fast->momy = 3*player->mo->momy/4; fast->momz = 3*player->mo->momz/4; + P_SetTarget(&fast->target, player->mo); // easier lua access K_MatchGenericExtraFlags(fast, player->mo); } @@ -5195,12 +5270,12 @@ void K_KartUpdatePosition(player_t *player) // void K_StripItems(player_t *player) { + K_DropRocketSneaker(player); + K_DropKitchenSink(player); player->kartstuff[k_itemtype] = KITEM_NONE; player->kartstuff[k_itemamount] = 0; player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_rocketsneakertimer] = 0; - if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2) { player->kartstuff[k_itemroulette] = 0; @@ -8634,7 +8709,7 @@ void K_drawKartFreePlay(UINT32 flashtime) return; V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy - LAPS_Y+3, V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); + LAPS_Y+3, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_HUDTRANS, "FREE PLAY"); } static void K_drawDistributionDebugger(void) diff --git a/src/k_kart.h b/src/k_kart.h index 2ba5d1bd..79606540 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -11,7 +11,7 @@ #define KART_FULLTURN 800 -UINT8 colortranslations[MAXTRANSLATIONS][16]; +extern UINT8 colortranslations[MAXTRANSLATIONS][16]; extern const char *KartColor_Names[MAXSKINCOLORS]; extern const UINT8 KartColor_Opposite[MAXSKINCOLORS*2]; void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor); @@ -55,6 +55,8 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue); INT32 K_GetKartDriftSparkValue(player_t *player); void K_KartUpdatePosition(player_t *player); void K_DropItems(player_t *player); +void K_DropRocketSneaker(player_t *player); +void K_DropKitchenSink(player_t *player); void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index d9766513..3aeeed73 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -17,6 +17,7 @@ #include "d_player.h" #include "g_game.h" #include "p_local.h" +#include "d_clisrv.h" #include "lua_script.h" #include "lua_libs.h" @@ -118,7 +119,7 @@ static int lib_iterateDisplayplayers(lua_State *L) for (i++; i < MAXSPLITSCREENPLAYERS; i++) { - if (!playeringame[displayplayers[i]] || i > splitscreen) + if (i > splitscreen || !playeringame[displayplayers[i]]) return 0; // Stop! There are no more players for us to go through. There will never be a player gap in displayplayers. if (!players[displayplayers[i]].mo) @@ -139,6 +140,8 @@ static int lib_getDisplayplayers(lua_State *L) lua_Integer i = luaL_checkinteger(L, 2); if (i < 0 || i >= MAXSPLITSCREENPLAYERS) return luaL_error(L, "displayplayers[] index %d out of range (0 - %d)", i, MAXSPLITSCREENPLAYERS-1); + if (i > splitscreen) + return 0; if (!playeringame[displayplayers[i]]) return 0; if (!players[displayplayers[i]].mo) @@ -385,6 +388,8 @@ static int player_get(lua_State *L) else if (fastcmp(field,"fovadd")) lua_pushfixed(L, plr->fovadd); #endif + else if (fastcmp(field,"ping")) + lua_pushinteger(L, playerpingtable[( plr - players )]); else { lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/m_argv.c b/src/m_argv.c index e8bfdd3d..e1046f8a 100644 --- a/src/m_argv.c +++ b/src/m_argv.c @@ -16,6 +16,7 @@ #include "doomdef.h" #include "command.h" #include "m_argv.h" +#include "m_misc.h" /** \brief number of arg */ @@ -166,7 +167,7 @@ void M_FindResponseFile(void) if (!file) I_Error("No more free memory for the response file"); if (fread(file, size, 1, handle) != 1) - I_Error("Couldn't read response file because %s", strerror(ferror(handle))); + I_Error("Couldn't read response file because %s", M_FileError(handle)); fclose(handle); // keep all the command line arguments following @responsefile diff --git a/src/m_menu.c b/src/m_menu.c index e08a53c5..850b8d74 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -32,6 +32,7 @@ #include "sounds.h" #include "s_sound.h" #include "i_system.h" +#include "i_threads.h" // Addfile #include "filesrch.h" @@ -118,6 +119,12 @@ typedef enum NUM_QUITMESSAGES } text_enum; +#ifdef HAVE_THREADS +I_mutex m_menu_mutex; +#endif + +M_waiting_mode_t m_waiting_mode = M_NOT_WAITING; + const char *quitmsg[NUM_QUITMESSAGES]; // Stuff for customizing the player select screen Tails 09-22-2003 @@ -1039,7 +1046,7 @@ enum FIRSTSERVERLINE }; -static menuitem_t MP_RoomMenu[] = +menuitem_t MP_RoomMenu[] = { {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9}, {IT_DISABLED, NULL, "", M_ChooseRoom, 18}, @@ -2896,7 +2903,6 @@ boolean M_Responder(event_t *ev) //make sure the game doesn't still think we're in a netgame. if (!Playing() && netgame && multiplayer) { - MSCloseUDPSocket(); // Clean up so we can re-open the connection later. netgame = false; multiplayer = false; } @@ -3304,6 +3310,30 @@ void M_SetupNextMenu(menu_t *menudef) { INT16 i; +#ifdef HAVE_THREADS + if (currentMenu == &MP_RoomDef || currentMenu == &MP_ConnectDef) + { + I_lock_mutex(&ms_QueryId_mutex); + { + ms_QueryId++; + } + I_unlock_mutex(ms_QueryId_mutex); + } + + if (currentMenu == &MP_ConnectDef) + { + I_lock_mutex(&ms_ServerList_mutex); + { + if (ms_ServerList) + { + free(ms_ServerList); + ms_ServerList = NULL; + } + } + I_unlock_mutex(ms_ServerList_mutex); + } +#endif/*HAVE_THREADS*/ + if (currentMenu->quitroutine) { // If you're going from a menu to itself, why are you running the quitroutine? You're not quitting it! -SH @@ -3361,6 +3391,19 @@ void M_Ticker(void) if (--vidm_testingmode == 0) setmodeneeded = vidm_previousmode + 1; } + +#ifdef HAVE_THREADS + I_lock_mutex(&ms_ServerList_mutex); + { + if (ms_ServerList) + { + CL_QueryServerList(ms_ServerList); + free(ms_ServerList); + ms_ServerList = NULL; + } + } + I_unlock_mutex(ms_ServerList_mutex); +#endif } // @@ -4833,7 +4876,7 @@ static boolean M_AddonsRefresh(void) else if (majormods && !prevmajormods) { S_StartSound(NULL, sfx_s221); - message = va("%c%s\x80\nGameplay has now been modified.\nIf you wish to play Record Attack mode, restart the game to clear existing addons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); + message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); prevmajormods = majormods; } @@ -8327,22 +8370,65 @@ static INT32 menuRoomIndex = 0; static void M_DrawRoomMenu(void) { + static int frame = -12; + int dot_frame; + char text[4]; + const char *rmotd; + const char *waiting_message; + + int dots; + + if (m_waiting_mode) + { + dot_frame = frame / 4; + dots = dot_frame + 3; + + strcpy(text, " "); + + if (dots > 0) + { + if (dot_frame < 0) + dot_frame = 0; + + strncpy(&text[dot_frame], "...", min(dots, 3 - dot_frame)); + } + + if (++frame == 12) + frame = -12; + + currentMenu->menuitems[0].text = text; + } // use generic drawer for cursor, items and title M_DrawGenericMenu(); V_DrawString(currentMenu->x - 16, currentMenu->y, highlightflags, M_GetText("Select a room")); - M_DrawTextBox(144, 24, 20, 20); + if (m_waiting_mode == M_NOT_WAITING) + { + M_DrawTextBox(144, 24, 20, 20); - if (itemOn == 0) - rmotd = M_GetText("Don't connect to the Master Server."); - else - rmotd = room_list[itemOn-1].motd; + if (itemOn == 0) + rmotd = M_GetText("Don't connect to the Master Server."); + else + rmotd = room_list[itemOn-1].motd; - rmotd = V_WordWrap(0, 20*8, 0, rmotd); - V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd); + rmotd = V_WordWrap(0, 20*8, 0, rmotd); + V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd); + } + + if (m_waiting_mode) + { + // Display a little "please wait" message. + M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3); + if (m_waiting_mode == M_WAITING_VERSION) + waiting_message = "Checking for updates..."; + else + waiting_message = "Fetching room info..."; + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, waiting_message); + V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait."); + } } static void M_DrawConnectMenu(void) @@ -8416,6 +8502,14 @@ static void M_DrawConnectMenu(void) localservercount = serverlistcount; M_DrawGenericMenu(); + + if (m_waiting_mode) + { + // Display a little "please wait" message. + M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "Searching for servers..."); + V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait."); + } } static boolean M_CancelConnect(void) @@ -8497,10 +8591,10 @@ void M_SortServerList(void) #ifndef NONET #ifdef UPDATE_ALERT -static boolean M_CheckMODVersion(void) +static boolean M_CheckMODVersion(int id) { char updatestring[500]; - const char *updatecheck = GetMODVersion(); + const char *updatecheck = GetMODVersion(id); if(updatecheck) { sprintf(updatestring, UPDATE_ALERT_STRING, VERSIONSTRING, updatecheck); @@ -8509,7 +8603,62 @@ static boolean M_CheckMODVersion(void) } else return true; } -#endif + +#ifdef HAVE_THREADS +static void +Check_new_version_thread (int *id) +{ + int hosting; + int okay; + + okay = 0; + + if (M_CheckMODVersion(*id)) + { + I_lock_mutex(&ms_QueryId_mutex); + { + okay = ( *id == ms_QueryId ); + } + I_unlock_mutex(ms_QueryId_mutex); + + if (okay) + { + I_lock_mutex(&m_menu_mutex); + { + m_waiting_mode = M_WAITING_ROOMS; + hosting = ( currentMenu->prevMenu == &MP_ServerDef ); + } + I_unlock_mutex(m_menu_mutex); + + GetRoomsList(hosting, *id); + } + } + else + { + I_lock_mutex(&ms_QueryId_mutex); + { + okay = ( *id == ms_QueryId ); + } + I_unlock_mutex(ms_QueryId_mutex); + } + + if (okay) + { + I_lock_mutex(&m_menu_mutex); + { + if (m_waiting_mode) + { + m_waiting_mode = M_NOT_WAITING; + MP_RoomMenu[0].text = ""; + } + } + I_unlock_mutex(m_menu_mutex); + } + + free(id); +} +#endif/*HAVE_THREADS*/ +#endif/*UPDATE_ALERT*/ static void M_ConnectMenu(INT32 choice) { @@ -8538,18 +8687,21 @@ static void M_ConnectMenuModChecks(INT32 choice) if (modifiedgame) { - M_StartMessage(M_GetText("Addons are currently loaded.\n\nYou will only be able to join a server if\nit has the same ones loaded in the same order, which may be unlikely.\n\nIf you wish to play on other servers,\nrestart the game to clear existing addons.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); + M_StartMessage(M_GetText("You have addons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2Kart will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); return; } M_ConnectMenu(-1); } -static UINT32 roomIds[NUM_LIST_ROOMS]; +UINT32 roomIds[NUM_LIST_ROOMS]; static void M_RoomMenu(INT32 choice) { INT32 i; +#ifdef HAVE_THREADS + int *id; +#endif (void)choice; @@ -8562,34 +8714,47 @@ static void M_RoomMenu(INT32 choice) if (rendermode == render_soft) I_FinishUpdate(); // page flip or blit buffer - if (GetRoomsList(currentMenu == &MP_ServerDef) < 0) - return; - -#ifdef UPDATE_ALERT - if (!M_CheckMODVersion()) - return; -#endif - for (i = 1; i < NUM_LIST_ROOMS+1; ++i) MP_RoomMenu[i].status = IT_DISABLED; memset(roomIds, 0, sizeof(roomIds)); - for (i = 0; room_list[i].header.buffer[0]; i++) - { - if(*room_list[i].name != '\0') - { - MP_RoomMenu[i+1].text = room_list[i].name; - roomIds[i] = room_list[i].id; - MP_RoomMenu[i+1].status = IT_STRING|IT_CALL; - } - } - MP_RoomDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_RoomDef); + +#ifdef UPDATE_ALERT +#ifdef HAVE_THREADS + m_waiting_mode = M_WAITING_VERSION; + MP_RoomMenu[0].text = ""; + + id = malloc(sizeof *id); + + I_lock_mutex(&ms_QueryId_mutex); + { + *id = ms_QueryId; + } + I_unlock_mutex(ms_QueryId_mutex); + + I_spawn_thread("check-new-version", + (I_thread_fn)Check_new_version_thread, id); +#else/*HAVE_THREADS*/ + if (M_CheckMODVersion(0)) + { + GetRoomsList(currentMenu->prevMenu == &MP_ServerDef, 0); + } +#endif/*HAVE_THREADS*/ +#endif/*UPDATE_ALERT*/ } static void M_ChooseRoom(INT32 choice) { +#ifdef HAVE_THREADS + I_lock_mutex(&ms_QueryId_mutex); + { + ms_QueryId++; + } + I_unlock_mutex(ms_QueryId_mutex); +#endif + if (choice == 0) ms_RoomId = -1; else diff --git a/src/m_menu.h b/src/m_menu.h index 86a84089..1ad20c77 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -17,6 +17,8 @@ #include "d_event.h" #include "command.h" +#include "i_threads.h" +#include "mserv.h" #include "r_things.h" // for SKINNAMESIZE // @@ -72,6 +74,18 @@ typedef enum } menumessagetype_t; void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype); +typedef enum +{ + M_NOT_WAITING, + + M_WAITING_VERSION, + M_WAITING_ROOMS, + M_WAITING_SERVERS, +} +M_waiting_mode_t; + +extern M_waiting_mode_t m_waiting_mode; + // Called by linux_x/i_video_xshm.c void M_QuitResponse(INT32 ch); @@ -161,6 +175,9 @@ typedef struct menuitem_s extern menuitem_t PlayerMenu[MAXSKINS]; +extern menuitem_t MP_RoomMenu[]; +extern UINT32 roomIds[NUM_LIST_ROOMS]; + typedef struct menu_s { const char *menutitlepic; @@ -176,6 +193,10 @@ typedef struct menu_s void M_SetupNextMenu(menu_t *menudef); void M_ClearMenus(boolean callexitmenufunc); +#ifdef HAVE_THREADS +extern I_mutex m_menu_mutex; +#endif + extern menu_t *currentMenu; extern menu_t MainDef; diff --git a/src/m_misc.c b/src/m_misc.c index 87d648b2..0246a2cf 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -23,6 +23,8 @@ #include #endif +#include + // Extended map support. #include @@ -2363,3 +2365,13 @@ void M_SetupMemcpy(void) M_Memcpy = cpu_cpy; #endif } + +/** Return the appropriate message for a file error or end of file. +*/ +const char *M_FileError(FILE *fp) +{ + if (ferror(fp)) + return strerror(errno); + else + return "end-of-file"; +} diff --git a/src/m_misc.h b/src/m_misc.h index 658028b4..1e7befb1 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -100,6 +100,8 @@ void strcatbf(char *s1, const char *s2, const char *s3); void M_SetupMemcpy(void); +const char *M_FileError(FILE *handle); + // counting bits, for weapon ammo code, usually FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size); diff --git a/src/mserv.c b/src/mserv.c index 12469928..f3d414c9 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -2,242 +2,77 @@ //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2018 by Sonic Team Junior. +// Copyright (C) 2020 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file mserv.c -/// \brief Commands used for communicate with the master server - -#ifdef __GNUC__ -#include -#include -#include -#endif +/// \brief Commands used to communicate with the master server #if !defined (UNDER_CE) #include #endif -#if (defined (NOMD5) || defined (NOMSERV)) && !defined (NONET) -#define NONET -#endif - -#ifndef NONET - -#ifndef NO_IPV6 -#define HAVE_IPV6 -#endif - -#if (defined (_WIN32) || defined (_WIN32_WCE)) && !defined (_XBOX) -#define RPC_NO_WINDOWS_H -#ifdef HAVE_IPV6 -#include -#else -#include // socket(),... -#endif //!HAVE_IPV6 -#else -#ifdef __OS2__ -#include -#endif // __OS2__ - -#ifdef HAVE_LWIP -#include -#include -#include -#define ioctl lwip_ioctl -#else -#include -#ifdef __APPLE_CC__ -#ifndef _BSD_SOCKLEN_T_ -#define _BSD_SOCKLEN_T_ -#endif -#endif -#include // socket(),... -#include // sockaddr_in -#ifdef _PS3 -#include -#elif !defined(_arch_dreamcast) -#include // getaddrinfo(),... -#include -#endif -#endif - -#ifdef _arch_dreamcast -#include "sdl12/SRB2DC/dchelp.h" -#endif - -#include // timeval,... (TIMEOUT) -#include -#endif // _WIN32/_WIN32_WCE - -#ifdef __OS2__ -#include -#endif // __OS2__ -#endif // !NONET - #include "doomstat.h" #include "doomdef.h" #include "command.h" -#include "i_net.h" -#include "console.h" +#include "i_threads.h" #include "mserv.h" -#include "d_net.h" -#include "i_tcp.h" -#include "i_system.h" -#include "byteptr.h" #include "m_menu.h" -#include "m_argv.h" // Alam is going to kill me <3 -#include "m_misc.h" // GetRevisionString() +#include "z_zone.h" -#ifdef _WIN32_WCE -#include "sdl12/SRB2CE/cehelp.h" -#endif +static int MSId; +static int MSRegisteredId = -1; -#include "i_addrinfo.h" +static boolean MSRegistered; +static boolean MSInProgress; +static boolean MSUpdateAgain; -// ================================ DEFINITIONS =============================== +static time_t MSLastPing; -#define PACKET_SIZE 1024 +#ifdef HAVE_THREADS +static I_mutex MSMutex; +static I_cond MSCond; +# define Lock_state() I_lock_mutex (&MSMutex) +# define Unlock_state() I_unlock_mutex (MSMutex) +#else/*HAVE_THREADS*/ +# define Lock_state() +# define Unlock_state() +#endif/*HAVE_THREADS*/ -#define MS_NO_ERROR 0 -#define MS_SOCKET_ERROR -201 -#define MS_CONNECT_ERROR -203 -#define MS_WRITE_ERROR -210 -#define MS_READ_ERROR -211 -#define MS_CLOSE_ERROR -212 -#define MS_GETHOSTBYNAME_ERROR -220 -#define MS_GETHOSTNAME_ERROR -221 -#define MS_TIMEOUT_ERROR -231 - -// see master server code for the values -#define ADD_SERVER_MSG 101 -#define REMOVE_SERVER_MSG 103 -#define ADD_SERVERv2_MSG 104 -#define GET_SERVER_MSG 200 -#define GET_SHORT_SERVER_MSG 205 -#define ASK_SERVER_MSG 206 -#define ANSWER_ASK_SERVER_MSG 207 -#define ASK_SERVER_MSG 206 -#define ANSWER_ASK_SERVER_MSG 207 -#define GET_MOTD_MSG 208 -#define SEND_MOTD_MSG 209 -#define GET_ROOMS_MSG 210 -#define SEND_ROOMS_MSG 211 -#define GET_ROOMS_HOST_MSG 212 -#define GET_VERSION_MSG 213 -#define SEND_VERSION_MSG 214 -#define GET_BANNED_MSG 215 // Someone's been baaaaaad! -#define PING_SERVER_MSG 216 - -#define HEADER_SIZE (sizeof (INT32)*4) - -#define HEADER_MSG_POS 0 -#define IP_MSG_POS 16 -#define PORT_MSG_POS 32 -#define HOSTNAME_MSG_POS 40 - - -#if defined(_MSC_VER) -#pragma pack(1) -#endif - -/** A message to be exchanged with the master server. - */ -typedef struct -{ - INT32 id; ///< Unused? - INT32 type; ///< Type of message. - INT32 room; ///< Because everyone needs a roomie. - UINT32 length; ///< Length of the message. - char buffer[PACKET_SIZE]; ///< Actual contents of the message. -} ATTRPACK msg_t; - -#if defined(_MSC_VER) -#pragma pack() -#endif - -typedef struct Copy_CVarMS_t -{ - char ip[64]; - char port[8]; - char name[64]; -} Copy_CVarMS_s; -static Copy_CVarMS_s registered_server; -static time_t MSLastPing; - -#if defined(_MSC_VER) -#pragma pack(1) -#endif -typedef struct -{ - char ip[16]; // Big enough to hold a full address. - UINT16 port; - UINT8 padding1[2]; - tic_t time; -} ATTRPACK ms_holepunch_packet_t; -#if defined(_MSC_VER) -#pragma pack() -#endif - -// win32 or djgpp -#if defined (_WIN32) || defined (_WIN32_WCE) || defined (__DJGPP__) -#define ioctl ioctlsocket -#define close closesocket -#ifdef WATTCP -#define strerror strerror_s -#endif -#if defined (_WIN32) || defined (_WIN32_WCE) -#undef errno -#define errno h_errno // some very strange things happen when not using h_error -#endif -#ifndef AI_ADDRCONFIG -#define AI_ADDRCONFIG 0x00000400 -#endif -#endif +static void Update_parameters (void); #ifndef NONET static void Command_Listserv_f(void); #endif static void MasterServer_OnChange(void); -static void ServerName_OnChange(void); -#define DEF_PORT "28900" -consvar_t cv_masterserver = {"masterserver", "ms.srb2.org:"DEF_PORT, CV_SAVE, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, ServerName_OnChange, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { + {2, "MIN"}, + {60, "MAX"}, + {0} +}; + +consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; + +consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; INT16 ms_RoomId = -1; -static enum { MSCS_NONE, MSCS_WAITING, MSCS_REGISTERED, MSCS_FAILED } con_state = MSCS_NONE; +#ifdef HAVE_THREADS +int ms_QueryId; +I_mutex ms_QueryId_mutex; + +msg_server_t *ms_ServerList; +I_mutex ms_ServerList_mutex; +#endif -static INT32 msnode = -1; UINT16 current_port = 0; -#if (defined (_WIN32) || defined (_WIN32_WCE) || defined (_WIN32)) && !defined (NONET) -typedef SOCKET SOCKET_TYPE; -#define ERRSOCKET (SOCKET_ERROR) -#else -#if (defined (__unix__) && !defined (MSDOS)) || defined (__APPLE__) || defined (__HAIKU__) || defined (_PS3) -typedef int SOCKET_TYPE; -#else -typedef unsigned long SOCKET_TYPE; -#endif -#define ERRSOCKET (-1) -#endif - -#if (defined (WATTCP) && !defined (__libsocket_socklen_t)) || defined (_WIN32) -typedef int socklen_t; -#endif - -#ifndef NONET -static SOCKET_TYPE socket_fd = ERRSOCKET; // WINSOCK socket -static struct timeval select_timeout; -static fd_set wset; -static size_t recvfull(SOCKET_TYPE s, char *buf, size_t len, int flags); -#endif - // Room list is an external variable now. // Avoiding having to get info ten thousand times... msg_rooms_t room_list[NUM_LIST_ROOMS+1]; // +1 for easy test @@ -251,371 +86,96 @@ void AddMServCommands(void) { #ifndef NONET CV_RegisterVar(&cv_masterserver); + CV_RegisterVar(&cv_masterserver_update_rate); + CV_RegisterVar(&cv_masterserver_timeout); + CV_RegisterVar(&cv_masterserver_debug); + CV_RegisterVar(&cv_masterserver_token); CV_RegisterVar(&cv_servername); COM_AddCommand("listserv", Command_Listserv_f); #endif } -/** Closes the connection to the master server. - * - * \todo Fix for Windows? - */ -static void CloseConnection(void) +static void WarnGUI (void) { -#ifndef NONET - if (socket_fd != (SOCKET_TYPE)ERRSOCKET) - close(socket_fd); - socket_fd = ERRSOCKET; +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); #endif -} - -// -// MS_Write(): -// -static INT32 MS_Write(msg_t *msg) -{ -#ifdef NONET - (void)msg; - return MS_WRITE_ERROR; -#else - size_t len; - - if (msg->length == 0) - msg->length = (INT32)strlen(msg->buffer); - len = msg->length + HEADER_SIZE; - - msg->type = htonl(msg->type); - msg->length = htonl(msg->length); - msg->room = htonl(msg->room); - - if ((size_t)send(socket_fd, (char *)msg, (int)len, 0) != len) - return MS_WRITE_ERROR; - return 0; + M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n\nCheck the console for details.\n"), NULL, MM_NOTHING); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); #endif } -// -// MS_Read(): -// -static INT32 MS_Read(msg_t *msg) -{ -#ifdef NONET - (void)msg; - return MS_READ_ERROR; -#else - if (recvfull(socket_fd, (char *)msg, HEADER_SIZE, 0) != HEADER_SIZE) - return MS_READ_ERROR; - - msg->type = ntohl(msg->type); - msg->length = ntohl(msg->length); - msg->room = ntohl(msg->room); - - if (!msg->length) // fix a bug in Windows 2000 - return 0; - - if (recvfull(socket_fd, (char *)msg->buffer, msg->length, 0) != msg->length) - return MS_READ_ERROR; - return 0; -#endif -} - -#ifndef NONET -/** Gets a list of game servers from the master server. - */ -static INT32 GetServersList(void) -{ - msg_t msg; - INT32 count = 0; - - msg.type = GET_SERVER_MSG; - msg.length = 0; - msg.room = 0; - if (MS_Write(&msg) < 0) - return MS_WRITE_ERROR; - - while (MS_Read(&msg) >= 0) - { - if (!msg.length) - { - if (!count) - CONS_Alert(CONS_NOTICE, M_GetText("No servers currently running.\n")); - return MS_NO_ERROR; - } - count++; - CONS_Printf("%s",msg.buffer); - } - - return MS_READ_ERROR; -} -#endif - -// -// MS_Connect() -// -static INT32 MS_Connect(const char *ip_addr, const char *str_port, INT32 async) -{ -#ifdef NONET - (void)ip_addr; - (void)str_port; - (void)async; -#else - struct my_addrinfo *ai, *runp, hints; - int gaie; - - memset (&hints, 0x00, sizeof(hints)); -#ifdef AI_ADDRCONFIG - hints.ai_flags = AI_ADDRCONFIG; -#endif - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - //I_InitTcpNetwork(); this is already done on startup in D_SRB2Main() - if (!I_InitTcpDriver()) // this is done only if not already done - return MS_SOCKET_ERROR; - - gaie = I_getaddrinfo(ip_addr, str_port, &hints, &ai); - if (gaie != 0) - return MS_GETHOSTBYNAME_ERROR; - else - runp = ai; - - while (runp != NULL) - { - socket_fd = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); - if (socket_fd != (SOCKET_TYPE)ERRSOCKET) - { - if (async) // do asynchronous connection - { -#ifdef FIONBIO -#ifdef WATTCP - char res = 1; -#else - unsigned long res = 1; -#endif - - ioctl(socket_fd, FIONBIO, &res); -#endif - - if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) == ERRSOCKET) - { -#ifdef _WIN32 // humm, on win32/win64 it doesn't work with EINPROGRESS (stupid windows) - if (WSAGetLastError() != WSAEWOULDBLOCK) -#else - if (errno != EINPROGRESS) -#endif - { - con_state = MSCS_FAILED; - CloseConnection(); - I_freeaddrinfo(ai); - return MS_CONNECT_ERROR; - } - } - con_state = MSCS_WAITING; - FD_ZERO(&wset); - FD_SET(socket_fd, &wset); - select_timeout.tv_sec = 0, select_timeout.tv_usec = 0; - I_freeaddrinfo(ai); - return 0; - } - else if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) != ERRSOCKET) - { - I_freeaddrinfo(ai); - return 0; - } - } - runp = runp->ai_next; - } - I_freeaddrinfo(ai); -#endif - return MS_CONNECT_ERROR; -} - #define NUM_LIST_SERVER MAXSERVERLIST -const msg_server_t *GetShortServersList(INT32 room) +msg_server_t *GetShortServersList(INT32 room, int id) { - static msg_server_t server_list[NUM_LIST_SERVER+1]; // +1 for easy test - msg_t msg; - INT32 i; + msg_server_t *server_list; - // we must be connected to the master server before writing to it - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING); - return NULL; - } + // +1 for easy test + server_list = malloc(( NUM_LIST_SERVER + 1 ) * sizeof *server_list); - msg.type = GET_SHORT_SERVER_MSG; - msg.length = 0; - msg.room = room; - if (MS_Write(&msg) < 0) - return NULL; - - for (i = 0; i < NUM_LIST_SERVER && MS_Read(&msg) >= 0; i++) - { - if (!msg.length) - { - server_list[i].header.buffer[0] = 0; - CloseConnection(); - return server_list; - } - M_Memcpy(&server_list[i], msg.buffer, sizeof (msg_server_t)); - server_list[i].header.buffer[0] = 1; - } - CloseConnection(); - if (i == NUM_LIST_SERVER) - { - server_list[i].header.buffer[0] = 0; + if (HMS_fetch_servers(server_list, room, id)) return server_list; - } else + { + free(server_list); + WarnGUI(); return NULL; + } } -INT32 GetRoomsList(boolean hosting) +INT32 GetRoomsList(boolean hosting, int id) { - static msg_ban_t banned_info[1]; - msg_t msg; - INT32 i; - - // we must be connected to the master server before writing to it - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING); - return -1; - } - - if (hosting) - msg.type = GET_ROOMS_HOST_MSG; - else - msg.type = GET_ROOMS_MSG; - msg.length = 0; - msg.room = 0; - if (MS_Write(&msg) < 0) - { - room_list[0].id = 1; - strcpy(room_list[0].motd,"Master Server Offline."); - strcpy(room_list[0].name,"Offline"); - return -1; - } - - for (i = 0; i < NUM_LIST_ROOMS && MS_Read(&msg) >= 0; i++) - { - if(msg.type == GET_BANNED_MSG) - { - char banmsg[1000]; - M_Memcpy(&banned_info[0], msg.buffer, sizeof (msg_ban_t)); - if (hosting) - sprintf(banmsg, M_GetText("You have been banned from\nhosting netgames.\n\nUnder the following IP Range:\n%s - %s\n\nFor the following reason:\n%s\n\nYour ban will expire on:\n%s"),banned_info[0].ipstart,banned_info[0].ipend,banned_info[0].reason,banned_info[0].endstamp); - else - sprintf(banmsg, M_GetText("You have been banned from\njoining netgames.\n\nUnder the following IP Range:\n%s - %s\n\nFor the following reason:\n%s\n\nYour ban will expire on:\n%s"),banned_info[0].ipstart,banned_info[0].ipend,banned_info[0].reason,banned_info[0].endstamp); - M_StartMessage(banmsg, NULL, MM_NOTHING); - ms_RoomId = -1; - return -2; - } - if (!msg.length) - { - room_list[i].header.buffer[0] = 0; - CloseConnection(); - return 1; - } - M_Memcpy(&room_list[i], msg.buffer, sizeof (msg_rooms_t)); - room_list[i].header.buffer[0] = 1; - } - CloseConnection(); - if (i == NUM_LIST_ROOMS) - { - room_list[i].header.buffer[0] = 0; + if (HMS_fetch_rooms( ! hosting, id)) return 1; - } else { - room_list[0].id = 1; - strcpy(room_list[0].motd,M_GetText("Master Server Offline.")); - strcpy(room_list[0].name,M_GetText("Offline")); + WarnGUI(); return -1; } } #ifdef UPDATE_ALERT -const char *GetMODVersion(void) +char *GetMODVersion(int id) { - static msg_t msg; + char *buffer; + int c; - // we must be connected to the master server before writing to it - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) + (void)id; + + buffer = malloc(16); + + c = HMS_compare_mod_version(buffer, 16); + +#ifdef HAVE_THREADS + I_lock_mutex(&ms_QueryId_mutex); { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING); - return NULL; + if (id != ms_QueryId) + c = -1; } + I_unlock_mutex(ms_QueryId_mutex); +#endif - msg.type = GET_VERSION_MSG; - msg.length = sizeof MODVERSION; - msg.room = MODID; // Might as well use it for something. - sprintf(msg.buffer,"%d",MODVERSION); - if (MS_Write(&msg) < 0) - { - CONS_Alert(CONS_ERROR, M_GetText("Could not send to the Master Server\n")); - M_StartMessage(M_GetText("Could not send to the Master Server\n"), NULL, MM_NOTHING); - CloseConnection(); - return NULL; - } - - if (MS_Read(&msg) < 0) - { - CONS_Alert(CONS_ERROR, M_GetText("No reply from the Master Server\n")); - M_StartMessage(M_GetText("No reply from the Master Server\n"), NULL, MM_NOTHING); - CloseConnection(); - return NULL; - } - - CloseConnection(); - - if(strcmp(msg.buffer,"NULL") != 0) - { - return msg.buffer; - } + if (c > 0) + return buffer; else + { + free(buffer); + + if (! c) + WarnGUI(); + return NULL; + } } // Console only version of the above (used before game init) void GetMODVersion_Console(void) { - static msg_t msg; + char buffer[16]; - // we must be connected to the master server before writing to it - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - return; - } - - msg.type = GET_VERSION_MSG; - msg.length = sizeof MODVERSION; - msg.room = MODID; // Might as well use it for something. - sprintf(msg.buffer,"%d",MODVERSION); - if (MS_Write(&msg) < 0) - { - CONS_Alert(CONS_ERROR, M_GetText("Could not send to the Master Server\n")); - CloseConnection(); - return; - } - - if (MS_Read(&msg) < 0) - { - CONS_Alert(CONS_ERROR, M_GetText("No reply from the Master Server\n")); - CloseConnection(); - return; - } - - CloseConnection(); - - if(strcmp(msg.buffer,"NULL") != 0) - I_Error(UPDATE_ALERT_STRING_CONSOLE, VERSIONSTRING, msg.buffer); + if (HMS_compare_mod_version(buffer, sizeof buffer) > 0) + I_Error(UPDATE_ALERT_STRING_CONSOLE, VERSIONSTRING, buffer); } #endif @@ -624,401 +184,347 @@ void GetMODVersion_Console(void) */ static void Command_Listserv_f(void) { - if (con_state == MSCS_WAITING) - { - CONS_Alert(CONS_NOTICE, M_GetText("Not yet connected to the Master Server.\n")); - return; - } - CONS_Printf(M_GetText("Retrieving server list...\n")); - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - return; + HMS_list_servers(); } - - if (GetServersList()) - CONS_Alert(CONS_ERROR, M_GetText("Cannot get server list\n")); - - CloseConnection(); } #endif -FUNCMATH static const char *int2str(INT32 n) +static void +Finish_registration (void) { - INT32 i; - static char res[16]; + int registered; - res[15] = '\0'; - res[14] = (char)((char)(n%10)+'0'); - for (i = 13; (n /= 10); i--) - res[i] = (char)((char)(n%10)+'0'); + CONS_Printf("Registering this server on the master server...\n"); - return &res[i+1]; -} + registered = HMS_register(); -#ifndef NONET -static INT32 ConnectionFailed(void) -{ - con_state = MSCS_FAILED; - CONS_Alert(CONS_ERROR, M_GetText("Connection to Master Server failed\n")); - CloseConnection(); - return MS_CONNECT_ERROR; -} -#endif - -/** Tries to register the local game server on the master server. - */ -static INT32 AddToMasterServer(boolean firstadd) -{ -#ifdef NONET - (void)firstadd; -#else - static INT32 retry = 0; - int i, res; - socklen_t j; - msg_t msg; - msg_server_t *info = (msg_server_t *)msg.buffer; - INT32 room = -1; - fd_set tset; - time_t timestamp = time(NULL); - UINT32 signature, tmp; - const char *insname; - - M_Memcpy(&tset, &wset, sizeof (tset)); - res = select(255, NULL, &tset, NULL, &select_timeout); - if (res != ERRSOCKET && !res) + Lock_state(); { - if (retry++ > 30) // an about 30 second timeout + MSRegistered = registered; + MSRegisteredId = MSId; + + time(&MSLastPing); + } + Unlock_state(); + + if (registered) + CONS_Printf("Master server registration successful.\n"); +} + +static void +Finish_update (void) +{ + int registered; + int done; + + Lock_state(); + { + registered = MSRegistered; + MSUpdateAgain = false;/* this will happen anyway */ + } + Unlock_state(); + + if (registered) + { + if (HMS_update()) { - retry = 0; - CONS_Alert(CONS_ERROR, M_GetText("Master Server timed out\n")); - MSLastPing = timestamp; - return ConnectionFailed(); + Lock_state(); + { + time(&MSLastPing); + MSRegistered = true; + } + Unlock_state(); + + CONS_Printf("Updated master server listing.\n"); } - return MS_CONNECT_ERROR; + else + Finish_registration(); } - retry = 0; - /* - Somehow we can still select our old socket despite it being closed(?). - Atleast, that's what I THINK is happening. Anyway, we have to check that we - haven't open a socket, and actually open it! - */ - /*if (res == ERRSOCKET)*//* wtf? no! */ - if (socket_fd == (SOCKET_TYPE)ERRSOCKET) + else + Finish_registration(); + + Lock_state(); { - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) + done = ! MSUpdateAgain; + + if (done) + MSInProgress = false; + } + Unlock_state(); + + if (! done) + Finish_update(); +} + +static void +Finish_unlist (void) +{ + int registered; + + Lock_state(); + { + registered = MSRegistered; + } + Unlock_state(); + + if (registered) + { + CONS_Printf("Removing this server from the master server...\n"); + + if (HMS_unlist()) + CONS_Printf("Server deregistration request successfully sent.\n"); + + Lock_state(); { - CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error #%u: %s\n"), errno, strerror(errno)); - MSLastPing = timestamp; - return ConnectionFailed(); + MSRegistered = false; } - } + Unlock_state(); - // so, the socket is writable, but what does that mean, that the connection is - // ok, or bad... let see that! - j = (socklen_t)sizeof (i); - getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&i, &j); - /* - This is also wrong. If getsockopt fails, i doesn't have to be set. Plus, if - it is set (which it appearantly is on linux), we check errno anyway. And in - the case that i is returned as normal, we don't even report the correct - value! So we accomplish NOTHING, except returning due to dumb luck. - If you care, fix this--I don't. -James (R.) - */ - if (i) // it was bad - { - CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error #%u: %s\n"), errno, strerror(errno)); - MSLastPing = timestamp; - return ConnectionFailed(); - } - -#ifdef PARANOIA - if (ms_RoomId <= 0) - I_Error("Attmepted to host in room \"All\"!\n"); +#ifdef HAVE_THREADS + I_wake_all_cond(&MSCond); #endif - room = ms_RoomId; - - for(signature = 0, insname = cv_servername.string; *insname; signature += *insname++); - tmp = (UINT32)(signature * (size_t)&MSLastPing); - signature *= tmp; - signature &= 0xAAAAAAAA; - M_Memcpy(&info->header.signature, &signature, sizeof (UINT32)); - - strcpy(info->ip, ""); - strcpy(info->port, int2str(current_port)); - strcpy(info->name, cv_servername.string); - M_Memcpy(&info->room, & room, sizeof (INT32)); -#if VERSION > 0 || SUBVERSION > 0 - sprintf(info->version, "%d.%d", VERSION, SUBVERSION); -#else // Trunk build, send revision info - strcpy(info->version, GetRevisionString()); -#endif - strcpy(registered_server.name, cv_servername.string); - - if(firstadd) - msg.type = ADD_SERVER_MSG; - else - msg.type = PING_SERVER_MSG; - - msg.length = (UINT32)sizeof (msg_server_t); - msg.room = 0; - if (MS_Write(&msg) < 0) - { - MSLastPing = timestamp; - return ConnectionFailed(); } - if(con_state != MSCS_REGISTERED) - CONS_Printf(M_GetText("Master Server update successful.\n")); - - MSLastPing = timestamp; - con_state = MSCS_REGISTERED; - CloseConnection(); -#endif - return MS_NO_ERROR; -} - -static INT32 RemoveFromMasterSever(void) -{ - msg_t msg; - msg_server_t *info = (msg_server_t *)msg.buffer; - - strcpy(info->header.buffer, ""); - strcpy(info->ip, ""); - strcpy(info->port, int2str(current_port)); - strcpy(info->name, registered_server.name); - sprintf(info->version, "%d.%d", VERSION, SUBVERSION); - - msg.type = REMOVE_SERVER_MSG; - msg.length = (UINT32)sizeof (msg_server_t); - msg.room = 0; - if (MS_Write(&msg) < 0) - return MS_WRITE_ERROR; - - return MS_NO_ERROR; -} - -const char *GetMasterServerPort(void) -{ - const char *t = cv_masterserver.string; - - while ((*t != ':') && (*t != '\0')) - t++; - - if (*t) - return ++t; - else - return DEF_PORT; -} - -/** Gets the IP address of the master server. Actually, it seems to just - * return the hostname, instead; the lookup is done elsewhere. - * - * \return Hostname of the master server, without port number on the end. - * \todo Rename function? - */ -const char *GetMasterServerIP(void) -{ - static char str_ip[64]; - char *t = str_ip; - - if (strstr(cv_masterserver.string, "srb2.ssntails.org:28910") - || strstr(cv_masterserver.string, "srb2.servegame.org:28910") - || strstr(cv_masterserver.string, "srb2.servegame.org:28900") - ) + Lock_state(); { - // replace it with the current default one - CV_Set(&cv_masterserver, cv_masterserver.defaultvalue); + if (MSId == MSRegisteredId) + MSId++; } - - strcpy(t, cv_masterserver.string); - - while ((*t != ':') && (*t != '\0')) - t++; - *t = '\0'; - - return str_ip; + Unlock_state(); } -void MSOpenUDPSocket(void) +#ifdef HAVE_THREADS +static int * +Server_id (void) { -#ifndef NONET - if (I_NetMakeNodewPort) + int *id; + id = malloc(sizeof *id); + Lock_state(); { - // If it's already open, there's nothing to do. - if (msnode < 0) - msnode = I_NetMakeNodewPort(GetMasterServerIP(), GetMasterServerPort()); + *id = MSId; } - else -#endif - msnode = -1; + Unlock_state(); + return id; } -void MSCloseUDPSocket(void) +static int * +New_server_id (void) { - if (msnode != INT16_MAX) I_NetFreeNodenum(msnode); - msnode = -1; + int *id; + id = malloc(sizeof *id); + Lock_state(); + { + *id = ++MSId; + I_wake_all_cond(&MSCond); + } + Unlock_state(); + return id; } +static void +Register_server_thread (int *id) +{ + int same; + + Lock_state(); + { + /* wait for previous unlist to finish */ + while (*id == MSId && MSRegistered) + I_hold_cond(&MSCond, MSMutex); + + same = ( *id == MSId );/* it could have been a while */ + } + Unlock_state(); + + if (same)/* it could have been a while */ + Finish_registration(); + + free(id); +} + +static void +Update_server_thread (int *id) +{ + int same; + + Lock_state(); + { + same = ( *id == MSRegisteredId ); + } + Unlock_state(); + + if (same) + Finish_update(); + + free(id); +} + +static void +Unlist_server_thread (int *id) +{ + int same; + + Lock_state(); + { + same = ( *id == MSRegisteredId ); + } + Unlock_state(); + + if (same) + Finish_unlist(); + + free(id); +} + +static void +Change_masterserver_thread (char *api) +{ + Lock_state(); + { + while (MSRegistered) + I_hold_cond(&MSCond, MSMutex); + } + Unlock_state(); + + HMS_set_api(api); +} +#endif/*HAVE_THREADS*/ + void RegisterServer(void) { - if (con_state == MSCS_REGISTERED || con_state == MSCS_WAITING) - return; - - CONS_Printf(M_GetText("Registering this server on the Master Server...\n")); - - strcpy(registered_server.ip, GetMasterServerIP()); - strcpy(registered_server.port, GetMasterServerPort()); - - if (MS_Connect(registered_server.ip, registered_server.port, 1)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - return; - } - MSOpenUDPSocket(); - - // keep the TCP connection open until AddToMasterServer() is completed; +#ifdef HAVE_THREADS + I_spawn_thread( + "register-server", + (I_thread_fn)Register_server_thread, + New_server_id() + ); +#else + Finish_registration(); +#endif } -static inline void SendPingToMasterServer(void) +static void UpdateServer(void) { -/* static tic_t next_time = 0; - tic_t cur_time; - char *inbuffer = (char*)netbuffer; - - cur_time = I_GetTime(); - if (!netgame) - UnregisterServer(); - else if (cur_time > next_time) // ping every 2 second if possible - { - next_time = cur_time+2*TICRATE; - - if (con_state == MSCS_WAITING) - AddToMasterServer(); - - if (con_state != MSCS_REGISTERED) - return; - - // cur_time is just a dummy data to send - WRITEUINT32(inbuffer, cur_time); - doomcom->datalength = sizeof (cur_time); - doomcom->remotenode = (INT16)msnode; - I_NetSend(); - } -*/ - -// Here, have a simpler MS Ping... - Cue - if(time(NULL) > (MSLastPing+(60*2)) && con_state != MSCS_NONE) - { - //CONS_Debug(DBG_NETPLAY, "%ld (current time) is greater than %d (Last Ping Time)\n", time(NULL), MSLastPing); - if(MSLastPing < 1) - AddToMasterServer(true); - else - AddToMasterServer(false); - } -} - -void SendAskInfoViaMS(INT32 node, tic_t asktime) -{ - const char *address; - UINT16 port; - char *inip; - ms_holepunch_packet_t mshpp; - - MSOpenUDPSocket(); - - // This must be called after calling MSOpenUDPSocket, due to the - // static buffer. - address = I_GetNodeAddress(node); - - // no address? - if (!address) - return; - - // Copy the IP address into the buffer. - inip = mshpp.ip; - while(*address && *address != ':') *inip++ = *address++; - *inip = '\0'; - - // Get the port. - port = (UINT16)(*address++ ? atoi(address) : 0); - mshpp.port = SHORT(port); - - // Set the time for ping calculation. - mshpp.time = LONG(asktime); - - // Send to the MS. - M_Memcpy(netbuffer, &mshpp, sizeof(mshpp)); - doomcom->datalength = sizeof(ms_holepunch_packet_t); - doomcom->remotenode = (INT16)msnode; - I_NetSend(); +#ifdef HAVE_THREADS + I_spawn_thread( + "update-server", + (I_thread_fn)Update_server_thread, + Server_id() + ); +#else + Finish_update(); +#endif } void UnregisterServer(void) { - if (con_state != MSCS_REGISTERED) +#ifdef HAVE_THREADS + I_spawn_thread( + "unlist-server", + (I_thread_fn)Unlist_server_thread, + Server_id() + ); +#else + Finish_unlist(); +#endif +} + +static boolean +Online (void) +{ + return ( serverrunning && ms_RoomId > 0 ); +} + +static inline void SendPingToMasterServer(void) +{ + int ready; + time_t now; + + if (Online()) { - con_state = MSCS_NONE; - CloseConnection(); - return; + time(&now); + + Lock_state(); + { + ready = ( + MSRegisteredId == MSId && + ! MSInProgress && + now >= ( MSLastPing + 60 * cv_masterserver_update_rate.value ) + ); + + if (ready) + MSInProgress = true; + } + Unlock_state(); + + if (ready) + UpdateServer(); } +} - con_state = MSCS_NONE; +static void +Update_parameters (void) +{ + int registered; + int delayed; - CONS_Printf(M_GetText("Removing this server from the Master Server...\n")); - - if (MS_Connect(registered_server.ip, registered_server.port, 0)) + if (Online()) { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - return; + Lock_state(); + { + delayed = MSInProgress; + + if (delayed)/* do another update after the current one */ + MSUpdateAgain = true; + else + registered = MSRegistered; + } + Unlock_state(); + + if (! delayed && registered) + UpdateServer(); } - - if (RemoveFromMasterSever() < 0) - CONS_Alert(CONS_ERROR, M_GetText("Cannot remove this server from the Master Server\n")); - - CloseConnection(); - MSCloseUDPSocket(); - MSLastPing = 0; } void MasterClient_Ticker(void) { - if (server && ms_RoomId > 0) - SendPingToMasterServer(); + SendPingToMasterServer(); } -static void ServerName_OnChange(void) +static void +Set_api (const char *api) { - if (con_state == MSCS_REGISTERED) - AddToMasterServer(false); +#ifdef HAVE_THREADS + I_spawn_thread( + "change-masterserver", + (I_thread_fn)Change_masterserver_thread, + strdup(api) + ); +#else + HMS_set_api(strdup(api)); +#endif } static void MasterServer_OnChange(void) { UnregisterServer(); - RegisterServer(); -} -#ifndef NONET -// Like recv, but waits until we've got enough data to fill the buffer. -static size_t recvfull(SOCKET_TYPE s, char *buf, size_t len, int flags) -{ - /* Total received. */ - size_t totallen = 0; - - while(totallen < len) - { - ssize_t ret = (ssize_t)recv(s, buf + totallen, (int)(len - totallen), flags); - - /* Error. */ - if(ret == -1) - return (size_t)-1; - - totallen += ret; + /* + TODO: remove this for v2, it's just a hack + for those coming in with an old config. + */ + if ( + ! cv_masterserver.changed && + strcmp(cv_masterserver.string, "ms.srb2.org:28900") == 0 + ){ + CV_StealthSet(&cv_masterserver, cv_masterserver.defaultvalue); } - return totallen; + Set_api(cv_masterserver.string); + + if (Online()) + RegisterServer(); } -#endif diff --git a/src/mserv.h b/src/mserv.h index 09cd4f08..9269c408 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -2,6 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2018 by Sonic Team Junior. +// Copyright (C) 2020 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -13,7 +14,7 @@ #ifndef _MSERV_H_ #define _MSERV_H_ -#define MASTERSERVERS21 // MasterServer v2.1 +#include "i_threads.h" // lowered from 32 due to menu changes #define NUM_LIST_ROOMS 16 @@ -64,33 +65,47 @@ typedef struct // ================================ GLOBALS =============================== extern consvar_t cv_masterserver, cv_servername; +extern consvar_t cv_masterserver_update_rate; +extern consvar_t cv_masterserver_timeout; +extern consvar_t cv_masterserver_debug; +extern consvar_t cv_masterserver_token; // < 0 to not connect (usually -1) (offline mode) // == 0 to show all rooms, not a valid hosting room // anything else is whatever room the MS assigns to that number (online mode) -INT16 ms_RoomId; +extern INT16 ms_RoomId; -const char *GetMasterServerPort(void); -const char *GetMasterServerIP(void); +#ifdef HAVE_THREADS +extern int ms_QueryId; +extern I_mutex ms_QueryId_mutex; -void MSOpenUDPSocket(void); -void MSCloseUDPSocket(void); - -void SendAskInfoViaMS(INT32 node, tic_t asktime); +extern msg_server_t *ms_ServerList; +extern I_mutex ms_ServerList_mutex; +#endif void RegisterServer(void); void UnregisterServer(void); void MasterClient_Ticker(void); -const msg_server_t *GetShortServersList(INT32 room); -INT32 GetRoomsList(boolean hosting); +msg_server_t *GetShortServersList(INT32 room, int id); +INT32 GetRoomsList(boolean hosting, int id); #ifdef UPDATE_ALERT -const char *GetMODVersion(void); +char *GetMODVersion(int id); void GetMODVersion_Console(void); #endif extern msg_rooms_t room_list[NUM_LIST_ROOMS+1]; void AddMServCommands(void); +/* HTTP */ +void HMS_set_api (char *api); +int HMS_fetch_rooms (int joining, int id); +int HMS_register (void); +int HMS_unlist (void); +int HMS_update (void); +void HMS_list_servers (void); +msg_server_t * HMS_fetch_servers (msg_server_t *list, int room, int id); +int HMS_compare_mod_version (char *buffer, size_t size_of_buffer); + #endif diff --git a/src/p_enemy.c b/src/p_enemy.c index a3bf5491..2693c8cc 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8517,6 +8517,7 @@ void A_SPBChase(mobj_t *actor) //fast->momz = (3*actor->momz)/4; fast->color = SKINCOLOR_RED; fast->colorized = true; + P_SetTarget(&fast->target, actor); // easier lua access K_MatchGenericExtraFlags(fast, actor); } diff --git a/src/p_mobj.c b/src/p_mobj.c index f7f2afe3..82a003a0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8312,18 +8312,7 @@ void P_MobjThinker(mobj_t *mobj) if (!mobj->extravalue2) { - if (mobj->eflags & MFE_VERTICALFLIP) - mobj->z -= mobj->height; - else - mobj->z += mobj->height; - - S_StartSound(mobj, mobj->info->deathsound); - P_SetObjectMomZ(mobj, 8*FRACUNIT, false); - P_InstaThrust(mobj, R_PointToAngle2(mobj->target->x, mobj->target->y, mobj->x, mobj->y)+ANGLE_90, 16*FRACUNIT); - mobj->momx += mobj->target->momx; - mobj->momy += mobj->target->momy; - mobj->momz += mobj->target->momz; - mobj->extravalue2 = 1; + K_DropRocketSneaker(mobj->target->player); } else if (P_IsObjectOnGround(mobj)) { diff --git a/src/p_setup.c b/src/p_setup.c index decdc529..85243d50 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1408,7 +1408,7 @@ static void P_LoadRawSideDefs2(void *data) UINT16 i; INT32 num; size_t j; - UINT32 cr, cg, cb; + RGBA_t color; for (i = 0; i < numsides; i++) { @@ -1490,23 +1490,21 @@ static void P_LoadRawSideDefs2(void *data) // encore mode colormaps! // do it like software by aproximating a color to a palette index, and then convert it to its encore variant and then back to a color code. // do this for both the start and fade colormaps. - - cr = (HEX2INT(col[1]) << 4) + (HEX2INT(col[2]) << 0); - cg = (HEX2INT(col[3]) << 12) + (HEX2INT(col[4]) << 8); - cb = (HEX2INT(col[5]) << 20) + (HEX2INT(col[6]) << 16); + + color.s.red = (HEX2INT(col[1]) << 4) + HEX2INT(col[2]); + color.s.green = (HEX2INT(col[3]) << 4) + HEX2INT(col[4]); + color.s.blue = (HEX2INT(col[5]) << 4) + HEX2INT(col[6]); #ifdef GLENCORE if (encoremap) { - j = encoremap[NearestColor((UINT8)cr, (UINT8)cg, (UINT8)cb)]; + j = encoremap[NearestColor(color.s.red, color.s.green, color.s.blue)]; //CONS_Printf("R_CreateColormap: encoremap[%d] = %d\n", j, encoremap[j]); -- moved encoremap upwards for optimisation - cr = pLocalPalette[j].s.red; - cg = pLocalPalette[j].s.green; - cb = pLocalPalette[j].s.blue; + color = pLocalPalette[j]; // note: this sets alpha to 255, we will reset it below } #endif - - sec->extra_colormap->rgba = cr + cg + cb; + color.s.alpha = 0; // reset/init the alpha, so the addition below will work correctly + sec->extra_colormap->rgba = color.rgba; // alpha if (msd->toptexture[7]) @@ -1533,23 +1531,21 @@ static void P_LoadRawSideDefs2(void *data) col = msd->bottomtexture; // do the exact same thing as above here. - - cr = (HEX2INT(col[1]) << 4) + (HEX2INT(col[2]) << 0); - cg = (HEX2INT(col[3]) << 12) + (HEX2INT(col[4]) << 8); - cb = (HEX2INT(col[5]) << 20) + (HEX2INT(col[6]) << 16); + + color.s.red = (HEX2INT(col[1]) << 4) + HEX2INT(col[2]); + color.s.green = (HEX2INT(col[3]) << 4) + HEX2INT(col[4]); + color.s.blue = (HEX2INT(col[5]) << 4) + HEX2INT(col[6]); #ifdef GLENCORE if (encoremap) { - j = encoremap[NearestColor((UINT8)cr, (UINT8)cg, (UINT8)cb)]; + j = encoremap[NearestColor(color.s.red, color.s.green, color.s.blue)]; //CONS_Printf("R_CreateColormap: encoremap[%d] = %d\n", j, encoremap[j]); -- moved encoremap upwards for optimisation - cr = pLocalPalette[j].s.red; - cg = pLocalPalette[j].s.green; - cb = pLocalPalette[j].s.blue; + color = pLocalPalette[j]; // note: this sets alpha to 255, we will reset it below } #endif - - sec->extra_colormap->fadergba = cr + cg + cb; + color.s.alpha = 0; // reset/init the alpha, so the addition below will work correctly + sec->extra_colormap->fadergba = color.rgba; // alpha if (msd->bottomtexture[7]) diff --git a/src/p_tick.c b/src/p_tick.c index eaa8b462..4cc6c9ba 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -726,9 +726,9 @@ void P_Ticker(boolean run) if (exitcountdown > 1) exitcountdown--; - if (indirectitemcooldown > 1) + if (indirectitemcooldown > 0) indirectitemcooldown--; - if (hyubgone > 1) + if (hyubgone > 0) hyubgone--; if (G_BattleGametype()) diff --git a/src/r_main.c b/src/r_main.c index 2ec06497..6f2319b6 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -1142,7 +1142,7 @@ void R_SetupFrame(player_t *player, boolean skybox) aimingangle = player->aiming; viewangle = viewmobj->angle; - if (/*!demo.playback && */player->playerstate != PST_DEAD) + if (!demo.playback && player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { diff --git a/src/r_plane.c b/src/r_plane.c index db5bfbda..659481ec 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -402,7 +402,7 @@ static visplane_t *new_visplane(unsigned hash) visplane_t *check = freetail; if (!check) { - check = calloc(2, sizeof (*check)); + check = calloc(1, sizeof (*check)); if (check == NULL) I_Error("%s: Out of memory", "new_visplane"); // FIXME: ugly } else diff --git a/src/s_sound.h b/src/s_sound.h index 2a904faf..eeb1f0ab 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -236,7 +236,7 @@ void S_StopSoundByNum(sfxenum_t sfxnum); #ifdef MUSICSLOT_COMPATIBILITY // For compatibility with code/scripts relying on older versions // This is a list of all the "special" slot names and their associated numbers -const char *compat_special_music_slots[16]; +extern const char *compat_special_music_slots[16]; #endif #endif diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg index 58c4d086..b0c591ce 100644 --- a/src/sdl/Makefile.cfg +++ b/src/sdl/Makefile.cfg @@ -83,6 +83,11 @@ else SDL_LDFLAGS+=-lSDL2_mixer endif +ifndef NOTHREADS + OPTS+=-DHAVE_THREADS + OBJS+=$(OBJDIR)/i_threads.o +endif + ifdef SDL_TTF OPTS+=-DHAVE_TTF SDL_LDFLAGS+=-lSDL2_ttf -lfreetype -lz diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 69b3465a..813e31a9 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -249,6 +249,7 @@ + @@ -399,6 +400,7 @@ + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 9e5b9837..f197a394 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -291,6 +291,9 @@ I_Interface + + I_Interface + LUA @@ -663,6 +666,9 @@ I_Interface + + I_Interface + LUA diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj index b21eedb8..115e17a3 100644 --- a/src/sdl/Srb2SDL-vc9.vcproj +++ b/src/sdl/Srb2SDL-vc9.vcproj @@ -2782,6 +2782,50 @@ RelativePath="..\mserv.h" > + + + + + + + + + + + + + + + + + +typedef void * (*Create_fn)(void); + +struct Link; +struct Thread; + +typedef struct Link * Link; +typedef struct Thread * Thread; + +struct Link +{ + void * data; + Link next; + Link prev; +}; + +struct Thread +{ + I_thread_fn entry; + void * userdata; + + SDL_Thread * thread; +}; + +static Link i_thread_pool; +static Link i_mutex_pool; +static Link i_cond_pool; + +static I_mutex i_thread_pool_mutex; +static I_mutex i_mutex_pool_mutex; +static I_mutex i_cond_pool_mutex; + +static SDL_atomic_t i_threads_running = {1}; + +static Link +Insert_link ( + Link * head, + Link link +){ + link->prev = NULL; + link->next = (*head); + if ((*head)) + (*head)->prev = link; + (*head) = link; + return link; +} + +static void +Free_link ( + Link * head, + Link link +){ + if (link->prev) + link->prev->next = link->next; + else + (*head) = link->next; + + if (link->next) + link->next->prev = link->prev; + + free(link->data); + free(link); +} + +static Link +New_link (void *data) +{ + Link link; + + link = malloc(sizeof *link); + + if (! link) + abort(); + + link->data = data; + + return link; +} + +static void * +Identity ( + Link * pool_anchor, + I_mutex pool_mutex, + + void ** anchor, + + Create_fn create_fn +){ + void * id; + + id = SDL_AtomicGetPtr(anchor); + + if (! id) + { + I_lock_mutex(&pool_mutex); + { + id = SDL_AtomicGetPtr(anchor); + + if (! id) + { + id = (*create_fn)(); + + if (! id) + abort(); + + Insert_link(pool_anchor, New_link(id)); + + SDL_AtomicSetPtr(anchor, id); + } + } + I_unlock_mutex(pool_mutex); + } + + return id; +} + +static int +Worker ( + Link link +){ + Thread th; + + th = link->data; + + (*th->entry)(th->userdata); + + if (SDL_AtomicGet(&i_threads_running)) + { + I_lock_mutex(&i_thread_pool_mutex); + { + if (SDL_AtomicGet(&i_threads_running)) + { + SDL_DetachThread(th->thread); + Free_link(&i_thread_pool, link); + } + } + I_unlock_mutex(i_thread_pool_mutex); + } + + return 0; +} + +void +I_spawn_thread ( + const char * name, + I_thread_fn entry, + void * userdata +){ + Link link; + Thread th; + + th = malloc(sizeof *th); + + if (! th) + abort();/* this is pretty GNU of me */ + + th->entry = entry; + th->userdata = userdata; + + I_lock_mutex(&i_thread_pool_mutex); + { + link = Insert_link(&i_thread_pool, New_link(th)); + + if (SDL_AtomicGet(&i_threads_running)) + { + th->thread = SDL_CreateThread( + (SDL_ThreadFunction)Worker, + name, + link + ); + + if (! th->thread) + abort(); + } + } + I_unlock_mutex(i_thread_pool_mutex); +} + +int +I_thread_is_stopped (void) +{ + return ( ! SDL_AtomicGet(&i_threads_running) ); +} + +void +I_start_threads (void) +{ + i_thread_pool_mutex = SDL_CreateMutex(); + i_mutex_pool_mutex = SDL_CreateMutex(); + i_cond_pool_mutex = SDL_CreateMutex(); + + if (!( + i_thread_pool_mutex && + i_mutex_pool_mutex && + i_cond_pool_mutex + )){ + abort(); + } +} + +void +I_stop_threads (void) +{ + Link link; + Link next; + + Thread th; + SDL_mutex * mutex; + SDL_cond * cond; + + if (i_threads_running.value) + { + /* rely on the good will of thread-san */ + SDL_AtomicSet(&i_threads_running, 0); + + I_lock_mutex(&i_thread_pool_mutex); + { + for ( + link = i_thread_pool; + link; + link = next + ){ + next = link->next; + th = link->data; + + SDL_WaitThread(th->thread, NULL); + + free(th); + free(link); + } + } + I_unlock_mutex(i_thread_pool_mutex); + + for ( + link = i_mutex_pool; + link; + link = next + ){ + next = link->next; + mutex = link->data; + + SDL_DestroyMutex(mutex); + + free(link); + } + + for ( + link = i_cond_pool; + link; + link = next + ){ + next = link->next; + cond = link->data; + + SDL_DestroyCond(cond); + + free(link); + } + + SDL_DestroyMutex(i_thread_pool_mutex); + SDL_DestroyMutex(i_mutex_pool_mutex); + SDL_DestroyMutex(i_cond_pool_mutex); + } +} + +void +I_lock_mutex ( + I_mutex * anchor +){ + SDL_mutex * mutex; + + mutex = Identity( + &i_mutex_pool, + i_mutex_pool_mutex, + anchor, + (Create_fn)SDL_CreateMutex + ); + + if (SDL_LockMutex(mutex) == -1) + abort(); +} + +void +I_unlock_mutex ( + I_mutex id +){ + if (SDL_UnlockMutex(id) == -1) + abort(); +} + +void +I_hold_cond ( + I_cond * cond_anchor, + I_mutex mutex_id +){ + SDL_cond * cond; + + cond = Identity( + &i_cond_pool, + i_cond_pool_mutex, + cond_anchor, + (Create_fn)SDL_CreateCond + ); + + if (SDL_CondWait(cond, mutex_id) == -1) + abort(); +} + +void +I_wake_one_cond ( + I_cond * anchor +){ + SDL_cond * cond; + + cond = Identity( + &i_cond_pool, + i_cond_pool_mutex, + anchor, + (Create_fn)SDL_CreateCond + ); + + if (SDL_CondSignal(cond) == -1) + abort(); +} + +void +I_wake_all_cond ( + I_cond * anchor +){ + SDL_cond * cond; + + cond = Identity( + &i_cond_pool, + i_cond_pool_mutex, + anchor, + (Create_fn)SDL_CreateCond + ); + + if (SDL_CondBroadcast(cond) == -1) + abort(); +} diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c index b006047f..eb8e12cb 100644 --- a/src/sdl/ogl_sdl.c +++ b/src/sdl/ogl_sdl.c @@ -33,6 +33,7 @@ #endif #include "../doomdef.h" +#include "../d_main.h" #ifdef HWRENDER #include "../hardware/r_opengl/r_opengl.h" @@ -154,11 +155,26 @@ boolean OglSdlSurface(INT32 w, INT32 h) { INT32 cbpp = cv_scr_depth.value < 16 ? 16 : cv_scr_depth.value; static boolean first_init = false; + const char *gllogdir = NULL; oglflags = 0; if (!first_init) { + if (!gllogstream) + { + gllogdir = D_Home(); + +#ifdef DEBUG_TO_FILE +#ifdef DEFAULTDIR + if (gllogdir) + gllogstream = fopen(va("%s/"DEFAULTDIR"/ogllog.txt",gllogdir), "wt"); + else +#endif + gllogstream = fopen("./ogllog.txt", "wt"); +#endif + } + gl_version = pglGetString(GL_VERSION); gl_renderer = pglGetString(GL_RENDERER); gl_extensions = pglGetString(GL_EXTENSIONS); diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c index d9967ae0..4bb1b567 100644 --- a/src/sdl/sdl_sound.c +++ b/src/sdl/sdl_sound.c @@ -1173,7 +1173,10 @@ void I_StartupSound(void) const char *sdrv_name = NULL; #endif #ifndef HAVE_MIXER - midi_disabled = digital_disabled = true; +#ifndef NO_MIDI + midi_disabled = +#endif + digital_disabled = true; #endif memset(channels, 0, sizeof (channels)); //Alam: Clean it diff --git a/src/sdl12/sdl_sound.c b/src/sdl12/sdl_sound.c index ed1afd8e..1a7525fe 100644 --- a/src/sdl12/sdl_sound.c +++ b/src/sdl12/sdl_sound.c @@ -1435,7 +1435,7 @@ static boolean LoadSong(void *data, size_t lumplength, size_t selectpos) if (fwrite(data, lumplength, 1, midfile) == 0) { - CONS_Printf(M_GetText("Couldn't write music into file %s because %s\n"), tempname, strerror(ferror(midfile))); + CONS_Printf(M_GetText("Couldn't write music into file %s because %s\n"), tempname, M_FileError(midfile)); Z_Free(data); fclose(midfile); return false; diff --git a/src/st_stuff.c b/src/st_stuff.c index 008a1461..9a9af635 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1797,10 +1797,12 @@ static void ST_doItemFinderIconsAndSound(void) // SRB2kart - unused. // static void ST_overlayDrawer(void) { - /* SRB2kart doesn't need this stuff //hu_showscores = auto hide score/time/rings when tab rankings are shown if (!(hu_showscores && (netgame || multiplayer))) { + K_drawKartHUD(); + + /* SRB2kart doesn't need this stuff if (maptol & TOL_NIGHTS) ST_drawNiGHTSHUD(); else @@ -1824,8 +1826,8 @@ static void ST_overlayDrawer(void) ) ST_drawLives(); } - } */ + } // GAME OVER pic /*if (G_GametypeUsesLives() && stplyr->lives <= 0 && !(hu_showscores && (netgame || multiplayer))) @@ -1845,8 +1847,6 @@ static void ST_overlayDrawer(void) // Countdown timer for Race Mode // ...moved to k_kart.c so we can take advantage of the LAPS_Y value - K_drawKartHUD(); - /* SRB2kart doesn't need this stuff, I think // If you are in overtime, put a big honkin' flashin' message on the screen. if (G_BattleGametype() && cv_overtime.value diff --git a/src/w_wad.c b/src/w_wad.c index 841d8fd3..7fd7ac12 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -366,7 +366,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen // read the header if (fread(&header, 1, sizeof header, handle) < sizeof header) { - CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header because %s\n"), strerror(ferror(handle))); + CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header because %s\n"), M_FileError(handle)); return NULL; } @@ -389,7 +389,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen if (fseek(handle, header.infotableofs, SEEK_SET) == -1 || fread(fileinfo, 1, i, handle) < i) { - CONS_Alert(CONS_ERROR, M_GetText("Corrupt wadfile directory (%s)\n"), strerror(ferror(handle))); + CONS_Alert(CONS_ERROR, M_GetText("Corrupt wadfile directory (%s)\n"), M_FileError(handle)); free(fileinfov); return NULL; } @@ -410,7 +410,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen handle) < sizeof realsize) { I_Error("corrupt compressed file: %s; maybe %s", /// \todo Avoid the bailout? - filename, strerror(ferror(handle))); + filename, M_FileError(handle)); } realsize = LONG(realsize); if (realsize != 0) @@ -548,7 +548,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) fseek(handle, -4, SEEK_CUR); if (fread(&zend, 1, sizeof zend, handle) < sizeof zend) { - CONS_Alert(CONS_ERROR, "Corrupt central directory (%s)\n", strerror(ferror(handle))); + CONS_Alert(CONS_ERROR, "Corrupt central directory (%s)\n", M_FileError(handle)); return NULL; } numlumps = zend.entries; @@ -565,7 +565,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) if (fread(zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t)) { - CONS_Alert(CONS_ERROR, "Failed to read central directory (%s)\n", strerror(ferror(handle))); + CONS_Alert(CONS_ERROR, "Failed to read central directory (%s)\n", M_FileError(handle)); Z_Free(lumpinfo); free(zentry); return NULL; @@ -585,7 +585,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) fullname = malloc(zentry->namelen + 1); if (fgets(fullname, zentry->namelen + 1, handle) != fullname) { - CONS_Alert(CONS_ERROR, "Unable to read lumpname (%s)\n", strerror(ferror(handle))); + CONS_Alert(CONS_ERROR, "Unable to read lumpname (%s)\n", M_FileError(handle)); Z_Free(lumpinfo); free(zentry); free(fullname); @@ -1319,8 +1319,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si { size = 0; zerr(zErr); - (void)inflateEnd(&strm); } + (void)inflateEnd(&strm); } else {