Browse Source

Fix errors where Monster::enemy references garbage memory

pull/8010/head
staphen 11 months ago committed by Anders Jenbo
parent
commit
308399e31e
  1. 70
      Source/monster.cpp
  2. 1
      Source/monster.h
  3. 6
      Source/msg.cpp
  4. 2
      Source/multi.cpp
  5. 2
      Source/player.cpp

70
Source/monster.cpp

@ -442,10 +442,11 @@ void ClrAllMonsters()
monster.position.old = { 0, 0 };
monster.direction = static_cast<Direction>(GenerateRnd(8));
monster.animInfo = {};
monster.flags = 0;
monster.flags = MFLAG_NO_ENEMY;
monster.isInvalid = false;
monster.enemy = GenerateRnd(gbActivePlayers);
monster.enemyPosition = Players[monster.enemy].position.future;
monster.enemy = 0;
monster.enemyPosition = {};
DiscardRandomValues(1);
}
}
@ -581,13 +582,21 @@ tl::expected<void, std::string> LoadDiabMonsts()
void DeleteMonster(size_t activeIndex)
{
const Monster &monster = Monsters[ActiveMonsters[activeIndex]];
const unsigned monsterId = ActiveMonsters[activeIndex];
const Monster &monster = Monsters[monsterId];
if ((monster.flags & MFLAG_BERSERK) != 0) {
AddUnLight(monster.lightId);
}
ActiveMonsterCount--;
std::swap(ActiveMonsters[activeIndex], ActiveMonsters[ActiveMonsterCount]); // This ensures alive monsters are before ActiveMonsterCount in the array and any deleted monster after
for (size_t i = 0; i < ActiveMonsterCount; i++) {
Monster &activeMonster = Monsters[ActiveMonsters[i]];
if ((activeMonster.flags & MFLAG_TARGETS_MONSTER) != 0 && activeMonster.enemy == monsterId) {
activeMonster.flags |= MFLAG_NO_ENEMY;
}
}
}
void NewMonsterAnim(Monster &monster, MonsterGraphic graphic, Direction md, AnimationDistributionFlags flags = AnimationDistributionFlags::None, int8_t numSkippedFrames = 0, int8_t distributeFramesBeforeFrame = 0)
@ -1212,10 +1221,12 @@ void MonsterAttackPlayer(Monster &monster, Player &player, int hit, int minDam,
void MonsterAttackEnemy(Monster &monster, int hit, int minDam, int maxDam)
{
if ((monster.flags & MFLAG_TARGETS_MONSTER) != 0)
MonsterAttackMonster(monster, Monsters[monster.enemy], hit, minDam, maxDam);
else
MonsterAttackPlayer(monster, Players[monster.enemy], hit, minDam, maxDam);
if ((monster.flags & MFLAG_NO_ENEMY) == 0) {
if ((monster.flags & MFLAG_TARGETS_MONSTER) != 0)
MonsterAttackMonster(monster, Monsters[monster.enemy], hit, minDam, maxDam);
else
MonsterAttackPlayer(monster, Players[monster.enemy], hit, minDam, maxDam);
}
}
bool MonsterAttack(Monster &monster)
@ -4056,6 +4067,20 @@ void DeleteMonsterList()
}
}
void RemoveEnemyReferences(const Player &player)
{
if (&player == MyPlayer || !player.isOnActiveLevel())
return;
const size_t playerId = player.getId();
for (size_t i = 0; i < ActiveMonsterCount; i++) {
Monster &activeMonster = Monsters[ActiveMonsters[i]];
if ((activeMonster.flags & MFLAG_TARGETS_MONSTER) == 0 && activeMonster.enemy == playerId) {
activeMonster.flags |= MFLAG_NO_ENEMY;
}
}
}
void ProcessMonsters()
{
DeleteMonsterList();
@ -4096,20 +4121,21 @@ void ProcessMonsters()
UpdateEnemy(monster);
}
if ((monster.flags & MFLAG_TARGETS_MONSTER) != 0) {
assert(monster.enemy >= 0 && monster.enemy < MaxMonsters);
// BUGFIX: enemy target may be dead at time of access, thus reading garbage data from `Monsters[monster.enemy].position.future`.
monster.position.last = Monsters[monster.enemy].position.future;
monster.enemyPosition = monster.position.last;
} else {
assert(monster.enemy >= 0 && monster.enemy < MAX_PLRS);
Player &player = Players[monster.enemy];
monster.enemyPosition = player.position.future;
if (IsTileVisible(monster.position.tile)) {
monster.activeForTicks = UINT8_MAX;
monster.position.last = player.position.future;
} else if (monster.activeForTicks != 0 && monster.type().type != MT_DIABLO) {
monster.activeForTicks--;
if ((monster.flags & MFLAG_NO_ENEMY) == 0) {
if ((monster.flags & MFLAG_TARGETS_MONSTER) != 0) {
assert(monster.enemy >= 0 && monster.enemy < MaxMonsters);
monster.position.last = Monsters[monster.enemy].position.future;
monster.enemyPosition = monster.position.last;
} else {
assert(monster.enemy >= 0 && monster.enemy < MAX_PLRS);
Player &player = Players[monster.enemy];
monster.enemyPosition = player.position.future;
if (IsTileVisible(monster.position.tile)) {
monster.activeForTicks = UINT8_MAX;
monster.position.last = player.position.future;
} else if (monster.activeForTicks != 0 && monster.type().type != MT_DIABLO) {
monster.activeForTicks--;
}
}
}
while (true) {

1
Source/monster.h

@ -534,6 +534,7 @@ void PrepDoEnding();
bool Walk(Monster &monster, Direction md);
void GolumAi(Monster &monster);
void DeleteMonsterList();
void RemoveEnemyReferences(const Player &player);
void ProcessMonsters();
void FreeMonsters();
bool DirOK(const Monster &monster, Direction mdir);

6
Source/msg.cpp

@ -34,6 +34,7 @@
#include "levels/trigs.h"
#include "lighting.h"
#include "missiles.h"
#include "monster.h"
#include "monsters/validation.hpp"
#include "nthread.h"
#include "objects.h"
@ -2280,6 +2281,11 @@ size_t OnPlayerJoinLevel(const TCmdLocParam2 &message, Player &player)
}
if (player.plractive && &player != MyPlayer) {
if (player.isOnActiveLevel()) {
RemoveEnemyReferences(player);
RemovePlrMissiles(player);
FixPlrWalkTags(player);
}
player.position.tile = position;
SetPlayerOld(player);
if (isSetLevel)

2
Source/multi.cpp

@ -21,6 +21,7 @@
#include "engine/random.hpp"
#include "engine/world_tile.hpp"
#include "menu.h"
#include "monster.h"
#include "msg.h"
#include "nthread.h"
#include "options.h"
@ -275,6 +276,7 @@ void PlayerLeftMsg(Player &player, bool left)
RemovePortalMissile(player);
DeactivatePortal(player);
delta_close_portal(player);
RemoveEnemyReferences(player);
RemovePlrMissiles(player);
if (left) {
std::string_view pszFmt = _("Player '{:s}' just left the game");

2
Source/player.cpp

@ -36,6 +36,7 @@
#include "loadsave.h"
#include "minitext.h"
#include "missiles.h"
#include "monster.h"
#include "nthread.h"
#include "objects.h"
#include "options.h"
@ -358,6 +359,7 @@ void InitLevelChange(Player &player)
{
Player &myPlayer = *MyPlayer;
RemoveEnemyReferences(player);
RemovePlrMissiles(player);
player.pManaShield = false;
player.wReflections = 0;

Loading…
Cancel
Save