Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
  • Jisk/srb-2-beef-jerky
117 results
Select Git revision
Show changes
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 2020-2023 by Jaime "Lactozilla" Passos. // Copyright (C) 2020-2023 by Lactozilla.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 2020-2023 by Jaime "Lactozilla" Passos. // Copyright (C) 2020-2023 by Lactozilla.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// Copyright (C) 2009 by Stephen McGranahan. // Copyright (C) 2009 by Stephen McGranahan.
// //
// This program is free software distributed under the // This program is free software distributed under the
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2011-2016 by Matthew "Kaito Sinclaire" Walsh. // Copyright (C) 2011-2016 by Matthew "Kaito Sinclaire" Walsh.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2025 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "p_setup.h" #include "p_setup.h"
#include "f_finale.h" #include "f_finale.h"
#include "lua_hook.h" #include "lua_hook.h"
#include "lua_libs.h"
#ifdef HWRENDER #ifdef HWRENDER
#include "hardware/hw_main.h" #include "hardware/hw_main.h"
...@@ -263,7 +264,7 @@ static void M_ConfirmTeamScramble(INT32 choice); ...@@ -263,7 +264,7 @@ static void M_ConfirmTeamScramble(INT32 choice);
static void M_ConfirmTeamChange(INT32 choice); static void M_ConfirmTeamChange(INT32 choice);
static void M_SecretsMenu(INT32 choice); static void M_SecretsMenu(INT32 choice);
static void M_SetupChoosePlayer(INT32 choice); static void M_SetupChoosePlayer(INT32 choice);
static UINT16 M_SetupChoosePlayerDirect(INT32 choice); static INT32 M_SetupChoosePlayerDirect(INT32 choice);
static void M_QuitSRB2(INT32 choice); static void M_QuitSRB2(INT32 choice);
menu_t SP_MainDef, OP_MainDef; menu_t SP_MainDef, OP_MainDef;
menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef; menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef;
...@@ -1073,7 +1074,6 @@ static menuitem_t OP_ChangeControlsMenu[] = ...@@ -1073,7 +1074,6 @@ static menuitem_t OP_ChangeControlsMenu[] =
{IT_CALL | IT_STRING2, NULL, "Move Right", M_ChangeControl, GC_STRAFERIGHT }, {IT_CALL | IT_STRING2, NULL, "Move Right", M_ChangeControl, GC_STRAFERIGHT },
{IT_CALL | IT_STRING2, NULL, "Jump", M_ChangeControl, GC_JUMP }, {IT_CALL | IT_STRING2, NULL, "Jump", M_ChangeControl, GC_JUMP },
{IT_CALL | IT_STRING2, NULL, "Spin", M_ChangeControl, GC_SPIN }, {IT_CALL | IT_STRING2, NULL, "Spin", M_ChangeControl, GC_SPIN },
{IT_CALL | IT_STRING2, NULL, "Shield Ability", M_ChangeControl, GC_SHIELD },
{IT_HEADER, NULL, "Camera", NULL, 0}, {IT_HEADER, NULL, "Camera", NULL, 0},
{IT_SPACE, NULL, NULL, NULL, 0}, // padding {IT_SPACE, NULL, NULL, NULL, 0}, // padding
{IT_CALL | IT_STRING2, NULL, "Look Up", M_ChangeControl, GC_LOOKUP }, {IT_CALL | IT_STRING2, NULL, "Look Up", M_ChangeControl, GC_LOOKUP },
...@@ -1122,15 +1122,13 @@ static menuitem_t OP_ChangeControlsMenu[] = ...@@ -1122,15 +1122,13 @@ static menuitem_t OP_ChangeControlsMenu[] =
static menuitem_t OP_Joystick1Menu[] = static menuitem_t OP_Joystick1Menu[] =
{ {
{IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup1PJoystickMenu, 0}, {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup1PJoystickMenu, 10},
{IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis , 30},
{IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis , 20}, {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis , 40},
{IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis , 30}, {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis , 50},
{IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis , 40}, {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis , 60},
{IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis , 50}, {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis , 70},
{IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis , 60}, {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis , 80},
{IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis , 70},
{IT_STRING | IT_CVAR, NULL, "Shield Axis" , &cv_shieldaxis , 80},
{IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis , 90}, {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis , 90},
{IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis ,100}, {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis ,100},
...@@ -1142,15 +1140,13 @@ static menuitem_t OP_Joystick1Menu[] = ...@@ -1142,15 +1140,13 @@ static menuitem_t OP_Joystick1Menu[] =
static menuitem_t OP_Joystick2Menu[] = static menuitem_t OP_Joystick2Menu[] =
{ {
{IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup2PJoystickMenu, 0}, {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup2PJoystickMenu, 10},
{IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis2 , 30},
{IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis2 , 20}, {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis2 , 40},
{IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis2 , 30}, {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis2 , 50},
{IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis2 , 40}, {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis2 , 60},
{IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis2 , 50}, {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis2 , 70},
{IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis2 , 60}, {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis2 , 80},
{IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis2 , 70},
{IT_STRING | IT_CVAR, NULL, "Shield Axis" , &cv_shieldaxis2 , 80},
{IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis2 , 90}, {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis2 , 90},
{IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis2 ,100}, {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis2 ,100},
...@@ -2102,6 +2098,12 @@ menu_t OP_PlaystyleDef = { ...@@ -2102,6 +2098,12 @@ menu_t OP_PlaystyleDef = {
0, 0, 0, NULL 0, 0, 0, NULL
}; };
static void M_UpdateItemOn(void)
{
I_SetTextInputMode((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING ||
(currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER);
}
static void M_VideoOptions(INT32 choice) static void M_VideoOptions(INT32 choice)
{ {
(void)choice; (void)choice;
...@@ -2329,6 +2331,7 @@ void Nextmap_OnChange(void) ...@@ -2329,6 +2331,7 @@ void Nextmap_OnChange(void)
{ {
currentMenu->lastOn = itemOn; currentMenu->lastOn = itemOn;
itemOn = nastart; itemOn = nastart;
M_UpdateItemOn();
} }
} }
else if (currentMenu == &SP_TimeAttackDef) else if (currentMenu == &SP_TimeAttackDef)
...@@ -2378,6 +2381,7 @@ void Nextmap_OnChange(void) ...@@ -2378,6 +2381,7 @@ void Nextmap_OnChange(void)
{ {
currentMenu->lastOn = itemOn; currentMenu->lastOn = itemOn;
itemOn = tastart; itemOn = tastart;
M_UpdateItemOn();
} }
if (mapheaderinfo[cv_nextmap.value-1] && mapheaderinfo[cv_nextmap.value-1]->forcecharacter[0] != '\0') if (mapheaderinfo[cv_nextmap.value-1] && mapheaderinfo[cv_nextmap.value-1]->forcecharacter[0] != '\0')
...@@ -3128,6 +3132,7 @@ static void M_NextOpt(void) ...@@ -3128,6 +3132,7 @@ static void M_NextOpt(void)
else else
itemOn++; itemOn++;
} while (oldItemOn != itemOn && ( (currentMenu->menuitems[itemOn].status & IT_TYPE) & IT_SPACE )); } while (oldItemOn != itemOn && ( (currentMenu->menuitems[itemOn].status & IT_TYPE) & IT_SPACE ));
M_UpdateItemOn();
} }
static void M_PrevOpt(void) static void M_PrevOpt(void)
...@@ -3140,6 +3145,7 @@ static void M_PrevOpt(void) ...@@ -3140,6 +3145,7 @@ static void M_PrevOpt(void)
else else
itemOn--; itemOn--;
} while (oldItemOn != itemOn && ( (currentMenu->menuitems[itemOn].status & IT_TYPE) & IT_SPACE )); } while (oldItemOn != itemOn && ( (currentMenu->menuitems[itemOn].status & IT_TYPE) & IT_SPACE ));
M_UpdateItemOn();
} }
// lock out further input in a tic when important buttons are pressed // lock out further input in a tic when important buttons are pressed
...@@ -3306,7 +3312,7 @@ boolean M_Responder(event_t *ev) ...@@ -3306,7 +3312,7 @@ boolean M_Responder(event_t *ev)
if (ch == -1) if (ch == -1)
return false; return false;
else if (ch == gamecontrol[GC_SYSTEMMENU][0] || ch == gamecontrol[GC_SYSTEMMENU][1]) // allow remappable ESC key else if (ev->type != ev_text && (ch == gamecontrol[GC_SYSTEMMENU][0] || ch == gamecontrol[GC_SYSTEMMENU][1])) // allow remappable ESC key
ch = KEY_ESCAPE; ch = KEY_ESCAPE;
// F-Keys // F-Keys
...@@ -3384,9 +3390,16 @@ boolean M_Responder(event_t *ev) ...@@ -3384,9 +3390,16 @@ boolean M_Responder(event_t *ev)
// Handle menuitems which need a specific key handling // Handle menuitems which need a specific key handling
if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER) if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER)
{ {
// block text input if ctrl is held, to allow using ctrl+c ctrl+v and ctrl+x
if (ctrldown)
{
routine(ch);
return true;
}
// ignore ev_keydown events if the key maps to a character, since // ignore ev_keydown events if the key maps to a character, since
// the ev_text event will follow immediately after in that case. // the ev_text event will follow immediately after in that case.
if (ev->type == ev_keydown && ch >= 32 && ch <= 127) if (ev->type == ev_keydown && ((ch >= 32 && ch <= 127) || (ch >= KEY_KEYPAD7 && ch <= KEY_KPADDEL)))
return true; return true;
routine(ch); routine(ch);
...@@ -3414,7 +3427,7 @@ boolean M_Responder(event_t *ev) ...@@ -3414,7 +3427,7 @@ boolean M_Responder(event_t *ev)
{ {
// dirty hack: for customising controls, I want only buttons/keys, not moves // dirty hack: for customising controls, I want only buttons/keys, not moves
if (ev->type == ev_mouse || ev->type == ev_mouse2 || ev->type == ev_joystick if (ev->type == ev_mouse || ev->type == ev_mouse2 || ev->type == ev_joystick
|| ev->type == ev_joystick2) || ev->type == ev_joystick2 || ev->type == ev_text)
return true; return true;
if (routine) if (routine)
{ {
...@@ -3651,21 +3664,27 @@ void M_StartControlPanel(void) ...@@ -3651,21 +3664,27 @@ void M_StartControlPanel(void)
currentMenu = &MainDef; currentMenu = &MainDef;
itemOn = singleplr; itemOn = singleplr;
M_UpdateItemOn();
} }
else if (modeattacking) else if (modeattacking)
{ {
currentMenu = &MAPauseDef; currentMenu = &MAPauseDef;
MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED); MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
itemOn = mapause_continue; itemOn = mapause_continue;
M_UpdateItemOn();
} }
else if (!(netgame || multiplayer)) // Single Player else if (!(netgame || multiplayer)) // Single Player
{ {
// Devmode unlocks Pandora's Box in the pause menu // Devmode unlocks Pandora's Box and Level Select in the pause menu
boolean pandora = ((M_SecretUnlocked(SECRET_PANDORA, serverGamedata) || cv_debug || devparm) && !marathonmode); boolean isforbidden = (marathonmode || ultimatemode);
boolean isdebug = ((cv_debug || devparm) && !isforbidden);
boolean pandora = ((M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !isforbidden) || isdebug);
boolean lselect = ((maplistoption != 0 && !isforbidden) || isdebug);
if (gamestate != GS_LEVEL || ultimatemode) // intermission, so gray out stuff. if (gamestate != GS_LEVEL) // intermission, so gray out stuff.
{ {
SPauseMenu[spause_pandora].status = (pandora) ? (IT_GRAYEDOUT) : (IT_DISABLED); SPauseMenu[spause_pandora].status = (pandora) ? (IT_GRAYEDOUT) : (IT_DISABLED);
SPauseMenu[spause_levelselect].status = (lselect) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
SPauseMenu[spause_retry].status = IT_GRAYEDOUT; SPauseMenu[spause_retry].status = IT_GRAYEDOUT;
} }
else else
...@@ -3675,6 +3694,11 @@ void M_StartControlPanel(void) ...@@ -3675,6 +3694,11 @@ void M_StartControlPanel(void)
++numlives; ++numlives;
SPauseMenu[spause_pandora].status = (pandora) ? (IT_STRING | IT_CALL) : (IT_DISABLED); SPauseMenu[spause_pandora].status = (pandora) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
SPauseMenu[spause_levelselect].status = (lselect) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
if (ultimatemode)
{
SPauseMenu[spause_retry].status = IT_GRAYEDOUT;
}
// The list of things that can disable retrying is (was?) a little too complex // The list of things that can disable retrying is (was?) a little too complex
// for me to want to use the short if statement syntax // for me to want to use the short if statement syntax
...@@ -3684,13 +3708,6 @@ void M_StartControlPanel(void) ...@@ -3684,13 +3708,6 @@ void M_StartControlPanel(void)
SPauseMenu[spause_retry].status = (IT_STRING | IT_CALL); SPauseMenu[spause_retry].status = (IT_STRING | IT_CALL);
} }
// We can always use level select though. :33
// Guarantee it if we have either it unlocked or devmode is enabled
if ((maplistoption != 0 || M_SecretUnlocked(SECRET_LEVELSELECT, serverGamedata) || cv_debug || devparm) && !marathonmode)
SPauseMenu[spause_levelselect].status = (IT_STRING | IT_CALL);
else
SPauseMenu[spause_levelselect].status = (IT_DISABLED);
// And emblem hints. // And emblem hints.
SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED); SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
...@@ -3703,6 +3720,7 @@ void M_StartControlPanel(void) ...@@ -3703,6 +3720,7 @@ void M_StartControlPanel(void)
currentMenu = &SPauseDef; currentMenu = &SPauseDef;
itemOn = spause_continue; itemOn = spause_continue;
M_UpdateItemOn();
} }
else // multiplayer else // multiplayer
{ {
...@@ -3744,6 +3762,7 @@ void M_StartControlPanel(void) ...@@ -3744,6 +3762,7 @@ void M_StartControlPanel(void)
currentMenu = &MPauseDef; currentMenu = &MPauseDef;
itemOn = mpause_continue; itemOn = mpause_continue;
M_UpdateItemOn();
} }
CON_ToggleOff(); // move away console CON_ToggleOff(); // move away console
...@@ -3775,6 +3794,7 @@ void M_ClearMenus(boolean callexitmenufunc) ...@@ -3775,6 +3794,7 @@ void M_ClearMenus(boolean callexitmenufunc)
hidetitlemap = false; hidetitlemap = false;
I_UpdateMouseGrab(); I_UpdateMouseGrab();
I_SetTextInputMode(textinputmodeenabledbylua);
} }
// //
...@@ -3837,6 +3857,7 @@ void M_SetupNextMenu(menu_t *menudef) ...@@ -3837,6 +3857,7 @@ void M_SetupNextMenu(menu_t *menudef)
} }
} }
} }
M_UpdateItemOn();
hidetitlemap = false; hidetitlemap = false;
} }
...@@ -3873,6 +3894,9 @@ void M_Ticker(void) ...@@ -3873,6 +3894,9 @@ void M_Ticker(void)
M_SetupScreenshotMenu(); M_SetupScreenshotMenu();
#if defined (MASTERSERVER) && defined (HAVE_THREADS) #if defined (MASTERSERVER) && defined (HAVE_THREADS)
if (!netgame)
return;
I_lock_mutex(&ms_ServerList_mutex); I_lock_mutex(&ms_ServerList_mutex);
{ {
if (ms_ServerList) if (ms_ServerList)
...@@ -4015,11 +4039,11 @@ static void M_DrawThermo(INT32 x, INT32 y, consvar_t *cv) ...@@ -4015,11 +4039,11 @@ static void M_DrawThermo(INT32 x, INT32 y, consvar_t *cv)
lumpnum_t leftlump, rightlump, centerlump[2], cursorlump; lumpnum_t leftlump, rightlump, centerlump[2], cursorlump;
patch_t *p; patch_t *p;
leftlump = W_GetNumForName("M_THERML"); leftlump = W_GetNumForPatchName("M_THERML");
rightlump = W_GetNumForName("M_THERMR"); rightlump = W_GetNumForPatchName("M_THERMR");
centerlump[0] = W_GetNumForName("M_THERMM"); centerlump[0] = W_GetNumForPatchName("M_THERMM");
centerlump[1] = W_GetNumForName("M_THERMM"); centerlump[1] = W_GetNumForPatchName("M_THERMM");
cursorlump = W_GetNumForName("M_THERMO"); cursorlump = W_GetNumForPatchName("M_THERMO");
V_DrawScaledPatch(xx, y, 0, p = W_CachePatchNum(leftlump,PU_PATCH)); V_DrawScaledPatch(xx, y, 0, p = W_CachePatchNum(leftlump,PU_PATCH));
xx += p->width - p->leftoffset; xx += p->width - p->leftoffset;
...@@ -6109,6 +6133,7 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp ...@@ -6109,6 +6133,7 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp
currentMenu = &MessageDef; currentMenu = &MessageDef;
itemOn = 0; itemOn = 0;
M_UpdateItemOn();
} }
static void M_DrawMessageMenu(void) static void M_DrawMessageMenu(void)
...@@ -6183,6 +6208,7 @@ static void M_HandleImageDef(INT32 choice) ...@@ -6183,6 +6208,7 @@ static void M_HandleImageDef(INT32 choice)
if (itemOn >= (INT16)(currentMenu->numitems-1)) if (itemOn >= (INT16)(currentMenu->numitems-1))
itemOn = 0; itemOn = 0;
else itemOn++; else itemOn++;
M_UpdateItemOn();
break; break;
case KEY_LEFTARROW: case KEY_LEFTARROW:
...@@ -6193,6 +6219,7 @@ static void M_HandleImageDef(INT32 choice) ...@@ -6193,6 +6219,7 @@ static void M_HandleImageDef(INT32 choice)
if (!itemOn) if (!itemOn)
itemOn = currentMenu->numitems - 1; itemOn = currentMenu->numitems - 1;
else itemOn--; else itemOn--;
M_UpdateItemOn();
break; break;
case KEY_ESCAPE: case KEY_ESCAPE:
...@@ -6982,7 +7009,10 @@ static void M_LevelSelectWarp(INT32 choice) ...@@ -6982,7 +7009,10 @@ static void M_LevelSelectWarp(INT32 choice)
if (currentMenu == &SP_LevelSelectDef || currentMenu == &SP_PauseLevelSelectDef) if (currentMenu == &SP_LevelSelectDef || currentMenu == &SP_PauseLevelSelectDef)
{ {
if (cursaveslot > 0) // do we have a save slot to load? if (cursaveslot > 0) // do we have a save slot to load?
{
CV_StealthSet(&cv_skin, DEFAULTSKIN); // already handled by loadgame so we don't want this
G_LoadGame((UINT32)cursaveslot, startmap); // reload from SP save data: this is needed to keep score/lives/continues from reverting to defaults G_LoadGame((UINT32)cursaveslot, startmap); // reload from SP save data: this is needed to keep score/lives/continues from reverting to defaults
}
else // no save slot, start new game but keep the current skin else // no save slot, start new game but keep the current skin
{ {
M_ClearMenus(true); M_ClearMenus(true);
...@@ -7389,6 +7419,7 @@ static void M_EmblemHints(INT32 choice) ...@@ -7389,6 +7419,7 @@ static void M_EmblemHints(INT32 choice)
SR_EmblemHintDef.prevMenu = currentMenu; SR_EmblemHintDef.prevMenu = currentMenu;
M_SetupNextMenu(&SR_EmblemHintDef); M_SetupNextMenu(&SR_EmblemHintDef);
itemOn = 2; // always start on back. itemOn = 2; // always start on back.
M_UpdateItemOn();
} }
static void M_DrawEmblemHints(void) static void M_DrawEmblemHints(void)
...@@ -7521,13 +7552,9 @@ static void M_PauseLevelSelect(INT32 choice) ...@@ -7521,13 +7552,9 @@ static void M_PauseLevelSelect(INT32 choice)
SP_PauseLevelSelectDef.prevMenu = currentMenu; SP_PauseLevelSelectDef.prevMenu = currentMenu;
levellistmode = LLM_LEVELSELECT; levellistmode = LLM_LEVELSELECT;
// maplistoption is only specified if not set already // maplistoption is NOT specified, so that this
// and we have the level select unlocked so that it
// transfers the level select list from the menu // transfers the level select list from the menu
// used to enter the game to the pause menu. // used to enter the game to the pause menu.
if (maplistoption == 0 && M_SecretUnlocked(SECRET_LEVELSELECT, serverGamedata))
maplistoption = 1;
if (!M_PrepareLevelPlatter(-1, true)) if (!M_PrepareLevelPlatter(-1, true))
{ {
M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING);
...@@ -8251,6 +8278,7 @@ static void M_StartTutorial(INT32 choice) ...@@ -8251,6 +8278,7 @@ static void M_StartTutorial(INT32 choice)
gamecomplete = 0; gamecomplete = 0;
cursaveslot = 0; cursaveslot = 0;
maplistoption = 0; maplistoption = 0;
CV_StealthSet(&cv_skin, DEFAULTSKIN); // tutorial accounts for sonic only
G_DeferedInitNew(false, G_BuildMapName(tutorialmap), 0, false, false); G_DeferedInitNew(false, G_BuildMapName(tutorialmap), 0, false, false);
} }
...@@ -8350,7 +8378,7 @@ static void M_DrawLoadGameData(void) ...@@ -8350,7 +8378,7 @@ static void M_DrawLoadGameData(void)
if (savegameinfo[savetodraw].lives == -42) if (savegameinfo[savetodraw].lives == -42)
col = 26; col = 26;
else if (savegameinfo[savetodraw].botskin == 3) // & knuckles else if (savegameinfo[savetodraw].botskin == 3) // & knuckles
col = 105; col = 106;
else if (savegameinfo[savetodraw].botskin) // tailsbot or custom else if (savegameinfo[savetodraw].botskin) // tailsbot or custom
col = 134; col = 134;
else else
...@@ -8410,7 +8438,17 @@ static void M_DrawLoadGameData(void) ...@@ -8410,7 +8438,17 @@ static void M_DrawLoadGameData(void)
if (savegameinfo[savetodraw].lives == -42) if (savegameinfo[savetodraw].lives == -42)
V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME"); V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME");
else if (savegameinfo[savetodraw].lives == -666) else if (savegameinfo[savetodraw].lives == -666)
{
if (savegameinfo[savetodraw].continuescore == -62)
{
V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "ADDON NOT LOADED");
V_DrawRightAlignedThinString(x + 79, y-10, V_REDMAP, savegameinfo[savetodraw].skinname);
}
else
{
V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "CAN'T LOAD!"); V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "CAN'T LOAD!");
}
}
else if (savegameinfo[savetodraw].gamemap & 8192) else if (savegameinfo[savetodraw].gamemap & 8192)
V_DrawRightAlignedThinString(x + 79, y, V_GREENMAP, "CLEAR!"); V_DrawRightAlignedThinString(x + 79, y, V_GREENMAP, "CLEAR!");
else else
...@@ -8630,14 +8668,20 @@ static void M_LoadSelect(INT32 choice) ...@@ -8630,14 +8668,20 @@ static void M_LoadSelect(INT32 choice)
M_NewGame(); M_NewGame();
} }
else if (savegameinfo[saveSlotSelected-1].gamemap & 8192) // Completed else if (savegameinfo[saveSlotSelected-1].gamemap & 8192) // Completed
{
M_LoadGameLevelSelect(0); M_LoadGameLevelSelect(0);
}
else else
{
CV_StealthSet(&cv_skin, DEFAULTSKIN); // already handled by loadgame so we don't want this
G_LoadGame((UINT32)saveSlotSelected, 0); G_LoadGame((UINT32)saveSlotSelected, 0);
}
cursaveslot = saveSlotSelected; cursaveslot = saveSlotSelected;
} }
#define VERSIONSIZE 16 #define VERSIONSIZE 16
#define MISSING { savegameinfo[slot].continuescore = -62; savegameinfo[slot].lives = -666; Z_Free(savebuffer); return; }
#define BADSAVE { savegameinfo[slot].lives = -666; Z_Free(savebuffer); return; } #define BADSAVE { savegameinfo[slot].lives = -666; Z_Free(savebuffer); return; }
#define CHECKPOS if (sav_p >= end_p) BADSAVE #define CHECKPOS if (sav_p >= end_p) BADSAVE
// Reads the save file to list lives, level, player, etc. // Reads the save file to list lives, level, player, etc.
...@@ -8734,10 +8778,11 @@ static void M_ReadSavegameInfo(UINT32 slot) ...@@ -8734,10 +8778,11 @@ static void M_ReadSavegameInfo(UINT32 slot)
CHECKPOS CHECKPOS
READSTRINGN(sav_p, ourSkinName, SKINNAMESIZE); READSTRINGN(sav_p, ourSkinName, SKINNAMESIZE);
savegameinfo[slot].skinnum = R_SkinAvailable(ourSkinName); savegameinfo[slot].skinnum = R_SkinAvailable(ourSkinName);
STRBUFCPY(savegameinfo[slot].skinname, ourSkinName);
if (savegameinfo[slot].skinnum >= numskins if (savegameinfo[slot].skinnum >= numskins
|| !R_SkinUsable(-1, savegameinfo[slot].skinnum)) || !R_SkinUsable(-1, savegameinfo[slot].skinnum))
BADSAVE MISSING
CHECKPOS CHECKPOS
READSTRINGN(sav_p, botSkinName, SKINNAMESIZE); READSTRINGN(sav_p, botSkinName, SKINNAMESIZE);
...@@ -8745,7 +8790,7 @@ static void M_ReadSavegameInfo(UINT32 slot) ...@@ -8745,7 +8790,7 @@ static void M_ReadSavegameInfo(UINT32 slot)
if (savegameinfo[slot].botskin-1 >= numskins if (savegameinfo[slot].botskin-1 >= numskins
|| !R_SkinUsable(-1, savegameinfo[slot].botskin-1)) || !R_SkinUsable(-1, savegameinfo[slot].botskin-1))
BADSAVE MISSING
} }
CHECKPOS CHECKPOS
...@@ -8790,6 +8835,7 @@ static void M_ReadSavegameInfo(UINT32 slot) ...@@ -8790,6 +8835,7 @@ static void M_ReadSavegameInfo(UINT32 slot)
} }
#undef CHECKPOS #undef CHECKPOS
#undef BADSAVE #undef BADSAVE
#undef MISSING
// //
// M_ReadSaveStrings // M_ReadSaveStrings
...@@ -9053,7 +9099,7 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum) ...@@ -9053,7 +9099,7 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum)
description[i].namepic = W_CachePatchName(description[i].nametag, PU_PATCH); description[i].namepic = W_CachePatchName(description[i].nametag, PU_PATCH);
} }
static UINT16 M_SetupChoosePlayerDirect(INT32 choice) static INT32 M_SetupChoosePlayerDirect(INT32 choice)
{ {
INT32 skinnum, botskinnum; INT32 skinnum, botskinnum;
UINT16 i; UINT16 i;
...@@ -9142,7 +9188,7 @@ static UINT16 M_SetupChoosePlayerDirect(INT32 choice) ...@@ -9142,7 +9188,7 @@ static UINT16 M_SetupChoosePlayerDirect(INT32 choice)
static void M_SetupChoosePlayer(INT32 choice) static void M_SetupChoosePlayer(INT32 choice)
{ {
UINT16 skinset = M_SetupChoosePlayerDirect(choice); INT32 skinset = M_SetupChoosePlayerDirect(choice);
if (skinset != MAXCHARACTERSLOTS) if (skinset != MAXCHARACTERSLOTS)
{ {
M_ChoosePlayer(skinset); M_ChoosePlayer(skinset);
...@@ -9491,6 +9537,8 @@ static void M_ChoosePlayer(INT32 choice) ...@@ -9491,6 +9537,8 @@ static void M_ChoosePlayer(INT32 choice)
//lastmapsaved = 0; //lastmapsaved = 0;
gamecomplete = 0; gamecomplete = 0;
CV_StealthSet(&cv_skin, skins[skinnum]->name);
G_DeferedInitNew(ultmode, G_BuildMapName(startmap), skinnum, false, fromlevelselect); G_DeferedInitNew(ultmode, G_BuildMapName(startmap), skinnum, false, fromlevelselect);
COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
...@@ -10050,6 +10098,7 @@ static void M_TimeAttack(INT32 choice) ...@@ -10050,6 +10098,7 @@ static void M_TimeAttack(INT32 choice)
Nextmap_OnChange(); Nextmap_OnChange();
itemOn = tastart; // "Start" is selected. itemOn = tastart; // "Start" is selected.
M_UpdateItemOn();
} }
// Drawing function for Nights Attack // Drawing function for Nights Attack
...@@ -10181,9 +10230,12 @@ void M_DrawNightsAttackMenu(void) ...@@ -10181,9 +10230,12 @@ void M_DrawNightsAttackMenu(void)
color = skins[skinnumber]->prefcolor; color = skins[skinnumber]->prefcolor;
angle_t fa = (FixedAngle(((FixedInt(ntsatkdrawtimer * 4)) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK; angle_t fa = (FixedAngle(((FixedInt(ntsatkdrawtimer * 4)) % 360)<<FRACBITS)>>ANGLETOFINESHIFT) & FINEMASK;
fixed_t scale = skins[skinnumber]->highresscale;
if (skins[skinnumber]->shieldscale)
scale = FixedDiv(scale, skins[skinnumber]->shieldscale);
V_DrawFixedPatch(270<<FRACBITS, (186<<FRACBITS) - 8*FINESINE(fa), V_DrawFixedPatch(270<<FRACBITS, (186<<FRACBITS) - 8*FINESINE(fa),
FixedDiv(skins[skinnumber]->highresscale, skins[skinnumber]->shieldscale), scale,
(sprframe->flip & 1<<6) ? V_FLIP : 0, (sprframe->flip & 1<<6) ? V_FLIP : 0,
natksprite, natksprite,
R_GetTranslationColormap(TC_BLINK, color, GTC_CACHE)); R_GetTranslationColormap(TC_BLINK, color, GTC_CACHE));
...@@ -10288,6 +10340,7 @@ static void M_NightsAttack(INT32 choice) ...@@ -10288,6 +10340,7 @@ static void M_NightsAttack(INT32 choice)
Nextmap_OnChange(); Nextmap_OnChange();
itemOn = nastart; // "Start" is selected. itemOn = nastart; // "Start" is selected.
M_UpdateItemOn();
} }
// Player has selected the "START" from the nights attack screen // Player has selected the "START" from the nights attack screen
...@@ -10607,6 +10660,7 @@ static void M_ModeAttackEndGame(INT32 choice) ...@@ -10607,6 +10660,7 @@ static void M_ModeAttackEndGame(INT32 choice)
break; break;
} }
itemOn = currentMenu->lastOn; itemOn = currentMenu->lastOn;
M_UpdateItemOn();
G_SetGamestate(GS_TIMEATTACK); G_SetGamestate(GS_TIMEATTACK);
modeattacking = ATTACKING_NONE; modeattacking = ATTACKING_NONE;
M_ChangeMenuMusic("_title", true); M_ChangeMenuMusic("_title", true);
...@@ -10688,6 +10742,7 @@ static void M_Marathon(INT32 choice) ...@@ -10688,6 +10742,7 @@ static void M_Marathon(INT32 choice)
titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please
M_SetupNextMenu(&SP_MarathonDef); M_SetupNextMenu(&SP_MarathonDef);
itemOn = marathonstart; // "Start" is selected. itemOn = marathonstart; // "Start" is selected.
M_UpdateItemOn();
recatkdrawtimer = (50-8) * FRACUNIT; recatkdrawtimer = (50-8) * FRACUNIT;
char_scroll = 0; char_scroll = 0;
} }
...@@ -11078,7 +11133,7 @@ static void M_Refresh(INT32 choice) ...@@ -11078,7 +11133,7 @@ static void M_Refresh(INT32 choice)
// note: this is the one case where 0 is a valid room number // note: this is the one case where 0 is a valid room number
// because it corresponds to "All" // because it corresponds to "All"
CL_UpdateServerList(!(ms_RoomId < 0), ms_RoomId); CL_UpdateServerList(cv_masterserver_room_id.value >= 0, cv_masterserver_room_id.value);
// first page of servers // first page of servers
serverlistpage = 0; serverlistpage = 0;
...@@ -11088,37 +11143,11 @@ static INT32 menuRoomIndex = 0; ...@@ -11088,37 +11143,11 @@ static INT32 menuRoomIndex = 0;
static void M_DrawRoomMenu(void) static void M_DrawRoomMenu(void)
{ {
static fixed_t frame = -(12 << FRACBITS);
int dot_frame;
char text[4];
const char *rmotd; const char *rmotd;
const char *waiting_message; const char *waiting_message;
int dots;
if (m_waiting_mode) if (m_waiting_mode)
{ currentMenu->menuitems[0].text = "...";
dot_frame = (int)(frame >> FRACBITS) / 4;
dots = dot_frame + 3;
strcpy(text, " ");
if (dots > 0)
{
if (dot_frame < 0)
dot_frame = 0;
if (dot_frame != 3)
strncpy(&text[dot_frame], "...", min(dots, 3 - dot_frame));
}
frame += renderdeltatics;
while (frame >= (12 << FRACBITS))
frame -= 12 << FRACBITS;
currentMenu->menuitems[0].text = text;
}
// use generic drawer for cursor, items and title // use generic drawer for cursor, items and title
M_DrawGenericMenu(); M_DrawGenericMenu();
...@@ -11164,7 +11193,7 @@ static void M_DrawConnectMenu(void) ...@@ -11164,7 +11193,7 @@ static void M_DrawConnectMenu(void)
numPages = 1; numPages = 1;
// Room name // Room name
if (ms_RoomId < 0) if (cv_masterserver_room_id.value < 0)
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey, V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey,
V_YELLOWMAP, (itemOn == mp_connect_room) ? "<Select to change>" : "<Unlisted Mode>"); V_YELLOWMAP, (itemOn == mp_connect_room) ? "<Select to change>" : "<Unlisted Mode>");
else else
...@@ -11392,7 +11421,7 @@ static void M_ConnectMenu(INT32 choice) ...@@ -11392,7 +11421,7 @@ static void M_ConnectMenu(INT32 choice)
// first page of servers // first page of servers
serverlistpage = 0; serverlistpage = 0;
if (ms_RoomId < 0) if (cv_masterserver_room_id.value < 0)
{ {
M_RoomMenu(0); // Select a room instead of staring at an empty list M_RoomMenu(0); // Select a room instead of staring at an empty list
// This prevents us from returning to the modified game alert. // This prevents us from returning to the modified game alert.
...@@ -11401,6 +11430,7 @@ static void M_ConnectMenu(INT32 choice) ...@@ -11401,6 +11430,7 @@ static void M_ConnectMenu(INT32 choice)
else else
M_SetupNextMenu(&MP_ConnectDef); M_SetupNextMenu(&MP_ConnectDef);
itemOn = 0; itemOn = 0;
M_UpdateItemOn();
M_Refresh(0); M_Refresh(0);
} }
...@@ -11487,10 +11517,10 @@ static void M_ChooseRoom(INT32 choice) ...@@ -11487,10 +11517,10 @@ static void M_ChooseRoom(INT32 choice)
#endif #endif
if (choice == 0) if (choice == 0)
ms_RoomId = -1; CV_SetValue(&cv_masterserver_room_id, -1);
else else
{ {
ms_RoomId = roomIds[choice-1]; CV_SetValue(&cv_masterserver_room_id, roomIds[choice-1]);
menuRoomIndex = choice - 1; menuRoomIndex = choice - 1;
} }
...@@ -11559,7 +11589,7 @@ static void M_DrawServerMenu(void) ...@@ -11559,7 +11589,7 @@ static void M_DrawServerMenu(void)
if (currentMenu == &MP_ServerDef) if (currentMenu == &MP_ServerDef)
{ {
M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight/2, "Server settings", true, false); M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight/2, "Server settings", true, false);
if (ms_RoomId < 0) if (cv_masterserver_room_id.value < 0)
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey,
V_YELLOWMAP, (itemOn == mp_server_room) ? "<Select to change>" : "<Unlisted Mode>"); V_YELLOWMAP, (itemOn == mp_server_room) ? "<Select to change>" : "<Unlisted Mode>");
else else
...@@ -11655,11 +11685,12 @@ static void M_ServerOptions(INT32 choice) ...@@ -11655,11 +11685,12 @@ static void M_ServerOptions(INT32 choice)
static void M_StartServerMenu(INT32 choice) static void M_StartServerMenu(INT32 choice)
{ {
(void)choice; (void)choice;
ms_RoomId = -1; CV_SetValue(&cv_masterserver_room_id, -1);
levellistmode = LLM_CREATESERVER; levellistmode = LLM_CREATESERVER;
Newgametype_OnChange(); Newgametype_OnChange();
M_SetupNextMenu(&MP_ServerDef); M_SetupNextMenu(&MP_ServerDef);
itemOn = 1; itemOn = 1;
M_UpdateItemOn();
} }
// ============== // ==============
...@@ -11826,8 +11857,7 @@ static void M_HandleConnectIP(INT32 choice) ...@@ -11826,8 +11857,7 @@ static void M_HandleConnectIP(INT32 choice)
if ( ctrldown ) { if ( ctrldown ) {
switch (choice) { switch (choice) {
case 'v': case 'v': // ctrl+v, pasting
case 'V': // ctrl+v, pasting
{ {
const char *paste = I_ClipboardPaste(); const char *paste = I_ClipboardPaste();
...@@ -11840,17 +11870,21 @@ static void M_HandleConnectIP(INT32 choice) ...@@ -11840,17 +11870,21 @@ static void M_HandleConnectIP(INT32 choice)
break; break;
} }
case KEY_INS: case KEY_INS:
case 'c': case 'c': // ctrl+c, ctrl+insert, copying
case 'C': // ctrl+c, ctrl+insert, copying if (l != 0) // Don't replace the clipboard without any text
{
I_ClipboardCopy(setupm_ip, l); I_ClipboardCopy(setupm_ip, l);
S_StartSound(NULL,sfx_menu1); // Tails S_StartSound(NULL,sfx_menu1); // Tails
}
break; break;
case 'x': case 'x': // ctrl+x, cutting
case 'X': // ctrl+x, cutting if (l != 0) // Don't replace the clipboard without any text
{
I_ClipboardCopy(setupm_ip, l); I_ClipboardCopy(setupm_ip, l);
S_StartSound(NULL,sfx_menu1); // Tails S_StartSound(NULL,sfx_menu1); // Tails
setupm_ip[0] = 0; setupm_ip[0] = 0;
}
break; break;
default: // otherwise do nothing default: // otherwise do nothing
...@@ -11874,9 +11908,12 @@ static void M_HandleConnectIP(INT32 choice) ...@@ -11874,9 +11908,12 @@ static void M_HandleConnectIP(INT32 choice)
break; break;
} }
case KEY_DEL: // shift+delete, cutting case KEY_DEL: // shift+delete, cutting
if (l != 0) // Don't replace the clipboard without any text
{
I_ClipboardCopy(setupm_ip, l); I_ClipboardCopy(setupm_ip, l);
S_StartSound(NULL,sfx_menu1); // Tails S_StartSound(NULL,sfx_menu1); // Tails
setupm_ip[0] = 0; setupm_ip[0] = 0;
}
break; break;
default: // otherwise do nothing. default: // otherwise do nothing.
break; break;
...@@ -11897,15 +11934,6 @@ static void M_HandleConnectIP(INT32 choice) ...@@ -11897,15 +11934,6 @@ static void M_HandleConnectIP(INT32 choice)
setupm_ip[l] = (char)choice; setupm_ip[l] = (char)choice;
setupm_ip[l+1] = 0; setupm_ip[l+1] = 0;
} }
else if (choice >= 199 && choice <= 211 && choice != 202 && choice != 206) //numpad too!
{
char keypad_translation[] = {'7','8','9','-','4','5','6','+','1','2','3','0','.'};
choice = keypad_translation[choice - 199];
S_StartSound(NULL,sfx_menu1); // Tails
setupm_ip[l] = (char)choice;
setupm_ip[l+1] = 0;
}
break; break;
} }
...@@ -12256,7 +12284,9 @@ static void M_DrawSetupMultiPlayerMenu(void) ...@@ -12256,7 +12284,9 @@ static void M_DrawSetupMultiPlayerMenu(void)
if (multi_frame >= sprdef->numframes) if (multi_frame >= sprdef->numframes)
multi_frame = 0; multi_frame = 0;
scale = FixedDiv(skins[setupm_fakeskin]->highresscale, skins[setupm_fakeskin]->shieldscale); scale = skins[setupm_fakeskin]->highresscale;
if (skins[setupm_fakeskin]->shieldscale)
scale = FixedDiv(scale, skins[setupm_fakeskin]->shieldscale);
#define chary (y+64) #define chary (y+64)
...@@ -12289,7 +12319,7 @@ static void M_DrawSetupMultiPlayerMenu(void) ...@@ -12289,7 +12319,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
V_DrawFixedPatch( V_DrawFixedPatch(
x<<FRACBITS, x<<FRACBITS,
chary<<FRACBITS, chary<<FRACBITS,
FixedDiv(skins[setupm_fakeskin]->highresscale, skins[setupm_fakeskin]->shieldscale), scale,
flags, patch, colormap); flags, patch, colormap);
goto colordraw; goto colordraw;
...@@ -13071,7 +13101,10 @@ static void M_SetupScreenshotMenu(void) ...@@ -13071,7 +13101,10 @@ static void M_SetupScreenshotMenu(void)
{ {
item->status = IT_GRAYEDOUT; item->status = IT_GRAYEDOUT;
if ((currentMenu == &OP_ScreenshotOptionsDef) && (itemOn == op_screenshot_colorprofile)) // Can't select that if ((currentMenu == &OP_ScreenshotOptionsDef) && (itemOn == op_screenshot_colorprofile)) // Can't select that
{
itemOn = op_screenshot_storagelocation; itemOn = op_screenshot_storagelocation;
M_UpdateItemOn();
}
} }
else else
#endif #endif
...@@ -13269,23 +13302,23 @@ static void M_Setup1PControlsMenu(INT32 choice) ...@@ -13269,23 +13302,23 @@ static void M_Setup1PControlsMenu(INT32 choice)
currentMenu->lastOn = itemOn; currentMenu->lastOn = itemOn;
// Unhide the nine non-P2 controls and their headers // Unhide the nine non-P2 controls and their headers
//OP_ChangeControlsMenu[19+0].status = IT_HEADER; //OP_ChangeControlsMenu[18+0].status = IT_HEADER;
//OP_ChangeControlsMenu[19+1].status = IT_SPACE; //OP_ChangeControlsMenu[18+1].status = IT_SPACE;
// ... // ...
OP_ChangeControlsMenu[19+2].status = IT_CALL|IT_STRING2; OP_ChangeControlsMenu[18+2].status = IT_CALL|IT_STRING2;
OP_ChangeControlsMenu[19+3].status = IT_CALL|IT_STRING2; OP_ChangeControlsMenu[18+3].status = IT_CALL|IT_STRING2;
OP_ChangeControlsMenu[19+4].status = IT_CALL|IT_STRING2; OP_ChangeControlsMenu[18+4].status = IT_CALL|IT_STRING2;
OP_ChangeControlsMenu[19+5].status = IT_CALL|IT_STRING2; OP_ChangeControlsMenu[18+5].status = IT_CALL|IT_STRING2;
OP_ChangeControlsMenu[19+6].status = IT_CALL|IT_STRING2; OP_ChangeControlsMenu[18+6].status = IT_CALL|IT_STRING2;
//OP_ChangeControlsMenu[19+7].status = IT_CALL|IT_STRING2; //OP_ChangeControlsMenu[18+7].status = IT_CALL|IT_STRING2;
//OP_ChangeControlsMenu[19+8].status = IT_CALL|IT_STRING2; //OP_ChangeControlsMenu[18+8].status = IT_CALL|IT_STRING2;
OP_ChangeControlsMenu[19+9].status = IT_CALL|IT_STRING2; OP_ChangeControlsMenu[18+9].status = IT_CALL|IT_STRING2;
// ... // ...
OP_ChangeControlsMenu[29+0].status = IT_HEADER; OP_ChangeControlsMenu[28+0].status = IT_HEADER;
OP_ChangeControlsMenu[29+1].status = IT_SPACE; OP_ChangeControlsMenu[28+1].status = IT_SPACE;
// ... // ...
OP_ChangeControlsMenu[29+2].status = IT_CALL|IT_STRING2; OP_ChangeControlsMenu[28+2].status = IT_CALL|IT_STRING2;
OP_ChangeControlsMenu[29+3].status = IT_CALL|IT_STRING2; OP_ChangeControlsMenu[28+3].status = IT_CALL|IT_STRING2;
OP_ChangeControlsDef.prevMenu = &OP_P1ControlsDef; OP_ChangeControlsDef.prevMenu = &OP_P1ControlsDef;
OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove second level OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove second level
...@@ -13301,23 +13334,23 @@ static void M_Setup2PControlsMenu(INT32 choice) ...@@ -13301,23 +13334,23 @@ static void M_Setup2PControlsMenu(INT32 choice)
currentMenu->lastOn = itemOn; currentMenu->lastOn = itemOn;
// Hide the nine non-P2 controls and their headers // Hide the nine non-P2 controls and their headers
//OP_ChangeControlsMenu[19+0].status = IT_GRAYEDOUT2; //OP_ChangeControlsMenu[18+0].status = IT_GRAYEDOUT2;
//OP_ChangeControlsMenu[19+1].status = IT_GRAYEDOUT2; //OP_ChangeControlsMenu[18+1].status = IT_GRAYEDOUT2;
// ... // ...
OP_ChangeControlsMenu[19+2].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[18+2].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[19+3].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[18+3].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[19+4].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[18+4].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[19+5].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[18+5].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[19+6].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[18+6].status = IT_GRAYEDOUT2;
//OP_ChangeControlsMenu[19+7].status = IT_GRAYEDOUT2; //OP_ChangeControlsMenu[18+7].status = IT_GRAYEDOUT2;
//OP_ChangeControlsMenu[19+8].status = IT_GRAYEDOUT2; //OP_ChangeControlsMenu[18+8].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[19+9].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[18+9].status = IT_GRAYEDOUT2;
// ... // ...
OP_ChangeControlsMenu[29+0].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[28+0].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[29+1].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[28+1].status = IT_GRAYEDOUT2;
// ... // ...
OP_ChangeControlsMenu[29+2].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[28+2].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[29+3].status = IT_GRAYEDOUT2; OP_ChangeControlsMenu[28+3].status = IT_GRAYEDOUT2;
OP_ChangeControlsDef.prevMenu = &OP_P2ControlsDef; OP_ChangeControlsDef.prevMenu = &OP_P2ControlsDef;
OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove second level OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove second level
...@@ -14118,6 +14151,33 @@ static INT32 quitsounds[] = ...@@ -14118,6 +14151,33 @@ static INT32 quitsounds[] =
sfx_chchng // Tails 11-09-99 sfx_chchng // Tails 11-09-99
}; };
const char *QuitScreenMessages[3] = {
(
"Design and content in\n"
"SRB2 is copyright\n"
"1998-2025 by STJr. All\n"
"original material in\n"
"this game is copyrighted\n"
"by their respective\n"
"owners, and no copyright\n"
"infringement is\n"
"intended. STJr's staff\n"
"make no profit\n"
"whatsoever (in\n"
"fact, we lose\n"
"money)."
),
(
"THIS GAME SHOULD NOT BE SOLD!"
),
(
"STJr is in no way affiliated\n"
"with SEGA or Sonic Team."
)
};
void M_QuitResponse(INT32 ch) void M_QuitResponse(INT32 ch)
{ {
tic_t ptime; tic_t ptime;
...@@ -14139,6 +14199,9 @@ void M_QuitResponse(INT32 ch) ...@@ -14139,6 +14199,9 @@ void M_QuitResponse(INT32 ch)
while (ptime > I_GetTime()) while (ptime > I_GetTime())
{ {
V_DrawScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_PATCH)); // Demo 3 Quit Screen Tails 06-16-2001 V_DrawScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_PATCH)); // Demo 3 Quit Screen Tails 06-16-2001
V_DrawCenteredString(2+(V_StringWidth(QuitScreenMessages[0], V_ALLOWLOWERCASE)/2), 4, V_ALLOWLOWERCASE, QuitScreenMessages[0]);
V_DrawCenteredString(160, 166, V_ALLOWLOWERCASE|V_REDMAP, QuitScreenMessages[1]);
V_DrawCenteredString(160, 176, V_ALLOWLOWERCASE, QuitScreenMessages[2]);
I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001
I_Sleep(cv_sleep.value); I_Sleep(cv_sleep.value);
I_UpdateTime(cv_timescale.value); I_UpdateTime(cv_timescale.value);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2011-2016 by Matthew "Kaito Sinclaire" Walsh. // Copyright (C) 2011-2016 by Matthew "Kaito Sinclaire" Walsh.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2025 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -143,7 +143,7 @@ typedef enum ...@@ -143,7 +143,7 @@ typedef enum
typedef struct typedef struct
{ {
char bgname[8]; // name for background gfx lump; lays over titlemap if this is set char bgname[8+1]; // name for background gfx lump; lays over titlemap if this is set
SINT8 fadestrength; // darken background when displaying this menu, strength 0-31 or -1 for undefined SINT8 fadestrength; // darken background when displaying this menu, strength 0-31 or -1 for undefined
INT32 bgcolor; // fill color, overrides bg name. -1 means follow bg name rules. INT32 bgcolor; // fill color, overrides bg name. -1 means follow bg name rules.
INT32 titlescrollxspeed; // background gfx scroll per menu; inherits global setting INT32 titlescrollxspeed; // background gfx scroll per menu; inherits global setting
...@@ -153,13 +153,13 @@ typedef struct ...@@ -153,13 +153,13 @@ typedef struct
SINT8 hidetitlepics; // hide title gfx per menu; -1 means undefined, inherits global setting SINT8 hidetitlepics; // hide title gfx per menu; -1 means undefined, inherits global setting
ttmode_enum ttmode; // title wing animation mode; default TTMODE_OLD ttmode_enum ttmode; // title wing animation mode; default TTMODE_OLD
UINT8 ttscale; // scale of title wing gfx (FRACUNIT / ttscale); -1 means undefined, inherits global setting UINT8 ttscale; // scale of title wing gfx (FRACUNIT / ttscale); -1 means undefined, inherits global setting
char ttname[9]; // lump name of title wing gfx. If name length is <= 6, engine will attempt to load numbered frames (TTNAMExx) char ttname[8+1]; // lump name of title wing gfx. If name length is <= 6, engine will attempt to load numbered frames (TTNAMExx)
INT16 ttx; // X position of title wing INT16 ttx; // X position of title wing
INT16 tty; // Y position of title wing INT16 tty; // Y position of title wing
INT16 ttloop; // # frame to loop; -1 means dont loop INT16 ttloop; // # frame to loop; -1 means dont loop
UINT16 tttics; // # of tics per frame UINT16 tttics; // # of tics per frame
char musname[7]; ///< Music track to play. "" for no music. char musname[6+1]; ///< Music track to play. "" for no music.
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
boolean muslooping; ///< Loop the music boolean muslooping; ///< Loop the music
boolean musstop; ///< Don't play any music boolean musstop; ///< Don't play any music
...@@ -369,8 +369,8 @@ extern menu_t OP_JoystickSetDef; ...@@ -369,8 +369,8 @@ extern menu_t OP_JoystickSetDef;
typedef struct typedef struct
{ {
boolean used; boolean used;
char notes[441]; char notes[440+1];
char picname[8]; char picname[8+1];
char skinname[SKINNAMESIZE*2+2]; // skin&skin\0 char skinname[SKINNAMESIZE*2+2]; // skin&skin\0
patch_t *charpic; patch_t *charpic;
UINT8 prev; UINT8 prev;
...@@ -421,6 +421,7 @@ typedef struct ...@@ -421,6 +421,7 @@ typedef struct
{ {
char levelname[32]; char levelname[32];
UINT8 skinnum; UINT8 skinnum;
char skinname [SKINNAMESIZE+1];
UINT8 botskin; UINT8 botskin;
UINT8 numemeralds; UINT8 numemeralds;
UINT8 numgameovers; UINT8 numgameovers;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -1720,7 +1720,7 @@ char *va(const char *format, ...) ...@@ -1720,7 +1720,7 @@ char *va(const char *format, ...)
static char string[1024]; static char string[1024];
va_start(argptr, format); va_start(argptr, format);
vsprintf(string, format, argptr); vsnprintf(string, 1024, format, argptr);
va_end(argptr); va_end(argptr);
return string; return string;
...@@ -2208,6 +2208,8 @@ int M_JumpWordReverse(const char *line, int offset) ...@@ -2208,6 +2208,8 @@ int M_JumpWordReverse(const char *line, int offset)
{ {
int (*is)(int); int (*is)(int);
int c; int c;
if (offset == 0) // Don't let "--offset" later result in a negative value
return 0;
c = line[--offset]; c = line[--offset];
if (isspace(c)) if (isspace(c))
is = isspace; is = isspace;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
......
...@@ -127,8 +127,8 @@ perfstatrow_t commoncounter_rows[] = { ...@@ -127,8 +127,8 @@ perfstatrow_t commoncounter_rows[] = {
}; };
perfstatrow_t interpolation_rows[] = { perfstatrow_t interpolation_rows[] = {
{"intpfrc", "Interp frac: ", &ps_interp_frac, PS_TIME}, {"intpfrc", "Interp frac: ", &ps_interp_frac, 0}, // PS_TIME is not applicable here, as it is meant for I_GetPreciseTime
{"intplag", "Interp lag: ", &ps_interp_lag, PS_TIME}, {"intplag", "Interp lag: ", &ps_interp_lag, 0},
{0} {0}
}; };
...@@ -453,7 +453,7 @@ static int PS_DrawPerfRows(int x, int y, int color, perfstatrow_t *rows) ...@@ -453,7 +453,7 @@ static int PS_DrawPerfRows(int x, int y, int color, perfstatrow_t *rows)
return draw_y; return draw_y;
} }
static void PS_UpdateMetricHistory(ps_metric_t *metric, boolean time_metric, boolean frame_metric, boolean set_user) static void PS_UpdateMetricHistory(ps_metric_t *metric, boolean time_metric, boolean frame_metric)
{ {
int index = frame_metric ? ps_frame_index : ps_tick_index; int index = frame_metric ? ps_frame_index : ps_tick_index;
...@@ -461,7 +461,7 @@ static void PS_UpdateMetricHistory(ps_metric_t *metric, boolean time_metric, boo ...@@ -461,7 +461,7 @@ static void PS_UpdateMetricHistory(ps_metric_t *metric, boolean time_metric, boo
{ {
// allocate history table // allocate history table
int value_size = time_metric ? sizeof(precise_t) : sizeof(INT32); int value_size = time_metric ? sizeof(precise_t) : sizeof(INT32);
void** memory_user = set_user ? &metric->history : NULL; void** memory_user = &metric->history;
metric->history = Z_Calloc(value_size * cv_ps_samplesize.value, PU_PERFSTATS, metric->history = Z_Calloc(value_size * cv_ps_samplesize.value, PU_PERFSTATS,
memory_user); memory_user);
...@@ -491,7 +491,7 @@ static void PS_UpdateRowHistories(perfstatrow_t *rows, boolean frame_metric) ...@@ -491,7 +491,7 @@ static void PS_UpdateRowHistories(perfstatrow_t *rows, boolean frame_metric)
for (row = rows; row->lores_label; row++) for (row = rows; row->lores_label; row++)
{ {
if (PS_IsRowValid(row)) if (PS_IsRowValid(row))
PS_UpdateMetricHistory(row->metric, !!(row->flags & PS_TIME), frame_metric, true); PS_UpdateMetricHistory(row->metric, !!(row->flags & PS_TIME), frame_metric);
} }
} }
...@@ -584,7 +584,7 @@ static void PS_CountThinkers(void) ...@@ -584,7 +584,7 @@ static void PS_CountThinkers(void)
for (thinker = thlist[i].next; thinker != &thlist[i]; thinker = thinker->next) for (thinker = thlist[i].next; thinker != &thlist[i]; thinker = thinker->next)
{ {
ps_thinkercount.value.i++; ps_thinkercount.value.i++;
if (thinker->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (thinker->removing)
ps_removecount.value.i++; ps_removecount.value.i++;
else if (i == THINK_POLYOBJ) else if (i == THINK_POLYOBJ)
ps_polythcount.value.i++; ps_polythcount.value.i++;
...@@ -649,17 +649,17 @@ void PS_UpdateTickStats(void) ...@@ -649,17 +649,17 @@ void PS_UpdateTickStats(void)
if (cv_perfstats.value == 3) if (cv_perfstats.value == 3)
{ {
for (i = 0; i < thinkframe_hooks_length; i++) for (i = 0; i < thinkframe_hooks_length; i++)
PS_UpdateMetricHistory(&thinkframe_hooks[i].time_taken, true, false, false); PS_UpdateMetricHistory(&thinkframe_hooks[i].time_taken, true, false);
} }
else if (cv_perfstats.value == 4) else if (cv_perfstats.value == 4)
{ {
for (i = 0; i < prethinkframe_hooks_length; i++) for (i = 0; i < prethinkframe_hooks_length; i++)
PS_UpdateMetricHistory(&prethinkframe_hooks[i].time_taken, true, false, false); PS_UpdateMetricHistory(&prethinkframe_hooks[i].time_taken, true, false);
} }
else if (cv_perfstats.value == 5) else if (cv_perfstats.value == 5)
{ {
for (i = 0; i < postthinkframe_hooks_length; i++) for (i = 0; i < postthinkframe_hooks_length; i++)
PS_UpdateMetricHistory(&postthinkframe_hooks[i].time_taken, true, false, false); PS_UpdateMetricHistory(&postthinkframe_hooks[i].time_taken, true, false);
} }
} }
if (cv_perfstats.value) if (cv_perfstats.value)
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 2013-2023 by Sonic Team Junior. // Copyright (C) 2013-2025 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -28,6 +28,7 @@ tokenizer_t *Tokenizer_Open(const char *inputString, size_t len, unsigned numTok ...@@ -28,6 +28,7 @@ tokenizer_t *Tokenizer_Open(const char *inputString, size_t len, unsigned numTok
tokenizer->endPos = 0; tokenizer->endPos = 0;
tokenizer->inputLength = 0; tokenizer->inputLength = 0;
tokenizer->inComment = 0; tokenizer->inComment = 0;
tokenizer->stringNeedsEscaping = false;
tokenizer->inString = 0; tokenizer->inString = 0;
tokenizer->get = Tokenizer_Read; tokenizer->get = Tokenizer_Read;
...@@ -92,6 +93,124 @@ static void DetectComment(tokenizer_t *tokenizer, UINT32 *pos) ...@@ -92,6 +93,124 @@ static void DetectComment(tokenizer_t *tokenizer, UINT32 *pos)
tokenizer->inComment = 2; tokenizer->inComment = 2;
} }
// This function detects escape sequences in a string and attempts to convert them.
static size_t EscapeString(char *output, const char *input, size_t inputLength)
{
const char *end = input + inputLength;
size_t i = 0;
while (input < end)
{
char chr = *input++;
if (chr == '\\')
{
chr = *input++;
switch (chr)
{
case 'n':
output[i] = '\n';
i++;
break;
case 't':
output[i] = '\t';
i++;
break;
case '\\':
output[i] = '\\';
i++;
break;
case '"':
output[i] = '\"';
i++;
break;
case 'x': {
int out = 0, c;
int j = 0;
chr = *input++;
for (j = 0; j < 5 && isxdigit(chr); j++)
{
c = ((chr <= '9') ? (chr - '0') : (tolower(chr) - 'a' + 10));
out = (out << 4) | c;
chr = *input++;
}
input--;
switch (j)
{
case 4:
output[i] = (out >> 8) & 0xFF;
i++;
/* FALLTHRU */
case 2:
output[i] = out & 0xFF;
i++;
break;
default:
// TODO: Displaying parsing errors properly will require
// some refactoring of the tokenizer itself. For now,
// this function will silently return an empty string
// if it encounters a malformed escape sequence.
// This situation cannot happen for i.e. UDMF comments,
// so it's okay to do this right now.
// CONS_Alert(CONS_WARNING, "Escape sequence has wrong size\n");
i = 0;
goto done;
}
break;
}
default:
if (isdigit(chr))
{
int out = 0;
int j = 0;
do
{
out = 10*out + (chr - '0');
chr = *input++;
} while (++j < 3 && isdigit(chr));
input--;
if (out > 255)
{
// CONS_Alert(CONS_WARNING, "Escape sequence is too large\n");
i = 0;
goto done;
}
output[i] = out;
i++;
}
else
{
// CONS_Alert(CONS_WARNING, "Unknown escape sequence '\\%c'\n", chr);
i = 0;
goto done;
}
break;
}
}
else
{
output[i] = chr;
i++;
}
}
done:
output[i] = '\0';
i++;
return i;
}
static void Tokenizer_ReadTokenString(tokenizer_t *tokenizer, UINT32 i) static void Tokenizer_ReadTokenString(tokenizer_t *tokenizer, UINT32 i)
{ {
UINT32 tokenLength = tokenizer->endPos - tokenizer->startPos; UINT32 tokenLength = tokenizer->endPos - tokenizer->startPos;
...@@ -101,11 +220,47 @@ static void Tokenizer_ReadTokenString(tokenizer_t *tokenizer, UINT32 i) ...@@ -101,11 +220,47 @@ static void Tokenizer_ReadTokenString(tokenizer_t *tokenizer, UINT32 i)
// Assign the memory. Don't forget an extra byte for the end of the string! // Assign the memory. Don't forget an extra byte for the end of the string!
tokenizer->token[i] = (char *)Z_Malloc(tokenizer->capacity[i] * sizeof(char), PU_STATIC, NULL); tokenizer->token[i] = (char *)Z_Malloc(tokenizer->capacity[i] * sizeof(char), PU_STATIC, NULL);
} }
// Copy the string. // Copy the string.
if (tokenizer->stringNeedsEscaping)
{
EscapeString(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength);
}
else
{
M_Memcpy(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength); M_Memcpy(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength);
// Make the final character NUL. // Make the final character NUL.
tokenizer->token[i][tokenLength] = '\0'; tokenizer->token[i][tokenLength] = '\0';
} }
}
static void ScanString(tokenizer_t *tokenizer)
{
tokenizer->stringNeedsEscaping = false;
while (tokenizer->input[tokenizer->endPos] != '"' && tokenizer->endPos < tokenizer->inputLength)
{
if (!DetectLineBreak(tokenizer, tokenizer->endPos))
{
// Skip one character ahead if this looks like an escape sequence
if (tokenizer->input[tokenizer->endPos] == '\\')
{
tokenizer->stringNeedsEscaping = true;
tokenizer->endPos++;
// Oh. Naughty. We hit the end of the input.
// Stop scanning, then.
if (tokenizer->endPos == tokenizer->inputLength)
return;
DetectLineBreak(tokenizer, tokenizer->endPos);
}
}
tokenizer->endPos++;
}
}
const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i) const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
{ {
...@@ -117,11 +272,7 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i) ...@@ -117,11 +272,7 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
// If in a string, return the entire string within quotes, except without the quotes. // If in a string, return the entire string within quotes, except without the quotes.
if (tokenizer->inString == 1) if (tokenizer->inString == 1)
{ {
while (tokenizer->input[tokenizer->endPos] != '"' && tokenizer->endPos < tokenizer->inputLength) ScanString(tokenizer);
{
DetectLineBreak(tokenizer, tokenizer->endPos);
tokenizer->endPos++;
}
Tokenizer_ReadTokenString(tokenizer, i); Tokenizer_ReadTokenString(tokenizer, i);
tokenizer->inString = 2; tokenizer->inString = 2;
...@@ -134,6 +285,7 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i) ...@@ -134,6 +285,7 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
tokenizer->token[i][0] = tokenizer->input[tokenizer->startPos]; tokenizer->token[i][0] = tokenizer->input[tokenizer->startPos];
tokenizer->token[i][1] = '\0'; tokenizer->token[i][1] = '\0';
tokenizer->inString = 0; tokenizer->inString = 0;
tokenizer->stringNeedsEscaping = false;
return tokenizer->token[i]; return tokenizer->token[i];
} }
...@@ -281,11 +433,7 @@ const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i) ...@@ -281,11 +433,7 @@ const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i)
else if (tokenizer->input[tokenizer->startPos] == '"') else if (tokenizer->input[tokenizer->startPos] == '"')
{ {
tokenizer->endPos = ++tokenizer->startPos; tokenizer->endPos = ++tokenizer->startPos;
while (tokenizer->input[tokenizer->endPos] != '"' && tokenizer->endPos < tokenizer->inputLength) ScanString(tokenizer);
{
DetectLineBreak(tokenizer, tokenizer->endPos);
tokenizer->endPos++;
}
Tokenizer_ReadTokenString(tokenizer, i); Tokenizer_ReadTokenString(tokenizer, i);
tokenizer->endPos++; tokenizer->endPos++;
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 2013-2023 by Sonic Team Junior. // Copyright (C) 2013-2025 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -26,6 +26,7 @@ typedef struct Tokenizer ...@@ -26,6 +26,7 @@ typedef struct Tokenizer
UINT32 inputLength; UINT32 inputLength;
UINT8 inComment; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */ UINT8 inComment; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */
UINT8 inString; // 0 = not in string, 1 = in string, 2 = just left string UINT8 inString; // 0 = not in string, 1 = in string, 2 = just left string
boolean stringNeedsEscaping;
int line; int line;
const char *(*get)(struct Tokenizer*, UINT32); const char *(*get)(struct Tokenizer*, UINT32);
} tokenizer_t; } tokenizer_t;
......
...@@ -21,6 +21,32 @@ void DVector3_Load(dvector3_t *vec, double x, double y, double z) ...@@ -21,6 +21,32 @@ void DVector3_Load(dvector3_t *vec, double x, double y, double z)
vec->z = z; vec->z = z;
} }
void DVector3_Copy(dvector3_t *a_o, const dvector3_t *a_i)
{
memcpy(a_o, a_i, sizeof(dvector3_t));
}
void DVector3_Add(const dvector3_t *a_i, const dvector3_t *a_c, dvector3_t *a_o)
{
a_o->x = a_i->x + a_c->x;
a_o->y = a_i->y + a_c->y;
a_o->z = a_i->z + a_c->z;
}
void DVector3_Subtract(const dvector3_t *a_i, const dvector3_t *a_c, dvector3_t *a_o)
{
a_o->x = a_i->x - a_c->x;
a_o->y = a_i->y - a_c->y;
a_o->z = a_i->z - a_c->z;
}
void DVector3_Multiply(const dvector3_t *a_i, double a_c, dvector3_t *a_o)
{
a_o->x = a_i->x * a_c;
a_o->y = a_i->y * a_c;
a_o->z = a_i->z * a_c;
}
double DVector3_Magnitude(const dvector3_t *a_normal) double DVector3_Magnitude(const dvector3_t *a_normal)
{ {
double xs = a_normal->x * a_normal->x; double xs = a_normal->x * a_normal->x;
......
...@@ -19,6 +19,10 @@ typedef struct ...@@ -19,6 +19,10 @@ typedef struct
} dvector3_t; } dvector3_t;
void DVector3_Load(dvector3_t *vec, double x, double y, double z); void DVector3_Load(dvector3_t *vec, double x, double y, double z);
void DVector3_Copy(dvector3_t *a_o, const dvector3_t *a_i);
void DVector3_Add(const dvector3_t *a_i, const dvector3_t *a_c, dvector3_t *a_o);
void DVector3_Subtract(const dvector3_t *a_i, const dvector3_t *a_c, dvector3_t *a_o);
void DVector3_Multiply(const dvector3_t *a_i, double a_c, dvector3_t *a_o);
double DVector3_Magnitude(const dvector3_t *a_normal); double DVector3_Magnitude(const dvector3_t *a_normal);
double DVector3_Normalize(dvector3_t *a_normal); double DVector3_Normalize(dvector3_t *a_normal);
void DVector3_Negate(dvector3_t *a_o); void DVector3_Negate(dvector3_t *a_o);
......
...@@ -54,6 +54,8 @@ static boolean IsDownloadingFile(void) ...@@ -54,6 +54,8 @@ static boolean IsDownloadingFile(void)
static void DrawConnectionStatusBox(void) static void DrawConnectionStatusBox(void)
{ {
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
if (cl_mode != CL_DOWNLOADSAVEGAME && filedownload.current != -1)
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-46-8, 32, 1);
if (cl_mode == CL_CONFIRMCONNECT || IsDownloadingFile()) if (cl_mode == CL_CONFIRMCONNECT || IsDownloadingFile())
return; return;
...@@ -84,6 +86,33 @@ static void DrawFileProgress(fileneeded_t *file, int y) ...@@ -84,6 +86,33 @@ static void DrawFileProgress(fileneeded_t *file, int y)
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, y, V_20TRANS|V_MONOSPACE, va("%3.1fK/s ", ((double)getbps)/1024)); V_DrawRightAlignedString(BASEVIDWIDTH/2+128, y, V_20TRANS|V_MONOSPACE, va("%3.1fK/s ", ((double)getbps)/1024));
} }
static void DrawOverallProgress(int y)
{
UINT32 totalsize = filedownload.totalsize;
INT32 downloadedfiles = filedownload.completednum;
INT32 totalfiles = filedownload.remaining + filedownload.completednum;
INT32 downloaded = filedownload.completedsize;
if (fileneeded[filedownload.current].currentsize != fileneeded[filedownload.current].totalsize)
downloaded = filedownload.completedsize + fileneeded[filedownload.current].currentsize;
INT32 dldlength = (INT32)((downloaded/(double)totalsize) * 256);
if (dldlength > 256)
dldlength = 256;
V_DrawFill(BASEVIDWIDTH/2-128, y, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, y, dldlength, 8, 96);
const char *progress_str;
if (totalsize >= 1024*1024)
progress_str = va(" %.2fMiB/%.2fMiB", (double)downloaded / (1024*1024), (double)totalsize / (1024*1024));
else if (totalsize < 1024)
progress_str = va(" %4uB/%4uB", downloaded, totalsize);
else
progress_str = va(" %.2fKiB/%.2fKiB", (double)downloaded / 1024, (double)totalsize / 1024);
V_DrawString(BASEVIDWIDTH/2-128, y, V_20TRANS|V_ALLOWLOWERCASE, progress_str);
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, y, V_20TRANS|V_ALLOWLOWERCASE, va("%2u/%2u Files ", downloadedfiles+1, totalfiles));
}
// //
// CL_DrawConnectionStatus // CL_DrawConnectionStatus
// //
...@@ -96,7 +125,7 @@ static void CL_DrawConnectionStatus(void) ...@@ -96,7 +125,7 @@ static void CL_DrawConnectionStatus(void)
// Draw background fade // Draw background fade
V_DrawFadeScreen(0xFF00, 16); // force default V_DrawFadeScreen(0xFF00, 16); // force default
if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_DOWNLOADHTTPFILES && cl_mode != CL_LOADFILES) if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_DOWNLOADHTTPFILES && cl_mode != CL_LOADFILES && cl_mode != CL_CHECKFILES && cl_mode != CL_ASKFULLFILELIST)
{ {
INT32 animtime = ((ccstime / 4) & 15) + 16; INT32 animtime = ((ccstime / 4) & 15) + 16;
UINT8 palstart; UINT8 palstart;
...@@ -179,6 +208,31 @@ static void CL_DrawConnectionStatus(void) ...@@ -179,6 +208,31 @@ static void CL_DrawConnectionStatus(void)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
va(" %2u/%2u files",loadcompletednum,fileneedednum)); va(" %2u/%2u files",loadcompletednum,fileneedednum));
} }
else if ((cl_mode == CL_CHECKFILES) || (cl_mode == CL_ASKFULLFILELIST))
{
INT32 totalfileslength;
INT32 checkcompletednum = 0;
INT32 i;
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
//ima just count files here
if (fileneeded)
{
for (i = 0; i < fileneedednum; i++)
if (fileneeded[i].status != FS_NOTCHECKED)
checkcompletednum++;
}
// Check progress
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, "Checking server addon list...");
totalfileslength = (INT32)((checkcompletednum/(double)(fileneedednum)) * 256);
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, totalfileslength, 8, 96);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
va(" %2u/%2u Files",checkcompletednum,fileneedednum));
}
else if (filedownload.current != -1) else if (filedownload.current != -1)
{ {
char tempname[28]; char tempname[28];
...@@ -224,7 +278,7 @@ static void CL_DrawConnectionStatus(void) ...@@ -224,7 +278,7 @@ static void CL_DrawConnectionStatus(void)
const char *download_str = M_GetText("Downloading \"%s\""); const char *download_str = M_GetText("Downloading \"%s\"");
#endif #endif
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_ALLOWLOWERCASE|V_YELLOWMAP, V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-46-24, V_ALLOWLOWERCASE|V_YELLOWMAP,
va(download_str, tempname)); va(download_str, tempname));
// Rusty: actually lets do this instead // Rusty: actually lets do this instead
...@@ -244,16 +298,18 @@ static void CL_DrawConnectionStatus(void) ...@@ -244,16 +298,18 @@ static void CL_DrawConnectionStatus(void)
strlcpy(tempname, http_source, sizeof(tempname)); strlcpy(tempname, http_source, sizeof(tempname));
} }
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_ALLOWLOWERCASE|V_YELLOWMAP, V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-46-16, V_ALLOWLOWERCASE|V_YELLOWMAP,
va(M_GetText("from %s"), tempname)); va(M_GetText("from %s"), tempname));
} }
else else
{ {
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_ALLOWLOWERCASE|V_YELLOWMAP, V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-46-16, V_ALLOWLOWERCASE|V_YELLOWMAP,
M_GetText("from the server")); M_GetText("from the server"));
} }
DrawFileProgress(file, BASEVIDHEIGHT-46);
DrawFileProgress(file, BASEVIDHEIGHT-16); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-14, V_ALLOWLOWERCASE|V_YELLOWMAP, "Total Progress");
DrawOverallProgress(BASEVIDHEIGHT-16);
} }
else else
{ {
...@@ -546,6 +602,7 @@ static void AbortConnection(void) ...@@ -546,6 +602,7 @@ static void AbortConnection(void)
{ {
Snake_Free(&snake); Snake_Free(&snake);
CURLAbortFile();
D_QuitNetGame(); D_QuitNetGame();
CL_Reset(); CL_Reset();
D_StartTitle(); D_StartTitle();
...@@ -656,6 +713,7 @@ static void ShowDownloadConsentMessage(void) ...@@ -656,6 +713,7 @@ static void ShowDownloadConsentMessage(void)
if (IsFileDownloadable(&fileneeded[i])) if (IsFileDownloadable(&fileneeded[i]))
totalsize += fileneeded[i].totalsize; totalsize += fileneeded[i].totalsize;
} }
filedownload.totalsize = totalsize;
const char *downloadsize = GetPrintableFileSize(totalsize); const char *downloadsize = GetPrintableFileSize(totalsize);
...@@ -1062,10 +1120,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic ...@@ -1062,10 +1120,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
} }
} }
// Rusty TODO: multithread
if (filedownload.http_running)
CURLGetFile();
if (waitmore) if (waitmore)
break; // exit the case break; // exit the case
...@@ -1217,7 +1271,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic ...@@ -1217,7 +1271,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
void CL_ConnectToServer(void) void CL_ConnectToServer(void)
{ {
INT32 pnumnodes, nodewaited = doomcom->numnodes, i; INT32 pnumnodes, nodewaited = numnetnodes, i;
tic_t oldtic; tic_t oldtic;
tic_t asksent; tic_t asksent;
char tmpsave[256]; char tmpsave[256];
...@@ -1243,7 +1297,7 @@ void CL_ConnectToServer(void) ...@@ -1243,7 +1297,7 @@ void CL_ConnectToServer(void)
if (gamestate == GS_INTERMISSION) if (gamestate == GS_INTERMISSION)
Y_EndIntermission(); // clean up intermission graphics etc Y_EndIntermission(); // clean up intermission graphics etc
DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); DEBFILE(va("waiting %d nodes\n", numnetnodes));
G_SetGamestate(GS_WAITINGPLAYERS); G_SetGamestate(GS_WAITINGPLAYERS);
wipegamestate = GS_WAITINGPLAYERS; wipegamestate = GS_WAITINGPLAYERS;
...@@ -1404,7 +1458,7 @@ void PT_ServerCFG(SINT8 node) ...@@ -1404,7 +1458,7 @@ void PT_ServerCFG(SINT8 node)
netnodes[(UINT8)servernode].ingame = true; netnodes[(UINT8)servernode].ingame = true;
serverplayer = netbuffer->u.servercfg.serverplayer; serverplayer = netbuffer->u.servercfg.serverplayer;
doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
mynode = netbuffer->u.servercfg.clientnode; mynode = netbuffer->u.servercfg.clientnode;
if (serverplayer >= 0) if (serverplayer >= 0)
playernode[(UINT8)serverplayer] = servernode; playernode[(UINT8)serverplayer] = servernode;
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
......
...@@ -113,8 +113,11 @@ consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_ ...@@ -113,8 +113,11 @@ consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_
static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL);
consvar_t cv_idletime = CVAR_INIT ("idletime", "0", CV_SAVE, CV_Unsigned, NULL); consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL);
consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE, CV_Unsigned, NULL);
static CV_PossibleValue_t idleaction_cons_t[] = {{1, "Kick"}, {2, "Spectate"}, {0, NULL}};
consvar_t cv_idleaction = CVAR_INIT ("idleaction", "Spectate", CV_SAVE|CV_NETVAR, idleaction_cons_t, NULL);
consvar_t cv_idletime = CVAR_INIT ("idletime", "3", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL);
consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL); consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL);
...@@ -146,8 +149,8 @@ void CL_Reset(void) ...@@ -146,8 +149,8 @@ void CL_Reset(void)
multiplayer = false; multiplayer = false;
servernode = 0; servernode = 0;
server = true; server = true;
doomcom->numnodes = 1; numnetnodes = 1;
doomcom->numslots = 1; numslots = 1;
SV_StopServer(); SV_StopServer();
SV_ResetServer(); SV_ResetServer();
...@@ -212,8 +215,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) ...@@ -212,8 +215,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
CL_ClearPlayer(newplayernum); CL_ClearPlayer(newplayernum);
playeringame[newplayernum] = true; playeringame[newplayernum] = true;
G_AddPlayer(newplayernum); G_AddPlayer(newplayernum);
if (newplayernum+1 > doomcom->numslots) if (newplayernum+1 > numslots)
doomcom->numslots = (INT16)(newplayernum+1); numslots = (INT16)(newplayernum+1);
if (server && I_GetNodeAddress) if (server && I_GetNodeAddress)
{ {
...@@ -609,8 +612,8 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) ...@@ -609,8 +612,8 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
// remove avatar of player // remove avatar of player
playeringame[playernum] = false; playeringame[playernum] = false;
while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1) while (!playeringame[numslots-1] && numslots > 1)
doomcom->numslots--; numslots--;
// Reset the name // Reset the name
sprintf(player_names[playernum], "Player %d", playernum+1); sprintf(player_names[playernum], "Player %d", playernum+1);
...@@ -638,6 +641,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) ...@@ -638,6 +641,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
void D_QuitNetGame(void) void D_QuitNetGame(void)
{ {
mousegrabbedbylua = true; mousegrabbedbylua = true;
textinputmodeenabledbylua = false;
I_UpdateMouseGrab(); I_UpdateMouseGrab();
if (!netgame || !netbuffer) if (!netgame || !netbuffer)
...@@ -660,7 +664,7 @@ void D_QuitNetGame(void) ...@@ -660,7 +664,7 @@ void D_QuitNetGame(void)
if (netnodes[i].ingame) if (netnodes[i].ingame)
HSendPacket(i, true, 0, 0); HSendPacket(i, true, 0, 0);
#ifdef MASTERSERVER #ifdef MASTERSERVER
if (serverrunning && ms_RoomId > 0) if (serverrunning && cv_masterserver_room_id.value > 0)
UnregisterServer(); UnregisterServer();
#endif #endif
} }
...@@ -670,6 +674,7 @@ void D_QuitNetGame(void) ...@@ -670,6 +674,7 @@ void D_QuitNetGame(void)
HSendPacket(servernode, true, 0, 0); HSendPacket(servernode, true, 0, 0);
} }
seenplayer = NULL;
D_CloseConnection(); D_CloseConnection();
ClearAdminPlayers(); ClearAdminPlayers();
...@@ -750,7 +755,7 @@ void SV_ResetServer(void) ...@@ -750,7 +755,7 @@ void SV_ResetServer(void)
if (server) if (server)
servernode = 0; servernode = 0;
doomcom->numslots = 0; numslots = 0;
// clear server_context // clear server_context
memset(server_context, '-', 8); memset(server_context, '-', 8);
...@@ -794,7 +799,7 @@ void SV_SpawnServer(void) ...@@ -794,7 +799,7 @@ void SV_SpawnServer(void)
{ {
I_NetOpenSocket(); I_NetOpenSocket();
#ifdef MASTERSERVER #ifdef MASTERSERVER
if (ms_RoomId > 0) if (cv_masterserver_room_id.value > 0)
RegisterServer(); RegisterServer();
#endif #endif
} }
...@@ -802,7 +807,9 @@ void SV_SpawnServer(void) ...@@ -802,7 +807,9 @@ void SV_SpawnServer(void)
// non dedicated server just connect to itself // non dedicated server just connect to itself
if (!dedicated) if (!dedicated)
CL_ConnectToServer(); CL_ConnectToServer();
else doomcom->numslots = 1; else numslots = 1;
LUA_HookVoid(HOOK(GameStart));
} }
} }
...@@ -1360,21 +1367,35 @@ static void IdleUpdate(void) ...@@ -1360,21 +1367,35 @@ static void IdleUpdate(void)
if (!server || !netgame) if (!server || !netgame)
return; return;
for (i = 1; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (cv_idletime.value && playeringame[i] && playernode[i] != UINT8_MAX && !players[i].quittime && !players[i].spectator && !players[i].bot && !IsPlayerAdmin(i) && i != serverplayer && gamestate == GS_LEVEL) if (playeringame[i] && playernode[i] != UINT8_MAX && !players[i].quittime && !players[i].spectator && !players[i].bot && gamestate == GS_LEVEL)
{ {
if (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons) if (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons)
players[i].lastinputtime = 0; players[i].lastinputtime = 0;
else else
players[i].lastinputtime++; players[i].lastinputtime++;
if (players[i].lastinputtime > (tic_t)cv_idletime.value * TICRATE * 60) if (cv_idletime.value && !IsPlayerAdmin(i) && i != serverplayer && !(players[i].pflags & PF_FINISHED) && players[i].lastinputtime > (tic_t)cv_idletime.value * TICRATE * 60)
{ {
players[i].lastinputtime = 0; players[i].lastinputtime = 0;
if (cv_idleaction.value == 2 && G_GametypeHasSpectators())
{
changeteam_union NetPacket;
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0;
NetPacket.packet.newteam = 0;
NetPacket.packet.playernum = i;
NetPacket.packet.verification = true; // This signals that it's a server change
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
}
else if (cv_idleaction.value == 1)
{
SendKick(i, KICK_MSG_IDLE | KICK_MSG_KEEP_BODY); SendKick(i, KICK_MSG_IDLE | KICK_MSG_KEEP_BODY);
} }
} }
}
else else
{ {
players[i].lastinputtime = 0; players[i].lastinputtime = 0;
...@@ -1399,6 +1420,83 @@ static void IdleUpdate(void) ...@@ -1399,6 +1420,83 @@ static void IdleUpdate(void)
} }
} }
static void DedicatedIdleUpdate(INT32 *realtics)
{
const tic_t dedicatedidletime = cv_dedicatedidletime.value * TICRATE;
static tic_t dedicatedidletimeprev = 0;
static tic_t dedicatedidle = 0;
if (!server || !dedicated || gamestate != GS_LEVEL)
return;
if (dedicatedidletime > 0)
{
INT32 i;
boolean empty = true;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
{
empty = false;
break;
}
if (empty)
{
if (leveltime == 2)
{
// On next tick...
dedicatedidle = dedicatedidletime - 1;
}
else if (dedicatedidle >= dedicatedidletime)
{
if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic + 1, 0))
{
CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n");
dedicatedidle = 0;
}
else
{
(*realtics) = 0;
}
}
else
{
dedicatedidle += (*realtics);
if (dedicatedidle >= dedicatedidletime)
{
const char *idlereason = "at round start";
if (leveltime > 3)
idlereason = va("for %d seconds", dedicatedidle / TICRATE);
CONS_Printf("DEDICATED: No players %s, idling...\n", idlereason);
(*realtics) = 0;
dedicatedidle = dedicatedidletime;
}
}
}
else
{
if (dedicatedidle >= dedicatedidletime)
{
CONS_Printf("DEDICATED: Awakening from idle (Player detected...)\n");
}
dedicatedidle = 0;
}
}
else
{
if (dedicatedidletimeprev > 0 && dedicatedidle >= dedicatedidletimeprev)
{
CONS_Printf("DEDICATED: Awakening from idle (Idle disabled...)\n");
}
dedicatedidle = 0;
}
dedicatedidletimeprev = dedicatedidletime;
}
// Handle timeouts to prevent definitive freezes from happenning // Handle timeouts to prevent definitive freezes from happenning
static void HandleNodeTimeouts(void) static void HandleNodeTimeouts(void)
{ {
...@@ -1475,69 +1573,7 @@ void NetUpdate(void) ...@@ -1475,69 +1573,7 @@ void NetUpdate(void)
realtics = 5; realtics = 5;
} }
if (server && dedicated && gamestate == GS_LEVEL) DedicatedIdleUpdate(&realtics);
{
const tic_t dedicatedidletime = cv_dedicatedidletime.value * TICRATE;
static tic_t dedicatedidletimeprev = 0;
static tic_t dedicatedidle = 0;
if (dedicatedidletime > 0)
{
INT32 i;
for (i = 1; i < MAXNETNODES; ++i)
if (netnodes[i].ingame)
{
if (dedicatedidle >= dedicatedidletime)
{
CONS_Printf("DEDICATED: Awakening from idle (Node %d detected...)\n", i);
dedicatedidle = 0;
}
break;
}
if (i == MAXNETNODES)
{
if (leveltime == 2)
{
// On next tick...
dedicatedidle = dedicatedidletime-1;
}
else if (dedicatedidle >= dedicatedidletime)
{
if (D_GetExistingTextcmd(gametic, 0) || D_GetExistingTextcmd(gametic+1, 0))
{
CONS_Printf("DEDICATED: Awakening from idle (Netxcmd detected...)\n");
dedicatedidle = 0;
}
else
{
realtics = 0;
}
}
else if ((dedicatedidle += realtics) >= dedicatedidletime)
{
const char *idlereason = "at round start";
if (leveltime > 3)
idlereason = va("for %d seconds", dedicatedidle/TICRATE);
CONS_Printf("DEDICATED: No nodes %s, idling...\n", idlereason);
realtics = 0;
dedicatedidle = dedicatedidletime;
}
}
}
else
{
if (dedicatedidletimeprev > 0 && dedicatedidle >= dedicatedidletimeprev)
{
CONS_Printf("DEDICATED: Awakening from idle (Idle disabled...)\n");
}
dedicatedidle = 0;
}
dedicatedidletimeprev = dedicatedidletime;
}
gametime = nowtime; gametime = nowtime;
...@@ -1784,7 +1820,7 @@ INT16 Consistancy(void) ...@@ -1784,7 +1820,7 @@ INT16 Consistancy(void)
{ {
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo = (mobj_t *)th; mo = (mobj_t *)th;
......
...@@ -73,7 +73,7 @@ extern UINT32 realpingtable[MAXPLAYERS]; ...@@ -73,7 +73,7 @@ extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS];
extern tic_t servermaxping; extern tic_t servermaxping;
extern consvar_t cv_netticbuffer, cv_resynchattempts, cv_blamecfail, cv_playbackspeed, cv_idletime, cv_dedicatedidletime; extern consvar_t cv_netticbuffer, cv_resynchattempts, cv_blamecfail, cv_playbackspeed, cv_idletime, cv_idleaction, cv_dedicatedidletime;
extern consvar_t cv_httpsource; extern consvar_t cv_httpsource;
// Used in d_net, the only dependence // Used in d_net, the only dependence
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -48,6 +48,10 @@ ...@@ -48,6 +48,10 @@
#define FORCECLOSE 0x8000 #define FORCECLOSE 0x8000
tic_t connectiontimeout = (10*TICRATE); tic_t connectiontimeout = (10*TICRATE);
INT16 numnetnodes;
INT16 numslots;
INT16 extratics;
/// \brief network packet /// \brief network packet
doomcom_t *doomcom = NULL; doomcom_t *doomcom = NULL;
/// \brief network packet data, points inside doomcom /// \brief network packet data, points inside doomcom
...@@ -62,16 +66,11 @@ static doomdata_t reboundstore[MAXREBOUND]; ...@@ -62,16 +66,11 @@ static doomdata_t reboundstore[MAXREBOUND];
static INT16 reboundsize[MAXREBOUND]; static INT16 reboundsize[MAXREBOUND];
static INT32 rebound_head, rebound_tail; static INT32 rebound_head, rebound_tail;
/// \brief bandwith of netgame
INT32 net_bandwidth;
/// \brief max length per packet /// \brief max length per packet
INT16 hardware_MAXPACKETLENGTH; INT16 hardware_MAXPACKETLENGTH;
boolean (*I_NetGet)(void) = NULL; boolean (*I_NetGet)(void) = NULL;
void (*I_NetSend)(void) = NULL; void (*I_NetSend)(void) = NULL;
boolean (*I_NetCanSend)(void) = NULL;
boolean (*I_NetCanGet)(void) = NULL;
void (*I_NetCloseSocket)(void) = NULL; void (*I_NetCloseSocket)(void) = NULL;
void (*I_NetFreeNodenum)(INT32 nodenum) = NULL; void (*I_NetFreeNodenum)(INT32 nodenum) = NULL;
SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL; SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL;
...@@ -168,12 +167,6 @@ typedef struct ...@@ -168,12 +167,6 @@ typedef struct
// ack return to send (like sliding window protocol) // ack return to send (like sliding window protocol)
UINT8 firstacktosend; UINT8 firstacktosend;
// when no consecutive packets are received we keep in mind what packets
// we already received in a queue
UINT8 acktosend_head;
UINT8 acktosend_tail;
UINT8 acktosend[MAXACKTOSEND];
// automatically send keep alive packet when not enough trafic // automatically send keep alive packet when not enough trafic
tic_t lasttimeacktosend_sent; tic_t lasttimeacktosend_sent;
// detect connection lost // detect connection lost
...@@ -193,22 +186,17 @@ static node_t nodes[MAXNETNODES]; ...@@ -193,22 +186,17 @@ static node_t nodes[MAXNETNODES];
// 0 if a = n (mod 256) // 0 if a = n (mod 256)
// >0 if a > b (mod 256) // >0 if a > b (mod 256)
// mnemonic: to use it compare to 0: cmpack(a,b)<0 is "a < b" ... // mnemonic: to use it compare to 0: cmpack(a,b)<0 is "a < b" ...
FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b) FUNCMATH static inline INT32 cmpack(UINT8 a, UINT8 b)
{ {
register INT32 d = a - b; return (SINT8)(a - b);
if (d >= 127 || d < -128)
return -d;
return d;
} }
/** Sets freeack to a free acknum and copies the netbuffer in the ackpak table /** Sets freeack to a free acknum and copies the netbuffer in the ackpak table
* *
* \param freeack The address to store the free acknum at * \param freeack The address to store the free acknum at
* \param lowtimer ???
* \return True if a free acknum was found * \return True if a free acknum was found
*/ */
static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) static boolean GetFreeAcknum(UINT8 *freeack)
{ {
node_t *node = &nodes[doomcom->remotenode]; node_t *node = &nodes[doomcom->remotenode];
INT32 numfreeslot = 0; INT32 numfreeslot = 0;
...@@ -237,17 +225,8 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) ...@@ -237,17 +225,8 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
node->nextacknum++; node->nextacknum++;
ackpak[i].destinationnode = (UINT8)(node - nodes); ackpak[i].destinationnode = (UINT8)(node - nodes);
ackpak[i].length = doomcom->datalength; ackpak[i].length = doomcom->datalength;
if (lowtimer)
{
// Lowtime means can't be sent now so try it as soon as possible
ackpak[i].senttime = 0;
ackpak[i].resentnum = 1;
}
else
{
ackpak[i].senttime = I_GetTime(); ackpak[i].senttime = I_GetTime();
ackpak[i].resentnum = 0; ackpak[i].resentnum = 0;
}
M_Memcpy(ackpak[i].pak.raw, netbuffer, ackpak[i].length); M_Memcpy(ackpak[i].pak.raw, netbuffer, ackpak[i].length);
*freeack = ackpak[i].acknum; *freeack = ackpak[i].acknum;
...@@ -264,38 +243,6 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) ...@@ -264,38 +243,6 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
return false; return false;
} }
/** Counts how many acks are free
*
* \param urgent True if the type of the packet meant to
* use an ack is lower than PT_CANFAIL
* If for some reason you don't want use it
* for any packet type in particular,
* just set to false
* \return The number of free acks
*
*/
INT32 Net_GetFreeAcks(boolean urgent)
{
INT32 numfreeslot = 0;
INT32 n = 0; // Number of free acks found
for (INT32 i = 0; i < MAXACKPACKETS; i++)
if (!ackpak[i].acknum)
{
// For low priority packets, make sure to let freeslots so urgent packets can be sent
if (!urgent)
{
numfreeslot++;
if (numfreeslot <= URGENTFREESLOTNUM)
continue;
}
n++;
}
return n;
}
// Get a ack to send in the queue of this node // Get a ack to send in the queue of this node
static UINT8 GetAcktosend(INT32 node) static UINT8 GetAcktosend(INT32 node)
{ {
...@@ -313,9 +260,9 @@ static void RemoveAck(INT32 i) ...@@ -313,9 +260,9 @@ static void RemoveAck(INT32 i)
} }
// We have got a packet, proceed the ack request and ack return // We have got a packet, proceed the ack request and ack return
static int Processackpak(void) static boolean Processackpak(void)
{ {
int goodpacket = 0; boolean goodpacket = true;
node_t *node = &nodes[doomcom->remotenode]; node_t *node = &nodes[doomcom->remotenode];
// Received an ack return, so remove the ack in the list // Received an ack return, so remove the ack in the list
...@@ -324,7 +271,7 @@ static int Processackpak(void) ...@@ -324,7 +271,7 @@ static int Processackpak(void)
node->remotefirstack = netbuffer->ackreturn; node->remotefirstack = netbuffer->ackreturn;
// Search the ackbuffer and free it // Search the ackbuffer and free it
for (INT32 i = 0; i < MAXACKPACKETS; i++) for (INT32 i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode
&& cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0) && cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0)
{ {
RemoveAck(i); RemoveAck(i);
...@@ -340,20 +287,9 @@ static int Processackpak(void) ...@@ -340,20 +287,9 @@ static int Processackpak(void)
{ {
DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack)); DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack));
duppacket++; duppacket++;
goodpacket = 1; // Discard packet (duplicate) goodpacket = false; // Discard packet (duplicate)
} }
else else
{
// Check if it is not already in the queue
for (INT32 i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
if (node->acktosend[i] == ack)
{
DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = 1; // Discard packet (duplicate)
break;
}
if (goodpacket == 0)
{ {
// Is a good packet so increment the acknowledge number, // Is a good packet so increment the acknowledge number,
// Then search for a "hole" in the queue // Then search for a "hole" in the queue
...@@ -363,96 +299,20 @@ static int Processackpak(void) ...@@ -363,96 +299,20 @@ static int Processackpak(void)
if (ack == nextfirstack) if (ack == nextfirstack)
{ {
UINT8 hm1; // head - 1 node->firstacktosend = nextfirstack;
boolean change = true;
node->firstacktosend = nextfirstack++;
if (!nextfirstack)
nextfirstack = 1;
hm1 = (UINT8)((node->acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND);
while (change)
{
change = false;
for (INT32 i = node->acktosend_tail; i != node->acktosend_head;
i = (i+1) % MAXACKTOSEND)
{
if (cmpack(node->acktosend[i], nextfirstack) <= 0)
{
if (node->acktosend[i] == nextfirstack)
{
node->firstacktosend = nextfirstack++;
if (!nextfirstack)
nextfirstack = 1;
change = true;
}
if (i == node->acktosend_tail)
{
node->acktosend[node->acktosend_tail] = 0;
node->acktosend_tail = (UINT8)((i+1) % MAXACKTOSEND);
}
else if (i == hm1)
{
node->acktosend[hm1] = 0;
node->acktosend_head = hm1;
hm1 = (UINT8)((hm1-1+MAXACKTOSEND) % MAXACKTOSEND);
}
}
}
}
} }
else // Out of order packet else // Out of order packet
{ {
// Don't increment firsacktosend, put it in asktosend queue // Don't increment firsacktosend, put it in asktosend queue
// Will be incremented when the nextfirstack comes (code above) // Will be incremented when the nextfirstack comes (code above)
UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND);
DEBFILE(va("out of order packet (%d expected)\n", nextfirstack)); DEBFILE(va("out of order packet (%d expected)\n", nextfirstack));
if (newhead != node->acktosend_tail) goodpacket = false;
{
node->acktosend[node->acktosend_head] = ack;
node->acktosend_head = newhead;
}
else // Buffer full discard packet, sender will resend it
{ // We can admit the packet but we will not detect the duplication after :(
DEBFILE("no more freeackret\n");
goodpacket = 2;
}
}
} }
} }
} }
// return values: 0 = ok, 1 = duplicate, 2 = out of order
return goodpacket; return goodpacket;
} }
// send special packet with only ack on it
void Net_SendAcks(INT32 node)
{
netbuffer->packettype = PT_NOTHING;
M_Memcpy(netbuffer->u.textcmd, nodes[node].acktosend, MAXACKTOSEND);
HSendPacket(node, false, 0, MAXACKTOSEND);
}
static void GotAcks(void)
{
for (INT32 j = 0; j < MAXACKTOSEND; j++)
if (netbuffer->u.textcmd[j])
for (INT32 i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode)
{
if (ackpak[i].acknum == netbuffer->u.textcmd[j])
RemoveAck(i);
// nextacknum is first equal to acknum, then when receiving bigger ack
// there is big chance the packet is lost
// When resent, nextacknum = nodes[node].nextacknum
// will redo the same but with different value
else if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
&& ackpak[i].senttime > 0)
{
ackpak[i].senttime--; // hurry up
}
}
}
void Net_ConnectionTimeout(INT32 node) void Net_ConnectionTimeout(INT32 node)
{ {
// Don't timeout several times // Don't timeout several times
...@@ -506,11 +366,6 @@ void Net_AckTicker(void) ...@@ -506,11 +366,6 @@ void Net_AckTicker(void)
// This is something like node open flag // This is something like node open flag
if (nodes[i].firstacktosend) if (nodes[i].firstacktosend)
{ {
// We haven't sent a packet for a long time
// Acknowledge packet if needed
if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime())
Net_SendAcks(i);
if (!(nodes[i].flags & NF_CLOSE) if (!(nodes[i].flags & NF_CLOSE)
&& nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime()) && nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime())
{ {
...@@ -524,38 +379,13 @@ void Net_AckTicker(void) ...@@ -524,38 +379,13 @@ void Net_AckTicker(void)
// (the higher layer doesn't have room, or something else ....) // (the higher layer doesn't have room, or something else ....)
void Net_UnAcknowledgePacket(INT32 node) void Net_UnAcknowledgePacket(INT32 node)
{ {
INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND;
DEBFILE(va("UnAcknowledge node %d\n", node)); DEBFILE(va("UnAcknowledge node %d\n", node));
if (!node) if (!node)
return; return;
if (nodes[node].acktosend[hm1] == netbuffer->ack)
{
nodes[node].acktosend[hm1] = 0;
nodes[node].acktosend_head = (UINT8)hm1;
}
else if (nodes[node].firstacktosend == netbuffer->ack)
{
nodes[node].firstacktosend--;
if (!nodes[node].firstacktosend)
nodes[node].firstacktosend = UINT8_MAX;
}
else
{
while (nodes[node].firstacktosend != netbuffer->ack)
{
nodes[node].acktosend_tail = (UINT8)
((nodes[node].acktosend_tail-1+MAXACKTOSEND) % MAXACKTOSEND);
nodes[node].acktosend[nodes[node].acktosend_tail] = nodes[node].firstacktosend;
nodes[node].firstacktosend--; nodes[node].firstacktosend--;
if (!nodes[node].firstacktosend) if (!nodes[node].firstacktosend)
nodes[node].firstacktosend = UINT8_MAX; nodes[node].firstacktosend = UINT8_MAX;
} }
nodes[node].firstacktosend++;
if (!nodes[node].firstacktosend)
nodes[node].firstacktosend = 1;
}
}
/** Checks if all acks have been received /** Checks if all acks have been received
* *
...@@ -597,7 +427,6 @@ void Net_WaitAllAckReceived(UINT32 timeout) ...@@ -597,7 +427,6 @@ void Net_WaitAllAckReceived(UINT32 timeout)
static void InitNode(node_t *node) static void InitNode(node_t *node)
{ {
node->acktosend_head = node->acktosend_tail = 0;
node->firstacktosend = 0; node->firstacktosend = 0;
node->nextacknum = 1; node->nextacknum = 1;
node->remotefirstack = 0; node->remotefirstack = 0;
...@@ -656,11 +485,11 @@ void Net_CloseConnection(INT32 node) ...@@ -656,11 +485,11 @@ void Net_CloseConnection(INT32 node)
nodes[node].flags |= NF_CLOSE; nodes[node].flags |= NF_CLOSE;
// try to Send ack back (two army problem) if (nodes[node].firstacktosend)
if (GetAcktosend(node))
{ {
Net_SendAcks(node); // send a PT_NOTHING back to acknowledge the packet
Net_SendAcks(node); netbuffer->packettype = PT_NOTHING;
HSendPacket(node, false, 0, 0);
} }
// check if we are waiting for an ack from this node // check if we are waiting for an ack from this node
...@@ -996,15 +825,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen ...@@ -996,15 +825,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
netbuffer->ackreturn = 0; netbuffer->ackreturn = 0;
if (reliable) if (reliable)
{ {
if (I_NetCanSend && !I_NetCanSend()) if (!GetFreeAcknum(&netbuffer->ack))
{
if (netbuffer->packettype < PT_CANFAIL)
GetFreeAcknum(&netbuffer->ack, true);
DEBFILE("HSendPacket: Out of bandwidth\n");
return false;
}
else if (!GetFreeAcknum(&netbuffer->ack, false))
return false; return false;
} }
else else
...@@ -1070,7 +891,6 @@ boolean HGetPacket(void) ...@@ -1070,7 +891,6 @@ boolean HGetPacket(void)
while(true) while(true)
{ {
//nodejustjoined = I_NetGet(); //nodejustjoined = I_NetGet();
int goodpacket;
I_NetGet(); I_NetGet();
if (doomcom->remotenode == -1) // No packet received if (doomcom->remotenode == -1) // No packet received
...@@ -1116,22 +936,12 @@ boolean HGetPacket(void) ...@@ -1116,22 +936,12 @@ boolean HGetPacket(void)
}*/ }*/
// Proceed the ack and ackreturn field // Proceed the ack and ackreturn field
goodpacket = Processackpak(); if (!Processackpak())
if (goodpacket != 0)
{
// resend the ACK in case the previous ACK didn't reach the client.
// prevents the client's netbuffer from locking up.
if (goodpacket == 1)
Net_SendAcks(doomcom->remotenode);
continue; // discarded (duplicated) continue; // discarded (duplicated)
}
// A packet with just ackreturn // A packet with just ackreturn
if (netbuffer->packettype == PT_NOTHING) if (netbuffer->packettype == PT_NOTHING)
{
GotAcks();
continue; continue;
}
break; break;
} }
...@@ -1156,7 +966,7 @@ static void Internal_FreeNodenum(INT32 nodenum) ...@@ -1156,7 +966,7 @@ static void Internal_FreeNodenum(INT32 nodenum)
char *I_NetSplitAddress(char *host, char **port) char *I_NetSplitAddress(char *host, char **port)
{ {
boolean v4 = (strchr(host, '.') != NULL); boolean v4 = (host[0] != '[');
host = strtok(host, v4 ? ":" : "[]"); host = strtok(host, v4 ? ":" : "[]");
...@@ -1189,11 +999,8 @@ void D_SetDoomcom(void) ...@@ -1189,11 +999,8 @@ void D_SetDoomcom(void)
{ {
if (doomcom) return; if (doomcom) return;
doomcom = Z_Calloc(sizeof (doomcom_t), PU_STATIC, NULL); doomcom = Z_Calloc(sizeof (doomcom_t), PU_STATIC, NULL);
doomcom->id = DOOMCOM_ID; numslots = numnetnodes = 1;
doomcom->numslots = doomcom->numnodes = 1; extratics = 0;
doomcom->gametype = 0;
doomcom->consoleplayer = 0;
doomcom->extratics = 0;
} }
// //
...@@ -1211,13 +1018,11 @@ boolean D_CheckNetGame(void) ...@@ -1211,13 +1018,11 @@ boolean D_CheckNetGame(void)
I_NetGet = Internal_Get; I_NetGet = Internal_Get;
I_NetSend = Internal_Send; I_NetSend = Internal_Send;
I_NetCanSend = NULL;
I_NetCloseSocket = NULL; I_NetCloseSocket = NULL;
I_NetFreeNodenum = Internal_FreeNodenum; I_NetFreeNodenum = Internal_FreeNodenum;
I_NetMakeNodewPort = NULL; I_NetMakeNodewPort = NULL;
hardware_MAXPACKETLENGTH = MAXPACKETLENGTH; hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
net_bandwidth = 30000;
// I_InitNetwork sets doomcom and netgame // I_InitNetwork sets doomcom and netgame
// check and initialize the network driver // check and initialize the network driver
multiplayer = false; multiplayer = false;
...@@ -1237,30 +1042,14 @@ boolean D_CheckNetGame(void) ...@@ -1237,30 +1042,14 @@ boolean D_CheckNetGame(void)
server = true; // WTF? server always true??? server = true; // WTF? server always true???
// no! The deault mode is server. Client is set elsewhere // no! The deault mode is server. Client is set elsewhere
// when the client executes connect command. // when the client executes connect command.
doomcom->ticdup = 1;
if (M_CheckParm("-extratic")) if (M_CheckParm("-extratic"))
{ {
if (M_IsNextParm()) if (M_IsNextParm())
doomcom->extratics = (INT16)atoi(M_GetNextParm()); extratics = (INT16)atoi(M_GetNextParm());
else
doomcom->extratics = 1;
CONS_Printf(M_GetText("Set extratics to %d\n"), doomcom->extratics);
}
if (M_CheckParm("-bandwidth"))
{
if (M_IsNextParm())
{
net_bandwidth = atoi(M_GetNextParm());
if (net_bandwidth < 1000)
net_bandwidth = 1000;
if (net_bandwidth > 100000)
hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
CONS_Printf(M_GetText("Network bandwidth set to %d\n"), net_bandwidth);
}
else else
I_Error("usage: -bandwidth <byte_per_sec>"); extratics = 1;
CONS_Printf(M_GetText("Set extratics to %d\n"), extratics);
} }
software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH; software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH;
...@@ -1282,10 +1071,8 @@ boolean D_CheckNetGame(void) ...@@ -1282,10 +1071,8 @@ boolean D_CheckNetGame(void)
if (netgame) if (netgame)
multiplayer = true; multiplayer = true;
if (doomcom->id != DOOMCOM_ID) if (numnetnodes > MAXNETNODES)
I_Error("Doomcom buffer invalid!"); I_Error("Too many nodes (%d), max:%d", numnetnodes, MAXNETNODES);
if (doomcom->numnodes > MAXNETNODES)
I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES);
netbuffer = (doomdata_t *)(void *)&doomcom->data; netbuffer = (doomdata_t *)(void *)&doomcom->data;
...@@ -1293,7 +1080,7 @@ boolean D_CheckNetGame(void) ...@@ -1293,7 +1080,7 @@ boolean D_CheckNetGame(void)
if (M_CheckParm("-debugfile")) if (M_CheckParm("-debugfile"))
{ {
char filename[21]; char filename[21];
INT32 k = doomcom->consoleplayer - 1; INT32 k = consoleplayer - 1;
if (M_IsNextParm()) if (M_IsNextParm())
k = atoi(M_GetNextParm()) - 1; k = atoi(M_GetNextParm()) - 1;
while (!debugfile && k < MAXPLAYERS) while (!debugfile && k < MAXPLAYERS)
...@@ -1400,7 +1187,6 @@ void D_CloseConnection(void) ...@@ -1400,7 +1187,6 @@ void D_CloseConnection(void)
I_NetGet = Internal_Get; I_NetGet = Internal_Get;
I_NetSend = Internal_Send; I_NetSend = Internal_Send;
I_NetCanSend = NULL;
I_NetCloseSocket = NULL; I_NetCloseSocket = NULL;
I_NetFreeNodenum = Internal_FreeNodenum; I_NetFreeNodenum = Internal_FreeNodenum;
I_NetMakeNodewPort = NULL; I_NetMakeNodewPort = NULL;
......
...@@ -60,7 +60,6 @@ extern netnode_t netnodes[MAXNETNODES]; ...@@ -60,7 +60,6 @@ extern netnode_t netnodes[MAXNETNODES];
extern boolean serverrunning; extern boolean serverrunning;
INT32 Net_GetFreeAcks(boolean urgent);
void Net_AckTicker(void); void Net_AckTicker(void);
// If reliable return true if packet sent, 0 else // If reliable return true if packet sent, 0 else
......