Skip to content

Commit

Permalink
Merge pull request #1718 from Daztek/script-cache
Browse files Browse the repository at this point in the history
Optimizations: add `NWNX_OPTIMIZATIONS_CACHE_SCRIPTS`
  • Loading branch information
Daztek authored Dec 29, 2023
2 parents f5bbb38 + a0e18bb commit 0a64f5b
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
https://github.com/nwnxee/unified/compare/build8193.36.8...HEAD

### Added
- N/A
- Optimizations: added `NWNX_OPTIMIZATIONS_CACHE_SCRIPTS` to cache scripts after first execution.

##### New Plugins
- N/A
Expand Down
1 change: 1 addition & 0 deletions Plugins/Optimizations/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ add_plugin(Optimizations
"PlayerLookup.cpp"
"CacheScriptChunks.cpp"
"CacheDebuggerInstances.cpp"
"CacheScripts.cpp"
)
104 changes: 104 additions & 0 deletions Plugins/Optimizations/CacheScripts.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "nwnx.hpp"

#include "API/CVirtualMachine.hpp"
#include "API/CScriptCompiler.hpp"
#include "API/CExoResMan.hpp"

namespace Optimizations {

using namespace NWNXLib;
using namespace NWNXLib::API;

static std::unordered_map<int32_t, DataBlockRef> s_CachedScripts;

void CacheScripts() __attribute__((constructor));
void CacheScripts()
{
if (Config::Get<bool>("CACHE_SCRIPTS", false))
{
LOG_INFO("Caching scripts");

static Hooks::Hook s_ReadScriptFile = Hooks::HookFunction(&CVirtualMachine::ReadScriptFile,
+[](CVirtualMachine *pVirtualMachine, CExoString *psFileName, int32_t nScriptEventID) -> int32_t
{
pVirtualMachine->m_nRecursionLevel += 1;
if (pVirtualMachine->m_nRecursionLevel >= 8)
{
--pVirtualMachine->m_nRecursionLevel;
return -633;
}

CExoString sScript;
if (psFileName)
sScript = *psFileName;

int32_t nHash = sScript.GetHash();

auto cachedScript = s_CachedScripts.find(nHash);
if (cachedScript != s_CachedScripts.end())
{
pVirtualMachine->m_pVirtualMachineScript[pVirtualMachine->m_nRecursionLevel].m_sScriptName = sScript;
pVirtualMachine->m_pVirtualMachineScript[pVirtualMachine->m_nRecursionLevel].m_nScriptEventID = nScriptEventID;
pVirtualMachine->m_pVirtualMachineScript[pVirtualMachine->m_nRecursionLevel].m_sScriptChunk = "";
pVirtualMachine->InitializeScript(&pVirtualMachine->m_pVirtualMachineScript[pVirtualMachine->m_nRecursionLevel], cachedScript->second);
return 0;
}

auto pVMFile = Globals::ExoResMan()->Get(sScript, pVirtualMachine->m_nResTypeCompiled);
if (!pVMFile)
{
--pVirtualMachine->m_nRecursionLevel;
return -634;
}

char *pScriptData = (char*)pVMFile->Data();
uint32_t nScriptDataSize = pVMFile->Used();

if (pScriptData[0] == 'N' && pScriptData[1] == 'C' && pScriptData[2] == 'S' && pScriptData[3] == ' ' &&
pScriptData[4] == 'V' && pScriptData[6] == '.' && pScriptData[8] == 'B')
{
int32_t nVersion = 0;
if (pScriptData[5] >= '1' && pScriptData[5] <= '9')
nVersion += (pScriptData[5] - '0') * 10;
if (pScriptData[7] >= '1' && pScriptData[7] <= '9')
nVersion += pScriptData[7] - '0';
if (nVersion != 10)
{
--pVirtualMachine->m_nRecursionLevel;
return -635;
}

pScriptData += 13;
nScriptDataSize -= 13;

DataBlockRef pScriptDataBlock = std::make_shared<DataBlock>();
pScriptDataBlock->Append(pScriptData, nScriptDataSize);
s_CachedScripts[nHash] = pScriptDataBlock;

pVirtualMachine->m_pVirtualMachineScript[pVirtualMachine->m_nRecursionLevel].m_sScriptName = sScript;
pVirtualMachine->m_pVirtualMachineScript[pVirtualMachine->m_nRecursionLevel].m_nScriptEventID = nScriptEventID;
pVirtualMachine->m_pVirtualMachineScript[pVirtualMachine->m_nRecursionLevel].m_sScriptChunk = "";
pVirtualMachine->InitializeScript(&pVirtualMachine->m_pVirtualMachineScript[pVirtualMachine->m_nRecursionLevel], pScriptDataBlock);
return 0;
}

--pVirtualMachine->m_nRecursionLevel;
return -635;
}, Hooks::Order::Final);
}
}

// No nwscript export, call it manually.
extern "C" ArgumentStack FlushCachedScripts(ArgumentStack&& args)
{
const auto script = args.extract<std::string>();

if (script.empty())
s_CachedScripts.clear();
else
s_CachedScripts.erase(CExoString(script).GetHash());

return {};
}

}
1 change: 1 addition & 0 deletions Plugins/Optimizations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ Game optimizations. Improves performance of various game elements.
| `NWNX_OPTIMIZATIONS_ALTERNATE_GAME_OBJECT_UPDATE` | true/false | Uses an experimental alternative update mechanism. Requires `LUO_LOOKUP`. **WARNING**: Will break all of NWNX_Appearance and the following NWNX_Player functions: SetObjectVisualTransformOverride, ApplyLoopingVisualEffectToObject, SetPlaceableNameOverride, SetCreatureNameOverride, SetObjectMouseCursorOverride and SetObjectHiliteColorOverride. Forcing objects to be always visible with NWNX_Visibility will also break. |
| `NWNX_OPTIMIZATIONS_CACHE_SCRIPT_CHUNKS` | true/false | Caches all script chunks, improving performance |
| `NWNX_OPTIMIZATIONS_CACHE_DEBUGGER_INSTANCES` | true/false | Caches all nwscript debugger instances, improving GetScriptBacktrace() performance |
| `NWNX_OPTIMIZATIONS_CACHE_SCRIPTS` | true/false | Caches all scripts, improving performance |

0 comments on commit 0a64f5b

Please sign in to comment.