From 3d6d2694b358f37511176253d4f0d56ae2c6dd3d Mon Sep 17 00:00:00 2001 From: SteelT Date: Sun, 16 Jul 2023 19:13:20 -0400 Subject: [PATCH 1/8] Fetch the server IP once Fixes ourIP being set on every map change Also catches a possible case where joining clients can sigfail in the window between map change and stun response --- src/d_clisrv.c | 54 +++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d5023265f..e47e383dc 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -118,7 +118,7 @@ UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. #define GENTLEMANSMOOTHING (TICRATE) static tic_t reference_lag; -static UINT8 spike_time; +static UINT8 spike_time; static tic_t lowest_lag; boolean server_lagless; static CV_PossibleValue_t mindelay_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}}; @@ -266,7 +266,7 @@ shouldsign_t ShouldSignChallenge(uint8_t *message) #ifndef SRB2_LITTLE_ENDIAN #error "FIXME: 64-bit timestamp field is not supported on Big Endian" #endif - + UINT64 then, now; UINT32 claimedIP, realIP; @@ -2280,7 +2280,7 @@ static void CL_ConnectToServer(void) { PR_ApplyProfile(cv_lastprofile[i].value, i); } - + // Slightly sucks that we have to duplicate these from d_main.c, but // the change to cv_lastprofile doesn't take in time for this codepath. if (M_CheckParm("-profile")) @@ -4029,7 +4029,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) K_SetBot(newplayernum, skinnum, difficulty, style); } -static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, +static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, const char *name, uint8_t *key, UINT16 *pwr, const char *name2, uint8_t *key2, UINT16 *pwr2, const char *name3, uint8_t *key3, UINT16 *pwr3, @@ -4192,9 +4192,15 @@ boolean SV_SpawnServer(void) serverrunning = true; SV_ResetServer(); SV_GenContext(); - if (netgame && I_NetOpenSocket) + if (netgame) { - I_NetOpenSocket(); + if (I_NetOpenSocket) + { + I_NetOpenSocket(); + } + + ourIP = 0; + STUN_bind(GotOurIP); } // non dedicated server just connect to itself @@ -4203,9 +4209,7 @@ boolean SV_SpawnServer(void) else doomcom->numslots = 1; } - ourIP = 0; - if (netgame && server) - STUN_bind(GotOurIP); + // strictly speaking, i'm not convinced the following is necessary // but I'm not confident enough to remove it entirely in case it breaks something @@ -4438,19 +4442,19 @@ 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. - { + { if (PR_IsKeyGuest(lastReceivedKey[node][i])) // IsSplitPlayerOnNodeGuest isn't appropriate here, they're not in-game yet! { if (!cv_allowguests.value) { SV_SendRefuse(node, M_GetText("The server doesn't allow GUESTs.\nCreate a profile to join!")); - return; + return; } sigcheck = 0; // Always succeeds. Yes, this is a success response. C R Y P T O } else - { + { sigcheck = crypto_eddsa_check(netbuffer->u.clientcfg.challengeResponse[i], lastReceivedKey[node][i], lastSentChallenge[node], CHALLENGELENGTH); } @@ -4473,7 +4477,7 @@ static void HandleConnect(SINT8 node) 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; + return; #endif } } @@ -4491,7 +4495,7 @@ static void HandleConnect(SINT8 node) 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; + return; #endif } } @@ -5013,7 +5017,7 @@ static void PT_Say(int node) return; } - stop_spamming[say.source] = 4; + stop_spamming[say.source] = 4; serverplayer_t *stats = SV_GetStatsByPlayerIndex(say.source); @@ -5044,7 +5048,7 @@ static char NodeToSplitPlayer(int node, int split) else if (split == 3) return nodetoplayer4[node]; return -1; -} +} /** Handles a packet received from a node that is in game * @@ -5073,7 +5077,7 @@ static void HandlePacketFromPlayer(SINT8 node) if (netconsole >= MAXPLAYERS) I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); #endif - + #ifdef SIGNGAMETRAFFIC if (server) @@ -5097,10 +5101,10 @@ static void HandlePacketFromPlayer(SINT8 node) { if (crypto_eddsa_check(netbuffer->signature[splitnodes], players[targetplayer].public_key, message, doomcom->datalength - BASEPACKETSIZE)) { - CONS_Alert(CONS_ERROR, "SIGFAIL! Packet type %d from node %d player %d\nkey %s size %d netconsole %d\n", + CONS_Alert(CONS_ERROR, "SIGFAIL! Packet type %d from node %d player %d\nkey %s size %d netconsole %d\n", netbuffer->packettype, node, splitnodes, GetPrettyRRID(players[targetplayer].public_key, true), doomcom->datalength - BASEPACKETSIZE, netconsole); - + // Something scary can happen when multiple kicks that resolve to the same node are processed in quick succession. // Sometimes, a kick will still be left to process after the player's been disposed, and that causes the kick to resolve on the server instead! // This sucks, so we check for a stale/misfiring kick beforehand. @@ -5111,7 +5115,7 @@ static void HandlePacketFromPlayer(SINT8 node) return; } } - + } } } @@ -5635,7 +5639,7 @@ static void HandlePacketFromPlayer(SINT8 node) SendKick(targetplayer, KICK_MSG_SIGFAIL); break; } - else + else { memcpy(lastReceivedSignature[targetplayer], netbuffer->u.responseall.signature[responseplayer], sizeof(lastReceivedSignature[targetplayer])); } @@ -5987,7 +5991,7 @@ static void CL_SendClientCmd(void) lagDelay = reference_lag; } } - else + else { reference_lag = lagDelay; // Adjust quickly if the connection improves. spike_time = 0; @@ -6601,10 +6605,10 @@ static void KickUnverifiedPlayers(void) SendKick(i, KICK_MSG_SIGFAIL); } } - } + } } -// +// static void SendChallengeResults(void) { int i; @@ -7130,5 +7134,5 @@ void SendServerNotice(SINT8 target, char *message) { if (client) return; - DoSayCommand(message, target + 1, HU_PRIVNOTICE, servernode); + DoSayCommand(message, target + 1, HU_PRIVNOTICE, servernode); } From a07a9e219195d75d0a7cd251dce6faa352430b86 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 17 Jul 2023 15:12:18 +0100 Subject: [PATCH 2/8] Don't use skincolor unlocks in situations where gamedata is inappropriate - Recieving a different client's XD_NAMEANDCOLOR - Loading profiles on game boot --- src/command.c | 2 +- src/d_netcmd.c | 10 +++++----- src/k_color.c | 6 +++--- src/k_color.h | 5 +++-- src/k_profiles.c | 8 ++++---- src/menus/play-char-select.c | 2 +- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/command.c b/src/command.c index 7184ace42..1a6c74806 100644 --- a/src/command.c +++ b/src/command.c @@ -917,7 +917,7 @@ static void COM_Help_f(void) boolean follower = (cvar->PossibleValue == Followercolor_cons_t); for (i = SKINCOLOR_NONE; i < numskincolors; ++i) { - if (K_ColorUsable(i, follower) == true) + if (K_ColorUsable(i, follower, true) == true) { CONS_Printf(" %-3d : %s\n", i, skincolors[i].name); if (i == cvar->value) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 823099cf6..e6c36578d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1497,9 +1497,9 @@ static void SendNameAndColor(const UINT8 n) UINT16 sendFollowerColor = cv_followercolor[n].value; // don't allow inaccessible colors - if (sendColor != SKINCOLOR_NONE && K_ColorUsable(sendColor, false) == false) + if (sendColor != SKINCOLOR_NONE && K_ColorUsable(sendColor, false, true) == false) { - if (player->skincolor && K_ColorUsable(player->skincolor, false) == true) + if (player->skincolor && K_ColorUsable(player->skincolor, false, true) == true) { // Use our previous color CV_StealthSetValue(&cv_playercolor[n], player->skincolor); @@ -1514,7 +1514,7 @@ static void SendNameAndColor(const UINT8 n) } // ditto for follower colour: - if (sendFollowerColor != SKINCOLOR_NONE && K_ColorUsable(sendFollowerColor, true) == false) + if (sendFollowerColor != SKINCOLOR_NONE && K_ColorUsable(sendFollowerColor, true, true) == false) { CV_StealthSet(&cv_followercolor[n], "Default"); // set it to "Default". I don't care about your stupidity! sendFollowerColor = cv_followercolor[n].value; @@ -1724,7 +1724,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) boolean kick = false; // don't allow inaccessible colors - if (K_ColorUsable(p->skincolor, false) == false) + if (K_ColorUsable(p->skincolor, false, false) == false) { kick = true; } @@ -6829,7 +6829,7 @@ static void Color_OnChange(const UINT8 p) I_Assert(p < MAXSPLITSCREENPLAYERS); UINT16 color = cv_playercolor[p].value; - boolean colorisgood = (color == SKINCOLOR_NONE || K_ColorUsable(color, false) == true); + boolean colorisgood = (color == SKINCOLOR_NONE || K_ColorUsable(color, false, true) == true); if (Playing() && splitscreen < p) { diff --git a/src/k_color.c b/src/k_color.c index 9078fc6bc..27d597f87 100644 --- a/src/k_color.c +++ b/src/k_color.c @@ -217,11 +217,11 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color) } /*-------------------------------------------------- - boolean K_ColorUsable(skincolornum_t color, boolean follower) + boolean K_ColorUsable(skincolornum_t color, boolean follower, boolean locked) See header file for description. --------------------------------------------------*/ -boolean K_ColorUsable(skincolornum_t color, boolean follower) +boolean K_ColorUsable(skincolornum_t color, boolean follower, boolean locked) { INT32 i = MAXUNLOCKABLES; @@ -237,7 +237,7 @@ boolean K_ColorUsable(skincolornum_t color, boolean follower) return false; } - if (demo.playback) + if (demo.playback || !locked) { // Simplifies things elsewhere... return true; diff --git a/src/k_color.h b/src/k_color.h index b835f9194..a66626f62 100644 --- a/src/k_color.h +++ b/src/k_color.h @@ -117,7 +117,7 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color); /*-------------------------------------------------- - boolean K_ColorUsable(skincolornum_t color, skin_t *skin, follower_t *follower); + boolean K_ColorUsable(skincolornum_t color, skin_t *skin, follower_t *follower, boolean locked); Determines whenever or not we meet the unlockable conditions to use a certain color. @@ -125,12 +125,13 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color); Input Arguments:- color - Color we want to use. follower - Set to include the special follower-only color options. + locked - use local player's unlocks. Return:- true if we can use it, otherwise false. --------------------------------------------------*/ -boolean K_ColorUsable(skincolornum_t color, boolean follower); +boolean K_ColorUsable(skincolornum_t color, boolean follower, boolean locked); #ifdef __cplusplus diff --git a/src/k_profiles.c b/src/k_profiles.c index 63dccc8b7..f0a0a8e89 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -373,7 +373,7 @@ void PR_LoadProfiles(void) ; // Valid, even outside the bounds } else if (profilesList[i]->color >= numskincolors - || K_ColorUsable(profilesList[i]->color, false) == false) + || K_ColorUsable(profilesList[i]->color, false, false) == false) { profilesList[i]->color = PROFILEDEFAULTCOLOR; } @@ -383,13 +383,13 @@ void PR_LoadProfiles(void) profilesList[i]->followercolor = READUINT16(save.p); if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH - || profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE) + || profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE + || profilesList[i]->followercolor == SKINCOLOR_NONE) { ; // Valid, even outside the bounds } else if (profilesList[i]->followercolor >= numskincolors - || profilesList[i]->followercolor == SKINCOLOR_NONE - || K_ColorUsable(profilesList[i]->followercolor, true) == false) + || K_ColorUsable(profilesList[i]->followercolor, true, false) == false) { profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR; } diff --git a/src/menus/play-char-select.c b/src/menus/play-char-select.c index 111c24ed8..257f0ad06 100644 --- a/src/menus/play-char-select.c +++ b/src/menus/play-char-select.c @@ -150,7 +150,7 @@ static void M_NewPlayerColors(setup_player_t *p) // Add all unlocked colors for (i = SKINCOLOR_NONE+1; i < numskincolors; i++) { - if (K_ColorUsable(i, follower) == true) + if (K_ColorUsable(i, follower, true) == true) { M_PushMenuColor(&p->colors, i); } From beebfd0d2f60fefd9d6584258208620cd1f8de27 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Jul 2023 15:29:32 +0100 Subject: [PATCH 3/8] Ring Shooter: Only P_SetTarget if player is in game --- src/objects/ring-shooter.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/objects/ring-shooter.c b/src/objects/ring-shooter.c index 3150cf16f..c9812a6b8 100644 --- a/src/objects/ring-shooter.c +++ b/src/objects/ring-shooter.c @@ -63,8 +63,11 @@ static void RemoveRingShooterPointer(mobj_t *base) } // NULL the player's pointer. - player = &players[ rs_base_playerid(base) ]; - P_SetTarget(&player->ringShooter, NULL); + if (playeringame[ rs_base_playerid(base) ]) + { + player = &players[ rs_base_playerid(base) ]; + P_SetTarget(&player->ringShooter, NULL); + } // Remove our player ID rs_base_playerid(base) = -1; From e6619df2d49b3d59377b9c4379ac90ea596db0be Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Jul 2023 15:45:49 +0100 Subject: [PATCH 4/8] Servant Hand: Refactor to use its own thinker Since it's already ticking for the sake of a fuse, make it handle its own movement/scaling as well. Spawning is still handled by the player thinker (and can be blocked by hitlag), but this permits it to disappear when a player dies/disconnects the server. --- src/k_kart.c | 2 +- src/k_objects.h | 3 +- src/objects/servant-hand.c | 81 ++++++++++++++++++++++---------------- src/p_mobj.c | 14 +++++-- 4 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 31ee0be8e..c1f793399 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8339,7 +8339,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) { K_KartEbrakeVisuals(player); - Obj_ServantHandHandling(player); + Obj_ServantHandSpawning(player); } if (K_GetKartButtons(player) & BT_BRAKE && diff --git a/src/k_objects.h b/src/k_objects.h index e00b4813f..5d063a68e 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -144,7 +144,8 @@ void Obj_GachaBomReboundThink(mobj_t *mobj); void Obj_SpawnGachaBomRebound(mobj_t *source, mobj_t *target); /* Servant Hand */ -void Obj_ServantHandHandling(player_t *player); +void Obj_ServantHandSpawning(player_t *player); +void Obj_ServantHandThink(mobj_t *hand); void Obj_PointPlayersToXY(fixed_t x, fixed_t y); /* Super Flicky Controller */ diff --git a/src/objects/servant-hand.c b/src/objects/servant-hand.c index 2b9cb7527..80c149cde 100644 --- a/src/objects/servant-hand.c +++ b/src/objects/servant-hand.c @@ -8,7 +8,7 @@ #include "../r_main.h" #include "../g_game.h" -void Obj_ServantHandHandling(player_t *player) +void Obj_ServantHandSpawning(player_t *player) { if (player->pflags & PF_WRONGWAY || player->pflags & PF_POINTME) { @@ -36,71 +36,86 @@ void Obj_ServantHandHandling(player_t *player) } } } - - if (player->hand) - { - player->hand->destscale = mapobjectscale; - } } else if (player->handtimer != 0) { player->handtimer--; } +} + +void Obj_ServantHandThink(mobj_t *hand) +{ + UINT8 handtimer = 0; + player_t *player = NULL; + + if (P_MobjWasRemoved(hand->target)) + { + P_RemoveMobj(hand); + return; + } + + if (hand->target->health && hand->target->player && hand->target->player->hand == hand) + { + player = hand->target->player; + handtimer = hand->target->player->handtimer; + } - if (player->hand) { const fixed_t handpokespeed = 4; - const fixed_t looping = handpokespeed - abs((player->hand->threshold % (handpokespeed*2)) - handpokespeed); + const fixed_t looping = handpokespeed - abs((hand->threshold % (handpokespeed*2)) - handpokespeed); fixed_t xoffs = 0, yoffs = 0; - player->hand->color = player->skincolor; - player->hand->angle = player->besthanddirection; - - if (player->hand->fuse != 0) + if (hand->fuse != 0) { ; } else if (looping != 0) { - xoffs = FixedMul(2 * looping * mapobjectscale, FINECOSINE(player->hand->angle >> ANGLETOFINESHIFT)), - yoffs = FixedMul(2 * looping * mapobjectscale, FINESINE(player->hand->angle >> ANGLETOFINESHIFT)), + xoffs = FixedMul(2 * looping * mapobjectscale, FINECOSINE(hand->angle >> ANGLETOFINESHIFT)), + yoffs = FixedMul(2 * looping * mapobjectscale, FINESINE(hand->angle >> ANGLETOFINESHIFT)), - player->hand->threshold++; + hand->threshold++; } - else if (player->handtimer == 0) + else if (handtimer == 0) { - player->hand->fuse = 8; + hand->fuse = 8; } else { - player->hand->threshold++; + hand->threshold++; } - if (player->hand->fuse != 0) + if (hand->fuse != 0) { - if ((player->hand->fuse > 4) ^ (player->handtimer < TICRATE/2)) + if ((hand->fuse > 4) ^ (handtimer < TICRATE/2)) { - player->hand->spritexscale = FRACUNIT/3; - player->hand->spriteyscale = 3*FRACUNIT; + hand->spritexscale = FRACUNIT/3; + hand->spriteyscale = 3*FRACUNIT; } else { - player->hand->spritexscale = 2*FRACUNIT; - player->hand->spriteyscale = FRACUNIT/2; + hand->spritexscale = 2*FRACUNIT; + hand->spriteyscale = FRACUNIT/2; } } - P_MoveOrigin(player->hand, - player->mo->x + xoffs, - player->mo->y + yoffs, - player->mo->z + player->mo->height + 30*mapobjectscale - ); - K_FlipFromObject(player->hand, player->mo); + if (player != NULL) + { + hand->color = player->skincolor; + hand->angle = player->besthanddirection; - player->hand->sprzoff = player->mo->sprzoff; + P_MoveOrigin(hand, + player->mo->x + xoffs, + player->mo->y + yoffs, + player->mo->z + player->mo->height + 30*mapobjectscale + ); + K_FlipFromObject(hand, player->mo); - player->hand->renderflags &= ~RF_DONTDRAW; - player->hand->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player)); + hand->sprzoff = player->mo->sprzoff; + + hand->renderflags &= ~RF_DONTDRAW; + hand->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player)); + } } } diff --git a/src/p_mobj.c b/src/p_mobj.c index d927090fb..103a1e452 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7380,6 +7380,13 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_MantaRingThink(mobj); break; } + case MT_SERVANTHAND: + { + Obj_ServantHandThink(mobj); + if (P_MobjWasRemoved(mobj)) + return false; + break; + } case MT_BALLHOG: { mobj_t *ghost = P_SpawnGhostMobj(mobj); @@ -9831,10 +9838,11 @@ static boolean P_FuseThink(mobj_t *mobj) } case MT_SERVANTHAND: { - if (!mobj->target - || P_MobjWasRemoved(mobj->target) + if (P_MobjWasRemoved(mobj->target) + || !mobj->target->health || !mobj->target->player - || mobj->target->player->handtimer == 0) + || mobj->target->player->handtimer == 0 + || mobj->target->player->hand != mobj) { P_RemoveMobj(mobj); return false; From ce08ac0f33df67761a7c17d9c55895520c76fa6c Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Jul 2023 15:50:39 +0100 Subject: [PATCH 5/8] General tidy of mobj_t pointers on player_t struct - CL_ClearPlayer - Delete followmobj, stumbleIndicator, and sliptideZipIndicator - Wipe flickyAttacker and powerup.flickyController - G_PlayerReborn - Properly destroy the Follower and its bubble overlays, etc - Ensure ringShooter pointer is kept - Delete followmobj, stumbleIndicator, and sliptideZipIndicator - Wipe flickyAttacker and powerup.flickyController - P_PlayerThink - Ensure all invalid pointers are erased, even on hitlag frames --- src/d_clisrv.c | 34 +++++++++++++++++++++++++--------- src/g_game.c | 43 +++++++++++++++++++++++++++++-------------- src/k_kart.c | 12 ------------ src/p_user.c | 19 +++++++++++++++++++ 4 files changed, 73 insertions(+), 35 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d5023265f..fc365a85b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2775,20 +2775,36 @@ void CL_ClearPlayer(INT32 playernum) K_RemoveFollower(&players[playernum]); } - if (players[playernum].mo) - { - P_RemoveMobj(players[playernum].mo); - P_SetTarget(&players[playernum].mo, NULL); +#define PlayerPointerRemove(field) \ + if (field) \ + { \ + P_RemoveMobj(field); \ + P_SetTarget(&field, NULL); \ } + // These are mostly subservient to the player, and may not clean themselves up. + PlayerPointerRemove(players[playernum].mo); + PlayerPointerRemove(players[playernum].followmobj); + PlayerPointerRemove(players[playernum].stumbleIndicator); + PlayerPointerRemove(players[playernum].sliptideZipIndicator); + +#undef PlayerPointerRemove + + // These have thinkers of their own. + P_SetTarget(&players[playernum].whip, NULL); + P_SetTarget(&players[playernum].hand, NULL); + P_SetTarget(&players[playernum].hoverhyudoro, NULL); + P_SetTarget(&players[playernum].ringShooter, NULL); + + // TODO: Any better handling in store? + P_SetTarget(&players[playernum].flickyAttacker, NULL); + P_SetTarget(&players[playernum].powerup.flickyController, NULL); + + // These are camera items and possibly belong to multiple players. P_SetTarget(&players[playernum].skybox.viewpoint, NULL); P_SetTarget(&players[playernum].skybox.centerpoint, NULL); P_SetTarget(&players[playernum].awayview.mobj, NULL); - P_SetTarget(&players[playernum].followmobj, NULL); - P_SetTarget(&players[playernum].hoverhyudoro, NULL); - P_SetTarget(&players[playernum].stumbleIndicator, NULL); - P_SetTarget(&players[playernum].sliptideZipIndicator, NULL); - P_SetTarget(&players[playernum].ringShooter, NULL); + } // Handle parties. diff --git a/src/g_game.c b/src/g_game.c index 1280a8319..708479fbc 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2455,10 +2455,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT32 followerskin; UINT16 followercolor; - mobj_t *follower; // old follower, will probably be removed by the time we're dead but you never know. - mobj_t *hoverhyudoro; - mobj_t *skyboxviewpoint; - mobj_t *skyboxcenterpoint; + mobj_t *ringShooter, *hoverhyudoro; + mobj_t *skyboxviewpoint, *skyboxcenterpoint; INT32 charflags; UINT32 followitem; @@ -2677,22 +2675,40 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) if (!betweenmaps) { - follower = players[player].follower; - P_SetTarget(&players[player].follower, NULL); - P_SetTarget(&players[player].awayview.mobj, NULL); - P_SetTarget(&players[player].stumbleIndicator, NULL); + K_RemoveFollower(&players[player]); + +#define PlayerPointerRemove(field) \ + if (field) \ + { \ + P_RemoveMobj(field); \ + P_SetTarget(&field, NULL); \ + } + + // These are mostly subservient to the player, and may not clean themselves up. + PlayerPointerRemove(players[player].followmobj); + PlayerPointerRemove(players[player].stumbleIndicator); + PlayerPointerRemove(players[player].sliptideZipIndicator); + +#undef PlayerPointerRemove + + // These will erase themselves. P_SetTarget(&players[player].whip, NULL); P_SetTarget(&players[player].hand, NULL); - P_SetTarget(&players[player].ringShooter, NULL); - P_SetTarget(&players[player].followmobj, NULL); + // TODO: Any better handling in store? + P_SetTarget(&players[player].awayview.mobj, NULL); + P_SetTarget(&players[player].flickyAttacker, NULL); + P_SetTarget(&players[player].powerup.flickyController, NULL); + + // The following pointers are safe to set directly, because the end goal should be refcount consistency before and after remanifestation. + ringShooter = players[player].ringShooter; hoverhyudoro = players[player].hoverhyudoro; skyboxviewpoint = players[player].skybox.viewpoint; skyboxcenterpoint = players[player].skybox.centerpoint; } else { - follower = hoverhyudoro = NULL; + ringShooter = hoverhyudoro = NULL; skyboxviewpoint = skyboxcenterpoint = NULL; } @@ -2769,9 +2785,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) if (saveroundconditions) memcpy(&p->roundconditions, &roundconditions, sizeof (p->roundconditions)); - if (follower) - P_RemoveMobj(follower); - + // See above comment about refcount consistency. + p->ringShooter = ringShooter; p->hoverhyudoro = hoverhyudoro; p->skybox.viewpoint = skyboxviewpoint; p->skybox.centerpoint = skyboxcenterpoint; diff --git a/src/k_kart.c b/src/k_kart.c index c1f793399..4f570f3b5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8065,9 +8065,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->guardCooldown) player->guardCooldown--; - - if (player->whip && P_MobjWasRemoved(player->whip)) - P_SetTarget(&player->whip, NULL); if (player->startboost > 0 && onground == true) { @@ -8326,15 +8323,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->tripwireState = TRIPSTATE_NONE; } - if (player->hand && P_MobjWasRemoved(player->hand)) - P_SetTarget(&player->hand, NULL); - - if (player->flickyAttacker && P_MobjWasRemoved(player->flickyAttacker)) - P_SetTarget(&player->flickyAttacker, NULL); - - if (player->powerup.flickyController && P_MobjWasRemoved(player->powerup.flickyController)) - P_SetTarget(&player->powerup.flickyController, NULL); - if (player->spectator == false) { K_KartEbrakeVisuals(player); diff --git a/src/p_user.c b/src/p_user.c index 74b5d6231..692eabdb0 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4249,6 +4249,25 @@ void P_PlayerThink(player_t *player) player->playerstate = PST_DEAD; } + // Erasing invalid player pointers + { +#define PlayerPointerErase(field) \ + if (field && P_MobjWasRemoved(field)) \ + P_SetTarget(&field, NULL); \ + + PlayerPointerErase(player->followmobj); + PlayerPointerErase(player->stumbleIndicator); + PlayerPointerErase(player->sliptideZipIndicator); + PlayerPointerErase(player->whip); + PlayerPointerErase(player->hand); + PlayerPointerErase(player->ringShooter); + PlayerPointerErase(player->hoverhyudoro); + PlayerPointerErase(player->flickyAttacker); + PlayerPointerErase(player->powerup.flickyController); + +#undef PlayerPointerErase + } + player->old_drawangle = player->drawangle; P_TickAltView(&player->awayview); From bfc8036e7e32f219648a8af2a822b594c8b2b3da Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Jul 2023 17:53:54 +0100 Subject: [PATCH 6/8] Use display players for hearing/seeing horns, not local players Matches other local effects, and permits spectators (such as in tournaments) and replay watchers to see the honk. --- src/k_follower.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_follower.c b/src/k_follower.c index 511e81932..b1e4261b0 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -722,7 +722,7 @@ void K_FollowerHornTaunt(player_t *taunter, player_t *victim) || victim == NULL || taunter->followerskin < 0 || taunter->followerskin >= numfollowers - || (P_IsLocalPlayer(victim) == false && cv_karthorns.value != 2) + || (P_IsDisplayPlayer(victim) == false && cv_karthorns.value != 2) || P_MobjWasRemoved(taunter->mo) == true || P_MobjWasRemoved(taunter->follower) == true ) From 0b3ebb0686fcbb8ce312f12a73cc2e5aee827f4e Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 18 Jul 2023 18:00:51 +0100 Subject: [PATCH 7/8] Kickstart Accel indicator sound modifications - Play in replays/as spectator - Do not play for bot-controlled players (exiting, Podium, etc) --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 74b5d6231..c9a3e0357 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4302,7 +4302,7 @@ void P_PlayerThink(player_t *player) else if (player->kickstartaccel < ACCEL_KICKSTART) { player->kickstartaccel++; - if ((player->kickstartaccel == ACCEL_KICKSTART) && P_IsLocalPlayer(player)) + if ((player->kickstartaccel == ACCEL_KICKSTART) && !K_PlayerUsesBotMovement(player) && P_IsDisplayPlayer(player)) { S_StartSound(NULL, sfx_ding); } From 55cda3dd5c40bbc85e25e7bf250930c89969d357 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 19 Jul 2023 22:29:02 +0100 Subject: [PATCH 8/8] Correctly flip minimap player icon dot in encore mode Resolves #582 --- src/k_hud.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index f60aff730..6c3a1d283 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3994,8 +3994,6 @@ static void K_drawKartMinimap(void) if (doprogressionbar == false) { - angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle); - interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpy = R_InterpolateFixed(mobj->old_y, mobj->y); @@ -4010,6 +4008,10 @@ static void K_drawKartMinimap(void) if (!nocontest) { + angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle); + if (encoremode) + ang = ANGLE_180 - ang; + K_drawKartMinimapIcon( interpx, interpy, @@ -4198,8 +4200,6 @@ static void K_drawKartMinimap(void) if (doprogressionbar == false) { - angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle); - interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpy = R_InterpolateFixed(mobj->old_y, mobj->y); @@ -4214,6 +4214,10 @@ static void K_drawKartMinimap(void) if (!nocontest) { + angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle); + if (encoremode) + ang = ANGLE_180 - ang; + K_drawKartMinimapIcon( interpx, interpy,