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

Target

Select target project
  • KartKrew/Kart-Public
  • SteelT/Kart-Public
  • SSNTails/Kart-Public
  • mazmazz_/Kart-Public
  • james/Kart-Public
  • Latius/Kart-Public
  • alphaRexJames/Kart-Public
  • heyjoeway/Kart-Public
  • Namolos/Kart-Public
  • SinnamonLat/Kart-Public
  • filpAM/Kart-Public
  • wolfy852/Kart-Public
  • bird/Kart-Public
  • TehRealSalt/Kart-Public
  • Snu/Kart-Public
  • Tyron/Kart-Public
  • kimmy/Kart-Public
  • Spice/Kart-Public
  • Callmore/Kart-Public
  • JugadorXEI/Kart-Public
  • Fafabis/Kart-Public
  • cspotcode/Kart-Public
  • Lonsfor_/Kart-Public
  • brokenspleentec/Kart-Public
  • minenice/Kart-Public
  • Lighto97/Kart-Public
  • X.organic/Kart-Public
  • Superjustinbros/srb2kart-custom-color-expansion
  • Galactice/Kart-Public
  • haya_/Kart-Public
  • QuantumToasted/Kart-Public
  • Indev/Kart-Public
  • chreas/kart-public-vr
  • alufolie91/Kart-Public2
  • Alam/Kart-Public
  • koi/Kart-Public
  • Alam/kart-public-vr
  • Hanicef/Kart-Public
  • hero0fnin/kart-public-batocera-edit
  • NepDisk/Kart-Public
  • Nep2Disk/Kart-Public
