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
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2214-pre1
  • 2214-pre2
  • 2214-pre3
  • 2214-pre4
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-opengl-parameter-crash
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • just-in-case
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • map-components-signedness-fixes
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.15
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
142 results

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
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2214-pre1
  • 2214-pre2
  • 2214-pre3
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • SRB_release_french_2.2.13
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • better-player-states
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-opengl-parameter-crash
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • gitlab-ci
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • just-in-case
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-debug-library
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
141 results
Show changes
Showing with 644 additions and 565 deletions
...@@ -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)
......
...@@ -193,9 +193,9 @@ INT32 M_RandomKey(INT32 a) ...@@ -193,9 +193,9 @@ INT32 M_RandomKey(INT32 a)
*/ */
INT32 M_RandomRange(INT32 a, INT32 b) INT32 M_RandomRange(INT32 a, INT32 b)
{ {
if (b < a) if (b < a)
{ {
INT32 temp; INT32 temp;
temp = a; temp = a;
a = b; a = b;
......
// 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,10 +220,46 @@ static void Tokenizer_ReadTokenString(tokenizer_t *tokenizer, UINT32 i) ...@@ -101,10 +220,46 @@ 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.
M_Memcpy(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength); if (tokenizer->stringNeedsEscaping)
// Make the final character NUL. {
tokenizer->token[i][tokenLength] = '\0'; EscapeString(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength);
}
else
{
M_Memcpy(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength);
// Make the final character NUL.
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,19 +1367,33 @@ static void IdleUpdate(void) ...@@ -1360,19 +1367,33 @@ 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;
SendKick(i, KICK_MSG_IDLE | KICK_MSG_KEEP_BODY); 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);
}
} }
} }
else else
...@@ -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) ackpak[i].senttime = I_GetTime();
{ ackpak[i].resentnum = 0;
// 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].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,119 +287,32 @@ static int Processackpak(void) ...@@ -340,119 +287,32 @@ 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 // Is a good packet so increment the acknowledge number,
for (INT32 i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) // Then search for a "hole" in the queue
if (node->acktosend[i] == ack) UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1);
{ if (!nextfirstack)
DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack)); nextfirstack = 1;
duppacket++;
goodpacket = 1; // Discard packet (duplicate) if (ack == nextfirstack)
break; {
} node->firstacktosend = nextfirstack;
if (goodpacket == 0) }
else // Out of order packet
{ {
// Is a good packet so increment the acknowledge number, // Don't increment firsacktosend, put it in asktosend queue
// Then search for a "hole" in the queue // Will be incremented when the nextfirstack comes (code above)
UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1); DEBFILE(va("out of order packet (%d expected)\n", nextfirstack));
if (!nextfirstack) goodpacket = false;
nextfirstack = 1;
if (ack == nextfirstack)
{
UINT8 hm1; // head - 1
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
{
// Don't increment firsacktosend, put it in asktosend queue
// 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));
if (newhead != node->acktosend_tail)
{
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,37 +379,12 @@ void Net_AckTicker(void) ...@@ -524,37 +379,12 @@ 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].firstacktosend--;
{ if (!nodes[node].firstacktosend)
nodes[node].acktosend[hm1] = 0; nodes[node].firstacktosend = UINT8_MAX;
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--;
if (!nodes[node].firstacktosend)
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
......
...@@ -320,7 +320,7 @@ consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOW ...@@ -320,7 +320,7 @@ consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOW
consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL); consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL);
static CV_PossibleValue_t timetic_cons_t[] = {{0, "Classic"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}}; static CV_PossibleValue_t timetic_cons_t[] = {{0, "Classic"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}};
consvar_t cv_timetic = CVAR_INIT ("timerres", "Classic", CV_SAVE, timetic_cons_t, NULL); consvar_t cv_timetic = CVAR_INIT ("timerres", "Mania", CV_SAVE, timetic_cons_t, NULL);
static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-person only"}, {2, "Always"}, {0, NULL}}; static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-person only"}, {2, "Always"}, {0, NULL}};
consvar_t cv_powerupdisplay = CVAR_INIT ("powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL); consvar_t cv_powerupdisplay = CVAR_INIT ("powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL);
...@@ -372,10 +372,10 @@ static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player ...@@ -372,10 +372,10 @@ static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player
consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, cooplives_cons_t, CoopLives_OnChange); consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, cooplives_cons_t, CoopLives_OnChange);
static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}}; static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}};
consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Next", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, advancemap_cons_t, NULL); consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Random", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, advancemap_cons_t, NULL);
static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}}; static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}};
consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "All", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, playersforexit_cons_t, NULL); consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "3/4", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, playersforexit_cons_t, NULL);
consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, ExitMove_OnChange); consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, ExitMove_OnChange);
...@@ -391,7 +391,7 @@ static CV_PossibleValue_t perfstats_cons_t[] = { ...@@ -391,7 +391,7 @@ static CV_PossibleValue_t perfstats_cons_t[] = {
consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", CV_CALL, perfstats_cons_t, PS_PerfStats_OnChange); consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", CV_CALL, perfstats_cons_t, PS_PerfStats_OnChange);
static CV_PossibleValue_t ps_samplesize_cons_t[] = { static CV_PossibleValue_t ps_samplesize_cons_t[] = {
{1, "MIN"}, {1000, "MAX"}, {0, NULL}}; {1, "MIN"}, {1000, "MAX"}, {0, NULL}};
consvar_t cv_ps_samplesize = CVAR_INIT ("ps_samplesize", "1", CV_CALL, ps_samplesize_cons_t, PS_SampleSize_OnChange); consvar_t cv_ps_samplesize = CVAR_INIT ("ps_samplesize", "175", CV_CALL, ps_samplesize_cons_t, PS_SampleSize_OnChange);
static CV_PossibleValue_t ps_descriptor_cons_t[] = { static CV_PossibleValue_t ps_descriptor_cons_t[] = {
{1, "Average"}, {2, "SD"}, {3, "Minimum"}, {4, "Maximum"}, {0, NULL}}; {1, "Average"}, {2, "SD"}, {3, "Minimum"}, {4, "Maximum"}, {0, NULL}};
consvar_t cv_ps_descriptor = CVAR_INIT ("ps_descriptor", "Average", 0, ps_descriptor_cons_t, NULL); consvar_t cv_ps_descriptor = CVAR_INIT ("ps_descriptor", "Average", 0, ps_descriptor_cons_t, NULL);
...@@ -620,6 +620,7 @@ void D_RegisterServerCommands(void) ...@@ -620,6 +620,7 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_blamecfail); CV_RegisterVar(&cv_blamecfail);
CV_RegisterVar(&cv_dedicatedidletime); CV_RegisterVar(&cv_dedicatedidletime);
CV_RegisterVar(&cv_idletime); CV_RegisterVar(&cv_idletime);
CV_RegisterVar(&cv_idleaction);
CV_RegisterVar(&cv_httpsource); CV_RegisterVar(&cv_httpsource);
COM_AddCommand("ping", Command_Ping_f, COM_LUA); COM_AddCommand("ping", Command_Ping_f, COM_LUA);
...@@ -691,6 +692,7 @@ void D_RegisterClientCommands(void) ...@@ -691,6 +692,7 @@ void D_RegisterClientCommands(void)
COM_AddCommand("timedemo", Command_Timedemo_f, 0); COM_AddCommand("timedemo", Command_Timedemo_f, 0);
COM_AddCommand("stopdemo", Command_Stopdemo_f, COM_LUA); COM_AddCommand("stopdemo", Command_Stopdemo_f, COM_LUA);
COM_AddCommand("playintro", Command_Playintro_f, COM_LUA); COM_AddCommand("playintro", Command_Playintro_f, COM_LUA);
CV_RegisterVar(&cv_resyncdemo);
COM_AddCommand("resetcamera", Command_ResetCamera_f, COM_LUA); COM_AddCommand("resetcamera", Command_ResetCamera_f, COM_LUA);
...@@ -822,8 +824,6 @@ void D_RegisterClientCommands(void) ...@@ -822,8 +824,6 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_jumpaxis2); CV_RegisterVar(&cv_jumpaxis2);
CV_RegisterVar(&cv_spinaxis); CV_RegisterVar(&cv_spinaxis);
CV_RegisterVar(&cv_spinaxis2); CV_RegisterVar(&cv_spinaxis2);
CV_RegisterVar(&cv_shieldaxis);
CV_RegisterVar(&cv_shieldaxis2);
CV_RegisterVar(&cv_fireaxis); CV_RegisterVar(&cv_fireaxis);
CV_RegisterVar(&cv_fireaxis2); CV_RegisterVar(&cv_fireaxis2);
CV_RegisterVar(&cv_firenaxis); CV_RegisterVar(&cv_firenaxis);
...@@ -1308,7 +1308,7 @@ static void SendNameAndColor(void) ...@@ -1308,7 +1308,7 @@ static void SendNameAndColor(void)
SetColorLocal(consoleplayer, cv_playercolor.value); SetColorLocal(consoleplayer, cv_playercolor.value);
if (splitscreen) if (splitscreen || (!pickedchar && stricmp(cv_skin.string, skins[consoleplayer]->name) != 0))
SetSkinLocal(consoleplayer, R_SkinAvailable(cv_skin.string)); SetSkinLocal(consoleplayer, R_SkinAvailable(cv_skin.string));
else else
SetSkinLocal(consoleplayer, pickedchar); SetSkinLocal(consoleplayer, pickedchar);
...@@ -2509,11 +2509,11 @@ static void MutePlayer(boolean mute) ...@@ -2509,11 +2509,11 @@ static void MutePlayer(boolean mute)
if (COM_Argc() < 2) if (COM_Argc() < 2)
{ {
CONS_Printf(M_GetText("muteplayer <playernum>: mute a player\n")); CONS_Printf(M_GetText("muteplayer <playername/playernum>: mute a player\n"));
return; return;
} }
data[0] = atoi(COM_Argv(1)); data[0] = nametonum(COM_Argv(1));
if (data[0] >= MAXPLAYERS || !playeringame[data[0]]) if (data[0] >= MAXPLAYERS || !playeringame[data[0]])
{ {
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %u!\n"), (unsigned int)data[0]); CONS_Alert(CONS_NOTICE, M_GetText("There is no player %u!\n"), (unsigned int)data[0]);
...@@ -2602,11 +2602,11 @@ static void Command_ServerTeamChange_f(void) ...@@ -2602,11 +2602,11 @@ static void Command_ServerTeamChange_f(void)
if (COM_Argc() < 3) if (COM_Argc() < 3)
{ {
if (G_TagGametype()) if (G_TagGametype())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator");
else if (G_GametypeHasTeams()) else if (G_GametypeHasTeams())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators()) else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
else else
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n")); CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return; return;
...@@ -2654,19 +2654,19 @@ static void Command_ServerTeamChange_f(void) ...@@ -2654,19 +2654,19 @@ static void Command_ServerTeamChange_f(void)
if (error) if (error)
{ {
if (G_TagGametype()) if (G_TagGametype())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator");
else if (G_GametypeHasTeams()) else if (G_GametypeHasTeams())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators()) else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
return; return;
} }
NetPacket.packet.playernum = atoi(COM_Argv(1)); NetPacket.packet.playernum = nametonum(COM_Argv(1));
if (!playeringame[NetPacket.packet.playernum]) if (NetPacket.packet.playernum == -1 || !playeringame[NetPacket.packet.playernum])
{ {
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %d!\n"), NetPacket.packet.playernum); CONS_Alert(CONS_NOTICE, M_GetText("There is no player %s!\n"), COM_Argv(1));
return; return;
} }
...@@ -3105,13 +3105,16 @@ static void Command_Verify_f(void) ...@@ -3105,13 +3105,16 @@ static void Command_Verify_f(void)
if (COM_Argc() != 2) if (COM_Argc() != 2)
{ {
CONS_Printf(M_GetText("promote <playernum>: give admin privileges to a player\n")); CONS_Printf(M_GetText("promote <playername/playernum>: give admin privileges to a player\n"));
return; return;
} }
strlcpy(buf, COM_Argv(1), sizeof (buf)); playernum = nametonum(COM_Argv(1));
if (playernum == -1)
playernum = atoi(buf); {
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %s!\n"), COM_Argv(1));
return;
}
temp = buf; temp = buf;
...@@ -3155,13 +3158,16 @@ static void Command_RemoveAdmin_f(void) ...@@ -3155,13 +3158,16 @@ static void Command_RemoveAdmin_f(void)
if (COM_Argc() != 2) if (COM_Argc() != 2)
{ {
CONS_Printf(M_GetText("demote <playernum>: remove admin privileges from a player\n")); CONS_Printf(M_GetText("demote <playername/playernum>: remove admin privileges from a player\n"));
return; return;
} }
strlcpy(buf, COM_Argv(1), sizeof(buf)); playernum = nametonum(COM_Argv(1));
if (playernum == -1)
playernum = atoi(buf); {
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %s!\n"), COM_Argv(1));
return;
}
temp = buf; temp = buf;
...@@ -4247,9 +4253,9 @@ void D_GameTypeChanged(INT32 lastgametype) ...@@ -4247,9 +4253,9 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_TEAMMATCH: case GT_TEAMMATCH:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{ {
// default settings for match: timelimit 10 mins, no pointlimit // default settings for match: timelimit 7 mins, no pointlimit
CV_SetValue(&cv_pointlimit, 0); CV_SetValue(&cv_pointlimit, 0);
CV_SetValue(&cv_timelimit, 10); CV_SetValue(&cv_timelimit, 7);
} }
if (!cv_itemrespawntime.changed) if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
...@@ -4258,9 +4264,9 @@ void D_GameTypeChanged(INT32 lastgametype) ...@@ -4258,9 +4264,9 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_HIDEANDSEEK: case GT_HIDEANDSEEK:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{ {
// default settings for tag: 5 mins, no pointlimit // default settings for tag: 7 mins, no pointlimit
// Note that tag mode also uses an alternate timing mechanism in tandem with timelimit. // Note that tag mode also uses an alternate timing mechanism in tandem with timelimit.
CV_SetValue(&cv_timelimit, 5); CV_SetValue(&cv_timelimit, 7);
CV_SetValue(&cv_pointlimit, 0); CV_SetValue(&cv_pointlimit, 0);
} }
if (!cv_itemrespawntime.changed) if (!cv_itemrespawntime.changed)
...@@ -4269,8 +4275,8 @@ void D_GameTypeChanged(INT32 lastgametype) ...@@ -4269,8 +4275,8 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_CTF: case GT_CTF:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{ {
// default settings for CTF: no timelimit, pointlimit 5 // default settings for CTF: 15 mins, pointlimit 5
CV_SetValue(&cv_timelimit, 0); CV_SetValue(&cv_timelimit, 15);
CV_SetValue(&cv_pointlimit, 5); CV_SetValue(&cv_pointlimit, 5);
} }
if (!cv_itemrespawntime.changed) if (!cv_itemrespawntime.changed)
...@@ -4608,7 +4614,7 @@ static void Command_ExitLevel_f(void) ...@@ -4608,7 +4614,7 @@ static void Command_ExitLevel_f(void)
SendNetXCmd(XD_EXITLEVEL, NULL, 0); SendNetXCmd(XD_EXITLEVEL, NULL, 0);
return; return;
} }
// Allow exiting without cheating if at least one player beat the level // Allow exiting without cheating if at least one player beat the level
// Consistent with just setting playersforexit to one // Consistent with just setting playersforexit to one
if (splitscreen || multiplayer) if (splitscreen || multiplayer)
...@@ -4622,7 +4628,7 @@ static void Command_ExitLevel_f(void) ...@@ -4622,7 +4628,7 @@ static void Command_ExitLevel_f(void)
continue; continue;
if (players[i].lives <= 0) if (players[i].lives <= 0)
continue; continue;
if ((players[i].pflags & PF_FINISHED) || players[i].exiting) if ((players[i].pflags & PF_FINISHED) || players[i].exiting)
{ {
SendNetXCmd(XD_EXITLEVEL, NULL, 0); SendNetXCmd(XD_EXITLEVEL, NULL, 0);
...@@ -4630,7 +4636,7 @@ static void Command_ExitLevel_f(void) ...@@ -4630,7 +4636,7 @@ static void Command_ExitLevel_f(void)
} }
} }
} }
// Only consider it a cheat if we're not allowed to go to the next map // Only consider it a cheat if we're not allowed to go to the next map
if (M_CampaignWarpIsCheat(gametype, G_GetNextMap(true, true) + 1, serverGamedata)) if (M_CampaignWarpIsCheat(gametype, G_GetNextMap(true, true) + 1, serverGamedata))
CONS_Alert(CONS_NOTICE, M_GetText("Cheats must be enabled to force exit to a locked level!\n")); CONS_Alert(CONS_NOTICE, M_GetText("Cheats must be enabled to force exit to a locked level!\n"));
...@@ -4769,7 +4775,7 @@ static void Command_Cheats_f(void) ...@@ -4769,7 +4775,7 @@ static void Command_Cheats_f(void)
G_SetUsedCheats(false); G_SetUsedCheats(false);
return; return;
} }
if (usedCheats) if (usedCheats)
CONS_Printf(M_GetText("Cheats are enabled, the game cannot be saved.\n")); CONS_Printf(M_GetText("Cheats are enabled, the game cannot be saved.\n"));
else else
...@@ -4791,12 +4797,11 @@ static void Command_Togglemodified_f(void) ...@@ -4791,12 +4797,11 @@ static void Command_Togglemodified_f(void)
modifiedgame = !modifiedgame; modifiedgame = !modifiedgame;
} }
extern UINT8 *save_p;
static void Command_Archivetest_f(void) static void Command_Archivetest_f(void)
{ {
UINT8 *buf;
UINT32 i, wrote; UINT32 i, wrote;
thinker_t *th; thinker_t *th;
save_t savebuffer;
if (gamestate != GS_LEVEL) if (gamestate != GS_LEVEL)
{ {
CONS_Printf("This command only works in-game, you dummy.\n"); CONS_Printf("This command only works in-game, you dummy.\n");
...@@ -4810,28 +4815,29 @@ static void Command_Archivetest_f(void) ...@@ -4810,28 +4815,29 @@ static void Command_Archivetest_f(void)
((mobj_t *)th)->mobjnum = i++; ((mobj_t *)th)->mobjnum = i++;
// allocate buffer // allocate buffer
buf = save_p = ZZ_Alloc(1024); savebuffer.size = 1024;
savebuffer.buf = malloc(savebuffer.size);
savebuffer.pos = 0;
// test archive // test archive
CONS_Printf("LUA_Archive...\n"); CONS_Printf("LUA_Archive...\n");
LUA_Archive(); LUA_Archive(&savebuffer);
WRITEUINT8(save_p, 0x7F); P_WriteUINT8(&savebuffer, 0x7F);
wrote = (UINT32)(save_p-buf); wrote = savebuffer.pos;
// clear Lua state, so we can really see what happens! // clear Lua state, so we can really see what happens!
CONS_Printf("Clearing state!\n"); CONS_Printf("Clearing state!\n");
LUA_ClearExtVars(); LUA_ClearExtVars();
// test unarchive // test unarchive
save_p = buf;
CONS_Printf("LUA_UnArchive...\n"); CONS_Printf("LUA_UnArchive...\n");
LUA_UnArchive(); LUA_UnArchive(&savebuffer);
i = READUINT8(save_p); i = P_ReadUINT8(&savebuffer);
if (i != 0x7F || wrote != (UINT32)(save_p-buf)) if (i != 0x7F || wrote != (UINT32)(savebuffer.pos))
CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(save_p-buf)); CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(savebuffer.pos));
// free buffer // free buffer
Z_Free(buf); free(savebuffer.buf);
CONS_Printf("Done. No crash.\n"); CONS_Printf("Done. No crash.\n");
} }
#endif #endif
...@@ -4902,11 +4908,13 @@ static void Name2_OnChange(void) ...@@ -4902,11 +4908,13 @@ static void Name2_OnChange(void)
static boolean Skin_CanChange(const char *valstr) static boolean Skin_CanChange(const char *valstr)
{ {
(void)valstr;
if (!Playing()) if (!Playing())
return true; // do whatever you want return true; // do whatever you want
// You already are that skin.
if (stricmp(skins[players[consoleplayer].skin]->name, valstr) == 0)
return false;
if (!(multiplayer || netgame)) // In single player. if (!(multiplayer || netgame)) // In single player.
return true; return true;
...@@ -4921,11 +4929,13 @@ static boolean Skin_CanChange(const char *valstr) ...@@ -4921,11 +4929,13 @@ static boolean Skin_CanChange(const char *valstr)
static boolean Skin2_CanChange(const char *valstr) static boolean Skin2_CanChange(const char *valstr)
{ {
(void)valstr;
if (!Playing() || !splitscreen) if (!Playing() || !splitscreen)
return true; // do whatever you want return true; // do whatever you want
// You already are that skin.
if (stricmp(skins[players[secondarydisplayplayer].skin]->name, valstr) == 0)
return false;
if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer)) if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer))
return true; return true;
else else
...@@ -4941,6 +4951,8 @@ static boolean Skin2_CanChange(const char *valstr) ...@@ -4941,6 +4951,8 @@ static boolean Skin2_CanChange(const char *valstr)
*/ */
static void Skin_OnChange(void) static void Skin_OnChange(void)
{ {
pickedchar = R_SkinAvailable(cv_skin.string);
if (!Playing()) if (!Playing())
return; return;
......
// 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.
......
...@@ -95,6 +95,7 @@ static filetran_t transfer[MAXNETNODES]; ...@@ -95,6 +95,7 @@ static filetran_t transfer[MAXNETNODES];
INT32 fileneedednum; // Number of files needed to join the server INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t *fileneeded; // List of needed files fileneeded_t *fileneeded; // List of needed files
static tic_t lasttimeackpacketsent = 0; static tic_t lasttimeackpacketsent = 0;
static I_mutex downloadmutex;
char downloaddir[512] = "DOWNLOAD"; char downloaddir[512] = "DOWNLOAD";
// For resuming failed downloads // For resuming failed downloads
...@@ -128,7 +129,7 @@ boolean waitingforluafilecommand = false; ...@@ -128,7 +129,7 @@ boolean waitingforluafilecommand = false;
char luafiledir[256 + 16] = "luafiles"; char luafiledir[256 + 16] = "luafiles";
// max file size to send to a player (in kilobytes) // max file size to send to a player (in kilobytes)
static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {204800, "MAX"}, {0, NULL}}; static CV_PossibleValue_t maxsend_cons_t[] = {{-1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL);
consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
...@@ -205,10 +206,10 @@ UINT8 *PutFileNeeded(UINT16 firstfile) ...@@ -205,10 +206,10 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
// Store in the upper four bits // Store in the upper four bits
if (!cv_downloading.value) if (!cv_downloading.value)
filestatus += (WILLSEND_NO << 4); // Won't send filestatus += (WILLSEND_NO << 4); // Won't send
else if (wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024) else if (cv_maxsend.value == -1 || wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024)
filestatus += (WILLSEND_YES << 4); // Will send if requested filestatus += (WILLSEND_YES << 4); // Will send if requested
// else else
// filestatus += (0 << 4); -- Won't send, too big filestatus += (WILLSEND_TOOLARGE << 4); // Won't send, too big
} }
WRITEUINT8(p, filestatus); WRITEUINT8(p, filestatus);
...@@ -606,7 +607,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode) ...@@ -606,7 +607,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
prevnext = &((*prevnext)->next); prevnext = &((*prevnext)->next);
// Allocate file transfer information and append it to the transfer list // Allocate file transfer information and append it to the transfer list
filetransfer = malloc(sizeof(luafiletransfer_t)); filetransfer = calloc(1, sizeof(luafiletransfer_t));
if (!filetransfer) if (!filetransfer)
I_Error("AddLuaFileTransfer: Out of memory\n"); I_Error("AddLuaFileTransfer: Out of memory\n");
*prevnext = filetransfer; *prevnext = filetransfer;
...@@ -848,7 +849,7 @@ static boolean AddFileToSendQueue(INT32 node, UINT8 fileid) ...@@ -848,7 +849,7 @@ static boolean AddFileToSendQueue(INT32 node, UINT8 fileid)
strlcpy(p->id.filename, wadfiles[wadnum]->filename, MAX_WADPATH); strlcpy(p->id.filename, wadfiles[wadnum]->filename, MAX_WADPATH);
// Handle huge file requests (i.e. bigger than cv_maxsend.value KB) // Handle huge file requests (i.e. bigger than cv_maxsend.value KB)
if (wadfiles[wadnum]->filesize > (UINT32)cv_maxsend.value * 1024) if (cv_maxsend.value != -1 && wadfiles[wadnum]->filesize > (UINT32)cv_maxsend.value * 1024)
{ {
// Too big // Too big
// Don't inform client (client sucks, man) // Don't inform client (client sucks, man)
...@@ -1033,7 +1034,6 @@ void FileSendTicker(void) ...@@ -1033,7 +1034,6 @@ void FileSendTicker(void)
netbuffer->packettype = PT_FILEFRAGMENT; netbuffer->packettype = PT_FILEFRAGMENT;
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
while (packetsent-- && filestosend != 0) while (packetsent-- && filestosend != 0)
{ {
for (i = currentnode, j = 0; j < MAXNETNODES; for (i = currentnode, j = 0; j < MAXNETNODES;
...@@ -1345,9 +1345,9 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) ...@@ -1345,9 +1345,9 @@ void PT_FileFragment(SINT8 node, INT32 netconsole)
if (!(strcmp(filename, "srb2.pk3") if (!(strcmp(filename, "srb2.pk3")
&& strcmp(filename, "zones.pk3") && strcmp(filename, "zones.pk3")
&& strcmp(filename, "player.dta") && strcmp(filename, "characters.pk3")
&& strcmp(filename, "patch.pk3") && strcmp(filename, "patch.pk3")
&& strcmp(filename, "music.dta") && strcmp(filename, "music.pk3")
)) ))
I_Error("Tried to download \"%s\"", filename); I_Error("Tried to download \"%s\"", filename);
...@@ -1610,11 +1610,13 @@ boolean CURLPrepareFile(const char* url, int dfilenum) ...@@ -1610,11 +1610,13 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
I_Error("Attempted to download files in -nodownload mode"); I_Error("Attempted to download files in -nodownload mode");
#endif #endif
curl_global_init(CURL_GLOBAL_ALL); if (!multi_handle)
{
curl_global_init(CURL_GLOBAL_ALL);
multi_handle = curl_multi_init();
}
http_handle = curl_easy_init(); http_handle = curl_easy_init();
multi_handle = curl_multi_init();
if (http_handle && multi_handle) if (http_handle && multi_handle)
{ {
I_mkdir(downloaddir, 0755); I_mkdir(downloaddir, 0755);
...@@ -1673,6 +1675,8 @@ boolean CURLPrepareFile(const char* url, int dfilenum) ...@@ -1673,6 +1675,8 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
filedownload.current = dfilenum; filedownload.current = dfilenum;
filedownload.http_running = true; filedownload.http_running = true;
I_spawn_thread("http-download", (I_thread_fn)CURLGetFile, NULL);
return true; return true;
} }
...@@ -1681,103 +1685,119 @@ boolean CURLPrepareFile(const char* url, int dfilenum) ...@@ -1681,103 +1685,119 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
return false; return false;
} }
void CURLAbortFile(void)
{
filedownload.http_running = false;
// lock and unlock to wait for the download thread to exit
I_lock_mutex(&downloadmutex);
I_unlock_mutex(downloadmutex);
}
void CURLGetFile(void) void CURLGetFile(void)
{ {
I_lock_mutex(&downloadmutex);
CURLMcode mc; /* return code used by curl_multi_wait() */ CURLMcode mc; /* return code used by curl_multi_wait() */
CURLcode easyres; /* Return from easy interface */ CURLcode easyres; /* Return from easy interface */
int numfds;
CURLMsg *m; /* for picking up messages with the transfer status */ CURLMsg *m; /* for picking up messages with the transfer status */
CURL *e; CURL *e;
int msgs_left; /* how many messages are left */ int msgs_left; /* how many messages are left */
const char *easy_handle_error; const char *easy_handle_error;
boolean running = true;
if (curl_runninghandles) while (running && filedownload.http_running)
{ {
curl_multi_perform(multi_handle, &curl_runninghandles); if (curl_runninghandles)
{
curl_multi_perform(multi_handle, &curl_runninghandles);
/* wait for activity, timeout or "nothing" */ /* wait for activity, timeout or "nothing" */
mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds); mc = curl_multi_wait(multi_handle, NULL, 0, 1000, NULL);
if (mc != CURLM_OK) if (mc != CURLM_OK)
{ {
CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc); CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc);
return; continue;
}
curl_curfile->currentsize = curl_dlnow;
curl_curfile->totalsize = curl_dltotal;
} }
curl_curfile->currentsize = curl_dlnow;
curl_curfile->totalsize = curl_dltotal;
}
/* See how the transfers went */ /* See how the transfers went */
while ((m = curl_multi_info_read(multi_handle, &msgs_left))) while ((m = curl_multi_info_read(multi_handle, &msgs_left)))
{
if (m && (m->msg == CURLMSG_DONE))
{ {
e = m->easy_handle; if (m && (m->msg == CURLMSG_DONE))
easyres = m->data.result;
char *filename = Z_StrDup(curl_realname);
nameonly(filename);
if (easyres != CURLE_OK)
{ {
long response_code = 0; running = false;
e = m->easy_handle;
easyres = m->data.result;
if (easyres == CURLE_HTTP_RETURNED_ERROR) char *filename = Z_StrDup(curl_realname);
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code); nameonly(filename);
if (response_code == 404) if (easyres != CURLE_OK)
curl_curfile->failed = FDOWNLOAD_FAIL_NOTFOUND; {
else long response_code = 0;
curl_curfile->failed = FDOWNLOAD_FAIL_OTHER;
easy_handle_error = (response_code) ? va("HTTP response code %ld", response_code) : curl_easy_strerror(easyres);
curl_curfile->status = FS_FALLBACK;
curl_curfile->currentsize = curl_origfilesize;
curl_curfile->totalsize = curl_origtotalfilesize;
filedownload.http_failed = true;
fclose(curl_curfile->file);
remove(curl_curfile->filename);
CONS_Alert(CONS_ERROR, M_GetText("Failed to download addon \"%s\" (%s)\n"), filename, easy_handle_error);
}
else
{
fclose(curl_curfile->file);
CONS_Printf(M_GetText("Finished download of \"%s\"\n"), filename); if (easyres == CURLE_HTTP_RETURNED_ERROR)
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code);
if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD) if (response_code == 404)
{ curl_curfile->failed = FDOWNLOAD_FAIL_NOTFOUND;
CONS_Alert(CONS_WARNING, M_GetText("File \"%s\" does not match the version used by the server\n"), filename); else
curl_curfile->failed = FDOWNLOAD_FAIL_OTHER;
easy_handle_error = (response_code) ? va("HTTP response code %ld", response_code) : curl_easy_strerror(easyres);
curl_curfile->status = FS_FALLBACK; curl_curfile->status = FS_FALLBACK;
curl_curfile->failed = FDOWNLOAD_FAIL_MD5SUMBAD; curl_curfile->currentsize = curl_origfilesize;
curl_curfile->totalsize = curl_origtotalfilesize;
filedownload.http_failed = true; filedownload.http_failed = true;
fclose(curl_curfile->file);
remove(curl_curfile->filename);
CONS_Alert(CONS_ERROR, M_GetText("Failed to download addon \"%s\" (%s)\n"), filename, easy_handle_error);
} }
else else
{ {
filedownload.completednum++; fclose(curl_curfile->file);
filedownload.completedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND; CONS_Printf(M_GetText("Finished download of \"%s\"\n"), filename);
if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD)
{
CONS_Alert(CONS_WARNING, M_GetText("File \"%s\" does not match the version used by the server\n"), filename);
curl_curfile->status = FS_FALLBACK;
curl_curfile->failed = FDOWNLOAD_FAIL_MD5SUMBAD;
filedownload.http_failed = true;
}
else
{
filedownload.completednum++;
filedownload.completedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
}
} }
}
Z_Free(filename); Z_Free(filename);
curl_curfile->file = NULL; curl_curfile->file = NULL;
filedownload.http_running = false; filedownload.remaining--;
filedownload.remaining--; curl_multi_remove_handle(multi_handle, e);
curl_multi_remove_handle(multi_handle, e); curl_easy_cleanup(e);
curl_easy_cleanup(e);
if (!filedownload.remaining) if (!filedownload.remaining)
break; break;
}
} }
} }
if (!filedownload.remaining) if (!filedownload.remaining || !filedownload.http_running)
{ {
curl_multi_cleanup(multi_handle); curl_multi_cleanup(multi_handle);
curl_global_cleanup(); curl_global_cleanup();
multi_handle = NULL;
} }
filedownload.http_running = false;
I_unlock_mutex(downloadmutex);
} }
HTTP_login * HTTP_login *
......
...@@ -95,6 +95,7 @@ typedef struct ...@@ -95,6 +95,7 @@ typedef struct
INT32 remaining; INT32 remaining;
INT32 completednum; INT32 completednum;
UINT32 completedsize; UINT32 completedsize;
UINT64 totalsize;
boolean http_failed; boolean http_failed;
boolean http_running; boolean http_running;
...@@ -140,6 +141,7 @@ boolean CL_SendFileRequest(void); ...@@ -140,6 +141,7 @@ boolean CL_SendFileRequest(void);
void PT_RequestFile(SINT8 node); void PT_RequestFile(SINT8 node);
boolean CURLPrepareFile(const char* url, int dfilenum); boolean CURLPrepareFile(const char* url, int dfilenum);
void CURLAbortFile(void);
void CURLGetFile(void); void CURLGetFile(void);
HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next); HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next);
......
// 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.
...@@ -54,30 +54,25 @@ boolean SV_ResendingSavegameToAnyone(void) ...@@ -54,30 +54,25 @@ boolean SV_ResendingSavegameToAnyone(void)
void SV_SendSaveGame(INT32 node, boolean resending) void SV_SendSaveGame(INT32 node, boolean resending)
{ {
size_t length, compressedlen; size_t length, compressedlen;
UINT8 *savebuffer; save_t savebuffer;
UINT8 *compressedsave; UINT8 *compressedsave;
UINT8 *buffertosend; UINT8 *buffertosend;
// first save it in a malloced buffer // first save it in a malloced buffer
savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); savebuffer.size = SAVEGAMESIZE;
if (!savebuffer) savebuffer.buf = (UINT8 *)malloc(savebuffer.size);
if (!savebuffer.buf)
{ {
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return; return;
} }
// Leave room for the uncompressed length. // Leave room for the uncompressed length.
save_p = savebuffer + sizeof(UINT32); savebuffer.pos = sizeof(UINT32);
P_SaveNetGame(resending); P_SaveNetGame(&savebuffer, resending);
length = save_p - savebuffer; length = savebuffer.pos;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// Allocate space for compressed save: one byte fewer than for the // Allocate space for compressed save: one byte fewer than for the
// uncompressed data to ensure that the compression is worthwhile. // uncompressed data to ensure that the compression is worthwhile.
...@@ -85,15 +80,16 @@ void SV_SendSaveGame(INT32 node, boolean resending) ...@@ -85,15 +80,16 @@ void SV_SendSaveGame(INT32 node, boolean resending)
if (!compressedsave) if (!compressedsave)
{ {
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
free(savebuffer.buf);
return; return;
} }
// Attempt to compress it. // Attempt to compress it.
if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) if((compressedlen = lzf_compress(savebuffer.buf + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
{ {
// Compressing succeeded; send compressed data // Compressing succeeded; send compressed data
free(savebuffer); free(savebuffer.buf);
// State that we're compressed. // State that we're compressed.
buffertosend = compressedsave; buffertosend = compressedsave;
...@@ -107,12 +103,12 @@ void SV_SendSaveGame(INT32 node, boolean resending) ...@@ -107,12 +103,12 @@ void SV_SendSaveGame(INT32 node, boolean resending)
free(compressedsave); free(compressedsave);
// State that we're not compressed // State that we're not compressed
buffertosend = savebuffer; buffertosend = savebuffer.buf;
WRITEUINT32(savebuffer, 0); savebuffer.pos = 0;
P_WriteUINT32(&savebuffer, 0);
} }
AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
save_p = NULL;
// Remember when we started sending the savegame so we can handle timeouts // Remember when we started sending the savegame so we can handle timeouts
netnodes[node].sendingsavegame = true; netnodes[node].sendingsavegame = true;
...@@ -125,8 +121,7 @@ static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SA ...@@ -125,8 +121,7 @@ static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SA
void SV_SavedGame(void) void SV_SavedGame(void)
{ {
size_t length; save_t savebuffer;
UINT8 *savebuffer;
char tmpsave[256]; char tmpsave[256];
if (!cv_dumpconsistency.value) if (!cv_dumpconsistency.value)
...@@ -135,29 +130,22 @@ void SV_SavedGame(void) ...@@ -135,29 +130,22 @@ void SV_SavedGame(void)
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
// first save it in a malloced buffer // first save it in a malloced buffer
save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); savebuffer.size = SAVEGAMESIZE;
if (!save_p) savebuffer.buf = (UINT8 *)malloc(savebuffer.size);
if (!savebuffer.buf)
{ {
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return; return;
} }
savebuffer.pos = 0;
P_SaveNetGame(false); P_SaveNetGame(&savebuffer, false);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// then save it! // then save it!
if (!FIL_WriteFile(tmpsave, savebuffer, length)) if (!FIL_WriteFile(tmpsave, savebuffer.buf, savebuffer.pos))
CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
free(savebuffer); free(savebuffer.pos);
save_p = NULL;
} }
#undef TMPSAVENAME #undef TMPSAVENAME
...@@ -167,33 +155,34 @@ void SV_SavedGame(void) ...@@ -167,33 +155,34 @@ void SV_SavedGame(void)
void CL_LoadReceivedSavegame(boolean reloading) void CL_LoadReceivedSavegame(boolean reloading)
{ {
UINT8 *savebuffer = NULL; save_t savebuffer;
size_t length, decompressedlen; size_t decompressedlen;
char tmpsave[256]; char tmpsave[256];
FreeFileNeeded(); FreeFileNeeded();
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
length = FIL_ReadFile(tmpsave, &savebuffer); savebuffer.size = FIL_ReadFile(tmpsave, &savebuffer.buf);
savebuffer.pos = 0;
CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(savebuffer.size));
if (!length) if (!savebuffer.size)
{ {
I_Error("Can't read savegame sent"); I_Error("Can't read savegame sent");
return; return;
} }
save_p = savebuffer;
// Decompress saved game if necessary. // Decompress saved game if necessary.
decompressedlen = READUINT32(save_p); decompressedlen = P_ReadUINT32(&savebuffer);
if(decompressedlen > 0) if(decompressedlen > 0)
{ {
UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); lzf_decompress(savebuffer.buf + sizeof(UINT32), savebuffer.size - sizeof(UINT32), decompressedbuffer, decompressedlen);
Z_Free(savebuffer); Z_Free(savebuffer.buf);
save_p = savebuffer = decompressedbuffer; savebuffer.buf = decompressedbuffer;
savebuffer.size = decompressedlen;
savebuffer.pos = 0;
} }
paused = false; paused = false;
...@@ -203,7 +192,7 @@ void CL_LoadReceivedSavegame(boolean reloading) ...@@ -203,7 +192,7 @@ void CL_LoadReceivedSavegame(boolean reloading)
automapactive = false; automapactive = false;
// load a base level // load a base level
if (P_LoadNetGame(reloading)) if (P_LoadNetGame(&savebuffer, reloading))
{ {
const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
...@@ -219,8 +208,7 @@ void CL_LoadReceivedSavegame(boolean reloading) ...@@ -219,8 +208,7 @@ void CL_LoadReceivedSavegame(boolean reloading)
} }
// done // done
Z_Free(savebuffer); Z_Free(savebuffer.buf);
save_p = NULL;
if (unlink(tmpsave) == -1) if (unlink(tmpsave) == -1)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
consistancy[gametic%BACKUPTICS] = Consistancy(); consistancy[gametic%BACKUPTICS] = Consistancy();
......