mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-30 21:32:42 +00:00
b88f0d2dbd
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.
224 lines
6.4 KiB
C++
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();
|
|
}
|
|
}
|