Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve: Dispatcher Walk Event #2845

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 36 additions & 18 deletions src/creatures/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ void Creature::stopEventWalk() {
}

void Creature::updateMapCache() {
if (!useCacheMap()) {
return;
}

metrics::method_latency measure(__METHOD_NAME__);
std::shared_ptr<Tile> newTile;
const Position &myPos = getPosition();
Expand Down Expand Up @@ -362,7 +366,7 @@ void Creature::onRemoveTileItem(std::shared_ptr<Tile> updateTile, const Position

void Creature::onCreatureAppear(std::shared_ptr<Creature> creature, bool isLogin) {
metrics::method_latency measure(__METHOD_NAME__);
if (creature == getCreature()) {
if (creature.get() == this) {
if (useCacheMap()) {
isMapLoaded = true;
updateMapCache();
Expand Down Expand Up @@ -470,7 +474,7 @@ void Creature::checkSummonMove(const Position &newPos, bool teleportSummon) {

void Creature::onCreatureMove(const std::shared_ptr<Creature> &creature, const std::shared_ptr<Tile> &newTile, const Position &newPos, const std::shared_ptr<Tile> &oldTile, const Position &oldPos, bool teleport) {
metrics::method_latency measure(__METHOD_NAME__);
if (creature == getCreature()) {
if (creature.get() == this) {
lastStep = OTSYS_TIME();
lastStepCost = 1;

Expand Down Expand Up @@ -1045,24 +1049,12 @@ void Creature::getPathSearchParams(const std::shared_ptr<Creature> &, FindPathPa
}

void Creature::goToFollowCreature_async(std::function<void()> &&onComplete) {
metrics::method_latency measure(__METHOD_NAME__);
if (pathfinderRunning.load()) {
return;
}

pathfinderRunning.store(true);
g_dispatcher().asyncEvent([self = getCreature()] {
if (!self || self->isRemoved()) {
return;
}

self->goToFollowCreature();
self->pathfinderRunning.store(false);
});

if (onComplete) {
if (!m_goToFollowCreature && onComplete) {
g_dispatcher().context().addEvent(std::move(onComplete));
}

m_goToFollowCreature = true;
sendAsyncTasks();
}

void Creature::goToFollowCreature() {
Expand Down Expand Up @@ -1906,3 +1898,29 @@ void Creature::iconChanged() {
spectator->getPlayer()->sendCreatureIcon(getCreature());
}
}

void Creature::sendAsyncTasks() {
if (asyncTasksRunning.load()) {
return;
}

asyncTasksRunning.store(true);
g_dispatcher().asyncEvent([self = getCreature()] {
if (!self || self->isRemoved()) {
return;
}

self->callAsyncTasks();
for (const auto &task : self->asyncTasks) {
task();
}
self->asyncTasks.clear();

if (self->m_goToFollowCreature) {
self->goToFollowCreature();
self->m_goToFollowCreature = false;
}

self->asyncTasksRunning.store(false);
});
}
15 changes: 13 additions & 2 deletions src/creatures/creature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -792,8 +792,6 @@ class Creature : virtual public Thing, public SharedObject {

uint8_t wheelOfDestinyDrainBodyDebuff = 0;

std::atomic_bool pathfinderRunning = false;

// use map here instead of phmap to keep the keys in a predictable order
std::map<std::string, CreatureIcon> creatureIcons = {};

Expand Down Expand Up @@ -828,11 +826,24 @@ class Creature : virtual public Thing, public SharedObject {
friend class Map;
friend class CreatureFunctions;

// async
void sendAsyncTasks();
virtual void callAsyncTasks() {};
std::atomic_bool asyncTasksRunning = false;
bool m_goToFollowCreature = false;

void addAsyncTask(std::function<void()> &&fnc) {
asyncTasks.emplace_back(std::move(fnc));
sendAsyncTasks();
}

private:
bool canFollowMaster();
bool isLostSummon();
void handleLostSummon(bool teleportSummons);

std::vector<std::function<void()>> asyncTasks;

struct {
uint16_t groundSpeed { 0 };
uint16_t calculatedStepSpeed { 1 };
Expand Down
98 changes: 62 additions & 36 deletions src/creatures/monsters/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ void Monster::onCreatureAppear(std::shared_ptr<Creature> creature, bool isLogin)
}

if (creature.get() == this) {
updateTargetList();
updateIdleStatus();
updateTargetList_async();
updateIdleStatus_async();
} else {
onCreatureEnter(creature);
}
Expand Down Expand Up @@ -243,43 +243,45 @@ void Monster::onCreatureMove(const std::shared_ptr<Creature> &creature, const st
}

if (creature.get() == this) {
updateTargetList();
updateIdleStatus();
updateTargetList_async();
updateIdleStatus_async();
} else {
bool canSeeNewPos = canSee(newPos);
bool canSeeOldPos = canSee(oldPos);

if (canSeeNewPos && !canSeeOldPos) {
onCreatureEnter(creature);
} else if (!canSeeNewPos && canSeeOldPos) {
onCreatureLeave(creature);
}

updateIdleStatus();

if (!isSummon()) {
if (const auto &followCreature = getFollowCreature()) {
const Position &followPosition = followCreature->getPosition();
const Position &pos = getPosition();

int32_t offset_x = Position::getDistanceX(followPosition, pos);
int32_t offset_y = Position::getDistanceY(followPosition, pos);
if ((offset_x > 1 || offset_y > 1) && mType->info.changeTargetChance > 0) {
Direction dir = getDirectionTo(pos, followPosition);
const auto &checkPosition = getNextPosition(dir, pos);

if (const auto &nextTile = g_game().map.getTile(checkPosition)) {
const auto &topCreature = nextTile->getTopCreature();
if (followCreature != topCreature && isOpponent(topCreature)) {
selectTarget(topCreature);
addAsyncTask([self = getMonster(), newPos, oldPos, creature] {
bool canSeeNewPos = self->canSee(newPos);
bool canSeeOldPos = self->canSee(oldPos);

if (canSeeNewPos && !canSeeOldPos) {
self->onCreatureEnter(creature);
} else if (!canSeeNewPos && canSeeOldPos) {
self->onCreatureLeave(creature);
}

self->updateIdleStatus();

if (!self->isSummon()) {
if (const auto &followCreature = self->getFollowCreature()) {
const Position &followPosition = followCreature->getPosition();
const Position &pos = self->getPosition();

int32_t offset_x = Position::getDistanceX(followPosition, pos);
int32_t offset_y = Position::getDistanceY(followPosition, pos);
if ((offset_x > 1 || offset_y > 1) && self->mType->info.changeTargetChance > 0) {
Direction dir = getDirectionTo(pos, followPosition);
const auto &checkPosition = getNextPosition(dir, pos);

if (const auto &nextTile = g_game().map.getTile(checkPosition)) {
const auto &topCreature = nextTile->getTopCreature();
if (followCreature != topCreature && self->isOpponent(topCreature)) {
self->selectTarget(topCreature);
}
}
}
} else if (self->isOpponent(creature)) {
// we have no target lets try pick this one
self->selectTarget(creature);
}
} else if (isOpponent(creature)) {
// we have no target lets try pick this one
selectTarget(creature);
}
}
});
}
}

Expand Down Expand Up @@ -388,7 +390,7 @@ void Monster::updateTargetList() {
return !target || target->getHealth() <= 0 || !canSee(target->getPosition());
});

for (const auto &spectator : Spectators().find<Creature>(position, true)) {
for (const auto &spectator : Spectators().find<Creature>(position, true, 0, 0, 0, 0, false)) {
if (spectator.get() != this && canSee(spectator->getPosition())) {
onCreatureFound(spectator);
}
Expand Down Expand Up @@ -713,7 +715,9 @@ void Monster::setIdle(bool idle) {
isIdle = idle;

if (!isIdle) {
g_game().addCreatureCheck(static_self_cast<Monster>());
g_dispatcher().context().tryAddEvent([self = static_self_cast<Monster>()] {
g_game().addCreatureCheck(self);
});
} else {
onIdleStatus();
clearTargetList();
Expand Down Expand Up @@ -2226,3 +2230,25 @@ std::vector<std::pair<int8_t, int8_t>> Monster::getPushItemLocationOptions(const

return {};
}

void Monster::updateTargetList_async() {
m_updateTargetList = true;
sendAsyncTasks();
}

void Monster::updateIdleStatus_async() {
m_updateIdleStatus = true;
sendAsyncTasks();
}

void Monster::callAsyncTasks() {
if (m_updateTargetList) {
updateTargetList();
m_updateTargetList = false;
}

if (m_updateIdleStatus) {
updateIdleStatus();
m_updateIdleStatus = false;
}
}
7 changes: 7 additions & 0 deletions src/creatures/monsters/monster.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,4 +472,11 @@ class Monster final : public Creature {
void doRandomStep(Direction &nextDirection, bool &result);

void onConditionStatusChange(const ConditionType_t &type);

// async
void callAsyncTasks() override;
void updateTargetList_async();
void updateIdleStatus_async();
bool m_updateTargetList = false;
bool m_updateIdleStatus = false;
};
15 changes: 9 additions & 6 deletions src/map/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,10 @@ void Map::moveCreature(const std::shared_ptr<Creature> &creature, const std::sha
++minRangeX;
}

spectators.find<Creature>(oldPos, true, minRangeX, maxRangeX, minRangeY, maxRangeY);
spectators.find<Creature>(oldPos, true, minRangeX, maxRangeX, minRangeY, maxRangeY, false);
} else {
spectators.find<Creature>(oldPos, true);
spectators.find<Creature>(newPos, true);
spectators.find<Creature>(oldPos, true, 0, 0, 0, 0, false);
spectators.find<Creature>(newPos, true, 0, 0, 0, 0, false);
}

auto playersSpectators = spectators.filter<Player>();
Expand Down Expand Up @@ -424,9 +424,12 @@ void Map::moveCreature(const std::shared_ptr<Creature> &creature, const std::sha
spectator->onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport);
}

oldTile->postRemoveNotification(creature, newTile, 0);
newTile->postAddNotification(creature, oldTile, 0);
g_game().afterCreatureZoneChange(creature, fromZones, toZones);
g_dispatcher().addEvent([=] {
oldTile->postRemoveNotification(creature, newTile, 0);
newTile->postAddNotification(creature, oldTile, 0);
g_game().afterCreatureZoneChange(creature, fromZones, toZones);
},
"Map::moveCreature");
}

bool Map::canThrowObjectTo(const Position &fromPos, const Position &toPos, const SightLines_t lineOfSight /*= SightLine_CheckSightLine*/, const int32_t rangex /*= Map::maxClientViewportX*/, const int32_t rangey /*= Map::maxClientViewportY*/) {
Expand Down
85 changes: 48 additions & 37 deletions src/map/spectators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,43 +75,7 @@ bool Spectators::checkCache(const SpectatorsCache::FloorData &specData, bool onl
return true;
}

Spectators Spectators::find(const Position &centerPos, bool multifloor, bool onlyPlayers, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY) {
minRangeX = (minRangeX == 0 ? -MAP_MAX_VIEW_PORT_X : -minRangeX);
maxRangeX = (maxRangeX == 0 ? MAP_MAX_VIEW_PORT_X : maxRangeX);
minRangeY = (minRangeY == 0 ? -MAP_MAX_VIEW_PORT_Y : -minRangeY);
maxRangeY = (maxRangeY == 0 ? MAP_MAX_VIEW_PORT_Y : maxRangeY);

const auto &it = spectatorsCache.find(centerPos);
const bool cacheFound = it != spectatorsCache.end();
if (cacheFound) {
auto &cache = it->second;
if (minRangeX < cache.minRangeX || maxRangeX > cache.maxRangeX || minRangeY < cache.minRangeY || maxRangeY > cache.maxRangeY) {
// recache with new range
cache.minRangeX = minRangeX = std::min<int32_t>(minRangeX, cache.minRangeX);
cache.minRangeY = minRangeY = std::min<int32_t>(minRangeY, cache.minRangeY);
cache.maxRangeX = maxRangeX = std::max<int32_t>(maxRangeX, cache.maxRangeX);
cache.maxRangeY = maxRangeY = std::max<int32_t>(maxRangeY, cache.maxRangeY);
} else {
const bool checkDistance = minRangeX != cache.minRangeX || maxRangeX != cache.maxRangeX || minRangeY != cache.minRangeY || maxRangeY != cache.maxRangeY;

if (onlyPlayers) {
// check players cache
if (checkCache(cache.players, true, centerPos, checkDistance, multifloor, minRangeX, maxRangeX, minRangeY, maxRangeY)) {
return *this;
}

// if there is no player cache, look for players in the creatures cache.
if (checkCache(cache.creatures, true, centerPos, true, multifloor, minRangeX, maxRangeX, minRangeY, maxRangeY)) {
return *this;
}

// All Creatures
} else if (checkCache(cache.creatures, false, centerPos, checkDistance, multifloor, minRangeX, maxRangeX, minRangeY, maxRangeY)) {
return *this;
}
}
}

CreatureVector Spectators::getSpectators(const Position &centerPos, bool multifloor, bool onlyPlayers, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY) {
uint8_t minRangeZ = centerPos.z;
uint8_t maxRangeZ = centerPos.z;

Expand Down Expand Up @@ -185,6 +149,53 @@ Spectators Spectators::find(const Position &centerPos, bool multifloor, bool onl
}
}

return spectators;
}

Spectators Spectators::find(const Position &centerPos, bool multifloor, bool onlyPlayers, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY, bool useCache) {
minRangeX = (minRangeX == 0 ? -MAP_MAX_VIEW_PORT_X : -minRangeX);
maxRangeX = (maxRangeX == 0 ? MAP_MAX_VIEW_PORT_X : maxRangeX);
minRangeY = (minRangeY == 0 ? -MAP_MAX_VIEW_PORT_Y : -minRangeY);
maxRangeY = (maxRangeY == 0 ? MAP_MAX_VIEW_PORT_Y : maxRangeY);

if (!useCache) {
insertAll(getSpectators(centerPos, multifloor, onlyPlayers, minRangeX, maxRangeX, minRangeY, maxRangeY));
return *this;
}

const auto &it = spectatorsCache.find(centerPos);
const bool cacheFound = it != spectatorsCache.end();
if (cacheFound) {
auto &cache = it->second;
if (minRangeX < cache.minRangeX || maxRangeX > cache.maxRangeX || minRangeY < cache.minRangeY || maxRangeY > cache.maxRangeY) {
// recache with new range
cache.minRangeX = minRangeX = std::min<int32_t>(minRangeX, cache.minRangeX);
cache.minRangeY = minRangeY = std::min<int32_t>(minRangeY, cache.minRangeY);
cache.maxRangeX = maxRangeX = std::max<int32_t>(maxRangeX, cache.maxRangeX);
cache.maxRangeY = maxRangeY = std::max<int32_t>(maxRangeY, cache.maxRangeY);
} else {
const bool checkDistance = minRangeX != cache.minRangeX || maxRangeX != cache.maxRangeX || minRangeY != cache.minRangeY || maxRangeY != cache.maxRangeY;

if (onlyPlayers) {
// check players cache
if (checkCache(cache.players, true, centerPos, checkDistance, multifloor, minRangeX, maxRangeX, minRangeY, maxRangeY)) {
return *this;
}

// if there is no player cache, look for players in the creatures cache.
if (checkCache(cache.creatures, true, centerPos, true, multifloor, minRangeX, maxRangeX, minRangeY, maxRangeY)) {
return *this;
}

// All Creatures
} else if (checkCache(cache.creatures, false, centerPos, checkDistance, multifloor, minRangeX, maxRangeX, minRangeY, maxRangeY)) {
return *this;
}
}
}

const auto &spectators = getSpectators(centerPos, multifloor, onlyPlayers, minRangeX, maxRangeX, minRangeY, maxRangeY);

// It is necessary to create the cache even if no spectators is found, so that there is no future query.
auto &cache = cacheFound ? it->second : spectatorsCache.emplace(centerPos, SpectatorsCache { .minRangeX = minRangeX, .maxRangeX = maxRangeX, .minRangeY = minRangeY, .maxRangeY = maxRangeY, .creatures = {}, .players = {} }).first->second;
auto &creaturesCache = onlyPlayers ? cache.players : cache.creatures;
Expand Down
Loading