perf: stop heap-allocating mob restriction state and Path nodes

Inlines restrictCenter and leashRestrictionGoal as value members and
stores Path nodes by value, removing per-mob and per-path malloc churn.
This commit is contained in:
MatthewBeshay 2026-04-09 13:23:02 +10:00
parent fe77d9c2a0
commit 45c85fcf79
5 changed files with 40 additions and 62 deletions

View file

@ -26,25 +26,20 @@ AttributeModifier* PathfinderMob::SPEED_MODIFIER_FLEEING =
AttributeModifier::OPERATION_MULTIPLY_TOTAL)) AttributeModifier::OPERATION_MULTIPLY_TOTAL))
->setSerialize(false); ->setSerialize(false);
PathfinderMob::PathfinderMob(Level* level) : Mob(level) { PathfinderMob::PathfinderMob(Level* level)
path = nullptr; : Mob(level),
attackTarget = nullptr; path(nullptr),
holdGround = false; attackTarget(nullptr),
fleeTime = 0; holdGround(false),
fleeTime(0),
restrictRadius = -1; restrictCenter(0, 0, 0),
restrictCenter = new Pos(0, 0, 0); restrictRadius(-1),
addedLeashRestrictionGoal = false; leashRestrictionGoal(this, 1.0f),
leashRestrictionGoal = new MoveTowardsRestrictionGoal(this, 1.0f); addedLeashRestrictionGoal(false) {}
}
bool PathfinderMob::shouldHoldGround() { return false; } bool PathfinderMob::shouldHoldGround() { return false; }
PathfinderMob::~PathfinderMob() { PathfinderMob::~PathfinderMob() { delete path; }
delete path;
delete restrictCenter;
delete leashRestrictionGoal;
}
void PathfinderMob::serverAiStep() { void PathfinderMob::serverAiStep() {
if (fleeTime > 0) { if (fleeTime > 0) {
@ -264,15 +259,15 @@ bool PathfinderMob::isWithinRestriction() {
bool PathfinderMob::isWithinRestriction(int x, int y, int z) { bool PathfinderMob::isWithinRestriction(int x, int y, int z) {
if (restrictRadius == -1) return true; if (restrictRadius == -1) return true;
return restrictCenter->distSqr(x, y, z) < restrictRadius * restrictRadius; return restrictCenter.distSqr(x, y, z) < restrictRadius * restrictRadius;
} }
void PathfinderMob::restrictTo(int x, int y, int z, int radius) { void PathfinderMob::restrictTo(int x, int y, int z, int radius) {
restrictCenter->set(x, y, z); restrictCenter.set(x, y, z);
restrictRadius = radius; restrictRadius = radius;
} }
Pos* PathfinderMob::getRestrictCenter() { return restrictCenter; } Pos* PathfinderMob::getRestrictCenter() { return &restrictCenter; }
float PathfinderMob::getRestrictRadius() { return restrictRadius; } float PathfinderMob::getRestrictRadius() { return restrictRadius; }
@ -305,7 +300,7 @@ void PathfinderMob::tickLeash() {
} }
if (!addedLeashRestrictionGoal) { if (!addedLeashRestrictionGoal) {
goalSelector.addGoal(2, leashRestrictionGoal, false); goalSelector.addGoal(2, &leashRestrictionGoal, false);
getNavigation()->setAvoidWater(false); getNavigation()->setAvoidWater(false);
addedLeashRestrictionGoal = true; addedLeashRestrictionGoal = true;
} }
@ -332,7 +327,7 @@ void PathfinderMob::tickLeash() {
} else if (!isLeashed() && addedLeashRestrictionGoal) { } else if (!isLeashed() && addedLeashRestrictionGoal) {
addedLeashRestrictionGoal = false; addedLeashRestrictionGoal = false;
goalSelector.removeGoal(leashRestrictionGoal); goalSelector.removeGoal(&leashRestrictionGoal);
getNavigation()->setAvoidWater(true); getNavigation()->setAvoidWater(true);
clearRestriction(); clearRestriction();
} }

View file

@ -3,14 +3,14 @@
#include <memory> #include <memory>
#include "Mob.h" #include "Mob.h"
#include "minecraft/Pos.h"
#include "minecraft/world/entity/Entity.h" #include "minecraft/world/entity/Entity.h"
#include "minecraft/world/entity/Mob.h" #include "minecraft/world/entity/Mob.h"
#include "minecraft/world/entity/ai/goal/MoveTowardsRestrictionGoal.h"
class Level; class Level;
class Path; class Path;
class AttributeModifier; class AttributeModifier;
class Goal;
class Pos;
class PathfinderMob : public Mob { class PathfinderMob : public Mob {
public: public:
@ -32,9 +32,9 @@ protected:
int fleeTime; int fleeTime;
private: private:
Pos* restrictCenter; Pos restrictCenter;
float restrictRadius; float restrictRadius;
Goal* leashRestrictionGoal; MoveTowardsRestrictionGoal leashRestrictionGoal;
bool addedLeashRestrictionGoal; bool addedLeashRestrictionGoal;
protected: protected:

View file

@ -11,10 +11,10 @@ class Node {
friend class EnderDragon; friend class EnderDragon;
public: public:
const int x, y, z; int x, y, z;
private: private:
const int hash; int hash;
protected: protected:
int heapIdx; int heapIdx;

View file

@ -1,31 +1,12 @@
#include "Path.h" #include "Path.h"
#include <string.h>
#include "minecraft/world/entity/Entity.h" #include "minecraft/world/entity/Entity.h"
#include "minecraft/world/level/pathfinder/Node.h" #include "minecraft/world/level/pathfinder/Node.h"
#include "minecraft/world/phys/Vec3.h" #include "minecraft/world/phys/Vec3.h"
Path::~Path() { Path::Path(std::vector<Node*>& src)
for (size_t i = 0; i < nodes.size(); i++) delete nodes[i]; : nodes(src.size()), index(0), length(static_cast<int>(src.size())) {
} for (int i = 0; i < length; i++) nodes[i] = *src[i];
Path::Path(std::vector<Node*>& nodes) {
index = 0;
length = nodes.size();
// 4J - copying these nodes over from a std::vector<Node*> (which is an
// array of Node
// * references) to just a straight array of Nodes, so that this Path is no
// longer dependent of Nodes allocated elsewhere and can handle its own
// destruction Note: cameFrom pointer will be useless now but that isn't
// used once this is just a path
this->nodes = std::vector<Node*>(length);
for (int i = 0; i < length; i++) {
this->nodes[i] = new Node();
memcpy(this->nodes[i], nodes[i], sizeof(Node));
}
} }
void Path::next() { index++; } void Path::next() { index++; }
@ -34,12 +15,12 @@ bool Path::isDone() { return index >= length; }
Node* Path::last() { Node* Path::last() {
if (length > 0) { if (length > 0) {
return nodes[length - 1]; return &nodes[length - 1];
} }
return nullptr; return nullptr;
} }
Node* Path::get(int i) { return nodes[i]; } Node* Path::get(int i) { return &nodes[i]; }
int Path::getSize() { return length; } int Path::getSize() { return length; }
@ -50,25 +31,25 @@ int Path::getIndex() { return index; }
void Path::setIndex(int index) { this->index = index; } void Path::setIndex(int index) { this->index = index; }
Vec3 Path::getPos(std::shared_ptr<Entity> e, int index) { Vec3 Path::getPos(std::shared_ptr<Entity> e, int index) {
double x = nodes[index]->x + (int)(e->bbWidth + 1) * 0.5; double x = nodes[index].x + (int)(e->bbWidth + 1) * 0.5;
double y = nodes[index]->y; double y = nodes[index].y;
double z = nodes[index]->z + (int)(e->bbWidth + 1) * 0.5; double z = nodes[index].z + (int)(e->bbWidth + 1) * 0.5;
return Vec3(x, y, z); return Vec3(x, y, z);
} }
Vec3 Path::currentPos(std::shared_ptr<Entity> e) { return getPos(e, index); } Vec3 Path::currentPos(std::shared_ptr<Entity> e) { return getPos(e, index); }
Vec3 Path::currentPos() { Vec3 Path::currentPos() {
return Vec3(nodes[index]->x, nodes[index]->y, nodes[index]->z); return Vec3(nodes[index].x, nodes[index].y, nodes[index].z);
} }
bool Path::sameAs(Path* path) { bool Path::sameAs(Path* path) {
if (path == nullptr) return false; if (path == nullptr) return false;
if (path->nodes.size() != nodes.size()) return false; if (path->nodes.size() != nodes.size()) return false;
for (int i = 0; i < nodes.size(); ++i) for (size_t i = 0; i < nodes.size(); ++i)
if (nodes[i]->x != path->nodes[i]->x || if (nodes[i].x != path->nodes[i].x ||
nodes[i]->y != path->nodes[i]->y || nodes[i].y != path->nodes[i].y ||
nodes[i]->z != path->nodes[i]->z) nodes[i].z != path->nodes[i].z)
return false; return false;
return true; return true;
} }

View file

@ -13,13 +13,16 @@ class Path {
friend class PathFinder; friend class PathFinder;
private: private:
std::vector<Node*> nodes; // Nodes are stored by value. The vector is sized once in the constructor
// and never resized, so Node* returned by get()/last() remain valid for
// the lifetime of the Path.
std::vector<Node> nodes;
int index; int index;
int length; int length;
public: public:
Path(std::vector<Node*>& nodes); Path(std::vector<Node*>& nodes);
~Path(); ~Path() = default;
void next(); void next();
bool isDone(); bool isDone();
@ -30,7 +33,6 @@ public:
int getIndex(); int getIndex();
void setIndex(int index); void setIndex(int index);
Vec3 getPos(std::shared_ptr<Entity> e, int index); Vec3 getPos(std::shared_ptr<Entity> e, int index);
std::vector<Node*> Getarray();
Vec3 currentPos(std::shared_ptr<Entity> e); Vec3 currentPos(std::shared_ptr<Entity> e);
Vec3 currentPos(); Vec3 currentPos();
bool sameAs(Path* path); bool sameAs(Path* path);