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
  • better-distance-math
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • classic-netcode-fixes
  • 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-dedi-pthread
  • fix-enemy-target
  • 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
  • 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
  • voltybystorm/SRB2
  • ZenithNeko/srb-2-xp
  • Nep2Disk/SRB2
  • Cloudeon/SRB2
  • mushe/srb-2-ps-b
  • GlassCanonLover77/SRB2
123 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
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • 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-1258
  • fix-1277
  • fix-167
  • fix-cvar-conflicts
  • 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
  • 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
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • map-components-signedness-fixes
  • master
  • 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 820 additions and 560 deletions
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// 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
// terms of the GNU General Public License, version 2.
......@@ -1720,7 +1720,7 @@ char *va(const char *format, ...)
static char string[1024];
va_start(argptr, format);
vsprintf(string, format, argptr);
vsnprintf(string, 1024, format, argptr);
va_end(argptr);
return string;
......@@ -2208,6 +2208,8 @@ int M_JumpWordReverse(const char *line, int offset)
{
int (*is)(int);
int c;
if (offset == 0) // Don't let "--offset" later result in a negative value
return 0;
c = line[--offset];
if (isspace(c))
is = isspace;
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// 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
// terms of the GNU General Public License, version 2.
......
......@@ -127,8 +127,8 @@ perfstatrow_t commoncounter_rows[] = {
};
perfstatrow_t interpolation_rows[] = {
{"intpfrc", "Interp frac: ", &ps_interp_frac, PS_TIME},
{"intplag", "Interp lag: ", &ps_interp_lag, 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, 0},
{0}
};
......@@ -453,7 +453,7 @@ static int PS_DrawPerfRows(int x, int y, int color, perfstatrow_t *rows)
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;
......@@ -461,7 +461,7 @@ static void PS_UpdateMetricHistory(ps_metric_t *metric, boolean time_metric, boo
{
// allocate history table
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,
memory_user);
......@@ -491,7 +491,7 @@ static void PS_UpdateRowHistories(perfstatrow_t *rows, boolean frame_metric)
for (row = rows; row->lores_label; 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)
for (thinker = thlist[i].next; thinker != &thlist[i]; thinker = thinker->next)
{
ps_thinkercount.value.i++;
if (thinker->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (thinker->removing)
ps_removecount.value.i++;
else if (i == THINK_POLYOBJ)
ps_polythcount.value.i++;
......@@ -592,7 +592,7 @@ static void PS_CountThinkers(void)
ps_mainthcount.value.i++;
else if (i == THINK_MOBJ)
{
if (thinker->function.acp1 == (actionf_p1)P_MobjThinker)
if (thinker->function == (actionf_p1)P_MobjThinker)
{
mobj_t *mobj = (mobj_t*)thinker;
ps_mobjcount.value.i++;
......@@ -649,17 +649,17 @@ void PS_UpdateTickStats(void)
if (cv_perfstats.value == 3)
{
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)
{
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)
{
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)
......
// 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
// 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
tokenizer->endPos = 0;
tokenizer->inputLength = 0;
tokenizer->inComment = 0;
tokenizer->stringNeedsEscaping = false;
tokenizer->inString = 0;
tokenizer->get = Tokenizer_Read;
......@@ -92,6 +93,124 @@ static void DetectComment(tokenizer_t *tokenizer, UINT32 *pos)
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)
{
UINT32 tokenLength = tokenizer->endPos - tokenizer->startPos;
......@@ -101,11 +220,47 @@ static void Tokenizer_ReadTokenString(tokenizer_t *tokenizer, UINT32 i)
// Assign the memory. Don't forget an extra byte for the end of the string!
tokenizer->token[i] = (char *)Z_Malloc(tokenizer->capacity[i] * sizeof(char), PU_STATIC, NULL);
}
// Copy the string.
if (tokenizer->stringNeedsEscaping)
{
EscapeString(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength);
}
else
{
M_Memcpy(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength);
// 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)
{
......@@ -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 (tokenizer->inString == 1)
{
while (tokenizer->input[tokenizer->endPos] != '"' && tokenizer->endPos < tokenizer->inputLength)
{
DetectLineBreak(tokenizer, tokenizer->endPos);
tokenizer->endPos++;
}
ScanString(tokenizer);
Tokenizer_ReadTokenString(tokenizer, i);
tokenizer->inString = 2;
......@@ -134,6 +285,7 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
tokenizer->token[i][0] = tokenizer->input[tokenizer->startPos];
tokenizer->token[i][1] = '\0';
tokenizer->inString = 0;
tokenizer->stringNeedsEscaping = false;
return tokenizer->token[i];
}
......@@ -281,11 +433,7 @@ const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i)
else if (tokenizer->input[tokenizer->startPos] == '"')
{
tokenizer->endPos = ++tokenizer->startPos;
while (tokenizer->input[tokenizer->endPos] != '"' && tokenizer->endPos < tokenizer->inputLength)
{
DetectLineBreak(tokenizer, tokenizer->endPos);
tokenizer->endPos++;
}
ScanString(tokenizer);
Tokenizer_ReadTokenString(tokenizer, i);
tokenizer->endPos++;
......
// 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
// terms of the GNU General Public License, version 2.
......@@ -26,6 +26,7 @@ typedef struct Tokenizer
UINT32 inputLength;
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
boolean stringNeedsEscaping;
int line;
const char *(*get)(struct Tokenizer*, UINT32);
} tokenizer_t;
......
......@@ -21,6 +21,32 @@ void DVector3_Load(dvector3_t *vec, double x, double y, double 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 xs = a_normal->x * a_normal->x;
......
......@@ -19,6 +19,10 @@ typedef struct
} dvector3_t;
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_Normalize(dvector3_t *a_normal);
void DVector3_Negate(dvector3_t *a_o);
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2025 by LJ Sonic
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file matrix.c
/// \brief Fixed-point 3D vector
#include <string.h>
#include "matrix.h"
static matrix_t identitymatrix = {{
{FRACUNIT, 0, 0, 0},
{0, FRACUNIT, 0, 0},
{0, 0, FRACUNIT, 0},
{0, 0, 0, FRACUNIT},
}};
matrix_t *Matrix_SetIdentity(matrix_t *mat)
{
return memcpy(mat, identitymatrix.matrix, sizeof(*mat));
}
matrix_t *Matrix_SetTranslation(matrix_t *mat, fixed_t x, fixed_t y, fixed_t z)
{
Matrix_SetIdentity(mat);
mat->matrix[0][3] = x;
mat->matrix[1][3] = y;
mat->matrix[2][3] = z;
return mat;
}
matrix_t *Matrix_SetScaling(matrix_t *mat, fixed_t x, fixed_t y, fixed_t z)
{
Matrix_SetIdentity(mat);
mat->matrix[0][0] = x;
mat->matrix[1][1] = y;
mat->matrix[2][2] = z;
return mat;
}
matrix_t *Matrix_Copy(matrix_t *out, matrix_t *in)
{
return memcpy(out, in, sizeof(*out));
}
matrix_t *Matrix_Mul(matrix_t *out, matrix_t *a, matrix_t *b)
{
for (size_t r = 0; r < 4; r++)
for (size_t c = 0; c < 4; c++)
for (size_t i = 0; i < 4; i++)
out->matrix[r][c] += FixedMul(a->matrix[r][i], b->matrix[i][c]);
return out;
}
vector3_t *Matrix_MulVector(vector3_t *out, matrix_t *a, vector3_t *b)
{
out->x = FixedMul(a->matrix[0][0], b->x) + FixedMul(a->matrix[0][1], b->y) + FixedMul(a->matrix[0][2], b->z) + a->matrix[0][3];
out->y = FixedMul(a->matrix[1][0], b->x) + FixedMul(a->matrix[1][1], b->y) + FixedMul(a->matrix[1][2], b->z) + a->matrix[1][3];
out->z = FixedMul(a->matrix[2][0], b->x) + FixedMul(a->matrix[2][1], b->y) + FixedMul(a->matrix[2][2], b->z) + a->matrix[2][3];
return out;
}
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2025 by LJ Sonic
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file matrix.c
/// \brief Fixed-point 4x4 matrix
#ifndef __MATRIX__
#define __MATRIX__
#include "m_fixed.h"
#include "vector3d.h"
typedef struct
{
fixed_t matrix[4][4];
} matrix_t;
matrix_t *Matrix_SetIdentity(matrix_t *mat);
matrix_t *Matrix_SetTranslation(matrix_t *mat, fixed_t x, fixed_t y, fixed_t z);
matrix_t *Matrix_SetScaling(matrix_t *mat, fixed_t x, fixed_t y, fixed_t z);
matrix_t *Matrix_Copy(matrix_t *out, matrix_t *in);
matrix_t *Matrix_Mul(matrix_t *out, matrix_t *a, matrix_t *b);
vector3_t *Matrix_MulVector(vector3_t *out, matrix_t *a, vector3_t *b);
#endif
......@@ -54,6 +54,8 @@ static boolean IsDownloadingFile(void)
static void DrawConnectionStatusBox(void)
{
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())
return;
......@@ -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));
}
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
//
......@@ -96,7 +125,7 @@ static void CL_DrawConnectionStatus(void)
// Draw background fade
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;
UINT8 palstart;
......@@ -179,6 +208,31 @@ static void CL_DrawConnectionStatus(void)
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
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)
{
char tempname[28];
......@@ -224,7 +278,7 @@ static void CL_DrawConnectionStatus(void)
const char *download_str = M_GetText("Downloading \"%s\"");
#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));
// Rusty: actually lets do this instead
......@@ -244,16 +298,18 @@ static void CL_DrawConnectionStatus(void)
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));
}
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"));
}
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
{
......@@ -392,7 +448,7 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
M_SortServerList();
}
#if defined (MASTERSERVER) && defined (HAVE_THREADS)
#if defined (MASTERSERVER)
struct Fetch_servers_ctx
{
int room;
......@@ -437,7 +493,7 @@ Fetch_servers_thread (struct Fetch_servers_ctx *ctx)
free(ctx);
}
#endif // defined (MASTERSERVER) && defined (HAVE_THREADS)
#endif // defined (MASTERSERVER)
void CL_QueryServerList (msg_server_t *server_list)
{
......@@ -493,7 +549,8 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
#ifdef MASTERSERVER
if (internetsearch)
{
#ifdef HAVE_THREADS
if (I_can_thread())
{
struct Fetch_servers_ctx *ctx;
ctx = malloc(sizeof *ctx);
......@@ -509,8 +566,13 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
ctx->room = room;
I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx);
#else
if (!I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx))
{
free(ctx);
}
}
else
{
msg_server_t *server_list;
server_list = GetShortServersList(room, 0);
......@@ -520,7 +582,8 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
CL_QueryServerList(server_list);
free(server_list);
}
#endif
}
}
#endif // MASTERSERVER
}
......@@ -546,6 +609,7 @@ static void AbortConnection(void)
{
Snake_Free(&snake);
CURLAbortFile();
D_QuitNetGame();
CL_Reset();
D_StartTitle();
......@@ -656,6 +720,7 @@ static void ShowDownloadConsentMessage(void)
if (IsFileDownloadable(&fileneeded[i]))
totalsize += fileneeded[i].totalsize;
}
filedownload.totalsize = totalsize;
const char *downloadsize = GetPrintableFileSize(totalsize);
......@@ -1062,10 +1127,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
}
}
// Rusty TODO: multithread
if (filedownload.http_running)
CURLGetFile();
if (waitmore)
break; // exit the case
......@@ -1190,13 +1251,9 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
F_TitleScreenDrawer();
}
CL_DrawConnectionStatus();
#ifdef HAVE_THREADS
I_lock_mutex(&m_menu_mutex);
#endif
M_Drawer(); //Needed for drawing messageboxes on the connection screen
#ifdef HAVE_THREADS
I_unlock_mutex(m_menu_mutex);
#endif
I_UpdateNoVsync(); // page flip or blit buffer
if (moviemode)
M_SaveFrame();
......@@ -1217,7 +1274,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
void CL_ConnectToServer(void)
{
INT32 pnumnodes, nodewaited = doomcom->numnodes, i;
INT32 pnumnodes, nodewaited = numnetnodes, i;
tic_t oldtic;
tic_t asksent;
char tmpsave[256];
......@@ -1243,7 +1300,7 @@ void CL_ConnectToServer(void)
if (gamestate == GS_INTERMISSION)
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);
wipegamestate = GS_WAITINGPLAYERS;
......@@ -1404,7 +1461,7 @@ void PT_ServerCFG(SINT8 node)
netnodes[(UINT8)servernode].ingame = true;
serverplayer = netbuffer->u.servercfg.serverplayer;
doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
mynode = netbuffer->u.servercfg.clientnode;
if (serverplayer >= 0)
playernode[(UINT8)serverplayer] = servernode;
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// 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
// 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_
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_idletime = CVAR_INIT ("idletime", "0", CV_SAVE, CV_Unsigned, NULL);
consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE, CV_Unsigned, NULL);
consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE|CV_NETVAR, 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);
......@@ -146,8 +149,8 @@ void CL_Reset(void)
multiplayer = false;
servernode = 0;
server = true;
doomcom->numnodes = 1;
doomcom->numslots = 1;
numnetnodes = 1;
numslots = 1;
SV_StopServer();
SV_ResetServer();
......@@ -212,8 +215,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
CL_ClearPlayer(newplayernum);
playeringame[newplayernum] = true;
G_AddPlayer(newplayernum);
if (newplayernum+1 > doomcom->numslots)
doomcom->numslots = (INT16)(newplayernum+1);
if (newplayernum+1 > numslots)
numslots = (INT16)(newplayernum+1);
if (server && I_GetNodeAddress)
{
......@@ -609,8 +612,8 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
// remove avatar of player
playeringame[playernum] = false;
while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1)
doomcom->numslots--;
while (!playeringame[numslots-1] && numslots > 1)
numslots--;
// Reset the name
sprintf(player_names[playernum], "Player %d", playernum+1);
......@@ -638,6 +641,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
void D_QuitNetGame(void)
{
mousegrabbedbylua = true;
textinputmodeenabledbylua = false;
I_UpdateMouseGrab();
if (!netgame || !netbuffer)
......@@ -660,7 +664,7 @@ void D_QuitNetGame(void)
if (netnodes[i].ingame)
HSendPacket(i, true, 0, 0);
#ifdef MASTERSERVER
if (serverrunning && ms_RoomId > 0)
if (serverrunning && cv_masterserver_room_id.value > 0)
UnregisterServer();
#endif
}
......@@ -670,6 +674,7 @@ void D_QuitNetGame(void)
HSendPacket(servernode, true, 0, 0);
}
seenplayer = NULL;
D_CloseConnection();
ClearAdminPlayers();
......@@ -750,7 +755,7 @@ void SV_ResetServer(void)
if (server)
servernode = 0;
doomcom->numslots = 0;
numslots = 0;
// clear server_context
memset(server_context, '-', 8);
......@@ -794,7 +799,7 @@ void SV_SpawnServer(void)
{
I_NetOpenSocket();
#ifdef MASTERSERVER
if (ms_RoomId > 0)
if (cv_masterserver_room_id.value > 0)
RegisterServer();
#endif
}
......@@ -802,7 +807,9 @@ void SV_SpawnServer(void)
// non dedicated server just connect to itself
if (!dedicated)
CL_ConnectToServer();
else doomcom->numslots = 1;
else numslots = 1;
LUA_HookVoid(HOOK(GameStart));
}
}
......@@ -1360,21 +1367,35 @@ static void IdleUpdate(void)
if (!server || !netgame)
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)
players[i].lastinputtime = 0;
else
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;
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
{
players[i].lastinputtime = 0;
......@@ -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
static void HandleNodeTimeouts(void)
{
......@@ -1475,69 +1573,7 @@ void NetUpdate(void)
realtics = 5;
}
if (server && dedicated && gamestate == GS_LEVEL)
{
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;
}
DedicatedIdleUpdate(&realtics);
gametime = nowtime;
......@@ -1613,13 +1649,9 @@ void NetUpdate(void)
if (nowtime != resptime)
{
resptime = nowtime;
#ifdef HAVE_THREADS
I_lock_mutex(&m_menu_mutex);
#endif
M_Ticker();
#ifdef HAVE_THREADS
I_unlock_mutex(m_menu_mutex);
#endif
CON_Ticker();
}
......@@ -1784,7 +1816,7 @@ INT16 Consistancy(void)
{
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;
mo = (mobj_t *)th;
......
......@@ -73,7 +73,7 @@ extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS];
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;
// Used in d_net, the only dependence
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// 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
// terms of the GNU General Public License, version 2.
......@@ -48,6 +48,10 @@
#define FORCECLOSE 0x8000
tic_t connectiontimeout = (10*TICRATE);
INT16 numnetnodes;
INT16 numslots;
INT16 extratics;
/// \brief network packet
doomcom_t *doomcom = NULL;
/// \brief network packet data, points inside doomcom
......@@ -62,16 +66,11 @@ static doomdata_t reboundstore[MAXREBOUND];
static INT16 reboundsize[MAXREBOUND];
static INT32 rebound_head, rebound_tail;
/// \brief bandwith of netgame
INT32 net_bandwidth;
/// \brief max length per packet
INT16 hardware_MAXPACKETLENGTH;
boolean (*I_NetGet)(void) = NULL;
void (*I_NetSend)(void) = NULL;
boolean (*I_NetCanSend)(void) = NULL;
boolean (*I_NetCanGet)(void) = NULL;
void (*I_NetCloseSocket)(void) = NULL;
void (*I_NetFreeNodenum)(INT32 nodenum) = NULL;
SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL;
......@@ -168,12 +167,6 @@ typedef struct
// ack return to send (like sliding window protocol)
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
tic_t lasttimeacktosend_sent;
// detect connection lost
......@@ -193,22 +186,17 @@ static node_t nodes[MAXNETNODES];
// 0 if a = n (mod 256)
// >0 if a > b (mod 256)
// 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;
if (d >= 127 || d < -128)
return -d;
return d;
return (SINT8)(a - b);
}
/** 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 lowtimer ???
* \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];
INT32 numfreeslot = 0;
......@@ -237,17 +225,8 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
node->nextacknum++;
ackpak[i].destinationnode = (UINT8)(node - nodes);
ackpak[i].length = doomcom->datalength;
if (lowtimer)
{
// Lowtime means can't be sent now so try it as soon as possible
ackpak[i].senttime = 0;
ackpak[i].resentnum = 1;
}
else
{
ackpak[i].senttime = I_GetTime();
ackpak[i].resentnum = 0;
}
M_Memcpy(ackpak[i].pak.raw, netbuffer, ackpak[i].length);
*freeack = ackpak[i].acknum;
......@@ -264,38 +243,6 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
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
static UINT8 GetAcktosend(INT32 node)
{
......@@ -313,9 +260,9 @@ static void RemoveAck(INT32 i)
}
// 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];
// Received an ack return, so remove the ack in the list
......@@ -324,7 +271,7 @@ static int Processackpak(void)
node->remotefirstack = netbuffer->ackreturn;
// Search the ackbuffer and free it
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)
{
RemoveAck(i);
......@@ -340,20 +287,9 @@ static int Processackpak(void)
{
DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = 1; // Discard packet (duplicate)
goodpacket = false; // Discard packet (duplicate)
}
else
{
// Check if it is not already in the queue
for (INT32 i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
if (node->acktosend[i] == ack)
{
DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = 1; // Discard packet (duplicate)
break;
}
if (goodpacket == 0)
{
// Is a good packet so increment the acknowledge number,
// Then search for a "hole" in the queue
......@@ -363,96 +299,20 @@ static int Processackpak(void)
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);
}
}
}
}
node->firstacktosend = nextfirstack;
}
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;
goodpacket = false;
}
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;
}
// 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)
{
// Don't timeout several times
......@@ -461,7 +321,12 @@ void Net_ConnectionTimeout(INT32 node)
nodes[node].flags |= NF_TIMEOUT;
if (server)
{
if (netnodes[node].ingame)
SendKicksForNode(node, KICK_MSG_TIMEOUT | KICK_MSG_KEEP_BODY);
else
Net_CloseConnection(node | FORCECLOSE);
}
else
CL_HandleTimeout();
......@@ -506,11 +371,6 @@ void Net_AckTicker(void)
// This is something like node open flag
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)
&& nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime())
{
......@@ -524,38 +384,13 @@ void Net_AckTicker(void)
// (the higher layer doesn't have room, or something else ....)
void Net_UnAcknowledgePacket(INT32 node)
{
INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND;
DEBFILE(va("UnAcknowledge node %d\n", node));
if (!node)
return;
if (nodes[node].acktosend[hm1] == netbuffer->ack)
{
nodes[node].acktosend[hm1] = 0;
nodes[node].acktosend_head = (UINT8)hm1;
}
else if (nodes[node].firstacktosend == netbuffer->ack)
{
nodes[node].firstacktosend--;
if (!nodes[node].firstacktosend)
nodes[node].firstacktosend = UINT8_MAX;
}
else
{
while (nodes[node].firstacktosend != netbuffer->ack)
{
nodes[node].acktosend_tail = (UINT8)
((nodes[node].acktosend_tail-1+MAXACKTOSEND) % MAXACKTOSEND);
nodes[node].acktosend[nodes[node].acktosend_tail] = nodes[node].firstacktosend;
nodes[node].firstacktosend--;
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
*
......@@ -597,7 +432,6 @@ void Net_WaitAllAckReceived(UINT32 timeout)
static void InitNode(node_t *node)
{
node->acktosend_head = node->acktosend_tail = 0;
node->firstacktosend = 0;
node->nextacknum = 1;
node->remotefirstack = 0;
......@@ -656,11 +490,11 @@ void Net_CloseConnection(INT32 node)
nodes[node].flags |= NF_CLOSE;
// try to Send ack back (two army problem)
if (GetAcktosend(node))
if (nodes[node].firstacktosend)
{
Net_SendAcks(node);
Net_SendAcks(node);
// send a PT_NOTHING back to acknowledge the packet
netbuffer->packettype = PT_NOTHING;
HSendPacket(node, false, 0, 0);
}
// check if we are waiting for an ack from this node
......@@ -996,15 +830,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
netbuffer->ackreturn = 0;
if (reliable)
{
if (I_NetCanSend && !I_NetCanSend())
{
if (netbuffer->packettype < PT_CANFAIL)
GetFreeAcknum(&netbuffer->ack, true);
DEBFILE("HSendPacket: Out of bandwidth\n");
return false;
}
else if (!GetFreeAcknum(&netbuffer->ack, false))
if (!GetFreeAcknum(&netbuffer->ack))
return false;
}
else
......@@ -1070,7 +896,6 @@ boolean HGetPacket(void)
while(true)
{
//nodejustjoined = I_NetGet();
int goodpacket;
I_NetGet();
if (doomcom->remotenode == -1) // No packet received
......@@ -1116,22 +941,12 @@ boolean HGetPacket(void)
}*/
// Proceed the ack and ackreturn field
goodpacket = 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);
if (!Processackpak())
continue; // discarded (duplicated)
}
// A packet with just ackreturn
if (netbuffer->packettype == PT_NOTHING)
{
GotAcks();
continue;
}
break;
}
......@@ -1156,7 +971,7 @@ static void Internal_FreeNodenum(INT32 nodenum)
char *I_NetSplitAddress(char *host, char **port)
{
boolean v4 = (strchr(host, '.') != NULL);
boolean v4 = (host[0] != '[');
host = strtok(host, v4 ? ":" : "[]");
......@@ -1189,11 +1004,8 @@ void D_SetDoomcom(void)
{
if (doomcom) return;
doomcom = Z_Calloc(sizeof (doomcom_t), PU_STATIC, NULL);
doomcom->id = DOOMCOM_ID;
doomcom->numslots = doomcom->numnodes = 1;
doomcom->gametype = 0;
doomcom->consoleplayer = 0;
doomcom->extratics = 0;
numslots = numnetnodes = 1;
extratics = 0;
}
//
......@@ -1211,13 +1023,11 @@ boolean D_CheckNetGame(void)
I_NetGet = Internal_Get;
I_NetSend = Internal_Send;
I_NetCanSend = NULL;
I_NetCloseSocket = NULL;
I_NetFreeNodenum = Internal_FreeNodenum;
I_NetMakeNodewPort = NULL;
hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
net_bandwidth = 30000;
// I_InitNetwork sets doomcom and netgame
// check and initialize the network driver
multiplayer = false;
......@@ -1237,30 +1047,14 @@ boolean D_CheckNetGame(void)
server = true; // WTF? server always true???
// no! The deault mode is server. Client is set elsewhere
// when the client executes connect command.
doomcom->ticdup = 1;
if (M_CheckParm("-extratic"))
{
if (M_IsNextParm())
doomcom->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);
}
extratics = (INT16)atoi(M_GetNextParm());
else
I_Error("usage: -bandwidth <byte_per_sec>");
extratics = 1;
CONS_Printf(M_GetText("Set extratics to %d\n"), extratics);
}
software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH;
......@@ -1282,10 +1076,8 @@ boolean D_CheckNetGame(void)
if (netgame)
multiplayer = true;
if (doomcom->id != DOOMCOM_ID)
I_Error("Doomcom buffer invalid!");
if (doomcom->numnodes > MAXNETNODES)
I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES);
if (numnetnodes > MAXNETNODES)
I_Error("Too many nodes (%d), max:%d", numnetnodes, MAXNETNODES);
netbuffer = (doomdata_t *)(void *)&doomcom->data;
......@@ -1293,7 +1085,7 @@ boolean D_CheckNetGame(void)
if (M_CheckParm("-debugfile"))
{
char filename[21];
INT32 k = doomcom->consoleplayer - 1;
INT32 k = consoleplayer - 1;
if (M_IsNextParm())
k = atoi(M_GetNextParm()) - 1;
while (!debugfile && k < MAXPLAYERS)
......@@ -1400,7 +1192,6 @@ void D_CloseConnection(void)
I_NetGet = Internal_Get;
I_NetSend = Internal_Send;
I_NetCanSend = NULL;
I_NetCloseSocket = NULL;
I_NetFreeNodenum = Internal_FreeNodenum;
I_NetMakeNodewPort = NULL;
......
......@@ -60,7 +60,6 @@ extern netnode_t netnodes[MAXNETNODES];
extern boolean serverrunning;
INT32 Net_GetFreeAcks(boolean urgent);
void Net_AckTicker(void);
// If reliable return true if packet sent, 0 else
......
......@@ -46,6 +46,7 @@
#include "mserv.h"
#include "../z_zone.h"
#include "../lua_script.h"
#include "../lua_archive.h"
#include "../lua_hook.h"
#include "../m_cond.h"
#include "../m_anigif.h"
......@@ -320,7 +321,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);
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}};
consvar_t cv_powerupdisplay = CVAR_INIT ("powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL);
......@@ -372,10 +373,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);
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}};
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);
......@@ -391,7 +392,7 @@ static CV_PossibleValue_t perfstats_cons_t[] = {
consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", CV_CALL, perfstats_cons_t, PS_PerfStats_OnChange);
static CV_PossibleValue_t ps_samplesize_cons_t[] = {
{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[] = {
{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);
......@@ -620,6 +621,7 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_blamecfail);
CV_RegisterVar(&cv_dedicatedidletime);
CV_RegisterVar(&cv_idletime);
CV_RegisterVar(&cv_idleaction);
CV_RegisterVar(&cv_httpsource);
COM_AddCommand("ping", Command_Ping_f, COM_LUA);
......@@ -691,6 +693,7 @@ void D_RegisterClientCommands(void)
COM_AddCommand("timedemo", Command_Timedemo_f, 0);
COM_AddCommand("stopdemo", Command_Stopdemo_f, COM_LUA);
COM_AddCommand("playintro", Command_Playintro_f, COM_LUA);
CV_RegisterVar(&cv_resyncdemo);
COM_AddCommand("resetcamera", Command_ResetCamera_f, COM_LUA);
......@@ -822,8 +825,6 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_jumpaxis2);
CV_RegisterVar(&cv_spinaxis);
CV_RegisterVar(&cv_spinaxis2);
CV_RegisterVar(&cv_shieldaxis);
CV_RegisterVar(&cv_shieldaxis2);
CV_RegisterVar(&cv_fireaxis);
CV_RegisterVar(&cv_fireaxis2);
CV_RegisterVar(&cv_firenaxis);
......@@ -1170,6 +1171,13 @@ static void SetPlayerName(INT32 playernum, char *newname)
{
if (strcasecmp(newname, player_names[playernum]) != 0)
{
if (!LUA_HookNameChange(&players[playernum], newname))
{
// Name change rejected by Lua
if (playernum == consoleplayer)
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
return;
}
if (netgame)
HU_AddChatText(va("\x82*%s renamed to %s", player_names[playernum], newname), false);
......@@ -1308,7 +1316,7 @@ static void SendNameAndColor(void)
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));
else
SetSkinLocal(consoleplayer, pickedchar);
......@@ -2509,11 +2517,11 @@ static void MutePlayer(boolean mute)
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;
}
data[0] = atoi(COM_Argv(1));
data[0] = nametonum(COM_Argv(1));
if (data[0] >= MAXPLAYERS || !playeringame[data[0]])
{
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %u!\n"), (unsigned int)data[0]);
......@@ -2602,11 +2610,11 @@ static void Command_ServerTeamChange_f(void)
if (COM_Argc() < 3)
{
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())
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())
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
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return;
......@@ -2654,19 +2662,19 @@ static void Command_ServerTeamChange_f(void)
if (error)
{
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())
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())
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;
}
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;
}
......@@ -3105,13 +3113,16 @@ static void Command_Verify_f(void)
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;
}
strlcpy(buf, COM_Argv(1), sizeof (buf));
playernum = atoi(buf);
playernum = nametonum(COM_Argv(1));
if (playernum == -1)
{
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %s!\n"), COM_Argv(1));
return;
}
temp = buf;
......@@ -3155,13 +3166,16 @@ static void Command_RemoveAdmin_f(void)
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;
}
strlcpy(buf, COM_Argv(1), sizeof(buf));
playernum = atoi(buf);
playernum = nametonum(COM_Argv(1));
if (playernum == -1)
{
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %s!\n"), COM_Argv(1));
return;
}
temp = buf;
......@@ -4247,9 +4261,9 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_TEAMMATCH:
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_timelimit, 10);
CV_SetValue(&cv_timelimit, 7);
}
if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
......@@ -4258,9 +4272,9 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_HIDEANDSEEK:
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.
CV_SetValue(&cv_timelimit, 5);
CV_SetValue(&cv_timelimit, 7);
CV_SetValue(&cv_pointlimit, 0);
}
if (!cv_itemrespawntime.changed)
......@@ -4269,8 +4283,8 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_CTF:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{
// default settings for CTF: no timelimit, pointlimit 5
CV_SetValue(&cv_timelimit, 0);
// default settings for CTF: 15 mins, pointlimit 5
CV_SetValue(&cv_timelimit, 15);
CV_SetValue(&cv_pointlimit, 5);
}
if (!cv_itemrespawntime.changed)
......@@ -4393,7 +4407,7 @@ static void SoundTest_OnChange(void)
}
S_StopSounds();
S_StartSound(NULL, cv_soundtest.value);
S_StartSoundFromEverywhere(cv_soundtest.value);
}
static void AutoBalance_OnChange(void)
......@@ -4791,12 +4805,11 @@ static void Command_Togglemodified_f(void)
modifiedgame = !modifiedgame;
}
extern UINT8 *save_p;
static void Command_Archivetest_f(void)
{
UINT8 *buf;
UINT32 i, wrote;
thinker_t *th;
save_t savebuffer;
if (gamestate != GS_LEVEL)
{
CONS_Printf("This command only works in-game, you dummy.\n");
......@@ -4806,32 +4819,33 @@ static void Command_Archivetest_f(void)
// assign mobjnum
i = 1;
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
if (th->function.acp1 != (actionf_p1)P_RemoveThinkerDelayed)
if (th->function != (actionf_p1)P_RemoveThinkerDelayed)
((mobj_t *)th)->mobjnum = i++;
// allocate buffer
buf = save_p = ZZ_Alloc(1024);
savebuffer.size = 1024;
savebuffer.buf = malloc(savebuffer.size);
savebuffer.pos = 0;
// test archive
CONS_Printf("LUA_Archive...\n");
LUA_Archive();
WRITEUINT8(save_p, 0x7F);
wrote = (UINT32)(save_p-buf);
LUA_Archive(&savebuffer);
P_WriteUINT8(&savebuffer, 0x7F);
wrote = savebuffer.pos;
// clear Lua state, so we can really see what happens!
CONS_Printf("Clearing state!\n");
LUA_ClearExtVars();
// test unarchive
save_p = buf;
CONS_Printf("LUA_UnArchive...\n");
LUA_UnArchive();
i = READUINT8(save_p);
if (i != 0x7F || wrote != (UINT32)(save_p-buf))
CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(save_p-buf));
LUA_UnArchive(&savebuffer);
i = P_ReadUINT8(&savebuffer);
if (i != 0x7F || wrote != (UINT32)(savebuffer.pos))
CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(savebuffer.pos));
// free buffer
Z_Free(buf);
free(savebuffer.buf);
CONS_Printf("Done. No crash.\n");
}
#endif
......@@ -4902,11 +4916,13 @@ static void Name2_OnChange(void)
static boolean Skin_CanChange(const char *valstr)
{
(void)valstr;
if (!Playing())
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.
return true;
......@@ -4921,11 +4937,13 @@ static boolean Skin_CanChange(const char *valstr)
static boolean Skin2_CanChange(const char *valstr)
{
(void)valstr;
if (!Playing() || !splitscreen)
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))
return true;
else
......@@ -4941,6 +4959,8 @@ static boolean Skin2_CanChange(const char *valstr)
*/
static void Skin_OnChange(void)
{
pickedchar = R_SkinAvailable(cv_skin.string);
if (!Playing())
return;
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// 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
// terms of the GNU General Public License, version 2.
......
......@@ -95,6 +95,7 @@ static filetran_t transfer[MAXNETNODES];
INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t *fileneeded; // List of needed files
static tic_t lasttimeackpacketsent = 0;
static I_mutex downloadmutex;
char downloaddir[512] = "DOWNLOAD";
// For resuming failed downloads
......@@ -111,6 +112,7 @@ static pauseddownload_t *pauseddownload = NULL;
file_download_t filedownload;
static CURL *http_handle;
static char curl_errbuf[CURL_ERROR_SIZE];
static CURLM *multi_handle;
static UINT32 curl_dlnow;
static UINT32 curl_dltotal;
......@@ -128,7 +130,7 @@ boolean waitingforluafilecommand = false;
char luafiledir[256 + 16] = "luafiles";
// 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_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
......@@ -205,10 +207,10 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
// Store in the upper four bits
if (!cv_downloading.value)
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
// else
// filestatus += (0 << 4); -- Won't send, too big
else
filestatus += (WILLSEND_TOOLARGE << 4); // Won't send, too big
}
WRITEUINT8(p, filestatus);
......@@ -606,7 +608,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
prevnext = &((*prevnext)->next);
// Allocate file transfer information and append it to the transfer list
filetransfer = malloc(sizeof(luafiletransfer_t));
filetransfer = calloc(1, sizeof(luafiletransfer_t));
if (!filetransfer)
I_Error("AddLuaFileTransfer: Out of memory\n");
*prevnext = filetransfer;
......@@ -848,7 +850,7 @@ static boolean AddFileToSendQueue(INT32 node, UINT8 fileid)
strlcpy(p->id.filename, wadfiles[wadnum]->filename, MAX_WADPATH);
// 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
// Don't inform client (client sucks, man)
......@@ -1033,7 +1035,6 @@ void FileSendTicker(void)
netbuffer->packettype = PT_FILEFRAGMENT;
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
while (packetsent-- && filestosend != 0)
{
for (i = currentnode, j = 0; j < MAXNETNODES;
......@@ -1345,9 +1346,9 @@ void PT_FileFragment(SINT8 node, INT32 netconsole)
if (!(strcmp(filename, "srb2.pk3")
&& strcmp(filename, "zones.pk3")
&& strcmp(filename, "player.dta")
&& strcmp(filename, "characters.pk3")
&& strcmp(filename, "patch.pk3")
&& strcmp(filename, "music.dta")
&& strcmp(filename, "music.pk3")
))
I_Error("Tried to download \"%s\"", filename);
......@@ -1604,19 +1605,44 @@ static int curlprogress_callback(void *clientp, curl_off_t dltotal, curl_off_t d
boolean CURLPrepareFile(const char* url, int dfilenum)
{
HTTP_login *login;
CURLcode cc;
if (!I_can_thread())
return false;
#ifdef PARANOIA
if (M_CheckParm("-nodownload"))
I_Error("Attempted to download files in -nodownload mode");
#endif
curl_global_init(CURL_GLOBAL_ALL);
http_handle = curl_easy_init();
if (!multi_handle)
{
cc = curl_global_init(CURL_GLOBAL_ALL);
if (cc < 0)
{
I_OutputMsg("libcurl: curl_global_init() returned %d\n", cc);
}
else
{
multi_handle = curl_multi_init();
}
if (!multi_handle)
{
I_OutputMsg("libcurl: curl_multi_init() failed\n");
curl_global_cleanup();
return false;
}
}
if (http_handle && multi_handle)
http_handle = curl_easy_init();
if (http_handle)
{
CURLMcode mc;
cc = curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, curl_errbuf);
if (cc != CURLE_OK) I_OutputMsg("libcurl: CURLOPT_ERRORBUFFER failed\n");
curl_errbuf[0] = 0x00;
I_mkdir(downloaddir, 0755);
curl_curfile = &fileneeded[dfilenum];
......@@ -1630,49 +1656,77 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
for (INT32 j = 0; j < 16; j++)
sprintf(&md5tmp[j*2], "%02x", curl_curfile->md5sum[j]);
curl_easy_setopt(http_handle, CURLOPT_URL, va("%s/%s?md5=%s", url, curl_realname, md5tmp));
cc = curl_easy_setopt(http_handle, CURLOPT_URL, va("%s/%s?md5=%s", url, curl_realname, md5tmp));
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
// Only allow HTTP and HTTPS
#if (LIBCURL_VERSION_MAJOR <= 7) && (LIBCURL_VERSION_MINOR < 85)
curl_easy_setopt(http_handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
cc = curl_easy_setopt(http_handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
#else
curl_easy_setopt(http_handle, CURLOPT_PROTOCOLS_STR, "http,https");
cc = curl_easy_setopt(http_handle, CURLOPT_PROTOCOLS_STR, "http,https");
#endif
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
// Set user agent, as some servers won't accept invalid user agents.
curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("Sonic Robo Blast 2/%s", VERSIONSTRING));
cc = curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("Sonic Robo Blast 2/%s", VERSIONSTRING));
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
// Authenticate if the user so wishes
login = CURLGetLogin(url, NULL);
if (login)
{
curl_easy_setopt(http_handle, CURLOPT_USERPWD, login->auth);
cc = curl_easy_setopt(http_handle, CURLOPT_USERPWD, login->auth);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
}
// Follow a redirect request, if sent by the server.
curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1L);
cc = curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1L);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
curl_easy_setopt(http_handle, CURLOPT_FAILONERROR, 1L);
cc = curl_easy_setopt(http_handle, CURLOPT_FAILONERROR, 1L);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
CONS_Printf("Downloading addon \"%s\" from %s\n", curl_realname, url);
strcatbf(curl_curfile->filename, downloaddir, "/");
curl_curfile->file = fopen(curl_curfile->filename, "wb");
curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, curl_curfile->file);
curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, curlwrite_data);
curl_easy_setopt(http_handle, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(http_handle, CURLOPT_XFERINFOFUNCTION, curlprogress_callback);
cc = curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, curl_curfile->file);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
cc = curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, curlwrite_data);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
cc = curl_easy_setopt(http_handle, CURLOPT_NOPROGRESS, 0L);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
cc = curl_easy_setopt(http_handle, CURLOPT_XFERINFOFUNCTION, curlprogress_callback);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
curl_curfile->status = FS_DOWNLOADING;
curl_multi_add_handle(multi_handle, http_handle);
curl_multi_perform(multi_handle, &curl_runninghandles);
mc = curl_multi_add_handle(multi_handle, http_handle);
if (mc != CURLM_OK) I_OutputMsg("libcurl: %s\n", curl_multi_strerror(mc));
mc = curl_multi_perform(multi_handle, &curl_runninghandles);
if (mc != CURLM_OK) I_OutputMsg("libcurl: %s\n", curl_multi_strerror(mc));
curl_starttime = time(NULL);
filedownload.current = dfilenum;
filedownload.http_running = true;
if (!I_spawn_thread("http-download", (I_thread_fn)CURLGetFile, NULL))
{
mc = curl_multi_cleanup(multi_handle);
if (mc != CURLM_OK) I_OutputMsg("libcurl: %s\n", curl_multi_strerror(mc));
curl_global_cleanup();
multi_handle = NULL;
filedownload.http_running = false;
return false;
}
return true;
}
......@@ -1681,27 +1735,40 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
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)
{
I_lock_mutex(&downloadmutex);
CURLMcode mc; /* return code used by curl_multi_wait() */
CURLcode easyres; /* Return from easy interface */
int numfds;
CURLMsg *m; /* for picking up messages with the transfer status */
CURL *e;
int msgs_left; /* how many messages are left */
const char *easy_handle_error;
boolean running = true;
while (running && filedownload.http_running)
{
if (curl_runninghandles)
{
curl_multi_perform(multi_handle, &curl_runninghandles);
mc = curl_multi_perform(multi_handle, &curl_runninghandles);
if (mc != CURLM_OK) I_OutputMsg("libcurl: %s\n", curl_multi_strerror(mc));
/* 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)
{
CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc);
return;
CONS_Alert(CONS_WARNING, "curl_multi_wait() failed: %s.\n", curl_multi_strerror(mc));
continue;
}
curl_curfile->currentsize = curl_dlnow;
curl_curfile->totalsize = curl_dltotal;
......@@ -1712,6 +1779,7 @@ void CURLGetFile(void)
{
if (m && (m->msg == CURLMSG_DONE))
{
running = false;
e = m->easy_handle;
easyres = m->data.result;
......@@ -1723,7 +1791,10 @@ void CURLGetFile(void)
long response_code = 0;
if (easyres == CURLE_HTTP_RETURNED_ERROR)
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code);
{
CURLcode cc = curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", curl_errbuf);
}
if (response_code == 404)
curl_curfile->failed = FDOWNLOAD_FAIL_NOTFOUND;
......@@ -1763,21 +1834,26 @@ void CURLGetFile(void)
Z_Free(filename);
curl_curfile->file = NULL;
filedownload.http_running = false;
filedownload.remaining--;
curl_multi_remove_handle(multi_handle, e);
mc = curl_multi_remove_handle(multi_handle, e);
if (mc != CURLM_OK) I_OutputMsg("libcurl: %s\n", curl_multi_strerror(mc));
curl_easy_cleanup(e);
if (!filedownload.remaining)
break;
}
}
}
if (!filedownload.remaining)
if (!filedownload.remaining || !filedownload.http_running)
{
curl_multi_cleanup(multi_handle);
mc = curl_multi_cleanup(multi_handle);
if (mc != CURLM_OK) I_OutputMsg("libcurl: %s\n", curl_multi_strerror(mc));
curl_global_cleanup();
multi_handle = NULL;
}
filedownload.http_running = false;
I_unlock_mutex(downloadmutex);
}
HTTP_login *
......
......@@ -95,6 +95,7 @@ typedef struct
INT32 remaining;
INT32 completednum;
UINT32 completedsize;
UINT64 totalsize;
boolean http_failed;
boolean http_running;
......@@ -140,6 +141,7 @@ boolean CL_SendFileRequest(void);
void PT_RequestFile(SINT8 node);
boolean CURLPrepareFile(const char* url, int dfilenum);
void CURLAbortFile(void);
void CURLGetFile(void);
HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next);
......