1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-30 12:32:36 +00:00
OpenMW/apps/openmw/mwscript/scriptmanagerimp.cpp
elsid b88f0d2dbd
Replace mwscript program serialization into a vector with simple struct
Mostly to avoid string literal lookup by index with iteration over all preciding
literals and calling strlen. This is very inefficient. In genral this makes code
much more straightforward but also makes it portable since now int and float of
different sizes are properly supported.
2023-01-14 00:07:37 +01:00

224 lines
6.4 KiB
C++

#include "scriptmanagerimp.hpp"
#include <algorithm>
#include <cassert>
#include <exception>
#include <sstream>
#include <components/debug/debuglog.hpp>
#include <components/esm/refid.hpp>
#include <components/esm3/loadscpt.hpp>
#include <components/misc/strings/lower.hpp>
#include <components/compiler/context.hpp>
#include <components/compiler/exception.hpp>
#include <components/compiler/quickfileparser.hpp>
#include <components/compiler/scanner.hpp>
#include "../mwworld/esmstore.hpp"
#include "extensions.hpp"
#include "interpretercontext.hpp"
namespace MWScript
{
ScriptManager::ScriptManager(const MWWorld::ESMStore& store, Compiler::Context& compilerContext, int warningsMode,
const std::vector<ESM::RefId>& scriptBlacklist)
: mErrorHandler()
, mStore(store)
, mCompilerContext(compilerContext)
, mParser(mErrorHandler, mCompilerContext)
, mOpcodesInstalled(false)
, mGlobalScripts(store)
{
mErrorHandler.setWarningsMode(warningsMode);
mScriptBlacklist.resize(scriptBlacklist.size());
std::sort(mScriptBlacklist.begin(), mScriptBlacklist.end());
}
bool ScriptManager::compile(const ESM::RefId& name)
{
mParser.reset();
mErrorHandler.reset();
if (const ESM::Script* script = mStore.get<ESM::Script>().find(name))
{
mErrorHandler.setContext(script->mId.getRefIdString());
bool Success = true;
try
{
std::istringstream input(script->mScriptText);
Compiler::Scanner scanner(mErrorHandler, input, mCompilerContext.getExtensions());
scanner.scan(mParser);
if (!mErrorHandler.isGood())
Success = false;
}
catch (const Compiler::SourceException&)
{
// error has already been reported via error handler
Success = false;
}
catch (const std::exception& error)
{
Log(Debug::Error) << "Error: An exception has been thrown: " << error.what();
Success = false;
}
if (!Success)
{
Log(Debug::Error) << "Error: script compiling failed: " << name;
}
if (Success)
{
mScripts.emplace(name, CompiledScript(mParser.getProgram(), mParser.getLocals()));
return true;
}
}
return false;
}
bool ScriptManager::run(const ESM::RefId& name, Interpreter::Context& interpreterContext)
{
// compile script
auto iter = mScripts.find(name);
if (iter == mScripts.end())
{
if (!compile(name))
{
// failed -> ignore script from now on.
mScripts.emplace(name, CompiledScript({}, Compiler::Locals()));
return false;
}
iter = mScripts.find(name);
assert(iter != mScripts.end());
}
// execute script
const auto& target = interpreterContext.getTarget();
if (!iter->second.mProgram.mInstructions.empty()
&& iter->second.mInactive.find(target) == iter->second.mInactive.end())
try
{
if (!mOpcodesInstalled)
{
installOpcodes(mInterpreter);
mOpcodesInstalled = true;
}
mInterpreter.run(iter->second.mProgram, interpreterContext);
return true;
}
catch (const MissingImplicitRefError& e)
{
Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what();
}
catch (const std::exception& e)
{
Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what();
iter->second.mInactive.insert(target); // don't execute again.
}
return false;
}
void ScriptManager::clear()
{
for (auto& script : mScripts)
{
script.second.mInactive.clear();
}
mGlobalScripts.clear();
}
std::pair<int, int> ScriptManager::compileAll()
{
int count = 0;
int success = 0;
for (auto& script : mStore.get<ESM::Script>())
{
if (!std::binary_search(mScriptBlacklist.begin(), mScriptBlacklist.end(), script.mId))
{
++count;
if (compile(script.mId))
++success;
}
}
return std::make_pair(count, success);
}
const Compiler::Locals& ScriptManager::getLocals(const ESM::RefId& name)
{
{
auto iter = mScripts.find(name);
if (iter != mScripts.end())
return iter->second.mLocals;
}
{
auto iter = mOtherLocals.find(name);
if (iter != mOtherLocals.end())
return iter->second;
}
if (const ESM::Script* script = mStore.get<ESM::Script>().search(name))
{
Compiler::Locals locals;
const Compiler::ContextOverride override(mErrorHandler, name.getRefIdString() + "[local variables]");
std::istringstream stream(script->mScriptText);
Compiler::QuickFileParser parser(mErrorHandler, mCompilerContext, locals);
Compiler::Scanner scanner(mErrorHandler, stream, mCompilerContext.getExtensions());
try
{
scanner.scan(parser);
}
catch (const Compiler::SourceException&)
{
// error has already been reported via error handler
locals.clear();
}
catch (const std::exception& error)
{
Log(Debug::Error) << "Error: An exception has been thrown: " << error.what();
locals.clear();
}
auto iter = mOtherLocals.emplace(name, locals).first;
return iter->second;
}
throw std::logic_error("script " + name.getRefIdString() + " does not exist");
}
GlobalScripts& ScriptManager::getGlobalScripts()
{
return mGlobalScripts;
}
const Compiler::Extensions& ScriptManager::getExtensions() const
{
return *mCompilerContext.getExtensions();
}
}