Merge pull request #878 from EQEmu/fix/eliminate-mob-ghosting

Fix/eliminate mob ghosting
This commit is contained in:
Chris Miles 2019-08-06 02:09:03 -07:00 committed by GitHub
commit a6af014d5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 666 additions and 364 deletions

View File

@ -1,5 +1,8 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 8/6/2019 ==
Akkadius: Optimizations to movement updates to eliminate ghosting possibilities in larger zones
== 7/22/2019 == == 7/22/2019 ==
Uleat: Added script 'vcxproj_dependencies.py' - a script to help determine conflicting project dependencies (alpha-stage) Uleat: Added script 'vcxproj_dependencies.py' - a script to help determine conflicting project dependencies (alpha-stage)

View File

@ -136,6 +136,7 @@ Client::Client(EQStreamInterface* ieqs)
forget_timer(0), forget_timer(0),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000),
client_zone_wide_full_position_update_timer(5 * 60 * 1000),
tribute_timer(Tribute_duration), tribute_timer(Tribute_duration),
proximity_timer(ClientProximity_interval), proximity_timer(ClientProximity_interval),
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
@ -9115,3 +9116,16 @@ bool Client::GotoPlayer(std::string player_name)
return false; return false;
} }
glm::vec4 &Client::GetLastPositionBeforeBulkUpdate()
{
return last_position_before_bulk_update;
}
/**
* @param in_last_position_before_bulk_update
*/
void Client::SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update)
{
Client::last_position_before_bulk_update = in_last_position_before_bulk_update;
}

View File

@ -1309,6 +1309,9 @@ public:
uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns. uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns.
void SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update);
glm::vec4 &GetLastPositionBeforeBulkUpdate();
protected: protected:
friend class Mob; friend class Mob;
void CalcItemBonuses(StatBonuses* newbon); void CalcItemBonuses(StatBonuses* newbon);
@ -1517,6 +1520,7 @@ private:
Timer forget_timer; // our 2 min everybody forgets you timer Timer forget_timer; // our 2 min everybody forgets you timer
Timer autosave_timer; Timer autosave_timer;
Timer client_scan_npc_aggro_timer; Timer client_scan_npc_aggro_timer;
Timer client_zone_wide_full_position_update_timer;
Timer tribute_timer; Timer tribute_timer;
Timer proximity_timer; Timer proximity_timer;
@ -1539,6 +1543,7 @@ private:
Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */
glm::vec3 m_Proximity; glm::vec3 m_Proximity;
glm::vec4 last_position_before_bulk_update;
void BulkSendInventoryItems(); void BulkSendInventoryItems();

View File

