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
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delete-docs
  • delete-unused-render-code
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-gl-shaders-colormap
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • font_drawer
  • freebsd-memfix-cmake
  • frictionrefactor
  • fruits-clipper
  • 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-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-gfx-2
  • lua-gfx-sprites
  • lua-local
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • map-components-signedness-fixes
  • maretimers
  • master
  • menu-edits
  • 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 1129 additions and 722 deletions
// 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.
......@@ -12,15 +12,23 @@
#include "m_tokenizer.h"
#include "z_zone.h"
tokenizer_t *Tokenizer_Open(const char *inputString, unsigned numTokens)
tokenizer_t *Tokenizer_Open(const char *inputString, size_t len, unsigned numTokens)
{
tokenizer_t *tokenizer = Z_Malloc(sizeof(tokenizer_t), PU_STATIC, NULL);
const size_t lenpan = 2;
tokenizer->input = inputString;
tokenizer->zdup = malloc(len+lenpan);
for (size_t i = 0; i < lenpan; i++)
{
tokenizer->zdup[len+i] = 0x00;
}
tokenizer->input = M_Memcpy(tokenizer->zdup, inputString, len);
tokenizer->startPos = 0;
tokenizer->endPos = 0;
tokenizer->inputLength = 0;
tokenizer->inComment = 0;
tokenizer->stringNeedsEscaping = false;
tokenizer->inString = 0;
tokenizer->get = Tokenizer_Read;
......@@ -51,6 +59,7 @@ void Tokenizer_Close(tokenizer_t *tokenizer)
Z_Free(tokenizer->token[i]);
Z_Free(tokenizer->capacity);
Z_Free(tokenizer->token);
free(tokenizer->zdup);
Z_Free(tokenizer);
}
......@@ -84,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;
......@@ -93,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)
{
......@@ -109,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;
......@@ -126,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];
}
......@@ -273,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.
......@@ -16,6 +16,7 @@
typedef struct Tokenizer
{
char *zdup;
const char *input;
unsigned numTokens;
UINT32 *capacity;
......@@ -25,11 +26,12 @@ 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;
tokenizer_t *Tokenizer_Open(const char *inputString, unsigned numTokens);
tokenizer_t *Tokenizer_Open(const char *inputString, size_t len, unsigned numTokens);
void Tokenizer_Close(tokenizer_t *tokenizer);
const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i);
......
......@@ -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
{
......@@ -307,7 +363,7 @@ boolean CL_SendJoin(void)
else
player2name = cv_playername2.zstring;
strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, sizeof(netbuffer->u.clientcfg.names[0])-1);
strncpy(netbuffer->u.clientcfg.names[1], player2name, MAXPLAYERNAME);
return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
......@@ -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.
......@@ -41,6 +41,8 @@ typedef struct banreason_s
static banreason_t *reasontail = NULL; //last entry, use prev
static banreason_t *reasonhead = NULL; //1st entry, use next
static boolean bans_loaded = false;
void Ban_Add(const char *reason)
{
banreason_t *reasonlist = malloc(sizeof(*reasonlist));
......@@ -85,6 +87,8 @@ void Ban_Load_File(boolean warning)
if (!I_ClearBans)
return;
bans_loaded = true;
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
if (!f)
......@@ -124,6 +128,12 @@ void D_SaveBan(void)
const char *address, *mask;
const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
if (!bans_loaded)
{
// don't save bans if they were never loaded.
return;
}
if (!reasonhead)
{
remove(path);
......
......@@ -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)
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
......@@ -940,7 +774,7 @@ void Command_Droprate(void)
static boolean ShouldDropPacket(void)
{
return (packetdropquantity[netbuffer->packettype])
|| (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100;
|| (packetdroprate != 0 && rand() < (((double)RAND_MAX) * (packetdroprate / 100.f))) || packetdroprate == 100;
}
#endif
......@@ -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"
......@@ -149,6 +150,10 @@ static void Command_Teamchange_f(void);
static void Command_Teamchange2_f(void);
static void Command_ServerTeamChange_f(void);
static void Command_MutePlayer_f(void);
static void Command_UnmutePlayer_f(void);
static void Got_MutePlayer(UINT8 **cp, INT32 playernum);
static void Command_Clearscores_f(void);
// Remote Administration
......@@ -209,6 +214,7 @@ static CV_PossibleValue_t matchboxes_cons_t[] = {{0, "Normal"}, {1, "Mystery"},
static CV_PossibleValue_t chances_cons_t[] = {{0, "MIN"}, {9, "MAX"}, {0, NULL}};
static CV_PossibleValue_t pause_cons_t[] = {{0, "Server"}, {1, "All"}, {0, NULL}};
consvar_t cv_showinput = CVAR_INIT ("showinput", "Off", CV_ALLOWLUA, CV_OnOff, NULL);
consvar_t cv_showinputjoy = CVAR_INIT ("showinputjoy", "Off", CV_ALLOWLUA, CV_OnOff, NULL);
#ifdef NETGAME_DEVMODE
......@@ -315,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);
......@@ -367,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);
......@@ -386,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);
......@@ -394,7 +400,7 @@ consvar_t cv_ps_descriptor = CVAR_INIT ("ps_descriptor", "Average", 0, ps_descri
consvar_t cv_freedemocamera = CVAR_INIT("freedemocamera", "Off", CV_SAVE, CV_OnOff, NULL);
// NOTE: this should be in hw_main.c, but we can't put it there as it breaks dedicated build
consvar_t cv_glallowshaders = CVAR_INIT ("gr_allowclientshaders", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_glallowshaders = CVAR_INIT ("gr_allowcustomshaders", "On", CV_NETVAR, CV_OnOff, NULL);
char timedemo_name[256];
boolean timedemo_csv;
......@@ -487,6 +493,10 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_TEAMCHANGE, Got_Teamchange);
COM_AddCommand("serverchangeteam", Command_ServerTeamChange_f, COM_LUA);
RegisterNetXCmd(XD_MUTEPLAYER, Got_MutePlayer);
COM_AddCommand("muteplayer", Command_MutePlayer_f, COM_LUA);
COM_AddCommand("unmuteplayer", Command_UnmutePlayer_f, COM_LUA);
RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores);
COM_AddCommand("clearscores", Command_Clearscores_f, COM_LUA);
COM_AddCommand("map", Command_Map_f, COM_LUA);
......@@ -611,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);
......@@ -662,6 +673,13 @@ void D_RegisterClientCommands(void)
for (i = 0; i < MAXPLAYERS; i++)
sprintf(player_names[i], "Player %d", 1 + i);
CV_RegisterVar(&cv_gravity);
CV_RegisterVar(&cv_tailspickup);
CV_RegisterVar(&cv_allowmlook);
CV_RegisterVar(&cv_flipcam);
CV_RegisterVar(&cv_flipcam2);
CV_RegisterVar(&cv_movebob);
if (dedicated)
return;
......@@ -675,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);
......@@ -736,6 +755,7 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_timetic);
CV_RegisterVar(&cv_powerupdisplay);
CV_RegisterVar(&cv_itemfinder);
CV_RegisterVar(&cv_showinput);
CV_RegisterVar(&cv_showinputjoy);
// time attack ghost options are also saved to config
......@@ -889,10 +909,6 @@ void D_RegisterClientCommands(void)
// screen.c
CV_RegisterVar(&cv_fullscreen);
CV_RegisterVar(&cv_renderview);
CV_RegisterVar(&cv_renderhitboxinterpolation);
CV_RegisterVar(&cv_renderhitboxgldepth);
CV_RegisterVar(&cv_renderhitbox);
CV_RegisterVar(&cv_renderer);
CV_RegisterVar(&cv_scr_depth);
CV_RegisterVar(&cv_scr_width);
......@@ -908,13 +924,11 @@ void D_RegisterClientCommands(void)
// ingame object placing
COM_AddCommand("objectplace", Command_ObjectPlace_f, COM_LUA);
//COM_AddCommand("writethings", Command_Writethings_f);
COM_AddCommand("writethings", Command_Writethings_f, COM_LUA);
CV_RegisterVar(&cv_speed);
CV_RegisterVar(&cv_opflags);
CV_RegisterVar(&cv_ophoopflags);
CV_RegisterVar(&cv_mapthingnum);
// CV_RegisterVar(&cv_grid);
// CV_RegisterVar(&cv_snapto);
CV_RegisterVar(&cv_freedemocamera);
......@@ -1157,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);
......@@ -1295,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);
......@@ -1310,7 +1331,7 @@ static void SendNameAndColor(void)
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
HU_AddChatText("\x85*You must wait to change your name again", false);
}
else if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
else if ((cv_mute.value || players[consoleplayer].muted) && !(server || IsPlayerAdmin(consoleplayer)))
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
else // Cleanup name if changing it
CleanupPlayerName(consoleplayer, cv_playername.zstring);
......@@ -2485,6 +2506,91 @@ static void Command_Teamchange2_f(void)
SendNetXCmd2(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
}
static void MutePlayer(boolean mute)
{
UINT8 data[2];
if (!(server || (IsPlayerAdmin(consoleplayer))))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
if (COM_Argc() < 2)
{
CONS_Printf(M_GetText("muteplayer <playername/playernum>: mute a player\n"));
return;
}
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]);
return;
}
if (players[data[0]].muted && mute)
{
CONS_Printf(M_GetText("%s is already muted!\n"), player_names[data[0]]);
return;
}
else if (!players[data[0]].muted && !mute)
{
CONS_Printf(M_GetText("%s is not muted!\n"), player_names[data[0]]);
return;
}
data[1] = mute;
SendNetXCmd(XD_MUTEPLAYER, &data, sizeof(data));
}
static void Command_MutePlayer_f(void)
{
MutePlayer(true);
}
static void Command_UnmutePlayer_f(void)
{
MutePlayer(false);
}
static void Got_MutePlayer(UINT8 **cp, INT32 playernum)
{
UINT8 player = READUINT8(*cp);
UINT8 muted = READUINT8(*cp);
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal mute received from player %s\n"), player_names[playernum]);
if (server)
SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
return;
}
if (player >= MAXPLAYERS || !playeringame[player])
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal mute received from player %s\n"), player_names[playernum]);
if (server)
SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
return;
}
if (!players[player].muted && muted)
{
if (player == consoleplayer)
CONS_Printf(M_GetText("You have been muted.\n"));
else
CONS_Printf(M_GetText("%s has been muted.\n"), player_names[player]);
}
else if (players[player].muted && !muted)
{
if (player == consoleplayer)
CONS_Printf(M_GetText("You are no longer muted.\n"));
else
CONS_Printf(M_GetText("%s is no longer muted.\n"), player_names[player]);
}
players[player].muted = muted;
}
static void Command_ServerTeamChange_f(void)
{
changeteam_union NetPacket;
......@@ -2504,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;
......@@ -2556,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;
}
......@@ -3007,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;
......@@ -3057,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;
......@@ -4149,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
......@@ -4160,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)
......@@ -4171,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)
......@@ -4295,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)
......@@ -4663,15 +4775,28 @@ static void Command_Cheats_f(void)
CV_ResetCheatNetVars();
return;
}
else if (COM_CheckParm("on"))
{
if (!(server || (IsPlayerAdmin(consoleplayer))))
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else
G_SetUsedCheats(false);
return;
}
if (usedCheats)
CONS_Printf(M_GetText("Cheats are enabled, the game cannot be saved.\n"));
else
CONS_Printf(M_GetText("Cheats are disabled, the game can be saved.\n"));
if (CV_CheatsEnabled())
{
CONS_Printf(M_GetText("At least one CHEAT-marked variable has been changed -- Cheats are enabled.\n"));
CONS_Printf(M_GetText("At least one CHEAT-marked variable has been changed.\n"));
if (server || (IsPlayerAdmin(consoleplayer)))
CONS_Printf(M_GetText("Type CHEATS OFF to reset all cheat variables to default.\n"));
}
else
CONS_Printf(M_GetText("No CHEAT-marked variables are changed -- Cheats are disabled.\n"));
CONS_Printf(M_GetText("No CHEAT-marked variables are changed.\n"));
}
#ifdef _DEBUG
......@@ -4680,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");
......@@ -4695,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
......@@ -4769,7 +4894,7 @@ static void ForceSkin_OnChange(void)
//Allows the player's name to be changed if cv_mute is off.
static void Name_OnChange(void)
{
if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
if ((cv_mute.value || players[consoleplayer].muted) && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
......@@ -4780,7 +4905,7 @@ static void Name_OnChange(void)
static void Name2_OnChange(void)
{
if (cv_mute.value) //Secondary player can't be admin.
if (cv_mute.value || players[consoleplayer].muted) //Secondary player can't be admin.
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername2, player_names[secondarydisplayplayer]);
......@@ -4791,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;
......@@ -4810,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
......@@ -4830,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.
......@@ -94,7 +94,10 @@ extern consvar_t cv_inttime, cv_coopstarposts, cv_cooplives, cv_advancemap, cv_p
extern consvar_t cv_overtime;
extern consvar_t cv_startinglives;
// for F_finale.c
extern consvar_t cv_gravity, cv_movebob;
extern consvar_t cv_tailspickup;
// for f_finale.c
extern consvar_t cv_rollingdemos;
extern consvar_t cv_ringslinger, cv_soundtest;
......@@ -105,7 +108,6 @@ extern consvar_t cv_maxping;
extern consvar_t cv_pingtimeout;
extern consvar_t cv_showping;
extern consvar_t cv_skipmapcheck;
extern consvar_t cv_sleep;
......@@ -147,6 +149,7 @@ typedef enum
XD_LUACMD, // 22
XD_LUAVAR, // 23
XD_LUAFILE, // 24
XD_MUTEPLAYER, // 25
MAXNETXCMD
} netxcmd_t;
......
......@@ -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);
......
// 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.
......@@ -54,30 +54,25 @@ boolean SV_ResendingSavegameToAnyone(void)
void SV_SendSaveGame(INT32 node, boolean resending)
{
size_t length, compressedlen;
UINT8 *savebuffer;
save_t savebuffer;
UINT8 *compressedsave;
UINT8 *buffertosend;
// first save it in a malloced buffer
savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
if (!savebuffer)
savebuffer.size = SAVEGAMESIZE;
savebuffer.buf = (UINT8 *)malloc(savebuffer.size);
if (!savebuffer.buf)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
// 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;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
length = savebuffer.pos;
// Allocate space for compressed save: one byte fewer than for the
// uncompressed data to ensure that the compression is worthwhile.
......@@ -85,15 +80,16 @@ void SV_SendSaveGame(INT32 node, boolean resending)
if (!compressedsave)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
free(savebuffer.buf);
return;
}
// 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
free(savebuffer);
free(savebuffer.buf);
// State that we're compressed.
buffertosend = compressedsave;
......@@ -107,12 +103,12 @@ void SV_SendSaveGame(INT32 node, boolean resending)
free(compressedsave);
// State that we're not compressed
buffertosend = savebuffer;
WRITEUINT32(savebuffer, 0);
buffertosend = savebuffer.buf;
savebuffer.pos = 0;
P_WriteUINT32(&savebuffer, 0);
}
AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
save_p = NULL;
// Remember when we started sending the savegame so we can handle timeouts
netnodes[node].sendingsavegame = true;
......@@ -125,8 +121,7 @@ static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SA
void SV_SavedGame(void)
{
size_t length;
UINT8 *savebuffer;
save_t savebuffer;
char tmpsave[256];
if (!cv_dumpconsistency.value)
......@@ -135,29 +130,22 @@ void SV_SavedGame(void)
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
// first save it in a malloced buffer
save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
if (!save_p)
savebuffer.size = SAVEGAMESIZE;
savebuffer.buf = (UINT8 *)malloc(savebuffer.size);
if (!savebuffer.buf)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
savebuffer.pos = 0;
P_SaveNetGame(false);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
P_SaveNetGame(&savebuffer, false);
// 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);
free(savebuffer);
save_p = NULL;
free(savebuffer.pos);
}
#undef TMPSAVENAME
......@@ -167,33 +155,34 @@ void SV_SavedGame(void)
void CL_LoadReceivedSavegame(boolean reloading)
{
UINT8 *savebuffer = NULL;
size_t length, decompressedlen;
save_t savebuffer;
size_t decompressedlen;
char tmpsave[256];
FreeFileNeeded();
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));
if (!length)
CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(savebuffer.size));
if (!savebuffer.size)
{
I_Error("Can't read savegame sent");
return;
}
save_p = savebuffer;
// Decompress saved game if necessary.
decompressedlen = READUINT32(save_p);
decompressedlen = P_ReadUINT32(&savebuffer);
if(decompressedlen > 0)
{
UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
Z_Free(savebuffer);
save_p = savebuffer = decompressedbuffer;
lzf_decompress(savebuffer.buf + sizeof(UINT32), savebuffer.size - sizeof(UINT32), decompressedbuffer, decompressedlen);
Z_Free(savebuffer.buf);
savebuffer.buf = decompressedbuffer;
savebuffer.size = decompressedlen;
savebuffer.pos = 0;
}
paused = false;
......@@ -203,7 +192,7 @@ void CL_LoadReceivedSavegame(boolean reloading)
automapactive = false;
// load a base level
if (P_LoadNetGame(reloading))
if (P_LoadNetGame(&savebuffer, reloading))
{
const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
......@@ -219,8 +208,7 @@ void CL_LoadReceivedSavegame(boolean reloading)
}
// done
Z_Free(savebuffer);
save_p = NULL;
Z_Free(savebuffer.buf);
if (unlink(tmpsave) == -1)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
consistancy[gametic%BACKUPTICS] = Consistancy();
......
......@@ -63,12 +63,14 @@ consvar_t cv_masterserver_token = CVAR_INIT
static int hms_started;
static boolean hms_args_checked;
#ifndef NO_IPV6
static boolean hms_allow_ipv6;
#endif
static boolean hms_allow_ipv4;
static char *hms_api;
#ifdef HAVE_THREADS
static I_mutex hms_api_mutex;
#endif
static char *hms_server_token;
#ifndef NO_IPV6
......@@ -83,6 +85,7 @@ struct HMS_buffer
char *buffer;
int needle;
int end;
char *errbuf;
};
static void
......@@ -134,7 +137,19 @@ HMS_on_read (char *s, size_t _1, size_t n, void *userdata)
return n;
}
static struct HMS_buffer *
static void HMS_check_args_once(void)
{
if (hms_args_checked)
return;
#ifndef NO_IPV6
hms_allow_ipv6 = !M_CheckParm("-noipv6");
#endif
hms_allow_ipv4 = !M_CheckParm("-noipv4");
hms_args_checked = true;
}
FUNCDEBUG static struct HMS_buffer *
HMS_connect (int proto, const char *format, ...)
{
va_list ap;
......@@ -144,6 +159,7 @@ HMS_connect (int proto, const char *format, ...)
size_t seek;
size_t token_length;
struct HMS_buffer *buffer;
CURLcode cc;
#ifdef NO_IPV6
if (proto == PROTO_V6)
......@@ -152,7 +168,6 @@ HMS_connect (int proto, const char *format, ...)
if (! hms_started)
{
hms_allow_ipv6 = !M_CheckParm("-noipv6");
if (curl_global_init(CURL_GLOBAL_ALL) != 0)
{
Contact_error();
......@@ -186,9 +201,7 @@ HMS_connect (int proto, const char *format, ...)
token_length = 0;
}
#ifdef HAVE_THREADS
I_lock_mutex(&hms_api_mutex);
#endif
init_user_agent_once();
......@@ -200,9 +213,7 @@ HMS_connect (int proto, const char *format, ...)
sprintf(url, "%s/", hms_api);
#ifdef HAVE_THREADS
I_unlock_mutex(hms_api_mutex);
#endif
va_start (ap, format);
seek += vsprintf(&url[seek], format, ap);
......@@ -218,33 +229,65 @@ HMS_connect (int proto, const char *format, ...)
buffer->end = DEFAULT_BUFFER_SIZE;
buffer->buffer = malloc(buffer->end);
buffer->needle = 0;
buffer->errbuf = malloc(CURL_ERROR_SIZE);
buffer->errbuf[0] = 0x00;
cc = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer->errbuf);
if (cv_masterserver_debug.value)
{
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_STDERR, logstream);
cc = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
cc = curl_easy_setopt(curl, CURLOPT_STDERR, logstream);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
}
cc = curl_easy_setopt(curl, CURLOPT_URL, url);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
cc = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
#ifndef NO_IPV6
if (proto == PROTO_V6 || (proto == PROTO_ANY && !hms_allow_ipv4))
{
cc = curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
if (M_CheckParm("-bindaddr6") && M_IsNextParm())
{
cc = curl_easy_setopt(curl, CURLOPT_INTERFACE, M_GetNextParm());
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
}
}
if (proto == PROTO_V4 || (proto == PROTO_ANY && !hms_allow_ipv6))
#endif
{
cc = curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
if (M_CheckParm("-bindaddr") && M_IsNextParm())
{
curl_easy_setopt(curl, CURLOPT_INTERFACE, M_GetNextParm());
cc = curl_easy_setopt(curl, CURLOPT_INTERFACE, M_GetNextParm());
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
}
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
cc = curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
#ifndef NO_IPV6
if (proto == PROTO_V6)
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
if (proto == PROTO_V4)
#endif
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
cc = curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 30L);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
cc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer);
cc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
curl_easy_setopt(curl, CURLOPT_USERAGENT, hms_useragent);
cc = curl_easy_setopt(curl, CURLOPT_USERAGENT, hms_useragent);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
curl_free(quack_token);
free(url);
......@@ -266,15 +309,15 @@ HMS_do (struct HMS_buffer *buffer)
{
Contact_error();
Blame(
"From curl_easy_perform: %s\n",
curl_easy_strerror(cc)
"From curl_easy_perform: %s\n", buffer->errbuf
);
return 0;
}
buffer->buffer[buffer->needle] = '\0';
curl_easy_getinfo(buffer->curl, CURLINFO_RESPONSE_CODE, &status);
cc = curl_easy_getinfo(buffer->curl, CURLINFO_RESPONSE_CODE, &status);
if (cc != CURLE_OK) I_OutputMsg("libcurl: %s\n", buffer->errbuf);
if (status != 200)
{
......@@ -326,6 +369,8 @@ HMS_fetch_rooms (int joining, int query_id)
(void)query_id;
HMS_check_args_once();
hms = HMS_connect(PROTO_ANY, "rooms");
if (! hms)
......@@ -355,7 +400,6 @@ HMS_fetch_rooms (int joining, int query_id)
*/
if (joining || id_no != 0)
{
#ifdef HAVE_THREADS
I_lock_mutex(&ms_QueryId_mutex);
{
if (query_id != ms_QueryId)
......@@ -365,7 +409,6 @@ HMS_fetch_rooms (int joining, int query_id)
if (! doing_shit)
break;
#endif
room_list[i].header.buffer[0] = 1;
......@@ -389,9 +432,7 @@ HMS_fetch_rooms (int joining, int query_id)
if (doing_shit)
{
#ifdef HAVE_THREADS
I_lock_mutex(&m_menu_mutex);
#endif
{
for (i = 0; room_list[i].header.buffer[0]; i++)
{
......@@ -403,9 +444,7 @@ HMS_fetch_rooms (int joining, int query_id)
}
}
}
#ifdef HAVE_THREADS
I_unlock_mutex(m_menu_mutex);
#endif
}
}
else
......@@ -420,18 +459,15 @@ int
HMS_register (void)
{
struct HMS_buffer *hms;
int ok;
int ok = 0;
char post[256];
char *title;
hms = HMS_connect(PROTO_V4, "rooms/%d/register", ms_RoomId);
HMS_check_args_once();
if (! hms)
return 0;
title = curl_easy_escape(hms->curl, cv_servername.string, 0);
title = curl_easy_escape(NULL, cv_servername.string, 0);
snprintf(post, sizeof post,
"port=%d&"
......@@ -447,6 +483,13 @@ HMS_register (void)
curl_free(title);
if (hms_allow_ipv4)
{
hms = HMS_connect(PROTO_V4, "rooms/%d/register", cv_masterserver_room_id.value);
if (! hms)
return 0;
curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post);
ok = HMS_do(hms);
......@@ -457,12 +500,13 @@ HMS_register (void)
}
HMS_end(hms);
}
#ifndef NO_IPV6
if (!hms_allow_ipv6)
return ok;
hms = HMS_connect(PROTO_V6, "rooms/%d/register", ms_RoomId);
hms = HMS_connect(PROTO_V6, "rooms/%d/register", cv_masterserver_room_id.value);
if (! hms)
return 0;
......@@ -486,8 +530,12 @@ int
HMS_unlist (void)
{
struct HMS_buffer *hms;
int ok;
int ok = 0;
HMS_check_args_once();
if (hms_server_token && hms_allow_ipv4)
{
hms = HMS_connect(PROTO_V4, "servers/%s/unlist", hms_server_token);
if (! hms)
......@@ -500,6 +548,7 @@ HMS_unlist (void)
HMS_end(hms);
free(hms_server_token);
}
#ifndef NO_IPV6
if (hms_server_token_ipv6 && hms_allow_ipv6)
......@@ -526,18 +575,14 @@ int
HMS_update (void)
{
struct HMS_buffer *hms;
int ok;
int ok = 0;
char post[256];
char *title;
hms = HMS_connect(PROTO_V4, "servers/%s/update", hms_server_token);
if (! hms)
return 0;
title = curl_easy_escape(hms->curl, cv_servername.string, 0);
HMS_check_args_once();
title = curl_easy_escape(NULL, cv_servername.string, 0);
snprintf(post, sizeof post,
"title=%s",
......@@ -546,10 +591,18 @@ HMS_update (void)
curl_free(title);
if (hms_server_token && hms_allow_ipv4)
{
hms = HMS_connect(PROTO_V4, "servers/%s/update", hms_server_token);
if (! hms)
return 0;
curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post);
ok = HMS_do(hms);
HMS_end(hms);
}
#ifndef NO_IPV6
if (hms_server_token_ipv6 && hms_allow_ipv6)
......@@ -577,6 +630,8 @@ HMS_list_servers (void)
char *list;
char *p;
HMS_check_args_once();
hms = HMS_connect(PROTO_ANY, "servers");
if (! hms)
......@@ -622,6 +677,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id)
int i;
HMS_check_args_once();
(void)query_id;
if (room_number > 0)
......@@ -668,7 +725,6 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id)
if (address && port && title && version)
{
#ifdef HAVE_THREADS
I_lock_mutex(&ms_QueryId_mutex);
{
if (query_id != ms_QueryId)
......@@ -678,7 +734,6 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id)
if (! doing_shit)
break;
#endif
if (strcmp(version, local_version) == 0)
{
......@@ -733,6 +788,8 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size)
char *version;
char *version_name;
HMS_check_args_once();
hms = HMS_connect(PROTO_ANY, "versions/%d", MODID);
if (! hms)
......@@ -765,16 +822,12 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size)
void
HMS_set_api (char *api)
{
#ifdef HAVE_THREADS
I_lock_mutex(&hms_api_mutex);
#endif
{
free(hms_api);
hms_api = api;
}
#ifdef HAVE_THREADS
I_unlock_mutex(hms_api_mutex);
#endif
}
#endif/*MASTERSERVER*/
......
......@@ -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.
......@@ -32,7 +32,6 @@
#define INETPACKETLENGTH 1024
extern INT16 hardware_MAXPACKETLENGTH;
extern INT32 net_bandwidth; // in byte/s
#if defined(_MSC_VER)
#pragma pack(1)
......@@ -40,39 +39,11 @@ extern INT32 net_bandwidth; // in byte/s
typedef struct
{
/// Supposed to be DOOMCOM_ID
INT32 id;
/// SRB2 executes an INT32 to execute commands.
INT16 intnum;
/// Communication between SRB2 and the driver.
/// Is CMD_SEND or CMD_GET.
INT16 command;
/// Is dest for send, set by get (-1 = no packet).
INT16 remotenode;
/// Number of bytes in doomdata to be sent
INT16 datalength;
/// Info common to all nodes.
/// Console is always node 0.
INT16 numnodes;
/// Flag: 1 = no duplication, 2-5 = dup for slow nets.
INT16 ticdup;
/// Flag: 1 = send a backup tic in every packet.
INT16 extratics;
/// kind of game
INT16 gametype;
/// Flag: -1 = new game, 0-5 = load savegame
INT16 savegame;
/// currect map
INT16 map;
/// Info specific to this node.
INT16 consoleplayer;
/// Number of "slots": the highest player number in use plus one.
INT16 numslots;
/// The packet data to be sent.
char data[MAXPACKETLENGTH];
} ATTRPACK doomcom_t;
......@@ -81,24 +52,28 @@ typedef struct
#pragma pack()
#endif
/** \brief Number of connected nodes.
*/
extern INT16 numnetnodes;
/** \brief Number of "slots": the highest player number in use plus one.
*/
extern INT16 numslots;
/** \brief Flag: 1 = send a backup tic in every packet.
*/
extern INT16 extratics;
extern doomcom_t *doomcom;
/** \brief return packet in doomcom struct
*/
extern boolean (*I_NetGet)(void);
/** \brief ask to driver if there is data waiting
*/
extern boolean (*I_NetCanGet)(void);
/** \brief send packet within doomcom struct
*/
extern void (*I_NetSend)(void);
/** \brief ask to driver if all is ok to send data now
*/
extern boolean (*I_NetCanSend)(void);
/** \brief close a connection
\param nodenum node to be closed
......