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

Replace AIMaster m_lEventQueue with a priority queue #1537

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ https://github.com/nwnxee/unified/compare/build8193.34...HEAD
- Optimizations: added `NWNX_OPTIMIZATIONS_CACHE_SCRIPT_CHUNKS` to cache script chunks after first execution.
- Optimizations: added `NWNX_OPTIMIZATIONS_CLIENT_GAMEOBJECT_UPDATE_TIME` to change the global client gameobject update time.
- Optimizations: added `NWNX_OPTIMIZATIONS_CLIENT_GAMEOBJECT_UPDATE_TIME_LOADING` to change the client gameobject update time for players loading an area.
- Optimizations: added `NWNX_OPTIMIZATIONS_EVENT_MASTER_PRIORITY_QUEUE` to replace AI event linked list with a priority queue, improving performance in some circumstances.
- Events: added skippable event `NWNX_ON_INPUT_DROP_ITEM_{BEFORE|AFTER}` which fires when a player attempts to drop an item.
- Events: added skippable event `NWNX_ON_DECREMENT_SPELL_COUNT_{BEFORE|AFTER}` which fires when spell count (Memorized, non-memorized, or spell-like ability) decreases.
- Events: added skippable event `NWNX_ON_DEBUG_PLAY_VISUAL_EFFECT_{BEFORE|AFTER}` which fires when the dm_visualeffect console command is used.
Expand Down
1 change: 1 addition & 0 deletions Plugins/Optimizations/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ add_plugin(Optimizations
"ReconcileAutoMap.cpp"
"CacheScriptChunks.cpp"
"ClientGameObjectUpdateTime.cpp"
"EventMasterPriorityQueue.cpp"
)
95 changes: 95 additions & 0 deletions Plugins/Optimizations/EventMasterPriorityQueue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include "nwnx.hpp"

#include "API/CAppManager.hpp"
#include "API/CGameObjectArray.hpp"
#include "API/CServerAIMaster.hpp"
#include "API/CServerExoApp.hpp"
#include "API/CServerExoAppInternal.hpp"
#include "API/CWorldTimer.hpp"
#include "API/CNWSPlayer.hpp"
#include "API/CNWSObject.hpp"
#include "API/CNWSCreature.hpp"
#include "API/CExoBase.hpp"
#include "API/CExoTimers.hpp"
#include "API/CNetLayer.hpp"
#include "API/CNWSMessage.hpp"

#include <queue>

namespace Optimizations
{

using namespace NWNXLib;
using namespace NWNXLib::API;

struct CServerAIEventNodeComparator
{
bool operator()(const CServerAIEventNode& lhs, const CServerAIEventNode& rhs) const
{
return (lhs.m_nCalendarDay == rhs.m_nCalendarDay) ? (lhs.m_nTimeOfDay > rhs.m_nTimeOfDay) : (lhs.m_nCalendarDay > rhs.m_nCalendarDay);
}
};

static std::priority_queue<CServerAIEventNode, std::vector<CServerAIEventNode>, CServerAIEventNodeComparator> s_event_queue;

static void ClearEventQueue(CServerAIMaster*)
{
s_event_queue = {};
}

static BOOL AddEventAbsoluteTime(CServerAIMaster*, uint32_t nCalendarDay, uint32_t nTimeOfDay, OBJECT_ID nCallerObjectId, OBJECT_ID nObjectId, uint32_t nEventId, void *pEventData)
{
s_event_queue.emplace(CServerAIEventNode { nCalendarDay, nTimeOfDay, nCallerObjectId, nObjectId, nEventId, pEventData });
return true;
}

static BOOL EventPending(CServerAIMaster* ai, uint32_t nCalendarDay, uint32_t nTimeOfDay)
{
if (s_event_queue.empty()) return false;

const CServerAIEventNode& eventNode = s_event_queue.top();
return ai->m_pExoAppInternal->m_pWorldTimer->CompareWorldTimes(eventNode.m_nCalendarDay, eventNode.m_nTimeOfDay, nCalendarDay, nTimeOfDay) <= 0;
}

static BOOL GetPendingEvent(CServerAIMaster*, uint32_t *nCalendarDay, uint32_t *nTimeOfDay, OBJECT_ID *nCallerObjectId, OBJECT_ID *nObjectId, uint32_t *nEventId, void **pEventData)
{
if (s_event_queue.empty()) return false;

const CServerAIEventNode& eventNode = s_event_queue.top();

*nCalendarDay = eventNode.m_nCalendarDay;
*nTimeOfDay = eventNode.m_nTimeOfDay;
*nCallerObjectId = eventNode.m_nCallerObjectId;
*nObjectId = eventNode.m_nObjectId;
*nEventId = eventNode.m_nEventId;
*pEventData = eventNode.m_pEventData;

s_event_queue.pop();
return true;
}

void EventMasterPriorityQueue() __attribute__((constructor));

void EventMasterPriorityQueue()
{
// Replace:
//
// * CServerAIMaster::ClearEventQueue()
// * CServerAIMaster::AddEventAbsoluteTime()
// * CServerAIMaster::AddEventAbsoluteTimeViaTail()
// * CServerAIMaster::EventPending()
// * CServerAIMaster::GetPendingEvent()
//
// Don't care about save/load event queue.

if (Config::Get<bool>("EVENT_MASTER_PRIORITY_QUEUE", false))
{
static Hooks::Hook _0 = Hooks::HookFunction(API::Functions::_ZN15CServerAIMaster15ClearEventQueueEv, (void*)&ClearEventQueue, Hooks::Order::Final);
static Hooks::Hook _1 = Hooks::HookFunction(API::Functions::_ZN15CServerAIMaster20AddEventAbsoluteTimeEjjjjjPv, (void*)&AddEventAbsoluteTime, Hooks::Order::Final);
static Hooks::Hook _2 = Hooks::HookFunction(API::Functions::_ZN15CServerAIMaster27AddEventAbsoluteTimeViaTailEjjjjjPv, (void*)&AddEventAbsoluteTime, Hooks::Order::Final);
static Hooks::Hook _3 = Hooks::HookFunction(API::Functions::_ZN15CServerAIMaster12EventPendingEjj, (void*)&EventPending, Hooks::Order::Final);
static Hooks::Hook _4 = Hooks::HookFunction(API::Functions::_ZN15CServerAIMaster15GetPendingEventEPjS0_S0_S0_S0_PPv, (void*)&GetPendingEvent, Hooks::Order::Final);
}
}

}
1 change: 1 addition & 0 deletions Plugins/Optimizations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ Game optimizations. Improves performance of various game elements.
| `NWNX_OPTIMIZATIONS_CACHE_SCRIPT_CHUNKS` | true/false | Caches all script chunks, improving performance |
| `NWNX_OPTIMIZATIONS_CLIENT_GAMEOBJECT_UPDATE_TIME` | int | The global client gameobject update time in microseconds, default 200000 (200 milliseconds) |
| `NWNX_OPTIMIZATIONS_CLIENT_GAMEOBJECT_UPDATE_TIME_LOADING` | int | The client gameobject update time in microseconds for players loading an area, default 200000 (200 milliseconds) |
| `NWNX_OPTIMIZATIONS_EVENT_MASTER_PRIORITY_QUEUE` | bool | Replaces the AI event linked list with a priority queue, improving event insert speed from o(n) to o(log n), and eliminating many allocations |