41 results
Show changes
Commits on Source (292)
......@@ -42,12 +42,12 @@ jobs:
paths:
- /var/cache/apt/archives
- checkout
- run:
name: Compile without network support and BLUA
command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1
- run:
name: wipe build
command: make -C src LINUX=1 cleandep
#- run:
# name: Compile without network support and BLUA
# command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1
#- run:
# name: wipe build
# command: make -C src LINUX=1 cleandep
- run:
name: rebuild depend
command: make -C src LINUX=1 clean
......
......@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
# DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string.
# Version change is fine.
project(SRB2
VERSION 1.4
VERSION 1.6
LANGUAGES C)
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
......
......@@ -14,7 +14,6 @@ set(SRB2_ASSET_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/installer"
set(SRB2_ASSET_HASHED
"srb2.srb;\
patch.kart;\
gfx.kart;\
textures.kart;\
chars.kart;\
......
......@@ -520,6 +520,7 @@ if(${SRB2_CONFIG_USEASM})
endif()
set(SRB2_USEASM ON)
add_definitions(-DUSEASM)
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -msse3 -mfpmath=sse)
else()
set(SRB2_USEASM OFF)
add_definitions(-DNONX86 -DNORUSEASM)
......@@ -552,10 +553,6 @@ add_definitions(-DCMAKECONFIG)
add_subdirectory(sdl)
if(${CMAKE_SYSTEM} MATCHES Windows)
add_subdirectory(win32)
endif()
if(NOT ${SRB2_SDL2_AVAILABLE} AND NOT ${SRB2_WIN32_AVAILABLE})
message(FATAL_ERROR "There are no targets available to build an SRB2Kart executable. :(")
endif()
......@@ -340,7 +340,7 @@ OPTS += -DCOMPVERSION
ifndef NONX86
ifndef GCC29
ARCHOPTS?=-march=pentium
ARCHOPTS?=-msse3 -mfpmath=sse
else
ARCHOPTS?=-mpentium
endif
......
......@@ -60,7 +60,7 @@ static void DumpVector(const void* b, int n, size_t size, DumpState* D)
static void DumpString(const TString* s, DumpState* D)
{
if (s==NULL || getstr(s)==NULL)
if (s==NULL)
{
size_t size=0;
DumpVar(size,D);
......
......@@ -353,6 +353,40 @@ size_t COM_CheckParm(const char *check)
return 0;
}
/** \brief COM_CheckParm, but checks only the start of each argument.
* E.g. checking for "-no" would match "-noerror" too.
*/
size_t COM_CheckPartialParm(const char *check)
{
int len;
size_t i;
len = strlen(check);
for (i = 1; i < com_argc; i++)
{
if (strncasecmp(check, com_argv[i], len) == 0)
return i;
}
return 0;
}
/** Find the first argument that starts with a hyphen (-).
* \return The index of the argument, or 0
* if there are no such arguments.
*/
size_t COM_FirstOption(void)
{
size_t i;
for (i = 1; i < com_argc; i++)
{
if (com_argv[i][0] == '-')/* options start with a hyphen */
return i;
}
return 0;
}
/** Parses a string into command-line tokens.
*
* \param ptext A null-terminated string. Does not need to be
......@@ -1725,6 +1759,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if (var->PossibleValue[max].value == var->value)
currentindice = max;
// The following options will NOT handle netsyncing.
if (var == &cv_chooseskin)
{
// Special case for the chooseskin variable, used only directly from the menu
......@@ -1783,28 +1818,7 @@ void CV_AddValue(consvar_t *var, INT32 increment)
}
else if (var == &cv_kartspeed)
{
INT32 maxspeed = (M_SecretUnlocked(SECRET_HARDSPEED) ? 2 : 1);
// Special case for the kartspeed variable, used only directly from the menu to prevent selecting hard mode
if (increment > 0) // Going up!
{
newvalue = var->value + 1;
if (newvalue > maxspeed)
newvalue = 0;
var->value = newvalue;
var->string = var->PossibleValue[var->value].strvalue;
var->func();
return;
}
else if (increment < 0) // Going down!
{
newvalue = var->value - 1;
if (newvalue < 0)
newvalue = maxspeed;
var->value = newvalue;
var->string = var->PossibleValue[var->value].strvalue;
var->func();
return;
}
max = (M_SecretUnlocked(SECRET_HARDSPEED) ? 3 : 2);
}
#ifdef PARANOIA
if (currentindice == -1)
......@@ -1895,6 +1909,45 @@ static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr)
return true;
}
// Block the Xbox DInput default axes and reset to the current defaults
static boolean CV_FilterJoyAxisVars2(consvar_t *v, const char *valstr)
{
if (!stricmp(v->name, "joyaxis_turn") && !stricmp(valstr, "X-Axis"))
return false;
if (!stricmp(v->name, "joyaxis2_turn") && !stricmp(valstr, "X-Axis"))
return false;
if (!stricmp(v->name, "joyaxis3_turn") && !stricmp(valstr, "X-Axis"))
return false;
if (!stricmp(v->name, "joyaxis4_turn") && !stricmp(valstr, "X-Axis"))
return false;
if (!stricmp(v->name, "joyaxis_aim") && !stricmp(valstr, "Y-Axis"))
return false;
if (!stricmp(v->name, "joyaxis2_aim") && !stricmp(valstr, "Y-Axis"))
return false;
if (!stricmp(v->name, "joyaxis3_aim") && !stricmp(valstr, "Y-Axis"))
return false;
if (!stricmp(v->name, "joyaxis4_aim") && !stricmp(valstr, "Y-Axis"))
return false;
if (!stricmp(v->name, "joyaxis_fire") && !stricmp(valstr, "None"))
return false;
if (!stricmp(v->name, "joyaxis2_fire") && !stricmp(valstr, "None"))
return false;
if (!stricmp(v->name, "joyaxis3_fire") && !stricmp(valstr, "None"))
return false;
if (!stricmp(v->name, "joyaxis4_fire") && !stricmp(valstr, "None"))
return false;
if (!stricmp(v->name, "joyaxis_drift") && !stricmp(valstr, "None"))
return false;
if (!stricmp(v->name, "joyaxis2_drift") && !stricmp(valstr, "None"))
return false;
if (!stricmp(v->name, "joyaxis3_drift") && !stricmp(valstr, "None"))
return false;
if (!stricmp(v->name, "joyaxis4_drift") && !stricmp(valstr, "None"))
return false;
return true;
}
static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr)
{
// True means allow the CV change, False means block it
......@@ -1904,6 +1957,13 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr)
if (!(v->flags & CV_SAVE))
return true;
if (GETMAJOREXECVERSION(cv_execversion.value) < 8) // 8 = 1.4
{
if (!stricmp(v->name, "masterserver") // Replaces a hack in MasterServer_OnChange for the original SRB2 MS.
|| !stricmp(v->name, "gamma")) // Too easy to accidentially change in prior versions.
return false;
}
if (GETMAJOREXECVERSION(cv_execversion.value) < 2) // 2 = 1.0.2
{
#if 0
......@@ -1921,6 +1981,13 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr)
return false;
}
if (GETMAJOREXECVERSION(cv_execversion.value) < 10) // 10 = 1.6
{
// axis defaults changed again to SDL game controllers
if (!CV_FilterJoyAxisVars2(v, valstr))
return false;
}
return true;
}
......
......@@ -37,6 +37,8 @@ size_t COM_Argc(void);
const char *COM_Argv(size_t arg); // if argv > argc, returns empty string
char *COM_Args(void);
size_t COM_CheckParm(const char *check); // like M_CheckParm :)
size_t COM_CheckPartialParm(const char *check);
size_t COM_FirstOption(void);
// match existing command or NULL
const char *COM_CompleteCommand(const char *partial, INT32 skips);
......
......@@ -23,7 +23,7 @@
#define ASSET_HASH_CHARS_KART "${SRB2_ASSET_chars.kart_HASH}"
#define ASSET_HASH_MAPS_KART "${SRB2_ASSET_maps.kart_HASH}"
#ifdef USE_PATCH_KART
#define ASSET_HASH_PATCH_KART "${SRB2_ASSET_patch.kart_HASH}"
#define ASSET_HASH_PATCH_KART "00000000000000000000000000000000"
#endif
#define SRB2_COMP_REVISION "${SRB2_COMP_REVISION}"
......@@ -38,7 +38,9 @@
* Last updated 2018 / 12 / 23 - SRB2 v2.1.22 - patch.dta
* Last updated 2019 / 01 / 18 - Kart v1.0.2 - Main assets
* Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart
* Last updated 2022 / 07 / 17 - Kart v1.4 - Main assets
* Last updated 2022 / 08 / 16 - Kart v1.4 - Main assets
* Last updated 2022 / 08 / 19 - Kart v1.5 - gfx.kart
* Last updated 2022 / 11 / 01 - Kart v1.6 - gfx.kart, maps.kart
*/
// Base SRB2 hashes
......@@ -48,10 +50,10 @@
#endif
// SRB2Kart-specific hashes
#define ASSET_HASH_GFX_KART "d25790c21cfcb6c3c02c05ba3eb7f820"
#define ASSET_HASH_GFX_KART "06f86ee16136eb8a7043b15001797034"
#define ASSET_HASH_TEXTURES_KART "abb53d56aba47c3a8cb0f764da1c8b80"
#define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964"
#define ASSET_HASH_MAPS_KART "ba4e25503a34dca3cf6142e031691ceb"
#define ASSET_HASH_MAPS_KART "d051e55141ba736582228c456953cd98"
#ifdef USE_PATCH_KART
#define ASSET_HASH_PATCH_KART "00000000000000000000000000000000"
#endif
......
......@@ -1117,6 +1117,7 @@ typedef enum
CL_DOWNLOADFILES,
CL_ASKJOIN,
CL_LOADFILES,
CL_SETUPFILES,
CL_WAITJOINRESPONSE,
#ifdef JOININGAME
CL_DOWNLOADSAVEGAME,
......@@ -1129,6 +1130,7 @@ typedef enum
CL_PREPAREHTTPFILES,
CL_DOWNLOADHTTPFILES,
#endif
CL_LEGACYREQUESTFAILED,
} cl_mode_t;
static void GetPackets(void);
......@@ -1226,8 +1228,12 @@ static inline void CL_DrawConnectionStatus(void)
#endif
case CL_ASKFULLFILELIST:
case CL_CONFIRMCONNECT:
case CL_LEGACYREQUESTFAILED:
cltext = "";
break;
case CL_SETUPFILES:
cltext = M_GetText("Configuring addons...");
break;
case CL_ASKJOIN:
case CL_WAITJOINRESPONSE:
if (serverisfull)
......@@ -1329,8 +1335,10 @@ static inline void CL_DrawConnectionStatus(void)
strncpy(tempname, filename, sizeof(tempname)-1);
}
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-30, 0,
va(M_GetText("%s downloading"), ((cl_mode == CL_DOWNLOADHTTPFILES) ? "\x82""HTTP" : "\x85""Direct")));
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-22, V_YELLOWMAP,
va(M_GetText("Downloading \"%s\""), tempname));
va(M_GetText("\"%s\""), tempname));
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
......@@ -1933,7 +1941,16 @@ static void CL_LoadReceivedSavegame(void)
#ifndef NONET
static void SendAskInfo(INT32 node)
{
const tic_t asktime = I_GetTime();
tic_t asktime;
if (node != 0 && node != BROADCASTADDR &&
cv_rendezvousserver.string[0])
{
I_NetRequestHolePunch(node);
}
asktime = I_GetTime();
netbuffer->packettype = PT_ASKINFO;
netbuffer->u.askinfo.version = VERSION;
netbuffer->u.askinfo.time = (tic_t)LONG(asktime);
......@@ -1942,16 +1959,14 @@ static void SendAskInfo(INT32 node)
// now allowed traffic from the host to us in, so once the MS relays
// our address to the host, it'll be able to speak to us.
HSendPacket(node, false, 0, sizeof (askinfo_pak));
if (node != 0 && node != BROADCASTADDR &&
cv_rendezvousserver.string[0])
{
I_NetRequestHolePunch();
}
}
serverelem_t serverlist[MAXSERVERLIST];
UINT32 serverlistcount = 0;
UINT32 serverlistultimatecount = 0;
static boolean resendserverlistnode[MAXNETNODES];
static tic_t serverlistepoch;
static void SL_ClearServerList(INT32 connectedserver)
{
......@@ -1964,6 +1979,8 @@ static void SL_ClearServerList(INT32 connectedserver)
serverlist[i].node = 0;
}
serverlistcount = 0;
memset(resendserverlistnode, 0, sizeof resendserverlistnode);
}
static UINT32 SL_SearchServer(INT32 node)
......@@ -1980,6 +1997,8 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
{
UINT32 i;
resendserverlistnode[node] = false;
// search if not already on it
i = SL_SearchServer(node);
if (i == UINT32_MAX)
......@@ -2037,6 +2056,8 @@ void CL_QueryServerList (msg_server_t *server_list)
CL_UpdateServerList();
serverlistepoch = I_GetTime();
for (i = 0; server_list[i].header.buffer[0]; i++)
{
// Make sure MS version matches our own, to
......@@ -2049,19 +2070,42 @@ void CL_QueryServerList (msg_server_t *server_list)
if (node == -1)
break; // no more node free
SendAskInfo(node);
// Force close the connection so that servers can't eat
// up nodes forever if we never get a reply back from them
// (usually when they've not forwarded their ports).
//
// Don't worry, we'll get in contact with the working
// servers again when they send SERVERINFO to us later!
//
// (Note: as a side effect this probably means every
// server in the list will probably be using the same node (e.g. node 1),
// not that it matters which nodes they use when
// the connections are closed afterwards anyway)
// -- Monster Iestyn 12/11/18
Net_CloseConnection(node|FORCECLOSE);
resendserverlistnode[node] = true;
// Leave this node open. It'll be closed if the
// request times out (CL_TimeoutServerList).
}
}
serverlistultimatecount = i;
}
#define SERVERLISTRESENDRATE NEWTICRATE
void CL_TimeoutServerList(void)
{
if (netgame && serverlistultimatecount > serverlistcount)
{
const tic_t timediff = I_GetTime() - serverlistepoch;
const tic_t timetoresend = timediff % SERVERLISTRESENDRATE;
const boolean timedout = timediff > connectiontimeout;
if (timedout || (timediff > 0 && timetoresend == 0))
{
INT32 node;
for (node = 1; node < MAXNETNODES; ++node)
{
if (resendserverlistnode[node])
{
if (timedout)
Net_CloseConnection(node|FORCECLOSE);
else
SendAskInfo(node);
}
}
if (timedout)
serverlistultimatecount = serverlistcount;
}
}
}
......@@ -2084,6 +2128,10 @@ static void M_ConfirmConnect(event_t *ev)
{
cl_mode = CL_DOWNLOADFILES;
}
else
{
cl_mode = CL_LEGACYREQUESTFAILED;
}
}
#ifdef HAVE_CURL
else
......@@ -2239,6 +2287,10 @@ static boolean CL_FinishedFileList(void)
{
cl_mode = CL_DOWNLOADFILES;
}
else
{
cl_mode = CL_LEGACYREQUESTFAILED;
}
}
#endif
}
......@@ -2425,8 +2477,28 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
cl_mode = CL_LOADFILES;
break;
case CL_LEGACYREQUESTFAILED:
{
CONS_Printf(M_GetText("Legacy downloader request packet failed.\n"));
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText(
"The direct download encountered an error.\n"
"See the logfile for more info.\n"
"\n"
"Press ESC\n"
), NULL, MM_NOTHING);
return false;
}
case CL_LOADFILES:
if (CL_LoadServerFiles())
if (CL_LoadServerFiles())
cl_mode = CL_SETUPFILES;
break;
case CL_SETUPFILES:
if (P_PartialAddGetStage() < 0 || P_MultiSetupWadFiles(false))
{
*asksent = 0; //This ensure the first join ask is right away
firstconnectattempttime = I_GetTime();
......@@ -2632,7 +2704,11 @@ static void CL_ConnectToServer(void)
#else
if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL))
#endif
{
if (P_PartialAddGetStage() >= 0)
P_MultiSetupWadFiles(true); // in case any partial adds were done
return;
}
if (server)
{
......@@ -2829,6 +2905,12 @@ void D_LoadBan(boolean warning)
address = strtok(buffer, " /\t\r\n");
mask = strtok(NULL, " \t\r\n");
if (!address)
{
malformed = true;
continue;
}
if (i == 0 && !strncmp(address, "BANFORMAT", 9))
{
if (mask)
......@@ -3986,7 +4068,7 @@ void D_QuitNetGame(void)
if (nodeingame[i])
HSendPacket(i, true, 0, 0);
#ifdef MASTERSERVER
if (serverrunning && cv_advertise.value)
if (serverrunning && netgame && cv_advertise.value) // see mserv.c Online()
UnregisterServer();
#endif
}
......@@ -4334,6 +4416,11 @@ static void HandleConnect(SINT8 node)
// If a server filled out, then it'd overwrite the host and turn everyone into weird husks.....
// It's too much effort to legimately fix right now. Just prevent it from reaching that state.
UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value);
UINT8 connectedplayers = 0;
for (UINT8 i = dedicated ? 1 : 0; i < MAXPLAYERS; i++)
if (playernode[i] != UINT8_MAX) // We use this to count players because it is affected by SV_AddWaitingPlayers when more than one client joins on the same tic, unlike playeringame and D_NumPlayers. UINT8_MAX denotes no node for that player
connectedplayers++;
if (bannednode && bannednode[node].banid != SIZE_MAX)
{
......@@ -4391,7 +4478,7 @@ static void HandleConnect(SINT8 node)
{
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment."));
}
else if (D_NumPlayers() >= maxplayers)
else if (connectedplayers >= maxplayers)
{
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), maxplayers));
}
......@@ -4399,7 +4486,7 @@ static void HandleConnect(SINT8 node)
{
SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
}
else if (netgame && D_NumPlayers() + netbuffer->u.clientcfg.localplayers > maxplayers)
else if (netgame && connectedplayers + netbuffer->u.clientcfg.localplayers > maxplayers)
{
SV_SendRefuse(node, va(M_GetText("Number of local players\nwould exceed maximum: %d"), maxplayers));
}
......@@ -5891,23 +5978,20 @@ boolean TryRunTics(tic_t realtics)
if (ticking)
{
if (advancedemo)
D_StartTitle();
else
// run the count * tics
while (neededtic > gametic)
{
DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
// run the count * tics
while (neededtic > gametic)
{
DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
G_Ticker((gametic % NEWTICRATERATIO) == 0);
ExtraDataTicker();
gametic++;
consistancy[gametic%TICQUEUE] = Consistancy();
G_Ticker((gametic % NEWTICRATERATIO) == 0);
ExtraDataTicker();
gametic++;
consistancy[gametic%TICQUEUE] = Consistancy();
// Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
break;
}
// Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
break;
}
}
else
{
......@@ -5929,53 +6013,77 @@ boolean TryRunTics(tic_t realtics)
static INT32 pingtimeout[MAXPLAYERS];
#define PINGKICK_TICQUEUE 2
#define PINGKICK_LIMIT 1
static inline void PingUpdate(void)
{
INT32 i;
boolean laggers[MAXPLAYERS];
UINT8 numlaggers = 0;
memset(laggers, 0, sizeof(boolean) * MAXPLAYERS);
UINT8 pingkick[MAXPLAYERS];
UINT8 nonlaggers = 0;
memset(pingkick, 0, sizeof(pingkick));
netbuffer->packettype = PT_PING;
//check for ping limit breakage.
if (cv_maxping.value)
//if (cv_maxping.value) -- always check for TICQUEUE overrun
{
for (i = 1; i < MAXPLAYERS; i++)
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
if (!playeringame[i] || P_IsLocalPlayer(&players[i])) // should be P_IsMachineLocalPlayer for DRRR
{
if (players[i].jointime > 30 * TICRATE)
laggers[i] = true;
numlaggers++;
pingtimeout[i] = 0;
continue;
}
if ((maketic + 5) >= nettics[playernode[i]] + (TICQUEUE-(2*TICRATE)))
{
// Anyone who's gobbled most of the TICQUEUE and is likely to halt the server the next few times this runs has to die *right now*. (See also NetUpdate)
pingkick[i] = PINGKICK_TICQUEUE;
}
else if ((cv_maxping.value)
&& (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
{
if (players[i].jointime > 10 * TICRATE)
{
pingkick[i] = PINGKICK_LIMIT;
}
}
else
pingtimeout[i] = 0;
{
nonlaggers++;
// you aren't lagging, but you aren't free yet. In case you'll keep spiking, we just make the timer go back down. (Very unstable net must still get kicked).
if (pingtimeout[i] > 0)
pingtimeout[i]--;
}
}
//kick lagging players... unless everyone but the server's ping sucks.
//in that case, it is probably the server's fault.
if (numlaggers < D_NumPlayers() - 1)
// Always kick TICQUEUE-overrunners, too.
{
for (i = 1; i < MAXPLAYERS; i++)
UINT8 minimumkicklevel = (nonlaggers > 0) ? PINGKICK_LIMIT : PINGKICK_TICQUEUE;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && laggers[i])
{
pingtimeout[i]++;
if (pingtimeout[i] > cv_pingtimeout.value) // ok your net has been bad for too long, you deserve to die.
{
XBOXSTATIC char buf[2];
XBOXSTATIC char buf[2];
pingtimeout[i] = 0;
if (!playeringame[i] || pingkick[i] < minimumkicklevel)
continue;
buf[0] = (char)i;
buf[1] = KICK_MSG_PING_HIGH;
SendNetXCmd(XD_KICK, &buf, 2);
}
if (pingkick[i] == PINGKICK_LIMIT)
{
// Don't kick on ping alone if we haven't reached our threshold yet.
if (++pingtimeout[i] < cv_pingtimeout.value)
continue;
}
else // you aren't lagging, but you aren't free yet. In case you'll keep spiking, we just make the timer go back down. (Very unstable net must still get kicked).
pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1);
pingtimeout[i] = 0;
buf[0] = (char)i;
buf[1] = KICK_MSG_PING_HIGH;
SendNetXCmd(XD_KICK, &buf, 2);
}
}
}
......@@ -6000,9 +6108,12 @@ static inline void PingUpdate(void)
if (nodeingame[i])
HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
pingmeasurecount = 1; //Reset count
pingmeasurecount = 0; //Reset count
}
#undef PINGKICK_DANGER
#undef PINGKICK_LIMIT
static tic_t gametime = 0;
static void UpdatePingTable(void)
......@@ -6093,6 +6204,9 @@ FILESTAMP
SV_FileSendTicker();
}
// If a tree falls in the forest but nobody is around to hear it, does it make a tic?
#define DEDICATEDIDLETIME (10*TICRATE)
void NetUpdate(void)
{
static tic_t resptime = 0;
......@@ -6105,6 +6219,55 @@ void NetUpdate(void)
if (realtics <= 0) // nothing new to update
return;
#ifdef DEDICATEDIDLETIME
if (server && dedicated && gamestate == GS_LEVEL)
{
static tic_t dedicatedidle = 0;
for (i = 1; i < MAXNETNODES; ++i)
if (nodeingame[i])
{
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 == 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;
}
}
}
#endif
if (realtics > 5)
{
if (server)
......@@ -6147,7 +6310,7 @@ FILESTAMP
}
else
{
if (!demo.playback)
if (!demo.playback && realtics > 0)
{
INT32 counts;
......@@ -6171,6 +6334,7 @@ FILESTAMP
// Do not make tics while resynching
if (counts != -666)
{
// See also PingUpdate
if (maketic + counts >= firstticstosend + TICQUEUE)
counts = firstticstosend+TICQUEUE-maketic-1;
......@@ -6221,6 +6385,9 @@ INT32 D_NumPlayers(void)
tic_t GetLag(INT32 node)
{
// If the client has caught up to the server -- say, during a wipe -- lag is meaningless.
if (nettics[node] > gametic)
return 0;
return gametic - nettics[node];
}
......
......@@ -502,6 +502,7 @@ typedef struct
extern serverelem_t serverlist[MAXSERVERLIST];
extern UINT32 serverlistcount;
extern UINT32 serverlistultimatecount;
extern INT32 mapchangepending;
// Points inside doomcom
......@@ -594,6 +595,7 @@ void CL_ClearPlayer(INT32 playernum);
void CL_RemovePlayer(INT32 playernum, INT32 reason);
void CL_QueryServerList(msg_server_t *list);
void CL_UpdateServerList(void);
void CL_TimeoutServerList(void);
// Is there a game running
boolean Playing(void);
......
......@@ -144,7 +144,6 @@ boolean sound_disabled = false;
boolean digital_disabled = false;
#endif
boolean advancedemo;
#ifdef DEBUGFILE
INT32 debugload = 0;
#endif
......@@ -799,7 +798,11 @@ void D_SRB2Loop(void)
if (!singletics)
{
INT64 elapsed = (INT64)(finishprecise - enterprecise);
if (elapsed > 0 && (INT64)capbudget > elapsed)
// in the case of "match refresh rate" + vsync, don't sleep at all
const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0;
if (elapsed > 0 && (INT64)capbudget > elapsed && !vsync_with_match_refresh)
{
I_SleepDuration(capbudget - (finishprecise - enterprecise));
}
......@@ -811,15 +814,6 @@ void D_SRB2Loop(void)
}
}
//
// D_AdvanceDemo
// Called after each demo or intro demosequence finishes
//
void D_AdvanceDemo(void)
{
advancedemo = true;
}
// =========================================================================
// D_SRB2Main
// =========================================================================
......@@ -879,7 +873,6 @@ void D_StartTitle(void)
//demosequence = -1;
gametype = GT_RACE; // SRB2kart
paused = false;
advancedemo = false;
F_StartTitleScreen();
// Reset the palette -- SRB2Kart: actually never mind let's do this in the middle of every fade
......@@ -1077,7 +1070,7 @@ void D_SRB2Main(void)
// Print GPL notice for our console users (Linux)
CONS_Printf(
"\n\nSonic Robo Blast 2 Kart\n"
"Copyright (C) 1998-2018 by Kart Krew & STJr\n\n"
"Copyright (C) 1998-2022 by Kart Krew & STJr\n\n"
"This program comes with ABSOLUTELY NO WARRANTY.\n\n"
"This is free software, and you are welcome to redistribute it\n"
"and/or modify it under the terms of the GNU General Public License\n"
......@@ -1150,6 +1143,8 @@ void D_SRB2Main(void)
{
const char *userhome = D_Home(); //Alam: path to home
FILE *tmpfile;
char testfile[MAX_WADPATH];
if (!userhome)
{
......@@ -1199,9 +1194,6 @@ void D_SRB2Main(void)
// If config isn't writable, tons of behavior will be broken.
// Fail loudly before things get confusing!
FILE *tmpfile;
char testfile[MAX_WADPATH];
snprintf(testfile, sizeof testfile, "%s" PATHSEP "file.tmp", srb2home);
testfile[sizeof testfile - 1] = '\0';
......@@ -1254,26 +1246,6 @@ void D_SRB2Main(void)
if (M_CheckParm("-server") || dedicated)
netgame = server = true;
if (M_CheckParm("-warp") && M_IsNextParm())
{
const char *word = M_GetNextParm();
char ch; // use this with sscanf to catch non-digits with
if (fastncmp(word, "MAP", 3)) // MAPxx name
pstartmap = M_MapNumber(word[3], word[4]);
else if (sscanf(word, "%d%c", &pstartmap, &ch) != 1) // a plain number
I_Error("Cannot warp to map %s (invalid map name)\n", word);
// Don't check if lump exists just yet because the wads haven't been loaded!
// Just do a basic range check here.
if (pstartmap < 1 || pstartmap > NUMMAPS)
I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
else
{
if (!M_CheckParm("-server"))
G_SetGameModified(true, true);
autostart = true;
}
}
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init();
......@@ -1339,7 +1311,7 @@ void D_SRB2Main(void)
//
// search for maps
//
for (wadnum = 4; wadnum < 6; wadnum++) // fucking arbitrary numbers
for (wadnum = 0; wadnum < mainwads; wadnum++)
{
lumpinfo = wadfiles[wadnum]->lumpinfo;
for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++)
......@@ -1453,6 +1425,23 @@ void D_SRB2Main(void)
//------------------------------------------------ COMMAND LINE PARAMS
// this must be done after loading gamedata,
// to avoid setting off the corrupted gamedata code in G_LoadGameData if a SOC with custom gamedata is added
// -- Monster Iestyn 20/02/20
if (M_CheckParm("-warp") && M_IsNextParm())
{
const char *word = M_GetNextParm();
pstartmap = G_FindMapByNameOrCode(word, 0);
if (! pstartmap)
I_Error("Cannot find a map remotely named '%s'\n", word);
else
{
if (!M_CheckParm("-server"))
G_SetGameModified(true, true);
autostart = true;
}
}
// Initialize CD-Audio
if (M_CheckParm("-usecd") && !dedicated)
I_InitCD();
......
......@@ -17,8 +17,6 @@
#include "d_event.h"
#include "w_wad.h" // for MAX_WADFILES
extern boolean advancedemo;
// make sure not to write back the config until it's been correctly loaded
extern tic_t rendergametic;
......@@ -34,7 +32,6 @@ void D_SRB2Loop(void) FUNCNORETURN;
// D_SRB2Main()
// Not a globally visible function, just included for source reference,
// calls all startup code, parses command line options.
// If not overrided by user input, calls D_AdvanceDemo.
//
void D_SRB2Main(void);
......@@ -51,7 +48,6 @@ const char *D_Home(void);
//
// BASE LEVEL
//
void D_AdvanceDemo(void);
void D_StartTitle(void);
#endif //__D_MAIN__
......@@ -75,7 +75,7 @@ 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;
void (*I_NetRequestHolePunch)(void) = NULL;
void (*I_NetRequestHolePunch)(INT32 node) = NULL;
void (*I_NetRegisterHolePunch)(void) = NULL;
boolean (*I_NetOpenSocket)(void) = NULL;
boolean (*I_Ban) (INT32 node) = NULL;
......@@ -1464,7 +1464,7 @@ void Command_Ping_f(void)
if (!server && playeringame[consoleplayer])
{
CONS_Printf("\nYour ping is %d frames (%d ms)\n", playerpingtable[consoleplayer], (INT32)(playerpingtable[i] * (1000.00f / TICRATE)));
CONS_Printf("\nYour ping is %d frames (%d ms)\n", playerpingtable[consoleplayer], (INT32)(playerpingtable[consoleplayer] * (1000.00f / TICRATE)));
}
}
......
......@@ -251,7 +251,7 @@ consvar_t cv_ingamecap = {"ingamecap", "0", CV_NETVAR, ingamecap_cons_t, NULL, 0
static CV_PossibleValue_t spectatorreentry_cons_t[] = {{0, "MIN"}, {10*60, "MAX"}, {0, NULL}};
consvar_t cv_spectatorreentry = {"spectatorreentry", "30", CV_NETVAR, spectatorreentry_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t antigrief_cons_t[] = {{20, "MIN"}, {60, "MAX"}, {0, "Off"}, {0, NULL}};
static CV_PossibleValue_t antigrief_cons_t[] = {{20, "MIN"}, {180, "MAX"}, {0, "Off"}, {0, NULL}};
consvar_t cv_antigrief = {"antigrief", "30", CV_NETVAR, antigrief_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_startinglives = {"startinglives", "3", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, startingliveslimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
......@@ -912,10 +912,18 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_driftaxis2);
CV_RegisterVar(&cv_driftaxis3);
CV_RegisterVar(&cv_driftaxis4);
CV_RegisterVar(&cv_deadzone);
CV_RegisterVar(&cv_deadzone2);
CV_RegisterVar(&cv_deadzone3);
CV_RegisterVar(&cv_deadzone4);
CV_RegisterVar(&cv_lookbackaxis);
CV_RegisterVar(&cv_lookbackaxis2);
CV_RegisterVar(&cv_lookbackaxis3);
CV_RegisterVar(&cv_lookbackaxis4);
CV_RegisterVar(&cv_xdeadzone);
CV_RegisterVar(&cv_ydeadzone);
CV_RegisterVar(&cv_xdeadzone2);
CV_RegisterVar(&cv_ydeadzone2);
CV_RegisterVar(&cv_xdeadzone3);
CV_RegisterVar(&cv_ydeadzone3);
CV_RegisterVar(&cv_xdeadzone4);
CV_RegisterVar(&cv_ydeadzone4);
// filesrch.c
CV_RegisterVar(&cv_addons_option);
......@@ -2511,127 +2519,197 @@ void D_PickVote(void)
SendNetXCmd(XD_PICKVOTE, &buf, 2);
}
static char *
ConcatCommandArgv (int start, int end)
{
char *final;
size_t size;
int i;
char *p;
size = 0;
for (i = start; i < end; ++i)
{
/*
one space after each argument, but terminating
character on final argument
*/
size += strlen(COM_Argv(i)) + 1;
}
final = ZZ_Alloc(size);
p = final;
--end;/* handle the final argument separately */
for (i = start; i < end; ++i)
{
p += sprintf(p, "%s ", COM_Argv(i));
}
/* at this point "end" is actually the last argument's position */
strcpy(p, COM_Argv(end));
return final;
}
//
// Warp to map code.
// Called either from map <mapname> console command, or idclev cheat.
//
// Largely rewritten by James.
//
static void Command_Map_f(void)
{
const char *mapname;
size_t i;
size_t first_option;
size_t option_force;
size_t option_gametype;
size_t option_encore;
const char *gametypename;
boolean newresetplayers;
boolean mustmodifygame;
INT32 newmapnum;
boolean newresetplayers, newencoremode;
INT32 newgametype = gametype;
// max length of command: map map03 -gametype race -noresetplayers -force -encore
// 1 2 3 4 5 6 7
// = 8 arg max
// i don't know whether this is intrinsic to the system or just someone being weird but
// "noresetplayers" is pretty useless for kart if it turns out this is too close to the limit
if (COM_Argc() < 2 || COM_Argc() > 8)
char * mapname;
char *realmapname = NULL;
INT32 newgametype = gametype;
boolean newencoremode = cv_kartencore.value;
INT32 d;
if (client && !IsPlayerAdmin(consoleplayer))
{
CONS_Printf(M_GetText("map <mapname> [-gametype <type> [-force]: warp to map\n"));
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
if (client && !IsPlayerAdmin(consoleplayer))
option_force = COM_CheckPartialParm("-f");
option_gametype = COM_CheckPartialParm("-g");
option_encore = COM_CheckPartialParm("-e");
newresetplayers = ! COM_CheckParm("-noresetplayers");
mustmodifygame = !( netgame || multiplayer || majormods );
if (mustmodifygame && !option_force)
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
/* May want to be more descriptive? */
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
return;
}
// internal wad lump always: map command doesn't support external files as in doom legacy
if (W_CheckNumForName(COM_Argv(1)) == LUMPERROR)
if (!newresetplayers && !cv_debug)
{
CONS_Alert(CONS_ERROR, M_GetText("Internal game level '%s' not found\n"), COM_Argv(1));
CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
return;
}
if (!(netgame || multiplayer) && !majormods)
if (option_gametype)
{
if (COM_CheckParm("-force"))
if (!multiplayer)
{
G_SetGameModified(false, true);
CONS_Printf(M_GetText(
"You can't switch gametypes in single player!\n"));
return;
}
else
else if (COM_Argc() < option_gametype + 2)/* no argument after? */
{
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
CONS_Alert(CONS_ERROR,
"No gametype name follows parameter '%s'.\n",
COM_Argv(option_gametype));
return;
}
}
newresetplayers = !COM_CheckParm("-noresetplayers");
if (!( first_option = COM_FirstOption() ))
first_option = COM_Argc();
if (!newresetplayers && !cv_debug)
if (first_option < 2)
{
CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
/* I'm going over the fucking lines and I DON'T CAREEEEE */
CONS_Printf("map <name / [MAP]code / number> [-gametype <type>] [-encore] [-force]:\n");
CONS_Printf(M_GetText(
"Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n"
"All parameters are case-insensitive and may be abbreviated.\n"));
return;
}
mapname = COM_Argv(1);
if (strlen(mapname) != 5
|| (newmapnum = M_MapNumber(mapname[3], mapname[4])) == 0)
mapname = ConcatCommandArgv(1, first_option);
newmapnum = G_FindMapByNameOrCode(mapname, &realmapname);
if (newmapnum == 0)
{
CONS_Alert(CONS_ERROR, M_GetText("Invalid level name %s\n"), mapname);
CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname);
Z_Free(mapname);
return;
}
// new gametype value
// use current one by default
i = COM_CheckParm("-gametype");
if (i)
if (mustmodifygame && option_force)
{
if (!multiplayer)
{
CONS_Printf(M_GetText("You can't switch gametypes in single player!\n"));
return;
}
newgametype = G_GetGametypeByName(COM_Argv(i+1));
if (newgametype == -1) // reached end of the list with no match
{
INT32 j = atoi(COM_Argv(i+1)); // assume they gave us a gametype number, which is okay too
if (j >= 0 && j < NUMGAMETYPES)
newgametype = (INT16)j;
}
G_SetGameModified(false, true);
}
// new encoremode value
// use cvar by default
// new gametype value
// use current one by default
if (option_gametype)
{
gametypename = COM_Argv(option_gametype + 1);
newencoremode = (boolean)cv_kartencore.value;
newgametype = G_GetGametypeByName(gametypename);
if (COM_CheckParm("-encore"))
{
if (!M_SecretUnlocked(SECRET_ENCORE) && !newencoremode)
if (newgametype == -1) // reached end of the list with no match
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
/* Did they give us a gametype number? That's okay too! */
if (isdigit(gametypename[0]))
{
d = atoi(gametypename);
if (d >= 0 && d < NUMGAMETYPES)
newgametype = d;
else
{
CONS_Alert(CONS_ERROR,
"Gametype number %d is out of range. Use a number between"
" 0 and %d inclusive. ...Or just use the name. :v\n",
d,
NUMGAMETYPES-1);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
else
{
CONS_Alert(CONS_ERROR,
"'%s' is not a gametype.\n",
gametypename);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
newencoremode = !newencoremode;
}
if (!(i = COM_CheckParm("-force")) && newgametype == gametype) // SRB2Kart
if (!option_force && newgametype == gametype) // SRB2Kart
newresetplayers = false; // if not forcing and gametypes is the same
// don't use a gametype the map doesn't support
if (cv_debug || i || cv_skipmapcheck.value)
; // The player wants us to trek on anyway. Do so.
if (cv_debug || option_force || cv_skipmapcheck.value)
fromlevelselect = false; // The player wants us to trek on anyway. Do so.
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
// Alternatively, bail if the map header is completely missing anyway.
else if (!mapheaderinfo[newmapnum-1]
|| !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
else
{
char gametypestring[32] = "Single Player";
if (multiplayer)
if (!(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
{
if (newgametype >= 0 && newgametype < NUMGAMETYPES
&& Gametype_Names[newgametype])
strcpy(gametypestring, Gametype_Names[newgametype]);
CONS_Alert(CONS_WARNING, M_GetText("Course %s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum),
(multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player"));
Z_Free(realmapname);
Z_Free(mapname);
return;
}
CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
return;
}
// Prevent warping to locked levels
......@@ -2641,11 +2719,25 @@ static void Command_Map_f(void)
if (!dedicated && M_MapLocked(newmapnum))
{
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
Z_Free(realmapname);
Z_Free(mapname);
return;
}
if (option_encore)
{
newencoremode = ! newencoremode;
if (! M_SecretUnlocked(SECRET_ENCORE) && newencoremode)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
}
}
fromlevelselect = false;
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
Z_Free(realmapname);
}
/** Receives a map command and changes the map.
......@@ -2694,7 +2786,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
lastgametype = gametype;
gametype = READUINT8(*cp);
if (gametype != lastgametype)
if (gametype < 0 || gametype >= NUMGAMETYPES)
gametype = lastgametype;
else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!G_RaceGametype())
......
......@@ -94,10 +94,20 @@ typedef struct filetran_s
{
filetx_t *txlist; // Linked list of all files for the node
UINT32 position; // The current position in the file
FILE *currentfile; // The file currently being sent/received
boolean init; // false if we want to reset position / open a new file
} filetran_t;
static filetran_t transfer[MAXNETNODES];
// The files currently being sent/received
typedef struct fileused_s
{
FILE *file;
UINT8 count;
UINT32 position;
} fileused_t;
static fileused_t transferFiles[UINT8_MAX + 1];
// Read time of file: stat _stmtime
// Write time of file: utime
......@@ -299,6 +309,9 @@ boolean CL_CheckDownloadable(void)
return false;
}
// The following was written and, against all odds, works.
#define MORELEGACYDOWNLOADER
/** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND.
*
......@@ -311,42 +324,132 @@ boolean CL_SendRequestFile(void)
char *p;
INT32 i;
INT64 totalfreespaceneeded = 0, availablefreespace;
INT32 skippedafile = -1;
#ifdef MORELEGACYDOWNLOADER
boolean firstloop = true;
#endif
#ifdef PARANOIA
if (M_CheckParm("-nodownload"))
I_Error("Attempted to download files in -nodownload mode");
{
CONS_Printf("Direct download - Attempted to download files in -nodownload mode");
return false;
}
#endif
for (i = 0; i < fileneedednum; i++)
{
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
&& (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
{
I_Error("Attempted to download files that were not sendable");
CONS_Printf("Direct download - attempted to download files that were not sendable\n");
return false;
}
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK))
{
// Error check for the first time around.
totalfreespaceneeded += fileneeded[i].totalsize;
}
}
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
{
CONS_Printf("Direct download -\n"
" To play on this server you must download %s KB,\n"
" but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
return false;
}
#ifdef MORELEGACYDOWNLOADER
tryagain:
skippedafile = -1;
#endif
#ifdef VERBOSEREQUESTFILE
CONS_Printf("Preparing packet\n");
#endif
netbuffer->packettype = PT_REQUESTFILE;
p = (char *)netbuffer->u.textcmd;
for (i = 0; i < fileneedednum; i++)
{
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK))
{
totalfreespaceneeded += fileneeded[i].totalsize;
// Pre-prepare.
size_t checklen;
nameonly(fileneeded[i].filename);
// Figure out if we'd overrun our buffer.
checklen = strlen(fileneeded[i].filename)+2; // plus the fileid (and terminator, in case this is last)
if ((UINT8 *)(p + checklen) >= netbuffer->u.textcmd + MAXTEXTCMD)
{
skippedafile = i;
// we might have a shorter file that can fit in the remaining space, and file ID permits out-of-order data
continue;
}
// Now write.
WRITEUINT8(p, i); // fileid
WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
#ifdef VERBOSEREQUESTFILE
CONS_Printf(" file \"%s\" (id %d)\n", i, fileneeded[i].filename);
#endif
// put it in download dir
strcatbf(fileneeded[i].filename, downloaddir, "/");
fileneeded[i].status = FS_REQUESTED;
}
WRITEUINT8(p, 0xFF);
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
I_Error("To play on this server you must download %s KB,\n"
"but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
}
#ifdef MORELEGACYDOWNLOADER
if (firstloop)
#else
// If we're not trying extralong legacy download requests, gotta bail.
if (skippedafile != -1)
{
CONS_Printf("Direct download - missing files are as follows:\n");
for (i = 0; i < fileneedednum; i++)
{
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK || fileneeded[i].status == FS_REQUESTED)) // FS_REQUESTED added
CONS_Printf(" %s\n", fileneeded[i].filename);
}
return false;
}
#endif
I_mkdir(downloaddir, 0755);
// Couldn't fit a single one in?
if (p == (char *)netbuffer->u.textcmd)
{
CONS_Printf("Direct download - fileneeded name for %s (fileneeded[%d]) too long??\n", (skippedafile != -1 ? fileneeded[skippedafile].filename : NULL), skippedafile);
return false;
}
WRITEUINT8(p, 0xFF); // terminator
if (!HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd))
{
CONS_Printf("Direct download - unable to send packet.\n");
return false;
}
// prepare to download
I_mkdir(downloaddir, 0755);
return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd);
#ifdef MORELEGACYDOWNLOADER
if (skippedafile != -1)
{
firstloop = false;
goto tryagain;
}
#endif
#ifdef VERBOSEREQUESTFILE
CONS_Printf("Returning true\n");
#endif
return true;
}
// get request filepak and put it on the send queue
......@@ -356,16 +459,18 @@ boolean Got_RequestFilePak(INT32 node)
char wad[MAX_WADPATH+1];
UINT8 *p = netbuffer->u.textcmd;
UINT8 id;
while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow
while (p < netbuffer->u.textcmd + MAXTEXTCMD) // Don't allow hacked client to overflow
{
id = READUINT8(p);
if (id == 0xFF)
break;
READSTRINGN(p, wad, MAX_WADPATH);
if (!SV_SendFile(node, wad, id))
if (p >= netbuffer->u.textcmd + MAXTEXTCMD || !SV_SendFile(node, wad, id))
{
if (cv_noticedownload.value)
CONS_Printf("Bad PT_REQUESTFILE from node %d!\n", node);
SV_AbortSendFiles(node);
return false; // don't read the rest of the files
return false; // don't read any more
}
}
return true; // no problems with any files
......@@ -486,7 +591,7 @@ boolean CL_LoadServerFiles(void)
continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND)
{
P_AddWadFile(fileneeded[i].filename);
P_PartialAddWadFile(fileneeded[i].filename);
G_SetGameModified(true, false);
fileneeded[i].status = FS_OPEN;
return false;
......@@ -538,7 +643,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
char wadfilename[MAX_WADPATH];
if (cv_noticedownload.value)
CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node));
CONS_Printf("Sending file \"%s\" (id %d) to node %d (%s)\n", filename, fileid, node, I_GetNodeAddress(node));
// Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist;
......@@ -664,9 +769,20 @@ static void SV_EndFileSend(INT32 node)
{
case SF_FILE: // It's a file, close it and free its filename
if (cv_noticedownload.value)
CONS_Printf("Ending file transfer for node %d\n", node);
if (transfer[node].currentfile)
fclose(transfer[node].currentfile);
CONS_Printf("Ending file transfer (id %d) for node %d\n", p->fileid, node);
if (transferFiles[p->fileid].file)
{
if (transferFiles[p->fileid].count > 0)
{
transferFiles[p->fileid].count--;
}
if (transferFiles[p->fileid].count == 0)
{
fclose(transferFiles[p->fileid].file);
transferFiles[p->fileid].file = NULL;
}
}
free(p->id.filename);
break;
case SF_Z_RAM: // It's a memory block allocated with Z_Alloc or the likes, use Z_Free
......@@ -683,7 +799,7 @@ static void SV_EndFileSend(INT32 node)
free(p);
// Indicate that the transmission is over
transfer[node].currentfile = NULL;
transfer[node].init = false;
filestosend--;
}
......@@ -747,21 +863,31 @@ void SV_FileSendTicker(void)
ram = f->ram;
// Open the file if it isn't open yet, or
if (!transfer[i].currentfile)
if (transfer[i].init == false)
{
if (!ram) // Sending a file
{
long filesize;
transfer[i].currentfile =
fopen(f->id.filename, "rb");
if (transferFiles[f->fileid].count == 0)
{
// It needs opened.
transferFiles[f->fileid].file =
fopen(f->id.filename, "rb");
if (!transferFiles[f->fileid].file)
{
I_Error("Can't open file %s: %s",
f->id.filename, strerror(errno));
}
}
if (!transfer[i].currentfile)
I_Error("File %s does not exist",
f->id.filename);
// Increment number of nodes using this file.
I_Assert(transferFiles[f->fileid].count < UINT8_MAX);
transferFiles[f->fileid].count++;
fseek(transfer[i].currentfile, 0, SEEK_END);
filesize = ftell(transfer[i].currentfile);
fseek(transferFiles[f->fileid].file, 0, SEEK_END);
filesize = ftell(transferFiles[f->fileid].file);
// Nobody wants to transfer a file bigger
// than 4GB!
......@@ -770,23 +896,43 @@ void SV_FileSendTicker(void)
if (filesize == -1)
I_Error("Error getting filesize of %s", f->id.filename);
f->size = (UINT32)filesize;
fseek(transfer[i].currentfile, 0, SEEK_SET);
f->size = transferFiles[f->fileid].position = (UINT32)filesize;
}
else // Sending RAM
transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open
transfer[i].position = 0;
transfer[i].init = true; // Indicate that it is open
}
if (!ram)
{
// Seek to the right position if we aren't already there.
if (transferFiles[f->fileid].position != transfer[i].position)
{
fseek(transferFiles[f->fileid].file, transfer[i].position, SEEK_SET);
}
}
// Build a packet containing a file fragment
p = &netbuffer->u.filetxpak;
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
if (f->size-transfer[i].position < size)
size = f->size-transfer[i].position;
if (f->size - transfer[i].position < size)
{
size = f->size - transfer[i].position;
}
if (ram)
{
M_Memcpy(p->data, &f->id.ram[transfer[i].position], size);
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
}
else if (fread(p->data, 1, size, transferFiles[f->fileid].file) != size)
{
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s",
sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transferFiles[f->fileid].file));
transferFiles[f->fileid].position = (UINT32)(transferFiles[f->fileid].position + size);
}
p->position = LONG(transfer[i].position);
// Put flag so receiver knows the total size
if (transfer[i].position + size == f->size)
......@@ -796,15 +942,18 @@ void SV_FileSendTicker(void)
// Send the packet
if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
{ // Success
{
// Success
transfer[i].position = (UINT32)(transfer[i].position + size);
if (transfer[i].position == f->size) // Finish?
{
SV_EndFileSend(i);
}
}
else
{ // Not sent for some odd reason, retry at next call
if (!ram)
fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET);
{
// Not sent for some odd reason, retry at next call
// Exit the while (can't send this one so why should i send the next?)
break;
}
......@@ -1156,6 +1305,7 @@ void CURLGetFile(void)
int msgs_left; /* how many messages are left */
const char *easy_handle_error;
long response_code = 0;
static char *filename;
if (curl_runninghandles)
{
......@@ -1180,6 +1330,8 @@ void CURLGetFile(void)
{
e = m->easy_handle;
easyres = m->data.result;
filename = Z_StrDup(curl_realname);
nameonly(filename);
if (easyres != CURLE_OK)
{
if (easyres == CURLE_HTTP_RETURNED_ERROR)
......@@ -1192,21 +1344,30 @@ void CURLGetFile(void)
curl_failedwebdownload = true;
fclose(curl_curfile->file);
remove(curl_curfile->filename);
curl_curfile->file = NULL;
//nameonly(curl_curfile->filename);
nameonly(curl_realname);
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), curl_realname, easy_handle_error);
CONS_Printf(M_GetText("Failed to download %s (%s)\n"), filename, easy_handle_error);
}
else
{
nameonly(curl_realname);
CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname);
downloadcompletednum++;
downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
fclose(curl_curfile->file);
if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD)
{
CONS_Alert(CONS_ERROR, M_GetText("HTTP Download of %s finished but is corrupt or has been modified\n"), filename);
curl_curfile->status = FS_FALLBACK;
curl_failedwebdownload = true;
}
else
{
CONS_Printf(M_GetText("Finished HTTP download of %s\n"), filename);
downloadcompletednum++;
downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
}
}
Z_Free(filename);
curl_curfile->file = NULL;
curl_running = false;
curl_transfers--;
curl_multi_remove_handle(multi_handle, e);
......
......@@ -2641,109 +2641,6 @@ static void readconditionset(MYFILE *f, UINT8 setnum)
Z_Free(s);
}
static void readtexture(MYFILE *f, const char *name)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word;
char *word2;
char *tmp;
INT32 i, j, value;
UINT16 width = 0, height = 0;
INT16 patchcount = 0;
texture_t *texture;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
value = searchvalue(s);
word = strtok(s, " ");
if (word)
strupr(word);
else
break;
word2 = strtok(NULL, " ");
if (word2)
strupr(word2);
else
break;
// Width of the texture.
if (fastcmp(word, "WIDTH"))
{
DEH_WriteUndoline(word, va("%d", width), UNDO_NONE);
width = SHORT((UINT16)value);
}
// Height of the texture.
else if (fastcmp(word, "HEIGHT"))
{
DEH_WriteUndoline(word, va("%d", height), UNDO_NONE);
height = SHORT((UINT16)value);
}
// Number of patches the texture has.
else if (fastcmp(word, "NUMPATCHES"))
{
DEH_WriteUndoline(word, va("%d", patchcount), UNDO_NONE);
patchcount = SHORT((UINT16)value);
}
else
deh_warning("readtexture: unknown word '%s'", word);
}
} while (!myfeof(f));
// Error checking.
if (!width)
I_Error("Texture %s has no width!\n", name);
if (!height)
I_Error("Texture %s has no height!\n", name);
if (!patchcount)
I_Error("Texture %s has no patches!\n", name);
// Allocate memory for the texture, and fill in information.
texture = Z_Calloc(sizeof(texture_t) + (sizeof(texpatch_t) * SHORT(patchcount)), PU_STATIC, NULL);
M_Memcpy(texture->name, name, sizeof(texture->name));
texture->width = width;
texture->height = height;
texture->patchcount = patchcount;
texture->holes = false;
// Fill out the texture patches, to allow them to be detected
// accurately by readpatch.
for (i = 0; i < patchcount; i++)
{
texture->patches[i].originx = 0;
texture->patches[i].originy = 0;
texture->patches[i].wad = UINT16_MAX;
texture->patches[i].lump = UINT16_MAX;
}
// Jump to the next empty texture entry.
i = 0;
while (textures[i])
i++;
// Fill the global texture buffer entries.
j = 1;
while (j << 1 <= texture->width)
j <<= 1;
textures[i] = texture;
texturewidthmask[i] = j - 1;
textureheight[i] = texture->height << FRACBITS;
// Clean up.
Z_Free(s);
}
static void readpatch(MYFILE *f, const char *name, UINT16 wad)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
......@@ -3350,14 +3247,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
if (word2[strlen(word2)-1] == '\n')
word2[strlen(word2)-1] = '\0';
i = atoi(word2);
if (fastcmp(word, "TEXTURE"))
{
// Read texture from spec file.
readtexture(f, word2);
DEH_WriteUndoline(word, word2, UNDO_HEADER);
// This is not a major mod.
}
else if (fastcmp(word, "PATCH"))
if (fastcmp(word, "PATCH"))
{
// Read patch from spec file.
readpatch(f, word2, wad);
......@@ -3397,7 +3287,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
if (i > 0 && i <= NUMMAPS)
{
if (mapheaderinfo[i])
if (mapheaderinfo[i-1])
G_SetGameModified(multiplayer, true); // only mark as a major mod if it replaces an already-existing mapheaderinfo
readlevelheader(f, i);
}
......@@ -9740,10 +9630,11 @@ static inline int lib_getenum(lua_State *L)
lua_pushinteger(L, mapmusposition);
return 1;
} else if (fastcmp(word,"server")) {
return LUA_PushServerPlayer(L);
if ((!multiplayer || !(netgame || demo.playback)) && !playeringame[serverplayer])
return 0;
LUA_PushUserdata(L, &players[serverplayer], META_PLAYER);
return 1;
} else if (fastcmp(word,"consoleplayer")) { // Player controlling the console, basically our local player
if (consoleplayer == serverplayer)
return LUA_PushServerPlayer(L);
if (consoleplayer < 0 || !playeringame[consoleplayer])
return 0;
LUA_PushUserdata(L, &players[consoleplayer], META_PLAYER);
......
......@@ -529,8 +529,10 @@ void DRPC_UpdatePresence(void)
else
{
// Map name on tool tip
snprintf(mapname, 48, "Map: %s", G_BuildMapTitle(gamemap));
char *title = G_BuildMapTitle(gamemap);
snprintf(mapname, 48, "Map: %s", title);
discordPresence.largeImageText = mapname;
Z_Free(title);
}
if (gamestate == GS_LEVEL && Playing())
......
......@@ -148,9 +148,9 @@ extern char logfilename[1024];
// we use comprevision and compbranch instead.
#else
#define VERSION 1 // Game version
#define SUBVERSION 4 // more precise version number
#define VERSIONSTRING "v1.4"
#define VERSIONSTRINGW L"v1.4"
#define SUBVERSION 6 // more precise version number
#define VERSIONSTRING "v1.6"
#define VERSIONSTRINGW L"v1.6"
// Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates!
// And change CMakeLists.txt (not src/, but in root), for CMake users!
// AND appveyor.yml, for the build bots!
......@@ -204,7 +204,7 @@ extern char logfilename[1024];
// it's only for detection of the version the player is using so the MS can alert them of an update.
// Only set it higher, not lower, obviously.
// Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
#define MODVERSION 8
#define MODVERSION 10
// Filter consvars by version
// To version config.cfg, MAJOREXECVERSION is set equal to MODVERSION automatically.
......@@ -227,7 +227,7 @@ extern char logfilename[1024];
// NOTE: it needs more than this to increase the number of players...
#define MAXPLAYERS 16
#define MAXSKINS 128
#define MAXSKINS 255
#define PLAYERSMASK (MAXPLAYERS-1)
#define MAXPLAYERNAME 21
......@@ -532,6 +532,7 @@ extern boolean capslock;
// if we ever make our alloc stuff...
#define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
#define ZZ_Calloc(x) Z_Calloc(x, PU_STATIC, NULL)
// i_system.c, replace getchar() once the keyboard has been appropriated
INT32 I_GetKey(void);
......
......@@ -139,6 +139,9 @@ typedef long ssize_t;
#define strlwr _strlwr
#endif
char *strcasestr(const char *in, const char *what);
#define stristr strcasestr
#if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap
#define true 1
#define false 0
......@@ -161,6 +164,15 @@ typedef long ssize_t;
#define HAVE_DOSSTR_FUNCS
#endif
#if defined (__APPLE__)
#define SRB2_HAVE_STRLCPY
#elif defined (__GLIBC_PREREQ)
// glibc 2.38: added strlcpy and strlcat to _DEFAULT_SOURCE
#if __GLIBC_PREREQ(2, 38)
#define SRB2_HAVE_STRLCPY
#endif
#endif
#ifndef HAVE_DOSSTR_FUNCS
int strupr(char *n); // from dosstr.c
int strlwr(char *n); // from dosstr.c
......@@ -168,7 +180,7 @@ int strlwr(char *n); // from dosstr.c
#include <stddef.h> // for size_t
#ifndef __APPLE__
#ifndef SRB2_HAVE_STRLCPY
size_t strlcat(char *dst, const char *src, size_t siz);
size_t strlcpy(char *dst, const char *src, size_t siz);
#endif
......