mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-01-01 20:52:47 +00:00
Merge branch 'serverside-pwr' into 'master'
Serverside PWR See merge request KartKrew/Kart!1132
This commit is contained in:
commit
1c4d6e064c
17 changed files with 425 additions and 169 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)"));
|
||||
|
|
@ -3846,6 +3853,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
|
|||
|
||||
READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
|
||||
READMEM(*p, players[newplayernum].public_key, PUBKEYLENGTH);
|
||||
READMEM(*p, clientpowerlevels[newplayernum], sizeof(((serverplayer_t *)0)->powerlevels));
|
||||
|
||||
console = READUINT8(*p);
|
||||
splitscreenplayer = READUINT8(*p);
|
||||
|
|
@ -4002,8 +4010,10 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
}
|
||||
|
||||
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities,
|
||||
const char *name, uint8_t *key, const char *name2, uint8_t *key2,
|
||||
const char *name3, uint8_t *key3, const char *name4, uint8_t *key4)
|
||||
const char *name, uint8_t *key, UINT16 *pwr,
|
||||
const char *name2, uint8_t *key2, UINT16 *pwr2,
|
||||
const char *name3, uint8_t *key3, UINT16 *pwr3,
|
||||
const char *name4, uint8_t *key4, UINT16 *pwr4)
|
||||
{
|
||||
INT32 n, newplayernum, i;
|
||||
UINT8 buf[4 + MAXPLAYERNAME + PUBKEYLENGTH + MAXAVAILABILITY];
|
||||
|
|
@ -4070,24 +4080,28 @@ const char *name3, uint8_t *key3, const char *name4, uint8_t *key4)
|
|||
nodetoplayer[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name, MAXPLAYERNAME);
|
||||
WRITEMEM(buf_p, key, PUBKEYLENGTH);
|
||||
WRITEMEM(buf_p, pwr, sizeof(((serverplayer_t *)0)->powerlevels));
|
||||
}
|
||||
else if (playerpernode[node] < 2)
|
||||
{
|
||||
nodetoplayer2[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name2, MAXPLAYERNAME);
|
||||
WRITEMEM(buf_p, key2, PUBKEYLENGTH);
|
||||
WRITEMEM(buf_p, pwr2, sizeof(((serverplayer_t *)0)->powerlevels));
|
||||
}
|
||||
else if (playerpernode[node] < 3)
|
||||
{
|
||||
nodetoplayer3[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name3, MAXPLAYERNAME);
|
||||
WRITEMEM(buf_p, key3, PUBKEYLENGTH);
|
||||
WRITEMEM(buf_p, pwr3, sizeof(((serverplayer_t *)0)->powerlevels));
|
||||
}
|
||||
else if (playerpernode[node] < 4)
|
||||
{
|
||||
nodetoplayer4[node] = newplayernum;
|
||||
WRITESTRINGN(buf_p, name4, MAXPLAYERNAME);
|
||||
WRITEMEM(buf_p, key4, PUBKEYLENGTH);
|
||||
WRITEMEM(buf_p, pwr4, sizeof(((serverplayer_t *)0)->powerlevels));
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, nodetoplayer[node]); // consoleplayer
|
||||
|
|
@ -4177,8 +4191,11 @@ boolean SV_SpawnServer(void)
|
|||
UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(false, false);
|
||||
SINT8 node = 0;
|
||||
for (; node < MAXNETNODES; node++)
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer, cv_playername[0].zstring, PR_GetLocalPlayerProfile(0)->public_key, cv_playername[1].zstring, PR_GetLocalPlayerProfile(1)->public_key,
|
||||
cv_playername[2].zstring, PR_GetLocalPlayerProfile(2)->public_key, cv_playername[3].zstring, PR_GetLocalPlayerProfile(3)->public_key);
|
||||
result |= SV_AddWaitingPlayers(node, availabilitiesbuffer,
|
||||
cv_playername[0].zstring, PR_GetLocalPlayerProfile(0)->public_key, SV_RetrieveStats(PR_GetLocalPlayerProfile(0)->public_key)->powerlevels,
|
||||
cv_playername[1].zstring, PR_GetLocalPlayerProfile(1)->public_key, SV_RetrieveStats(PR_GetLocalPlayerProfile(1)->public_key)->powerlevels,
|
||||
cv_playername[2].zstring, PR_GetLocalPlayerProfile(2)->public_key, SV_RetrieveStats(PR_GetLocalPlayerProfile(2)->public_key)->powerlevels,
|
||||
cv_playername[3].zstring, PR_GetLocalPlayerProfile(3)->public_key, SV_RetrieveStats(PR_GetLocalPlayerProfile(3)->public_key)->powerlevels);
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
|
|
@ -4259,13 +4276,13 @@ static size_t TotalTextCmdPerTic(tic_t tic)
|
|||
memset(allZero, 0, PUBKEYLENGTH);
|
||||
|
||||
if (split == 0)
|
||||
return (memcmp(players[nodetoplayer[node]].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
return PR_IsKeyGuest(players[nodetoplayer[node]].public_key);
|
||||
else if (split == 1)
|
||||
return (memcmp(players[nodetoplayer2[node]].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
return PR_IsKeyGuest(players[nodetoplayer2[node]].public_key);
|
||||
else if (split == 2)
|
||||
return (memcmp(players[nodetoplayer3[node]].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
return PR_IsKeyGuest(players[nodetoplayer3[node]].public_key);
|
||||
else if (split == 3)
|
||||
return (memcmp(players[nodetoplayer4[node]].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
return PR_IsKeyGuest(players[nodetoplayer4[node]].public_key);
|
||||
else
|
||||
I_Error("IsSplitPlayerOnNodeGuest: Out of bounds");
|
||||
return false; // unreachable
|
||||
|
|
@ -4274,10 +4291,7 @@ static size_t TotalTextCmdPerTic(tic_t tic)
|
|||
|
||||
static boolean IsPlayerGuest(int player)
|
||||
{
|
||||
char allZero[PUBKEYLENGTH];
|
||||
memset(allZero, 0, PUBKEYLENGTH);
|
||||
|
||||
return (memcmp(players[player].public_key, allZero, PUBKEYLENGTH) == 0);
|
||||
return PR_IsKeyGuest(players[player].public_key);
|
||||
}
|
||||
|
||||
/** Called when a PT_CLIENTJOIN packet is received
|
||||
|
|
@ -4288,7 +4302,7 @@ static boolean IsPlayerGuest(int player)
|
|||
static void HandleConnect(SINT8 node)
|
||||
{
|
||||
char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
|
||||
INT32 i;
|
||||
INT32 i, j;
|
||||
UINT8 availabilitiesbuffer[MAXAVAILABILITY];
|
||||
|
||||
// Sal: Dedicated mode is INCREDIBLY hacked together.
|
||||
|
|
@ -4402,11 +4416,8 @@ static void HandleConnect(SINT8 node)
|
|||
memcpy(lastReceivedKey[node][i], PR_GetLocalPlayerProfile(i)->public_key, sizeof(lastReceivedKey[node][i]));
|
||||
}
|
||||
else // Remote player, gotta check their signature.
|
||||
{
|
||||
char allZero[PUBKEYLENGTH];
|
||||
memset(allZero, 0, sizeof(allZero));
|
||||
|
||||
if (memcmp(lastReceivedKey[node][i], allZero, PUBKEYLENGTH) == 0) // IsSplitPlayerOnNodeGuest isn't appropriate here, they're not in-game yet!
|
||||
{
|
||||
if (PR_IsKeyGuest(lastReceivedKey[node][i])) // IsSplitPlayerOnNodeGuest isn't appropriate here, they're not in-game yet!
|
||||
{
|
||||
if (!cv_allowguests.value)
|
||||
{
|
||||
|
|
@ -4427,6 +4438,42 @@ static void HandleConnect(SINT8 node)
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check non-GUESTS for duplicate pubkeys, they'll create nonsense stats
|
||||
if (!PR_IsKeyGuest(lastReceivedKey[node][i]))
|
||||
{
|
||||
// Players already here
|
||||
for (j = 0; j < MAXPLAYERS; j++)
|
||||
{
|
||||
if (memcmp(lastReceivedKey[node][i], players[j].public_key, PUBKEYLENGTH) == 0)
|
||||
{
|
||||
#ifdef DEVELOP
|
||||
CONS_Alert(CONS_WARNING, "Joining player's pubkey matches existing player, stat updates will be nonsense!\n");
|
||||
#else
|
||||
SV_SendRefuse(node, M_GetText("Duplicate pubkey already on server.\n(Did you share your profile?)"));
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Players we're trying to add
|
||||
for (j = 0; j < netbuffer->u.clientcfg.localplayers - playerpernode[node]; j++)
|
||||
{
|
||||
if (PR_IsKeyGuest(lastReceivedKey[node][j]))
|
||||
continue;
|
||||
if (i == j)
|
||||
continue;
|
||||
if (memcmp(lastReceivedKey[node][i], lastReceivedKey[node][j], PUBKEYLENGTH) == 0)
|
||||
{
|
||||
#ifdef DEVELOP
|
||||
CONS_Alert(CONS_WARNING, "Players with same pubkey in the joning party, stat updates will be nonsense!\n");
|
||||
#else
|
||||
SV_SendRefuse(node, M_GetText("Duplicate pubkey in local party.\n(How did you even do this?)"));
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(availabilitiesbuffer, netbuffer->u.clientcfg.availabilities, sizeof(availabilitiesbuffer));
|
||||
|
|
@ -4466,8 +4513,11 @@ static void HandleConnect(SINT8 node)
|
|||
DEBFILE("send savegame\n");
|
||||
}
|
||||
|
||||
SV_AddWaitingPlayers(node, availabilitiesbuffer, names[0], lastReceivedKey[node][0], names[1], lastReceivedKey[node][1],
|
||||
names[2], lastReceivedKey[node][2], names[3], lastReceivedKey[node][3]);
|
||||
SV_AddWaitingPlayers(node, availabilitiesbuffer,
|
||||
names[0], lastReceivedKey[node][0], SV_RetrieveStats(lastReceivedKey[node][0])->powerlevels,
|
||||
names[1], lastReceivedKey[node][1], SV_RetrieveStats(lastReceivedKey[node][1])->powerlevels,
|
||||
names[2], lastReceivedKey[node][2], SV_RetrieveStats(lastReceivedKey[node][2])->powerlevels,
|
||||
names[3], lastReceivedKey[node][3], SV_RetrieveStats(lastReceivedKey[node][3])->powerlevels);
|
||||
joindelay += cv_joindelay.value * TICRATE;
|
||||
player_joining = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@
|
|||
|
||||
static void Got_NameAndColor(UINT8 **cp, INT32 playernum);
|
||||
static void Got_WeaponPref(UINT8 **cp, INT32 playernum);
|
||||
static void Got_PowerLevel(UINT8 **cp, INT32 playernum);
|
||||
static void Got_PartyInvite(UINT8 **cp, INT32 playernum);
|
||||
static void Got_AcceptPartyInvite(UINT8 **cp, INT32 playernum);
|
||||
static void Got_CancelPartyInvite(UINT8 **cp, INT32 playernum);
|
||||
|
|
@ -618,7 +617,6 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
|
|||
"MODIFYVOTE", // XD_MODIFYVOTE
|
||||
"PICKVOTE", // XD_PICKVOTE
|
||||
"REMOVEPLAYER", // XD_REMOVEPLAYER
|
||||
"POWERLEVEL", // XD_POWERLEVEL
|
||||
"PARTYINVITE", // XD_PARTYINVITE
|
||||
"ACCEPTPARTYINVITE", // XD_ACCEPTPARTYINVITE
|
||||
"LEAVEPARTY", // XD_LEAVEPARTY
|
||||
|
|
@ -657,7 +655,6 @@ void D_RegisterServerCommands(void)
|
|||
|
||||
RegisterNetXCmd(XD_NAMEANDCOLOR, Got_NameAndColor);
|
||||
RegisterNetXCmd(XD_WEAPONPREF, Got_WeaponPref);
|
||||
RegisterNetXCmd(XD_POWERLEVEL, Got_PowerLevel);
|
||||
RegisterNetXCmd(XD_PARTYINVITE, Got_PartyInvite);
|
||||
RegisterNetXCmd(XD_ACCEPTPARTYINVITE, Got_AcceptPartyInvite);
|
||||
RegisterNetXCmd(XD_CANCELPARTYINVITE, Got_CancelPartyInvite);
|
||||
|
|
@ -1807,17 +1804,6 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum)
|
|||
demo_extradata[playernum] |= DXD_WEAPONPREF;
|
||||
}
|
||||
|
||||
static void Got_PowerLevel(UINT8 **cp,INT32 playernum)
|
||||
{
|
||||
UINT16 race = (UINT16)READUINT16(*cp);
|
||||
UINT16 battle = (UINT16)READUINT16(*cp);
|
||||
|
||||
clientpowerlevels[playernum][PWRLV_RACE] = min(PWRLVRECORD_MAX, race);
|
||||
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)
|
||||
{
|
||||
UINT8 invitee;
|
||||
|
|
@ -1969,28 +1955,8 @@ static void Got_LeaveParty(UINT8 **cp,INT32 playernum)
|
|||
|
||||
void D_SendPlayerConfig(UINT8 n)
|
||||
{
|
||||
const profile_t *pr = PR_GetProfile(cv_lastprofile[n].value);
|
||||
|
||||
UINT8 buf[4];
|
||||
UINT8 *p = buf;
|
||||
|
||||
SendNameAndColor(n);
|
||||
WeaponPref_Send(n);
|
||||
|
||||
if (pr != NULL)
|
||||
{
|
||||
// Send it over
|
||||
WRITEUINT16(p, pr->powerlevels[PWRLV_RACE]);
|
||||
WRITEUINT16(p, pr->powerlevels[PWRLV_BATTLE]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Guest players have no power level
|
||||
WRITEUINT16(p, 0);
|
||||
WRITEUINT16(p, 0);
|
||||
}
|
||||
|
||||
SendNetXCmdForPlayer(n, XD_POWERLEVEL, buf, p-buf);
|
||||
}
|
||||
|
||||
void D_Cheat(INT32 playernum, INT32 cheat, ...)
|
||||
|
|
|
|||
|
|
@ -169,18 +169,17 @@ typedef enum
|
|||
XD_MODIFYVOTE, // 24
|
||||
XD_PICKVOTE, // 25
|
||||
XD_REMOVEPLAYER,// 26
|
||||
XD_POWERLEVEL, // 27
|
||||
XD_PARTYINVITE, // 28
|
||||
XD_ACCEPTPARTYINVITE, // 29
|
||||
XD_LEAVEPARTY, // 30
|
||||
XD_CANCELPARTYINVITE, // 31
|
||||
XD_CHEAT, // 32
|
||||
XD_ADDBOT, // 33
|
||||
XD_DISCORD, // 34
|
||||
XD_PLAYSOUND, // 35
|
||||
XD_SCHEDULETASK, // 36
|
||||
XD_SCHEDULECLEAR, // 37
|
||||
XD_AUTOMATE, // 38
|
||||
XD_PARTYINVITE, // 27
|
||||
XD_ACCEPTPARTYINVITE, // 28
|
||||
XD_LEAVEPARTY, // 29
|
||||
XD_CANCELPARTYINVITE, // 30
|
||||
XD_CHEAT, // 31
|
||||
XD_ADDBOT, // 32
|
||||
XD_DISCORD, // 33
|
||||
XD_PLAYSOUND, // 34
|
||||
XD_SCHEDULETASK, // 35
|
||||
XD_SCHEDULECLEAR, // 36
|
||||
XD_AUTOMATE, // 37
|
||||
|
||||
MAXNETXCMD
|
||||
} netxcmd_t;
|
||||
|
|
|
|||
|
|
@ -2458,25 +2458,6 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "POWERLEVEL"))
|
||||
{
|
||||
PARAMCHECK(2);
|
||||
ty = UC_POWERLEVEL;
|
||||
re = atoi(params[1]);
|
||||
x1 = atoi(params[2]);
|
||||
|
||||
if (re < PWRLVRECORD_MIN || re > PWRLVRECORD_MAX)
|
||||
{
|
||||
deh_warning("Power level requirement %d out of range (%d - %d) for condition ID %d", re, PWRLVRECORD_MIN, PWRLVRECORD_MAX, id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (x1 < 0 || x1 >= PWRLV_NUMTYPES)
|
||||
{
|
||||
deh_warning("Power level type %d out of range (0 - %d) for condition ID %d", x1, PWRLV_NUMTYPES-1, id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "GAMECLEAR"))
|
||||
{
|
||||
ty = UC_GAMECLEAR;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1673,7 +1673,6 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
|
|||
UINT16 truecol = SKINCOLOR_BLACK;
|
||||
UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_BLACK, GTC_CACHE);
|
||||
INT32 skinnum = -1;
|
||||
INT32 powerlevel = -1;
|
||||
|
||||
char pname[PROFILENAMELEN+1] = "NEW";
|
||||
|
||||
|
|
@ -1683,7 +1682,6 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
|
|||
colormap = R_GetTranslationColormap(TC_DEFAULT, truecol, GTC_CACHE);
|
||||
strcpy(pname, p->profilename);
|
||||
skinnum = R_SkinAvailable(p->skinname);
|
||||
powerlevel = p->powerlevels[0]; // Only display race power level.
|
||||
}
|
||||
|
||||
// check setup_player for colormap for the card.
|
||||
|
|
@ -1700,13 +1698,13 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
|
|||
if (greyedout)
|
||||
return; // only used for profiles we can't select.
|
||||
|
||||
// Draw pwlv if we can
|
||||
if (powerlevel > -1)
|
||||
if (p != NULL)
|
||||
{
|
||||
V_DrawFixedPatch((x+30)*FRACUNIT, (y+84)*FRACUNIT, FRACUNIT, 0, pwrlv, colormap);
|
||||
V_DrawCenteredKartString(x+30, y+87, 0, va("%d\n", powerlevel));
|
||||
V_DrawCenteredKartString(x+30, y+87, 0, va("%d", p->wins));
|
||||
}
|
||||
|
||||
|
||||
// check what setup_player is doing in priority.
|
||||
if (sp->mdepth >= CSSTEP_CHARS)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ profile_t* PR_MakeProfile(
|
|||
boolean guest)
|
||||
{
|
||||
profile_t *new = Z_Malloc(sizeof(profile_t), PU_STATIC, NULL);
|
||||
UINT8 i;
|
||||
|
||||
new->version = PROFILEVER;
|
||||
|
||||
|
|
@ -72,11 +71,7 @@ profile_t* PR_MakeProfile(
|
|||
// Copy from gamecontrol directly as we'll be setting controls up directly in the profile.
|
||||
memcpy(new->controls, controlarray, sizeof(new->controls));
|
||||
|
||||
// Init both power levels
|
||||
for (i = 0; i < PWRLV_NUMTYPES; i++)
|
||||
{
|
||||
new->powerlevels[i] = (guest ? 0 : PWRLVRECORD_START);
|
||||
}
|
||||
new->wins = 0;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
|
@ -270,11 +265,7 @@ void PR_SaveProfiles(void)
|
|||
WRITESTRINGN(save.p, profilesList[i]->follower, SKINNAMESIZE);
|
||||
WRITEUINT16(save.p, profilesList[i]->followercolor);
|
||||
|
||||
// PWR.
|
||||
for (j = 0; j < PWRLV_NUMTYPES; j++)
|
||||
{
|
||||
WRITEUINT16(save.p, profilesList[i]->powerlevels[j]);
|
||||
}
|
||||
WRITEUINT32(save.p, profilesList[i]->wins);
|
||||
|
||||
// Consvars.
|
||||
WRITEUINT8(save.p, profilesList[i]->kickstartaccel);
|
||||
|
|
@ -397,16 +388,15 @@ void PR_LoadProfiles(void)
|
|||
profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR;
|
||||
}
|
||||
|
||||
// PWR.
|
||||
for (j = 0; j < PWRLV_NUMTYPES; j++)
|
||||
// Profile update 5-->6: PWR isn't in profile data anymore.
|
||||
if (version < 6)
|
||||
{
|
||||
profilesList[i]->powerlevels[j] = READUINT16(save.p);
|
||||
if (profilesList[i]->powerlevels[j] < PWRLVRECORD_MIN
|
||||
|| profilesList[i]->powerlevels[j] > PWRLVRECORD_MAX)
|
||||
{
|
||||
// invalid, reset
|
||||
profilesList[i]->powerlevels[j] = PWRLVRECORD_START;
|
||||
}
|
||||
save.p += PWRLV_NUMTYPES*2;
|
||||
profilesList[i]->wins = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
profilesList[i]->wins = READUINT32(save.p);
|
||||
}
|
||||
|
||||
// Consvars.
|
||||
|
|
@ -425,7 +415,7 @@ void PR_LoadProfiles(void)
|
|||
{
|
||||
#ifdef DEVELOP
|
||||
// Profile update 1-->2: Add gc_rankings.
|
||||
// Profile update 3-->5: Add gc_startlossless.
|
||||
// Profile update 4-->5: Add gc_startlossless.
|
||||
if ((j == gc_rankings && version < 2) ||
|
||||
(j == gc_startlossless && version < 5))
|
||||
{
|
||||
|
|
@ -630,3 +620,12 @@ char *GetPrettyRRID(const unsigned char *bin, boolean brief)
|
|||
|
||||
return rrid_buf;
|
||||
}
|
||||
|
||||
|
||||
static char allZero[PUBKEYLENGTH];
|
||||
|
||||
boolean PR_IsKeyGuest(uint8_t *key)
|
||||
{
|
||||
//memset(allZero, 0, PUBKEYLENGTH); -- not required, allZero is 0's
|
||||
return (memcmp(key, allZero, PUBKEYLENGTH) == 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ extern "C" {
|
|||
#define SKINNAMESIZE 16
|
||||
|
||||
#define PROFILENAMELEN 6
|
||||
#define PROFILEVER 5
|
||||
#define PROFILEVER 6
|
||||
#define MAXPROFILES 16
|
||||
#define PROFILESFILE "ringprofiles.prf"
|
||||
#define PROFILE_GUEST 0
|
||||
|
|
@ -69,7 +69,7 @@ struct profile_t
|
|||
char follower[SKINNAMESIZE+1]; // Follower
|
||||
UINT16 followercolor; // Follower color
|
||||
|
||||
UINT16 powerlevels[PWRLV_NUMTYPES]; // PWRLV for each gametype.
|
||||
UINT32 wins; // I win I win I win
|
||||
|
||||
// Player-specific consvars.
|
||||
// @TODO: List all of those
|
||||
|
|
@ -166,6 +166,8 @@ boolean PR_IsLocalPlayerGuest(INT32 player);
|
|||
|
||||
char *GetPrettyRRID(const unsigned char *bin, boolean brief);
|
||||
|
||||
boolean PR_IsKeyGuest(uint8_t *key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -399,7 +400,6 @@ void K_CashInPowerLevels(void)
|
|||
{
|
||||
SINT8 powerType = K_UsingPowerLevels();
|
||||
UINT8 i;
|
||||
boolean gamedataupdate;
|
||||
|
||||
//CONS_Printf("\n========\n");
|
||||
//CONS_Printf("Cashing in power level changes...\n");
|
||||
|
|
@ -409,29 +409,17 @@ void K_CashInPowerLevels(void)
|
|||
{
|
||||
if (playeringame[i] == true && powerType != PWRLV_DISABLED)
|
||||
{
|
||||
profile_t *pr = PR_GetPlayerProfile(&players[i]);
|
||||
INT16 inc = K_FinalPowerIncrement(&players[i], clientpowerlevels[i][powerType], clientPowerAdd[i]);
|
||||
|
||||
clientpowerlevels[i][powerType] += inc;
|
||||
|
||||
//CONS_Printf("%s: %d -> %d (%d)\n", player_names[i], clientpowerlevels[i][powerType] - inc, clientpowerlevels[i][powerType], inc);
|
||||
|
||||
if (pr != NULL && inc != 0)
|
||||
{
|
||||
pr->powerlevels[powerType] = clientpowerlevels[i][powerType];
|
||||
|
||||
gamedataupdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
clientPowerAdd[i] = 0;
|
||||
}
|
||||
|
||||
if (gamedataupdate)
|
||||
{
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
G_SaveGameData();
|
||||
}
|
||||
SV_UpdateStats();
|
||||
|
||||
//CONS_Printf("========\n");
|
||||
}
|
||||
|
|
@ -565,7 +553,6 @@ void K_SetPowerLevelScrambles(SINT8 powertype)
|
|||
|
||||
void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss)
|
||||
{
|
||||
profile_t *pr;
|
||||
UINT8 p = 0;
|
||||
|
||||
SINT8 powerType = PWRLV_DISABLED;
|
||||
|
|
@ -636,18 +623,10 @@ 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();
|
||||
clientpowerlevels[playerNum][powerType] += clientPowerAdd[playerNum];
|
||||
clientPowerAdd[playerNum] = 0;
|
||||
SV_UpdateStats();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
236
src/k_serverstats.c
Normal file
236
src/k_serverstats.c
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
// SONIC ROBO BLAST 2
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||||
// Copyright (C) 1999-2020 by Sonic Team Junior.
|
||||
// Copyright (C) 2023 by AJ "Tyron" Martinez
|
||||
//
|
||||
// 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"
|
||||
#include "time.h"
|
||||
|
||||
static serverplayer_t *trackedList;
|
||||
static size_t numtracked = 0;
|
||||
static size_t numallocated = 0;
|
||||
static boolean initialized = false;
|
||||
|
||||
UINT16 guestpwr[PWRLV_NUMTYPES]; // All-zero power level to reference for guests
|
||||
|
||||
static void SV_InitializeStats(void)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
numallocated = 8;
|
||||
trackedList = Z_Calloc(
|
||||
sizeof(serverplayer_t) * numallocated,
|
||||
PU_STATIC,
|
||||
&trackedList
|
||||
);
|
||||
|
||||
if (trackedList == NULL)
|
||||
{
|
||||
I_Error("Not enough memory for server stats\n");
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void SV_ExpandStats(size_t needed)
|
||||
{
|
||||
I_Assert(trackedList != NULL);
|
||||
|
||||
while (numallocated < needed)
|
||||
{
|
||||
numallocated *= 2;
|
||||
trackedList = Z_Realloc(
|
||||
trackedList,
|
||||
sizeof(serverplayer_t) * numallocated,
|
||||
PU_STATIC,
|
||||
&trackedList
|
||||
);
|
||||
|
||||
if (trackedList == NULL)
|
||||
{
|
||||
I_Error("Not enough memory for server stats\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Read stats file to trackedList for ingame use
|
||||
void SV_LoadStats(void)
|
||||
{
|
||||
const size_t headerlen = strlen(SERVERSTATSHEADER);
|
||||
savebuffer_t save = {0};
|
||||
unsigned int i, j;
|
||||
|
||||
if (!server)
|
||||
return;
|
||||
|
||||
if (P_SaveBufferFromFile(&save, va(pandf, srb2home, SERVERSTATSFILE)) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SV_InitializeStats();
|
||||
|
||||
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;
|
||||
UINT8 version = READUINT8(save.p);
|
||||
(void)version; // for now
|
||||
|
||||
numtracked = READUINT32(save.p);
|
||||
|
||||
SV_ExpandStats(numtracked);
|
||||
|
||||
for(i = 0; i < numtracked; i++)
|
||||
{
|
||||
READMEM(save.p, trackedList[i].public_key, PUBKEYLENGTH);
|
||||
READMEM(save.p, &trackedList[i].lastseen, sizeof(trackedList[i].lastseen));
|
||||
for(j = 0; j < PWRLV_NUMTYPES; j++)
|
||||
{
|
||||
trackedList[i].powerlevels[j] = READUINT16(save.p);
|
||||
}
|
||||
trackedList[i].hash = quickncasehash((char*)trackedList[i].public_key, PUBKEYLENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Save trackedList to disc
|
||||
void SV_SaveStats(void)
|
||||
{
|
||||
size_t length = 0;
|
||||
const size_t headerlen = strlen(SERVERSTATSHEADER);
|
||||
savebuffer_t save = {0};
|
||||
unsigned int i, j;
|
||||
|
||||
if (!server)
|
||||
return;
|
||||
|
||||
// header + version + numtracked + payload
|
||||
if (P_SaveBufferAlloc(&save, headerlen + sizeof(UINT32) + sizeof(UINT8) + (numtracked * sizeof(serverplayer_t))) == false)
|
||||
{
|
||||
I_Error("No more free memory for saving server stats\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add header.
|
||||
WRITESTRINGN(save.p, SERVERSTATSHEADER, headerlen);
|
||||
|
||||
WRITEUINT8(save.p, SERVERSTATSVER);
|
||||
|
||||
WRITEUINT32(save.p, numtracked);
|
||||
|
||||
for(i = 0; i < numtracked; i++)
|
||||
{
|
||||
WRITEMEM(save.p, trackedList[i].public_key, PUBKEYLENGTH);
|
||||
WRITEMEM(save.p, &trackedList[i].lastseen, sizeof(trackedList[i].lastseen));
|
||||
for(j = 0; j < PWRLV_NUMTYPES; j++)
|
||||
{
|
||||
WRITEUINT16(save.p, trackedList[i].powerlevels[j]);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
serverplayer_t *SV_RetrieveStats(uint8_t *key)
|
||||
{
|
||||
UINT32 j, hash;
|
||||
|
||||
SV_InitializeStats();
|
||||
|
||||
hash = quickncasehash((char*)key, PUBKEYLENGTH);
|
||||
|
||||
// Existing record?
|
||||
for(j = 0; j < numtracked; j++)
|
||||
{
|
||||
if (hash != trackedList[j].hash) // Not crypto magic, just an early out with a faster comparison
|
||||
continue;
|
||||
if (memcmp(trackedList[j].public_key, key, PUBKEYLENGTH) == 0)
|
||||
return &trackedList[j];
|
||||
}
|
||||
|
||||
// Untracked below this point, make a new record
|
||||
SV_ExpandStats(numtracked+1);
|
||||
|
||||
// Default stats
|
||||
trackedList[numtracked].lastseen = time(NULL);
|
||||
memcpy(&trackedList[numtracked].public_key, key, PUBKEYLENGTH);
|
||||
for(j = 0; j < PWRLV_NUMTYPES; j++)
|
||||
{
|
||||
trackedList[numtracked].powerlevels[j] = PR_IsKeyGuest(key) ? 0 : PWRLVRECORD_START;
|
||||
}
|
||||
trackedList[numtracked].hash = quickncasehash((char*)key, PUBKEYLENGTH);
|
||||
|
||||
numtracked++;
|
||||
|
||||
return &trackedList[numtracked - 1];
|
||||
}
|
||||
|
||||
// Write player stats to trackedList, then save to disk
|
||||
void SV_UpdateStats(void)
|
||||
{
|
||||
UINT32 i, j, hash;
|
||||
|
||||
if (!server)
|
||||
return;
|
||||
|
||||
SV_InitializeStats();
|
||||
|
||||
for(i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
if (PR_IsKeyGuest(players[i].public_key))
|
||||
continue;
|
||||
|
||||
hash = quickncasehash((char*)players[i].public_key, PUBKEYLENGTH);
|
||||
|
||||
for(j = 0; j < numtracked; j++)
|
||||
{
|
||||
if (hash != trackedList[j].hash) // Not crypto magic, just an early out with a faster comparison
|
||||
continue;
|
||||
if (memcmp(&trackedList[j].public_key, players[i].public_key, PUBKEYLENGTH) == 0)
|
||||
{
|
||||
trackedList[j].lastseen = time(NULL);
|
||||
memcpy(&trackedList[j].powerlevels, clientpowerlevels[i], sizeof(trackedList[j].powerlevels));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SV_RetrievePWR should always be called for a key before SV_UpdateStats runs,
|
||||
// so this shouldn't be reachable.
|
||||
}
|
||||
|
||||
SV_SaveStats();
|
||||
}
|
||||
53
src/k_serverstats.h
Normal file
53
src/k_serverstats.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// 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.
|
||||
// Copyright (C) 2023 by AJ "Tyron" Martinez
|
||||
//
|
||||
// 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 SERVERSTATSHEADER "Doctor Robotnik's Ring Racers Server Stats"
|
||||
#define SERVERSTATSVER 1
|
||||
|
||||
struct serverplayer_t
|
||||
{
|
||||
uint8_t public_key[PUBKEYLENGTH];
|
||||
UINT32 lastseen;
|
||||
UINT16 powerlevels[PWRLV_NUMTYPES];
|
||||
|
||||
UINT32 hash; // Not persisted! Used for early outs during key comparisons
|
||||
};
|
||||
|
||||
void SV_SaveStats(void);
|
||||
|
||||
void SV_LoadStats(void);
|
||||
|
||||
serverplayer_t *SV_RetrieveStats(uint8_t *key);
|
||||
|
||||
void SV_UpdateStats(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
25
src/m_cond.c
25
src/m_cond.c
|
|
@ -683,25 +683,6 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
|
|||
}
|
||||
case UC_TOTALRINGS: // Requires grabbing >= x rings
|
||||
return (gamedata->totalrings >= (unsigned)cn->requirement);
|
||||
case UC_POWERLEVEL: // Requires power level >= x on a certain gametype
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
if (gamestate == GS_LEVEL)
|
||||
return false; // this one could be laggy with many profiles available
|
||||
|
||||
for (i = PROFILE_GUEST; i < PR_GetNumProfiles(); i++)
|
||||
{
|
||||
profile_t *p = PR_GetProfile(i);
|
||||
|
||||
if (p->powerlevels[cn->extrainfo1] >= (unsigned)cn->requirement)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
case UC_GAMECLEAR: // Requires game beaten >= x times
|
||||
return (gamedata->timesBeaten >= (unsigned)cn->requirement);
|
||||
case UC_OVERALLTIME: // Requires overall time <= x
|
||||
|
|
@ -1039,12 +1020,6 @@ static const char *M_GetConditionString(condition_t *cn)
|
|||
return va("collect %u,%03u Rings", (cn->requirement/1000), (cn->requirement%1000));
|
||||
return va("collect %u Rings", cn->requirement);
|
||||
|
||||
case UC_POWERLEVEL: // Requires power level >= x on a certain gametype
|
||||
return va("get a PWR of %d in %s", cn->requirement,
|
||||
(cn->extrainfo1 == PWRLV_RACE)
|
||||
? "Race"
|
||||
: "Battle");
|
||||
|
||||
case UC_GAMECLEAR: // Requires game beaten >= x times
|
||||
if (cn->requirement > 1)
|
||||
return va("beat game %d times", cn->requirement);
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ typedef enum
|
|||
UC_ROUNDSPLAYED, // ROUNDSPLAYED [x played]
|
||||
UC_TOTALRINGS, // TOTALRINGS [x collected]
|
||||
|
||||
UC_POWERLEVEL, // SRB2Kart: POWERLEVEL [power level to reach] [gametype, "0" for race, "1" for battle]
|
||||
|
||||
UC_GAMECLEAR, // GAMECLEAR <x times>
|
||||
UC_OVERALLTIME, // OVERALLTIME [time to beat, tics]
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
#include "k_rank.h"
|
||||
#include "k_director.h"
|
||||
#include "g_party.h"
|
||||
#include "k_profiles.h"
|
||||
|
||||
#ifdef HW3SOUND
|
||||
#include "hardware/hw3sound.h"
|
||||
|
|
@ -1378,6 +1379,13 @@ void P_DoPlayerExit(player_t *player)
|
|||
if (modeattacking)
|
||||
G_UpdateRecords();
|
||||
|
||||
profile_t *pr = PR_GetPlayerProfile(player);
|
||||
if (pr != NULL && !losing)
|
||||
{
|
||||
pr->wins++;
|
||||
PR_SaveProfiles();
|
||||
}
|
||||
|
||||
player->karthud[khud_cardanimation] = 0; // srb2kart: reset battle animation
|
||||
|
||||
if (player == &players[consoleplayer])
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue