diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 21ef13ce9..f92be390e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -141,6 +141,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_podium.c k_rank.c k_vote.c + k_serverstats.c ) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 37bb6323b..f1ff9cf96 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -61,6 +61,7 @@ #include "m_cond.h" // netUnlocked #include "g_party.h" #include "k_vote.h" +#include "k_serverstats.h" // cl loading screen #include "v_video.h" @@ -2972,12 +2973,18 @@ static void Command_Nodes(void) if (playernode[i] != UINT8_MAX) { - CONS_Printf(" - node %.2d", playernode[i]); + CONS_Printf(" [node %.2d]", playernode[i]); if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL) CONS_Printf(" - %s", address); } - CONS_Printf(" [RRID-%s] ", GetPrettyRRID(players[i].public_key, true)); + if (K_UsingPowerLevels() != PWRLV_DISABLED) // No power type?! + { + CONS_Printf(" [%.4d PWR]", clientpowerlevels[i][K_UsingPowerLevels()]); + } + + + CONS_Printf(" [RRID-%s]", GetPrettyRRID(players[i].public_key, true)); if (IsPlayerAdmin(i)) CONS_Printf(M_GetText(" (verified admin)")); @@ -3910,6 +3917,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) HU_AddChatText(joinmsg, false); } + SV_RetrieveStats(newplayernum); + if (server && multiplayer && motd[0] != '\0') COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); diff --git a/src/d_main.c b/src/d_main.c index c96ff0dc9..2821d7686 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -82,6 +82,7 @@ #include "acs/interface.h" #include "k_podium.h" #include "k_vote.h" +#include "k_serverstats.h" #ifdef HWRENDER #include "hardware/hw_main.h" // 3D View Rendering @@ -1619,6 +1620,8 @@ void D_SRB2Main(void) // Load Profiles now that default controls have been defined PR_LoadProfiles(); // load control profiles + SV_LoadStats(); + #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen #endif @@ -1888,6 +1891,8 @@ void D_SRB2Main(void) } } + SV_SaveStats(); + if (autostart || netgame) { gameaction = ga_nothing; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 27cdc9042..c1fa1b7d0 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1809,6 +1809,9 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum) static void Got_PowerLevel(UINT8 **cp,INT32 playernum) { + // Server keeps track of this now, no-sell XD_POWERLEVEL + + /* UINT16 race = (UINT16)READUINT16(*cp); UINT16 battle = (UINT16)READUINT16(*cp); @@ -1816,6 +1819,7 @@ static void Got_PowerLevel(UINT8 **cp,INT32 playernum) clientpowerlevels[playernum][PWRLV_BATTLE] = min(PWRLVRECORD_MAX, battle); CONS_Debug(DBG_GAMELOGIC, "set player %d to power %d\n", playernum, race); + */ } static void Got_PartyInvite(UINT8 **cp,INT32 playernum) @@ -1977,6 +1981,7 @@ void D_SendPlayerConfig(UINT8 n) SendNameAndColor(n); WeaponPref_Send(n); + /* if (pr != NULL) { // Send it over @@ -1991,6 +1996,7 @@ void D_SendPlayerConfig(UINT8 n) } SendNetXCmdForPlayer(n, XD_POWERLEVEL, buf, p-buf); + */ } void D_Cheat(INT32 playernum, INT32 cheat, ...) diff --git a/src/g_game.c b/src/g_game.c index f5173e642..7a767bdf7 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -67,6 +67,7 @@ #include "acs/interface.h" #include "g_party.h" #include "k_vote.h" +#include "k_serverstats.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -1580,6 +1581,8 @@ void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate) // clear hud messages remains (usually from game startup) CON_ClearHUD(); + SV_UpdateStats(); + server_lagless = cv_lagless.value; if (doAutomate == true) diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index 0f234a504..5b1611d7b 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -17,6 +17,7 @@ #include "p_tick.h" // leveltime #include "k_grandprix.h" #include "k_profiles.h" +#include "k_serverstats.h" // Client-sided calculations done for Power Levels. // This is done so that clients will never be able to hack someone else's score over the server. @@ -636,18 +637,9 @@ void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss) return; } - if (inc < 0 && pointLoss == false) + if (pointLoss) { - // Don't record point losses for sync-out / crashes. - return; - } - - pr = PR_GetPlayerProfile(&players[playerNum]); - if (pr != NULL) - { - pr->powerlevels[powerType] = yourPower + inc; - - M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(); + CONS_Printf("Stats update by %d\n", inc); + SV_UpdateStats(); } } diff --git a/src/k_serverstats.c b/src/k_serverstats.c new file mode 100644 index 000000000..49541e315 --- /dev/null +++ b/src/k_serverstats.c @@ -0,0 +1,179 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// 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 k_serverstats.c +/// \brief implements methods for serverside stat tracking. + +#include "doomtype.h" +#include "d_main.h" // pandf +#include "byteptr.h" // READ/WRITE macros +#include "p_saveg.h" // savebuffer_t +#include "m_misc.h" //FIL_WriteFile() +#include "k_serverstats.h" +#include "z_zone.h" + +static serverplayer_t trackedList[MAXTRACKEDSERVERPLAYERS]; +static UINT32 numtracked = 0; + +// Read stats file to trackedList for ingame use +void SV_LoadStats(void) +{ + const size_t headerlen = strlen(SERVERSTATSHEADER); + savebuffer_t save = {0}; + + if (!server) + return; + + if (P_SaveBufferFromFile(&save, va(pandf, srb2home, SERVERSTATSFILE)) == false) + { + return; + } + + if (strncmp(SERVERSTATSHEADER, (const char *)save.buffer, headerlen)) + { + const char *gdfolder = "the Ring Racers folder"; + if (strcmp(srb2home,".")) + gdfolder = srb2home; + + P_SaveBufferFree(&save); + I_Error("Not a valid server stats file.\nDelete %s (maybe in %s) and try again.", SERVERSTATSFILE, gdfolder); + } + + save.p += headerlen; + + numtracked = READUINT32(save.p); + if (numtracked > MAXTRACKEDSERVERPLAYERS) + numtracked = MAXTRACKEDSERVERPLAYERS; + + READMEM(save.p, trackedList, (numtracked * sizeof(serverplayer_t))); +} + +// Save trackedList to disc +void SV_SaveStats(void) +{ + size_t length = 0; + const size_t headerlen = strlen(SERVERSTATSHEADER); + UINT8 i; + savebuffer_t save = {0}; + + if (!server) + return; + + /* + if (profilesList[PROFILE_GUEST] == NULL) + { + // Profiles have not been loaded yet, don't overwrite with garbage. + return; + } + */ + + if (P_SaveBufferAlloc(&save, headerlen + sizeof(UINT32) + (numtracked * sizeof(serverplayer_t))) == false) + { + I_Error("No more free memory for saving server stats\n"); + return; + } + + // Add header. + WRITESTRINGN(save.p, SERVERSTATSHEADER, headerlen); + + WRITEUINT32(save.p, numtracked); + + WRITEMEM(save.p, trackedList, (numtracked * sizeof(serverplayer_t))); + + for (i = 0; i < numtracked; i++) + { + + } + + length = save.p - save.buffer; + + if (!FIL_WriteFile(va(pandf, srb2home, SERVERSTATSFILE), save.buffer, length)) + { + P_SaveBufferFree(&save); + I_Error("Couldn't save server stats. Are you out of Disk space / playing in a protected folder?"); + } + P_SaveBufferFree(&save); +} + +// New player, grab their stats from trackedList or initialize new ones if they're new +void SV_RetrieveStats(int player) +{ + if (!server) + return; + + UINT32 j; + + for(j = 0; j < numtracked; j++) + { + if (memcmp(trackedList[j].public_key, players[player].public_key, PUBKEYLENGTH) == 0) + { + memcpy(clientpowerlevels[player], trackedList[j].powerlevels, sizeof(trackedList[j].powerlevels)); + return; + } + } + + uint8_t allZero[PUBKEYLENGTH]; + memset(allZero, 0, PUBKEYLENGTH); + + for(j = 0; j < PWRLV_NUMTYPES; j++) + { + if (memcmp(players[player].public_key, allZero, PUBKEYLENGTH) == 0) + clientpowerlevels[player][j] = 0; + else + clientpowerlevels[player][j] = PWRLVRECORD_START; + } + +} + +// Write player stats to trackedList, then save to disk +void SV_UpdateStats(void) +{ + UINT32 i, j; + uint8_t allZero[PUBKEYLENGTH]; + memset(allZero, 0, PUBKEYLENGTH); + + if (!server) + return; + + CONS_Printf("SV_UpdateStats\n"); + + for(i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + if (memcmp(players[i].public_key, allZero, PUBKEYLENGTH) == 0) + { + continue; + } + + CONS_Printf("updating %d\n", i); + + boolean match = false; + for(j = 0; j < numtracked; j++) + { + if (memcmp(trackedList[j].public_key, players[i].public_key, PUBKEYLENGTH) == 0) + { + memcpy(trackedList[j].powerlevels, clientpowerlevels[i], sizeof(trackedList[j].powerlevels)); + match = true; + break; + } + } + + if (match) + continue; + + memcpy(trackedList[numtracked].public_key, players[i].public_key, PUBKEYLENGTH); + memcpy(trackedList[numtracked].powerlevels, clientpowerlevels[i], sizeof(trackedList[numtracked].powerlevels)); + + numtracked++; + } + + SV_SaveStats(); +} \ No newline at end of file diff --git a/src/k_serverstats.h b/src/k_serverstats.h new file mode 100644 index 000000000..161d11cd7 --- /dev/null +++ b/src/k_serverstats.h @@ -0,0 +1,49 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 2011-2016 by Matthew "Inuyasha" Walsh. +// Copyright (C) 1999-2018 by Sonic Team Junior. +// +// 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 k_serverstats.h +/// \brief serverside stat tracking definitions + +#ifndef __SERVERSTATS_H__ +#define __SERVERSTATS_H__ + +#include "doomdef.h" // MAXPLAYERNAME +#include "g_input.h" // Input related stuff +#include "string.h" // strcpy etc +#include "g_game.h" // game CVs + +#ifdef __cplusplus +extern "C" { +#endif + +#define SERVERSTATSFILE "srvstats.dat" +#define MAXTRACKEDSERVERPLAYERS 9999 +#define SERVERSTATSHEADER "Doctor Robotnik's Ring Racers Server Stats" + +struct serverplayer_t +{ + uint8_t public_key[PUBKEYLENGTH]; + UINT16 powerlevels[PWRLV_NUMTYPES]; +}; + +void SV_SaveStats(void); + +void SV_LoadStats(void); + +void SV_RetrieveStats(int player); + +void SV_UpdateStats(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/src/typedef.h b/src/typedef.h index 2683bff70..a8d2d669f 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -195,6 +195,9 @@ TYPEDEF (pathfindsetup_t); // k_profiles.h TYPEDEF (profile_t); +// h_serverstats.h +TYPEDEF (serverplayer_t); + // k_terrain.h TYPEDEF (t_splash_t); TYPEDEF (t_footstep_t);