Skip to content

Commit 1e38cc3

Browse files
Merge pull request chocolate-doom#1669 from mikeday0/hexendesync
hexen: Fix P_LookForPlayers desync
2 parents 6969f6d + 82f4fee commit 1e38cc3

File tree

2 files changed

+58
-16
lines changed

2 files changed

+58
-16
lines changed

src/hexen/p_enemy.c

+29-8
Original file line numberDiff line numberDiff line change
@@ -528,25 +528,46 @@ boolean P_LookForPlayers(mobj_t * actor, boolean allaround)
528528
player_t *player;
529529
angle_t an;
530530
fixed_t dist;
531+
int consecutive_missing = 0; // for breaking infinite loop
531532

532533
if (!netgame && players[0].health <= 0)
533534
{ // Single player game and player is dead, look for monsters
534535
return (P_LookForMonsters(actor));
535536
}
536537
c = 0;
537538

538-
// NOTE: This behavior has been changed from the Vanilla behavior, where
539-
// an infinite loop can occur if players 0-3 all quit the game. Although
540-
// technically this is not what Vanilla does, fixing this is highly
541-
// desirable, and having the game simply lock up is not acceptable.
542-
// stop = (actor->lastlook - 1) & 3;
543-
// for (;; actor->lastlook = (actor->lastlook + 1) & 3)
539+
// The 3 below is probably a mistake (it should be MAXPLAYERS - 1, or 7)
540+
// and in vanilla this can potentially cause an infinite loop in
541+
// multiplayer. Unfortunately we can't correct the mistake - doing so will
542+
// cause desyncs. Upon spawning, each enemy's lastlook is initialized to a
543+
// random value between 0 and 7 (i.e MAXPLAYERS - 1). There's a chance
544+
// that the first call of this function for that enemy will return early
545+
// courtesy of the actor->lastlook == stop condition. In a single-player
546+
// game this occurs when (actor->lastlook - 1) & 3 equals 0, or when
547+
// lastlook equals 1 or 5.
544548

545-
stop = (actor->lastlook + maxplayers - 1) % maxplayers;
546-
for (;; actor->lastlook = (actor->lastlook + 1) % maxplayers)
549+
// If you use MAXPLAYERS - 1, it has the side effect of altering which
550+
// enemies are affected by an early actor->lastlook == stop return. Now it
551+
// happens when (actor->lastlook - 1) & 7 equals 0, or when lastlook equals
552+
// 1, *not* 1 and 5 as above.
553+
554+
stop = (actor->lastlook - 1) & 3;
555+
for (;; actor->lastlook = (actor->lastlook + 1) & 3)
547556
{
548557
if (!playeringame[actor->lastlook])
558+
{
559+
// Break the vanilla infinite loop here. It can occur if there are
560+
// > 4 players and players 0 - 3 all quit the game. Error out
561+
// instead.
562+
if (consecutive_missing == 4)
563+
{
564+
I_Error("P_LookForPlayers: No player 1 - 4.\n");
565+
}
566+
consecutive_missing++;
549567
continue;
568+
}
569+
570+
consecutive_missing = 0;
550571

551572
if (c++ == 2 || actor->lastlook == stop)
552573
return false; // done looking

src/strife/p_enemy.c

+29-8
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ P_LookForPlayers
731731
angle_t an;
732732
fixed_t dist;
733733
mobj_t * master = players[actor->miscdata].mo;
734+
int consecutive_missing = 0; // for breaking infinite loop
734735

735736
// haleyjd 09/05/10: handle Allies
736737
if(actor->flags & MF_ALLY)
@@ -788,19 +789,39 @@ P_LookForPlayers
788789

789790
c = 0;
790791

791-
// NOTE: This behavior has been changed from the Vanilla behavior, where
792-
// an infinite loop can occur if players 0-3 all quit the game. Although
793-
// technically this is not what Vanilla does, fixing this is highly
794-
// desirable, and having the game simply lock up is not acceptable.
795-
// stop = (actor->lastlook - 1) & 3;
796-
// for (;; actor->lastlook = (actor->lastlook + 1) & 3)
792+
// The 3 below is probably a mistake (it should be MAXPLAYERS - 1, or 7)
793+
// and in vanilla this can potentially cause an infinite loop in
794+
// multiplayer. Unfortunately we can't correct the mistake - doing so will
795+
// cause desyncs. Upon spawning, each enemy's lastlook is initialized to a
796+
// random value between 0 and 7 (i.e MAXPLAYERS - 1). There's a chance
797+
// that the first call of this function for that enemy will return early
798+
// courtesy of the actor->lastlook == stop condition. In a single-player
799+
// game this occurs when (actor->lastlook - 1) & 3 equals 0, or when
800+
// lastlook equals 1 or 5.
797801

798-
stop = (actor->lastlook + MAXPLAYERS - 1) % MAXPLAYERS;
802+
// If you use MAXPLAYERS - 1, it has the side effect of altering which
803+
// enemies are affected by an early actor->lastlook == stop return. Now it
804+
// happens when (actor->lastlook - 1) & 7 equals 0, or when lastlook equals
805+
// 1, *not* 1 and 5 as above.
799806

800-
for ( ; ; actor->lastlook = (actor->lastlook + 1) % MAXPLAYERS)
807+
stop = (actor->lastlook-1)&3;
808+
809+
for ( ; ; actor->lastlook = (actor->lastlook+1)&3 )
801810
{
802811
if (!playeringame[actor->lastlook])
812+
{
813+
// Break the vanilla infinite loop here. It can occur if there are
814+
// > 4 players and players 0 - 3 all quit the game. Error out
815+
// instead.
816+
if (consecutive_missing == 4)
817+
{
818+
I_Error("P_LookForPlayers: No player 1 - 4.\n");
819+
}
820+
consecutive_missing++;
803821
continue;
822+
}
823+
824+
consecutive_missing = 0;
804825

805826
if (c++ == 2
806827
|| actor->lastlook == stop)

0 commit comments

Comments
 (0)