- 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)
This commit is contained in:
Randy Heit 2012-03-17 00:52:33 +00:00
parent 07118147d8
commit 77c663a9b8
8 changed files with 109 additions and 54 deletions

View File

@ -592,13 +592,16 @@ void PlayerIsGone (int netnode, int netconsole)
Printf ("%s left the game\n", players[netconsole].userinfo.netname); Printf ("%s left the game\n", players[netconsole].userinfo.netname);
} }
// [RH] Revert to your own view if spying through the player who left // [RH] Revert each player to their own view if spying through the player who left
if (players[consoleplayer].camera == players[netconsole].mo) for (int ii = 0; ii < MAXPLAYERS; ++ii)
{ {
players[consoleplayer].camera = players[consoleplayer].mo; if (playeringame[ii] && players[ii].camera == players[netconsole].mo)
if (StatusBar != NULL)
{ {
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(); F_AdvanceIntermission();
break; break;
case DEM_REVERTCAMERA:
players[player].camera = players[player].mo;
break;
default: default:
I_Error ("Unknown net command: %d", type); I_Error ("Unknown net command: %d", type);
break; break;

View File

@ -162,6 +162,7 @@ enum EDemoCommand
DEM_SETPITCHLIMIT, // 63 Byte: Up limit, Byte: Down limit (in degrees) DEM_SETPITCHLIMIT, // 63 Byte: Up limit, Byte: Down limit (in degrees)
DEM_ADVANCEINTER, // 64 Advance intermission screen state 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_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 // The following are implemented by cht_DoCheat in m_cheat.cpp

View File

@ -831,7 +831,12 @@ static void ChangeSpy (bool forward)
// If not viewing through a player, return your eyes to your own head. // If not viewing through a player, return your eyes to your own head.
if (players[consoleplayer].camera->player == NULL) 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; return;
} }

View File

@ -920,10 +920,13 @@ void G_DoLoadLevel (int position, bool autosave)
level.starttime = gametic; level.starttime = gametic;
G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level. G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level.
G_FinishTravel (); G_FinishTravel ();
if (players[consoleplayer].camera == NULL || // For each player, if they are viewing through a player, make sure it is themselves.
players[consoleplayer].camera->player != NULL) for (int ii = 0; i < MAXPLAYERS; ++i)
{ // If we are viewing through a player, make sure it is us. {
players[consoleplayer].camera = players[consoleplayer].mo; if (playeringame[ii] && (players[ii].camera == NULL || players[ii].camera->player != NULL))
{
players[ii].camera = players[ii].mo;
}
} }
StatusBar->AttachToPlayer (&players[consoleplayer]); StatusBar->AttachToPlayer (&players[consoleplayer]);
P_DoDeferedScripts (); // [RH] Do script actions that were triggered on another map. P_DoDeferedScripts (); // [RH] Do script actions that were triggered on another map.
@ -1143,6 +1146,7 @@ void G_FinishTravel ()
pawn->target = NULL; pawn->target = NULL;
pawn->lastenemy = NULL; pawn->lastenemy = NULL;
pawn->player->mo = pawn; pawn->player->mo = pawn;
pawn->player->camera = pawn;
DObject::StaticPointerSubstitution (oldpawn, pawn); DObject::StaticPointerSubstitution (oldpawn, pawn);
oldpawn->Destroy(); oldpawn->Destroy();
pawndup->Destroy (); pawndup->Destroy ();

View File

@ -4051,9 +4051,12 @@ APlayerPawn *P_SpawnPlayer (FMapThing *mthing, bool tempplayer)
p->velx = p->vely = 0; // killough 10/98: initialize bobbing to 0. 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 // [RH] Allow chasecam for demo watching

View File

@ -326,21 +326,25 @@ size_t player_t::FixPointers (const DObject *old, DObject *rep)
{ {
APlayerPawn *replacement = static_cast<APlayerPawn *>(rep); APlayerPawn *replacement = static_cast<APlayerPawn *>(rep);
size_t changed = 0; size_t changed = 0;
if (mo == old) mo = replacement, changed++;
if (poisoner == old) poisoner = replacement, changed++; // The construct *& is used in several of these to avoid the read barriers
if (attacker == old) attacker = replacement, changed++; // that would turn the pointer we want to check to NULL if the old object
if (camera == old) camera = replacement, changed++; // is pending deletion.
if (dest == old) dest = replacement, changed++; if (mo == old) mo = replacement, changed++;
if (prev == old) prev = replacement, changed++; if (*&poisoner == old) poisoner = replacement, changed++;
if (enemy == old) enemy = replacement, changed++; if (*&attacker == old) attacker = replacement, changed++;
if (missile == old) missile = replacement, changed++; if (*&camera == old) camera = replacement, changed++;
if (mate == old) mate = replacement, changed++; if (*&dest == old) dest = replacement, changed++;
if (last_mate == old) last_mate = replacement, changed++; if (*&prev == old) prev = replacement, changed++;
if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++; if (*&enemy == old) enemy = replacement, changed++;
if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++; if (*&missile == old) missile = replacement, changed++;
if (PremorphWeapon == old) PremorphWeapon = static_cast<AWeapon *>(rep), changed++; if (*&mate == old) mate = replacement, changed++;
if (ConversationNPC == old) ConversationNPC = replacement, changed++; if (*&last_mate == old) last_mate = replacement, changed++;
if (ConversationPC == old) ConversationPC = replacement, changed++; if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++;
if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++;
if (*&PremorphWeapon == old) PremorphWeapon = static_cast<AWeapon *>(rep), changed++;
if (*&ConversationNPC == old) ConversationNPC = replacement, changed++;
if (*&ConversationPC == old) ConversationPC = replacement, changed++;
return changed; return changed;
} }

View File

@ -2253,7 +2253,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight)
for (int i = 0; i < MAXPLAYERS; i++) 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); ACTION_JUMP(jump);
@ -2266,6 +2279,42 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight)
// Useful for maps with many multi-actor special effects. // 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) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange)
{ {
ACTION_PARAM_START(2); ACTION_PARAM_START(2);
@ -2279,33 +2328,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange)
{ {
if (playeringame[i]) if (playeringame[i])
{ {
AActor *camera = players[i].camera; // Always check from each player.
if (DoCheckSightOrRange(self, players[i].mo, range))
// 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; return;
} }
// If a player is viewing from a non-player, check that too.
// Now check LOS. if (players[i].camera != NULL && players[i].camera->player == NULL &&
if (P_CheckSight(camera, self, SF_IGNOREVISIBILITY)) DoCheckSightOrRange(self, players[i].camera, range))
{ // Visible {
return; return;
} }
} }

View File

@ -54,7 +54,7 @@
// Version identifier for network games. // Version identifier for network games.
// Bump it every time you do a release unless you're certain you // Bump it every time you do a release unless you're certain you
// didn't change anything that will affect sync. // didn't change anything that will affect sync.
#define NETGAMEVERSION 226 #define NETGAMEVERSION 227
// Version stored in the ini's [LastRun] section. // Version stored in the ini's [LastRun] section.
// Bump it if you made some configuration change that you want to // Bump it if you made some configuration change that you want to
@ -64,7 +64,7 @@
// Protocol version used in demos. // Protocol version used in demos.
// Bump it if you change existing DEM_ commands or add new ones. // Bump it if you change existing DEM_ commands or add new ones.
// Otherwise, it should be safe to leave it alone. // Otherwise, it should be safe to leave it alone.
#define DEMOGAMEVERSION 0x217 #define DEMOGAMEVERSION 0x218
// Minimum demo version we can play. // Minimum demo version we can play.
// Bump it whenever you change or remove existing DEM_ commands. // Bump it whenever you change or remove existing DEM_ commands.