@ -60,6 +60,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "water_map.h" #include "water_map.h"
#include "worldserver.h" #include "worldserver.h"
#include "zone.h" #include "zone.h"
#include "mob_movement_manager.h"
#ifdef BOTS #ifdef BOTS
#include "bot.h" #include "bot.h"
@ -805,6 +806,8 @@ void Client::CompleteConnect()
parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0);
SetLastPositionBeforeBulkUpdate(GetPosition());
/* This sub event is for if a player logs in for the first time since entering world. */ /* This sub event is for if a player logs in for the first time since entering world. */
if (firstlogon == 1) { if (firstlogon == 1) {
parse->EventPlayer(EVENT_CONNECT, this, "", 0); parse->EventPlayer(EVENT_CONNECT, this, "", 0);
@ -4466,16 +4469,16 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
/* Handle client aggro scanning timers NPCs */ /* Handle client aggro scanning timers NPCs */
is_client_moving = (ppu->y_pos == m_Position.y && ppu->x_pos == m_Position.x) ? false : true; is_client_moving = (ppu->y_pos == m_Position.y && ppu->x_pos == m_Position.x) ? false : true;
if (is_client_moving) { if (is_client_moving) {
Log(Logs::Detail, Logs::Normal, "ClientUpdate: Client is moving - scan timer is: %u", Log(Logs::Detail, Logs::Normal, "ClientUpdate: Client is moving - scan timer is: %u",
client_scan_npc_aggro_timer.GetDuration()); client_scan_npc_aggro_timer.GetDuration());
if (client_scan_npc_aggro_timer.GetDuration() > 1000) { if (client_scan_npc_aggro_timer.GetDuration() > 1000) {
client_scan_npc_aggro_timer.Disable(); client_scan_npc_aggro_timer.Disable();
client_scan_npc_aggro_timer.Start(500); client_scan_npc_aggro_timer.Start(500);
} }
} else { }
else {
Log(Logs::Detail, Logs::Normal, "ClientUpdate: Client is NOT moving - scan timer is: %u", Log(Logs::Detail, Logs::Normal, "ClientUpdate: Client is NOT moving - scan timer is: %u",
client_scan_npc_aggro_timer.GetDuration()); client_scan_npc_aggro_timer.GetDuration());
if (client_scan_npc_aggro_timer.GetDuration() < 1000) { if (client_scan_npc_aggro_timer.GetDuration() < 1000) {
@ -4483,7 +4486,46 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
client_scan_npc_aggro_timer.Start(3000); client_scan_npc_aggro_timer.Start(3000);
} }
} }
/**
* On a normal basis we limit mob movement updates based on distance
* This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes
*
* For very large zones we will also force a full update based on distance
*
* We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear
* to full stop when they are actually still pathing
*/
float distance_moved = DistanceNoZ(GetLastPositionBeforeBulkUpdate(), GetPosition());
bool moved_far_enough_before_bulk_update = distance_moved >= 1200;
bool is_ready_to_update = (
client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update
);
if (is_client_moving && is_ready_to_update) {
Log(Logs::Detail, Logs::Normal, "[%s] Client Zone Wide Position Update NPCs", GetCleanName());
auto &mob_movement_manager = MobMovementManager::Get();
auto &mob_list = entity_list.GetMobList();
for (auto &it : mob_list) {
Mob *entity = it.second;
if (!entity->IsNPC()) {
continue;
}
float distance_from_client_to_ignore = zone->GetMaxMovementUpdateRange() - 100;
if (CalculateDistance(entity->GetX(), entity->GetY(), entity->GetZ()) <= distance_from_client_to_ignore) {
continue;
}
mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny, this);
}
SetLastPositionBeforeBulkUpdate(GetPosition());
}
float new_heading = EQ12toFloat(ppu->heading); float new_heading = EQ12toFloat(ppu->heading);
int32 new_animation = ppu->animation; int32 new_animation = ppu->animation;

View File

@ -592,8 +592,8 @@ bool Client::Process() {
// only if client is not feigned // only if client is not feigned
if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) { if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) {
int npc_scan_count = 0; int npc_scan_count = 0;
for (auto it = close_mobs.begin(); it != close_mobs.end(); ++it) { for (auto & close_mob : close_mobs) {
Mob *mob = it->first; Mob *mob = close_mob.first;
if (!mob) if (!mob)
continue; continue;
@ -604,6 +604,7 @@ bool Client::Process() {
if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) { if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) {
mob->AddToHateList(this, 25); mob->AddToHateList(this, 25);
} }
npc_scan_count++; npc_scan_count++;
} }
Log(Logs::General, Logs::Aggro, "Checking Reverse Aggro (client->npc) scanned_npcs (%i)", npc_scan_count); Log(Logs::General, Logs::Aggro, "Checking Reverse Aggro (client->npc) scanned_npcs (%i)", npc_scan_count);

File diff suppressed because it is too large Load Diff

View File

@ -41,18 +41,29 @@ class MobMovementManager
public: public:
~MobMovementManager(); ~MobMovementManager();
void Process(); void Process();
void AddMob(Mob *m); void AddMob(Mob *mob);
void RemoveMob(Mob *m); void RemoveMob(Mob *mob);
void AddClient(Client *c); void AddClient(Client *client);
void RemoveClient(Client *c); void RemoveClient(Client *client);
void RotateTo(Mob *who, float to, MobMovementMode mode = MovementRunning); void RotateTo(Mob *who, float to, MobMovementMode mob_movement_mode = MovementRunning);
void Teleport(Mob *who, float x, float y, float z, float heading); void Teleport(Mob *who, float x, float y, float z, float heading);
void NavigateTo(Mob *who, float x, float y, float z, MobMovementMode mode = MovementRunning); void NavigateTo(Mob *who, float x, float y, float z, MobMovementMode mode = MovementRunning);
void StopNavigation(Mob *who); void StopNavigation(Mob *who);
void SendCommandToClients(Mob *m, float dx, float dy, float dz, float dh, int anim, ClientRange range);
void SendCommandToClients(
Mob *mob,
float delta_x,
float delta_y,
float delta_z,
float delta_heading,
int anim,
ClientRange range,
Client* single_client = nullptr
);
float FixHeading(float in); float FixHeading(float in);
void DumpStats(Client *to); void DumpStats(Client *client);
void ClearStats(); void ClearStats();
static MobMovementManager &Get() { static MobMovementManager &Get() {
@ -65,18 +76,18 @@ private:
MobMovementManager(const MobMovementManager&); MobMovementManager(const MobMovementManager&);
MobMovementManager& operator=(const MobMovementManager&); MobMovementManager& operator=(const MobMovementManager&);
void FillCommandStruct(PlayerPositionUpdateServer_Struct *spu, Mob *m, float dx, float dy, float dz, float dh, int anim); void FillCommandStruct(PlayerPositionUpdateServer_Struct *position_update, Mob *mob, float delta_x, float delta_y, float delta_z, float delta_heading, int anim);
void UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mode); void UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode);
void UpdatePathGround(Mob *who, float x, float y, float z, MobMovementMode mode); void UpdatePathGround(Mob *who, float x, float y, float z, MobMovementMode mode);
void UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode mode); void UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode movement_mode);
void UpdatePathBoat(Mob *who, float x, float y, float z, MobMovementMode mode); void UpdatePathBoat(Mob *who, float x, float y, float z, MobMovementMode mode);
void PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading); void PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading);
void PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode); void PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode);
void PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode); void PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode);
void PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mode); void PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mob_movement_mode);
void PushStopMoving(MobMovementEntry &ent); void PushStopMoving(MobMovementEntry &mob_movement_entry);
void PushEvadeCombat(MobMovementEntry &ent); void PushEvadeCombat(MobMovementEntry &mob_movement_entry);
void HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mode); void HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode);
struct Implementation; struct Implementation;
std::unique_ptr<Implementation> _impl; std::unique_ptr<Implementation> _impl;