From 77c663a9b8c478df2be9b027799fa06792d0292b Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 17 Mar 2012 00:52:33 +0000 Subject: [PATCH] - In conjunction with all the below changes, attempt to fix A_CheckSightOrRange and A_CheckSight for multiplayer: They now always check through the eyes of every player. For players whose cameras are not players, they also check through the eyes of those cameras. - Using spynext/spyprev to switch from a non-player to a player now writes a command to the network stream and lets Net_DoCommand() take care of it later. The logic here is that if a player is viewing from something that isn't another player, then every player needs to know about it for sync purposes. Consequently, when they stop viewing from a non-player and switch to a player, everybody needs to know about that too. But if they are viewing from a player, it doesn't matter which player it is, so they can spynext/spyprev all they want without letting the other players know about it (and without potentially breaking demos--due to the above-mentioned two codepointers--while doing it during demo playback). - Replaced the instances of checking players[consoleplayer].camera for a valid pointer to ones that do it for every player. - Fixed: Upon changing levels, all players but the consoleplayer would have their cameras NULLed. - Fixed: player_t::FixPointers() needs to bypass the read barriers, or it won't be able to do substitutions of old objects that are pending deletion. SVN r3448 (trunk) --- src/d_net.cpp | 17 +++++-- src/d_protocol.h | 1 + src/g_game.cpp | 7 ++- src/g_level.cpp | 12 +++-- src/p_mobj.cpp | 7 ++- src/p_user.cpp | 34 +++++++------ src/thingdef/thingdef_codeptr.cpp | 81 +++++++++++++++++++++---------- src/version.h | 4 +- 8 files changed, 109 insertions(+), 54 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index b2742871ca..27af76c8d4 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -592,13 +592,16 @@ void PlayerIsGone (int netnode, int netconsole) Printf ("%s left the game\n", players[netconsole].userinfo.netname); } - // [RH] Revert to your own view if spying through the player who left - if (players[consoleplayer].camera == players[netconsole].mo) + // [RH] Revert each player to their own view if spying through the player who left + for (int ii = 0; ii < MAXPLAYERS; ++ii) { - players[consoleplayer].camera = players[consoleplayer].mo; - if (StatusBar != NULL) + if (playeringame[ii] && players[ii].camera == players[netconsole].mo) { - StatusBar->AttachToPlayer (&players[consoleplayer]); + players[ii].camera = players[ii].mo; + if (ii == consoleplayer && StatusBar != NULL) + { + StatusBar->AttachToPlayer (&players[ii]); + } } } @@ -2460,6 +2463,10 @@ void Net_DoCommand (int type, BYTE **stream, int player) F_AdvanceIntermission(); break; + case DEM_REVERTCAMERA: + players[player].camera = players[player].mo; + break; + default: I_Error ("Unknown net command: %d", type); break; diff --git a/src/d_protocol.h b/src/d_protocol.h index 22461010b4..cee2fca6c1 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -162,6 +162,7 @@ enum EDemoCommand DEM_SETPITCHLIMIT, // 63 Byte: Up limit, Byte: Down limit (in degrees) DEM_ADVANCEINTER, // 64 Advance intermission screen state DEM_RUNNAMEDSCRIPT, // 65 String: Script name, Byte: Arg count + Always flag; each arg is a 4-byte int + DEM_REVERTCAMERA, // 66 }; // The following are implemented by cht_DoCheat in m_cheat.cpp diff --git a/src/g_game.cpp b/src/g_game.cpp index ee8092b727..a090dd641b 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -831,7 +831,12 @@ static void ChangeSpy (bool forward) // If not viewing through a player, return your eyes to your own head. if (players[consoleplayer].camera->player == NULL) { - players[consoleplayer].camera = players[consoleplayer].mo; + // When watching demos, you will just have to wait until your player + // has done this for you, since it could desync otherwise. + if (!demoplayback) + { + Net_WriteByte(DEM_REVERTCAMERA); + } return; } diff --git a/src/g_level.cpp b/src/g_level.cpp index 9e874c5d3c..903a9a6c55 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -920,10 +920,13 @@ void G_DoLoadLevel (int position, bool autosave) level.starttime = gametic; G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level. G_FinishTravel (); - if (players[consoleplayer].camera == NULL || - players[consoleplayer].camera->player != NULL) - { // If we are viewing through a player, make sure it is us. - players[consoleplayer].camera = players[consoleplayer].mo; + // For each player, if they are viewing through a player, make sure it is themselves. + for (int ii = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[ii] && (players[ii].camera == NULL || players[ii].camera->player != NULL)) + { + players[ii].camera = players[ii].mo; + } } StatusBar->AttachToPlayer (&players[consoleplayer]); P_DoDeferedScripts (); // [RH] Do script actions that were triggered on another map. @@ -1143,6 +1146,7 @@ void G_FinishTravel () pawn->target = NULL; pawn->lastenemy = NULL; pawn->player->mo = pawn; + pawn->player->camera = pawn; DObject::StaticPointerSubstitution (oldpawn, pawn); oldpawn->Destroy(); pawndup->Destroy (); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 3d676cddc1..c9ed7e30c2 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4051,9 +4051,12 @@ APlayerPawn *P_SpawnPlayer (FMapThing *mthing, bool tempplayer) p->velx = p->vely = 0; // killough 10/98: initialize bobbing to 0. - if (players[consoleplayer].camera == oldactor) + for (int ii = 0; ii < MAXPLAYERS; ++ii) { - players[consoleplayer].camera = mobj; + if (playeringame[ii] && players[ii].camera == oldactor) + { + players[ii].camera = oldactor; + } } // [RH] Allow chasecam for demo watching diff --git a/src/p_user.cpp b/src/p_user.cpp index de48cf20b7..1e5dd2bd94 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -326,21 +326,25 @@ size_t player_t::FixPointers (const DObject *old, DObject *rep) { APlayerPawn *replacement = static_cast(rep); size_t changed = 0; - if (mo == old) mo = replacement, changed++; - if (poisoner == old) poisoner = replacement, changed++; - if (attacker == old) attacker = replacement, changed++; - if (camera == old) camera = replacement, changed++; - if (dest == old) dest = replacement, changed++; - if (prev == old) prev = replacement, changed++; - if (enemy == old) enemy = replacement, changed++; - if (missile == old) missile = replacement, changed++; - if (mate == old) mate = replacement, changed++; - if (last_mate == old) last_mate = replacement, changed++; - if (ReadyWeapon == old) ReadyWeapon = static_cast(rep), changed++; - if (PendingWeapon == old) PendingWeapon = static_cast(rep), changed++; - if (PremorphWeapon == old) PremorphWeapon = static_cast(rep), changed++; - if (ConversationNPC == old) ConversationNPC = replacement, changed++; - if (ConversationPC == old) ConversationPC = replacement, changed++; + + // The construct *& is used in several of these to avoid the read barriers + // that would turn the pointer we want to check to NULL if the old object + // is pending deletion. + if (mo == old) mo = replacement, changed++; + if (*&poisoner == old) poisoner = replacement, changed++; + if (*&attacker == old) attacker = replacement, changed++; + if (*&camera == old) camera = replacement, changed++; + if (*&dest == old) dest = replacement, changed++; + if (*&prev == old) prev = replacement, changed++; + if (*&enemy == old) enemy = replacement, changed++; + if (*&missile == old) missile = replacement, changed++; + if (*&mate == old) mate = replacement, changed++; + if (*&last_mate == old) last_mate = replacement, changed++; + if (ReadyWeapon == old) ReadyWeapon = static_cast(rep), changed++; + if (PendingWeapon == old) PendingWeapon = static_cast(rep), changed++; + if (*&PremorphWeapon == old) PremorphWeapon = static_cast(rep), changed++; + if (*&ConversationNPC == old) ConversationNPC = replacement, changed++; + if (*&ConversationPC == old) ConversationPC = replacement, changed++; return changed; } diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index d2ff8dda71..a3288db87b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2253,7 +2253,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY)) return; + if (playeringame[i]) + { + // Always check sight from each player. + if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY)) + { + return; + } + // If a player is viewing from a non-player, then check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY)) + { + return; + } + } } ACTION_JUMP(jump); @@ -2266,6 +2279,42 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight) // Useful for maps with many multi-actor special effects. // //=========================================================================== +static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range) +{ + if (camera == NULL) + { + return false; + } + // Check distance first, since it's cheaper than checking sight. + double dx = self->x - camera->x; + double dy = self->y - camera->y; + double dz; + fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight + if (eyez > self->z + self->height) + { + dz = self->z + self->height - eyez; + } + else if (eyez < self->z) + { + dz = self->z - eyez; + } + else + { + dz = 0; + } + if ((dx*dx) + (dy*dy) + (dz*dz) <= range) + { // Within range + return true; + } + + // Now check LOS. + if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) + { // Visible + return true; + } + return false; +} + DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) { ACTION_PARAM_START(2); @@ -2279,33 +2328,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange) { if (playeringame[i]) { - AActor *camera = players[i].camera; - - // Check distance first, since it's cheaper than checking sight. - double dx = self->x - camera->x; - double dy = self->y - camera->y; - double dz; - fixed_t eyez = (camera->z + camera->height - (camera->height>>2)); // same eye height as P_CheckSight - if (eyez > self->z + self->height) + // Always check from each player. + if (DoCheckSightOrRange(self, players[i].mo, range)) { - dz = self->z + self->height - eyez; - } - else if (eyez < self->z) - { - dz = self->z - eyez; - } - else - { - dz = 0; - } - if ((dx*dx) + (dy*dy) + (dz*dz) <= range) - { // Within range return; } - - // Now check LOS. - if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) - { // Visible + // If a player is viewing from a non-player, check that too. + if (players[i].camera != NULL && players[i].camera->player == NULL && + DoCheckSightOrRange(self, players[i].camera, range)) + { return; } } diff --git a/src/version.h b/src/version.h index ab877820df..ec77671bd4 100644 --- a/src/version.h +++ b/src/version.h @@ -54,7 +54,7 @@ // Version identifier for network games. // Bump it every time you do a release unless you're certain you // didn't change anything that will affect sync. -#define NETGAMEVERSION 226 +#define NETGAMEVERSION 227 // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to @@ -64,7 +64,7 @@ // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. // Otherwise, it should be safe to leave it alone. -#define DEMOGAMEVERSION 0x217 +#define DEMOGAMEVERSION 0x218 // Minimum demo version we can play. // Bump it whenever you change or remove existing DEM_ commands.