Fixes and refactoring

dont-compose-content
Petr Mikheev 4 years ago
parent 25cc884c17
commit 702eb19271

@ -833,18 +833,17 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
class OMW::Engine::LuaWorker class OMW::Engine::LuaWorker
{ {
public: public:
explicit LuaWorker(Engine* engine) : explicit LuaWorker(Engine* engine) : mEngine(engine)
mEngine(engine),
mSeparateThread(Settings::Manager::getInt("lua num threads", "Lua") > 0)
{ {
if (mSeparateThread) if (Settings::Manager::getInt("lua num threads", "Lua") > 0)
mThread = std::thread([this]{ threadBody(); }); mThread = std::thread([this]{ threadBody(); });
}; };
void allowUpdate(double dt) void allowUpdate(double dt)
{ {
mDt = dt; mDt = dt;
if (!mSeparateThread) mIsGuiMode = mEngine->mEnvironment.getWindowManager()->isGuiMode();
if (!mThread)
return; return;
{ {
std::lock_guard<std::mutex> lk(mMutex); std::lock_guard<std::mutex> lk(mMutex);
@ -855,7 +854,7 @@ public:
void finishUpdate() void finishUpdate()
{ {
if (mSeparateThread) if (mThread)
{ {
std::unique_lock<std::mutex> lk(mMutex); std::unique_lock<std::mutex> lk(mMutex);
mCV.wait(lk, [&]{ return !mUpdateRequest; }); mCV.wait(lk, [&]{ return !mUpdateRequest; });
@ -867,8 +866,8 @@ public:
void join() void join()
{ {
if (mSeparateThread) if (mThread)
mThread.join(); mThread->join();
} }
private: private:
@ -879,7 +878,7 @@ private:
const unsigned int frameNumber = viewer->getFrameStamp()->getFrameNumber(); const unsigned int frameNumber = viewer->getFrameStamp()->getFrameNumber();
ScopedProfile<UserStatsType::Lua> profile(frameStart, frameNumber, *osg::Timer::instance(), *viewer->getViewerStats()); ScopedProfile<UserStatsType::Lua> profile(frameStart, frameNumber, *osg::Timer::instance(), *viewer->getViewerStats());
mEngine->mLuaManager->update(mEngine->mEnvironment.getWindowManager()->isGuiMode(), mDt); mEngine->mLuaManager->update(mIsGuiMode, mDt);
} }
void threadBody() void threadBody()
@ -898,12 +897,12 @@ private:
} }
Engine* mEngine; Engine* mEngine;
const bool mSeparateThread;
std::mutex mMutex; std::mutex mMutex;
std::condition_variable mCV; std::condition_variable mCV;
bool mUpdateRequest; bool mUpdateRequest;
double mDt = 0; double mDt = 0;
std::thread mThread; bool mIsGuiMode = false;
std::optional<std::thread> mThread;
}; };
// Initialise and enter main loop. // Initialise and enter main loop.

@ -17,7 +17,7 @@ namespace ESM
{ {
class ESMReader; class ESMReader;
class ESMWriter; class ESMWriter;
class LuaScripts; struct LuaScripts;
} }
namespace MWBase namespace MWBase
@ -40,15 +40,16 @@ namespace MWBase
// virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, // virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object,
// const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) = 0; // const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) = 0;
struct ActorControls { struct ActorControls
bool disableAI; {
bool controlledFromLua; bool mDisableAI;
bool mControlledFromLua;
bool jump; bool mJump;
bool run; bool mRun;
float movement; float mMovement;
float sideMovement; float mSideMovement;
float turn; float mTurn;
}; };
virtual ActorControls* getActorControls(const MWWorld::Ptr&) const = 0; virtual ActorControls* getActorControls(const MWWorld::Ptr&) const = 0;

@ -53,7 +53,7 @@ namespace MWLua
std::fill(usedSlots.begin(), usedSlots.end(), false); std::fill(usedSlots.begin(), usedSlots.end(), false);
constexpr int anySlot = -1; constexpr int anySlot = -1;
auto tryEquipToSlot = [&](int slot, const Item& item) -> bool auto tryEquipToSlot = [&actor, &store, &usedSlots, &worldView, anySlot](int slot, const Item& item) -> bool
{ {
auto old_it = slot != anySlot ? store.getSlot(slot) : store.end(); auto old_it = slot != anySlot ? store.getSlot(slot) : store.end();
MWWorld::Ptr itemPtr; MWWorld::Ptr itemPtr;
@ -83,23 +83,16 @@ namespace MWLua
} }
auto [allowedSlots, _] = itemPtr.getClass().getEquipmentSlots(itemPtr); auto [allowedSlots, _] = itemPtr.getClass().getEquipmentSlots(itemPtr);
bool requestedSlotIsAllowed = false; bool requestedSlotIsAllowed = std::find(allowedSlots.begin(), allowedSlots.end(), slot) != allowedSlots.end();
for (int allowedSlot : allowedSlots)
requestedSlotIsAllowed = requestedSlotIsAllowed || allowedSlot == slot;
if (!requestedSlotIsAllowed) if (!requestedSlotIsAllowed)
{ {
slot = anySlot; auto firstAllowed = std::find_if(allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; });
for (int allowedSlot : allowedSlots) if (firstAllowed == allowedSlots.end())
if (!usedSlots[allowedSlot])
{
slot = allowedSlot;
break;
}
if (slot == anySlot)
{ {
Log(Debug::Warning) << "No suitable slot for " << ptrToString(itemPtr); Log(Debug::Warning) << "No suitable slot for " << ptrToString(itemPtr);
return false; return false;
} }
slot = *firstAllowed;
} }
// TODO: Refactor InventoryStore to accept Ptr and get rid of this linear search. // TODO: Refactor InventoryStore to accept Ptr and get rid of this linear search.
@ -124,7 +117,7 @@ namespace MWLua
if (tryEquipToSlot(slot, new_it->second)) if (tryEquipToSlot(slot, new_it->second))
usedSlots[slot] = true; usedSlots[slot] = true;
} }
for (auto [slot, item] : mEquipment) for (const auto& [slot, item] : mEquipment)
if (slot >= MWWorld::InventoryStore::Slots) if (slot >= MWWorld::InventoryStore::Slots)
tryEquipToSlot(anySlot, item); tryEquipToSlot(anySlot, item);
} }

@ -17,6 +17,7 @@ namespace MWLua
sol::function getAsyncPackageInitializer(const Context& context) sol::function getAsyncPackageInitializer(const Context& context)
{ {
using TimeUnit = LuaUtil::ScriptsContainer::TimeUnit;
sol::usertype<AsyncPackageId> api = context.mLua->sol().new_usertype<AsyncPackageId>("AsyncPackage"); sol::usertype<AsyncPackageId> api = context.mLua->sol().new_usertype<AsyncPackageId>("AsyncPackage");
api["registerTimerCallback"] = [](const AsyncPackageId& asyncId, std::string_view name, sol::function callback) api["registerTimerCallback"] = [](const AsyncPackageId& asyncId, std::string_view name, sol::function callback)
{ {
@ -27,21 +28,25 @@ namespace MWLua
const TimerCallback& callback, sol::object callbackArg) const TimerCallback& callback, sol::object callbackArg)
{ {
callback.mAsyncId.mContainer->setupSerializableTimer( callback.mAsyncId.mContainer->setupSerializableTimer(
false, world->getGameTimeInSeconds() + delay, callback.mAsyncId.mScript, callback.mName, std::move(callbackArg)); TimeUnit::SECONDS, world->getGameTimeInSeconds() + delay,
callback.mAsyncId.mScript, callback.mName, std::move(callbackArg));
}; };
api["newTimerInHours"] = [world=context.mWorldView](const AsyncPackageId&, double delay, api["newTimerInHours"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
const TimerCallback& callback, sol::object callbackArg) const TimerCallback& callback, sol::object callbackArg)
{ {
callback.mAsyncId.mContainer->setupSerializableTimer( callback.mAsyncId.mContainer->setupSerializableTimer(
true, world->getGameTimeInHours() + delay, callback.mAsyncId.mScript, callback.mName, std::move(callbackArg)); TimeUnit::HOURS, world->getGameTimeInHours() + delay,
callback.mAsyncId.mScript, callback.mName, std::move(callbackArg));
}; };
api["newUnsavableTimerInSeconds"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback) api["newUnsavableTimerInSeconds"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
{ {
asyncId.mContainer->setupUnsavableTimer(false, world->getGameTimeInSeconds() + delay, asyncId.mScript, std::move(callback)); asyncId.mContainer->setupUnsavableTimer(
TimeUnit::SECONDS, world->getGameTimeInSeconds() + delay, asyncId.mScript, std::move(callback));
}; };
api["newUnsavableTimerInHours"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback) api["newUnsavableTimerInHours"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
{ {
asyncId.mContainer->setupUnsavableTimer(true, world->getGameTimeInHours() + delay, asyncId.mScript, std::move(callback)); asyncId.mContainer->setupUnsavableTimer(
TimeUnit::HOURS, world->getGameTimeInHours() + delay, asyncId.mScript, std::move(callback));
}; };
auto initializer = [](sol::table hiddenData) auto initializer = [](sol::table hiddenData)

@ -22,11 +22,11 @@ namespace MWLua
{ {
using ActorControls = MWBase::LuaManager::ActorControls; using ActorControls = MWBase::LuaManager::ActorControls;
sol::usertype<ActorControls> controls = context.mLua->sol().new_usertype<ActorControls>("ActorControls"); sol::usertype<ActorControls> controls = context.mLua->sol().new_usertype<ActorControls>("ActorControls");
controls["movement"] = &ActorControls::movement; controls["movement"] = &ActorControls::mMovement;
controls["sideMovement"] = &ActorControls::sideMovement; controls["sideMovement"] = &ActorControls::mSideMovement;
controls["turn"] = &ActorControls::turn; controls["turn"] = &ActorControls::mTurn;
controls["run"] = &ActorControls::run; controls["run"] = &ActorControls::mRun;
controls["jump"] = &ActorControls::jump; controls["jump"] = &ActorControls::mJump;
sol::usertype<SelfObject> selfAPI = sol::usertype<SelfObject> selfAPI =
context.mLua->sol().new_usertype<SelfObject>("SelfObject", sol::base_classes, sol::bases<LObject>()); context.mLua->sol().new_usertype<SelfObject>("SelfObject", sol::base_classes, sol::bases<LObject>());
@ -34,8 +34,8 @@ namespace MWLua
selfAPI["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); }); selfAPI["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); });
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; }); selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; }; selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
selfAPI["setDirectControl"] = [](SelfObject& self, bool v) { self.mControls.controlledFromLua = v; }; selfAPI["setDirectControl"] = [](SelfObject& self, bool v) { self.mControls.mControlledFromLua = v; };
selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.disableAI = !v; }; selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; };
selfAPI["setEquipment"] = [manager=context.mLuaManager](const SelfObject& obj, sol::table equipment) selfAPI["setEquipment"] = [manager=context.mLuaManager](const SelfObject& obj, sol::table equipment)
{ {
if (!obj.ptr().getClass().hasInventoryStore(obj.ptr())) if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
@ -79,16 +79,11 @@ namespace MWLua
}; };
} }
std::unique_ptr<LocalScripts> LocalScripts::create(LuaUtil::LuaState* lua, const LObject& obj)
{
return std::unique_ptr<LocalScripts>(new LocalScripts(lua, obj));
}
LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj) LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj)
: LuaUtil::ScriptsContainer(lua, "L" + idToString(obj.id())), mData(obj) : LuaUtil::ScriptsContainer(lua, "L" + idToString(obj.id())), mData(obj)
{ {
mData.mControls.controlledFromLua = false; mData.mControls.mControlledFromLua = false;
mData.mControls.disableAI = false; mData.mControls.mDisableAI = false;
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData)); this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
registerEngineHandlers({&mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers}); registerEngineHandlers({&mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers});
} }

@ -19,8 +19,8 @@ namespace MWLua
class LocalScripts : public LuaUtil::ScriptsContainer class LocalScripts : public LuaUtil::ScriptsContainer
{ {
public: public:
static std::unique_ptr<LocalScripts> create(LuaUtil::LuaState* lua, const LObject& obj);
static void initializeSelfPackage(const Context&); static void initializeSelfPackage(const Context&);
LocalScripts(LuaUtil::LuaState* lua, const LObject& obj);
MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; } MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; }
@ -42,8 +42,8 @@ namespace MWLua
void receiveEngineEvent(const EngineEvent&, ObjectRegistry*); void receiveEngineEvent(const EngineEvent&, ObjectRegistry*);
protected: protected:
LocalScripts(LuaUtil::LuaState* lua, const LObject& obj);
SelfObject mData; SelfObject mData;
private: private:
EngineHandlerList mOnActiveHandlers{"onActive"}; EngineHandlerList mOnActiveHandlers{"onActive"};
EngineHandlerList mOnInactiveHandlers{"onInactive"}; EngineHandlerList mOnInactiveHandlers{"onInactive"};

@ -76,7 +76,7 @@ namespace MWLua
if (cell) if (cell)
return GCell{cell}; return GCell{cell};
else else
return {}; return sol::nullopt;
}; };
api["getExteriorCell"] = [worldView=context.mWorldView](int x, int y) -> sol::optional<GCell> api["getExteriorCell"] = [worldView=context.mWorldView](int x, int y) -> sol::optional<GCell>
{ {
@ -84,7 +84,7 @@ namespace MWLua
if (cell) if (cell)
return GCell{cell}; return GCell{cell};
else else
return {}; return sol::nullopt;
}; };
api["activeActors"] = GObjectList{worldView->getActorsInScene()}; api["activeActors"] = GObjectList{worldView->getActorsInScene()};
api["selectObjects"] = [context](const Queries::Query& query) api["selectObjects"] = [context](const Queries::Query& query)
@ -156,7 +156,9 @@ namespace MWLua
for (const Queries::Field* field : group.mFields) for (const Queries::Field* field : group.mFields)
{ {
sol::table subgroup = res; sol::table subgroup = res;
for (int i = 0; i < static_cast<int>(field->path().size()) - 1; ++i) if (field->path().empty())
throw std::logic_error("Empty path in Queries::Field");
for (size_t i = 0; i < field->path().size() - 1; ++i)
{ {
const std::string& name = field->path()[i]; const std::string& name = field->path()[i];
if (subgroup[name] == sol::nil) if (subgroup[name] == sol::nil)

@ -7,6 +7,7 @@
#include <components/esm/luascripts.hpp> #include <components/esm/luascripts.hpp>
#include <components/lua/utilpackage.hpp> #include <components/lua/utilpackage.hpp>
#include <components/lua/omwscriptsparser.hpp>
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -19,9 +20,10 @@
namespace MWLua namespace MWLua
{ {
LuaManager::LuaManager(const VFS::Manager* vfs, const std::vector<std::string>& globalScriptLists) : mLua(vfs) LuaManager::LuaManager(const VFS::Manager* vfs, const std::vector<std::string>& scriptLists) : mLua(vfs)
{ {
Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion(); Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion();
mGlobalScriptList = LuaUtil::parseOMWScriptsFiles(vfs, scriptLists);
mGlobalSerializer = createUserdataSerializer(false, mWorldView.getObjectRegistry()); mGlobalSerializer = createUserdataSerializer(false, mWorldView.getObjectRegistry());
mLocalSerializer = createUserdataSerializer(true, mWorldView.getObjectRegistry()); mLocalSerializer = createUserdataSerializer(true, mWorldView.getObjectRegistry());
@ -58,37 +60,6 @@ namespace MWLua
mCameraPackage = initCameraPackage(localContext); mCameraPackage = initCameraPackage(localContext);
mUserInterfacePackage = initUserInterfacePackage(localContext); mUserInterfacePackage = initUserInterfacePackage(localContext);
mNearbyPackage = initNearbyPackage(localContext); mNearbyPackage = initNearbyPackage(localContext);
auto endsWith = [](std::string_view s, std::string_view suffix)
{
return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
};
for (const std::string& scriptListFile : globalScriptLists)
{
if (!endsWith(scriptListFile, ".omwscripts"))
{
Log(Debug::Error) << "Script list should have suffix '.omwscripts', got: '" << scriptListFile << "'";
continue;
}
std::string content(std::istreambuf_iterator<char>(*vfs->get(scriptListFile)), {});
std::string_view view(content);
while (!view.empty())
{
size_t pos = 0;
while (pos < view.size() && view[pos] != '\n')
pos++;
std::string_view line = view.substr(0, pos);
view = view.substr(pos + 1);
if (line.empty() || line[0] == '#')
continue;
if (line.back() == '\r')
line = line.substr(0, pos - 1);
if (endsWith(line, ".lua"))
mGlobalScriptList.push_back(std::string(line));
else
Log(Debug::Error) << "Lua script should have suffix '.lua', got: '" << line.substr(0, 300) << "'";
}
}
} }
void LuaManager::init() void LuaManager::init()
@ -306,18 +277,18 @@ namespace MWLua
LocalScripts* LuaManager::createLocalScripts(const MWWorld::Ptr& ptr) LocalScripts* LuaManager::createLocalScripts(const MWWorld::Ptr& ptr)
{ {
std::unique_ptr<LocalScripts> scripts; std::shared_ptr<LocalScripts> scripts;
// When loading a game, it can be called before LuaManager::setPlayer, // When loading a game, it can be called before LuaManager::setPlayer,
// so we can't just check ptr == mPlayer here. // so we can't just check ptr == mPlayer here.
if (*ptr.getCellRef().getRefIdPtr() == "player") if (*ptr.getCellRef().getRefIdPtr() == "player")
{ {
mPlayerScripts = new PlayerScripts(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry())); mPlayerScripts = new PlayerScripts(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()));
scripts = std::unique_ptr<LocalScripts>(mPlayerScripts); scripts = std::shared_ptr<LocalScripts>(mPlayerScripts);
scripts->addPackage("openmw.ui", mUserInterfacePackage); scripts->addPackage("openmw.ui", mUserInterfacePackage);
scripts->addPackage("openmw.camera", mCameraPackage); scripts->addPackage("openmw.camera", mCameraPackage);
} }
else else
scripts = LocalScripts::create(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry())); scripts = std::make_shared<LocalScripts>(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()));
scripts->addPackage("openmw.nearby", mNearbyPackage); scripts->addPackage("openmw.nearby", mNearbyPackage);
scripts->setSerializer(mLocalSerializer.get()); scripts->setSerializer(mLocalSerializer.get());

@ -23,7 +23,6 @@ namespace MWLua
{ {
public: public:
LuaManager(const VFS::Manager* vfs, const std::vector<std::string>& globalScriptLists); LuaManager(const VFS::Manager* vfs, const std::vector<std::string>& globalScriptLists);
~LuaManager() {}
// Called by engine.cpp when environment is fully initialized. // Called by engine.cpp when environment is fully initialized.
void init(); void init();

@ -104,7 +104,7 @@ namespace MWLua
if (ptr.isInCell()) if (ptr.isInCell())
return Cell<ObjectT>{ptr.getCell()}; return Cell<ObjectT>{ptr.getCell()};
else else
return {}; return sol::nullopt;
}); });
objectT["position"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f objectT["position"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
{ {
@ -123,17 +123,17 @@ namespace MWLua
context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)}); context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
}; };
objectT["canMove"] = [context](const ObjectT& o) objectT["canMove"] = [](const ObjectT& o)
{ {
const MWWorld::Class& cls = o.ptr().getClass(); const MWWorld::Class& cls = o.ptr().getClass();
return cls.getMaxSpeed(o.ptr()) > 0; return cls.getMaxSpeed(o.ptr()) > 0;
}; };
objectT["getRunSpeed"] = [context](const ObjectT& o) objectT["getRunSpeed"] = [](const ObjectT& o)
{ {
const MWWorld::Class& cls = o.ptr().getClass(); const MWWorld::Class& cls = o.ptr().getClass();
return cls.getRunSpeed(o.ptr()); return cls.getRunSpeed(o.ptr());
}; };
objectT["getWalkSpeed"] = [context](const ObjectT& o) objectT["getWalkSpeed"] = [](const ObjectT& o)
{ {
const MWWorld::Class& cls = o.ptr().getClass(); const MWWorld::Class& cls = o.ptr().getClass();
return cls.getWalkSpeed(o.ptr()); return cls.getWalkSpeed(o.ptr());
@ -182,12 +182,12 @@ namespace MWLua
{ {
const MWWorld::CellRef& cellRef = ptr(o).getCellRef(); const MWWorld::CellRef& cellRef = ptr(o).getCellRef();
if (!cellRef.getTeleport()) if (!cellRef.getTeleport())
return {}; return sol::nullopt;
MWWorld::CellStore* cell = worldView->findCell(cellRef.getDestCell(), cellRef.getDoorDest().asVec3()); MWWorld::CellStore* cell = worldView->findCell(cellRef.getDestCell(), cellRef.getDoorDest().asVec3());
if (cell) if (cell)
return Cell<ObjectT>{cell}; return Cell<ObjectT>{cell};
else else
return {}; return sol::nullopt;
}); });
} }

@ -84,9 +84,12 @@ namespace MWLua
} }
if (ptr.getRefData().getCount() == 0) if (ptr.getRefData().getCount() == 0)
return false; return false;
// It is stupid, but "prisonmarker" has class "Door" despite that it is only an invisible marker. So we ignore all markers.
// It is important to exclude all markers before checking what class it is.
// For example "prisonmarker" has class "Door" despite that it is only an invisible marker.
if (isMarker(ptr)) if (isMarker(ptr))
return false; return false;
const MWWorld::Class& cls = ptr.getClass(); const MWWorld::Class& cls = ptr.getClass();
if (cls.isActivator() != (query.mQueryType == ObjectQueryTypes::ACTIVATORS)) if (cls.isActivator() != (query.mQueryType == ObjectQueryTypes::ACTIVATORS))
return false; return false;

@ -2065,7 +2065,7 @@ namespace MWMechanics
if (iter->first != player) if (iter->first != player)
{ {
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
if (isConscious(iter->first) && !(luaControls && luaControls->disableAI)) if (isConscious(iter->first) && !(luaControls && luaControls->mDisableAI))
{ {
stats.getAiSequence().execute(iter->first, *ctrl, duration); stats.getAiSequence().execute(iter->first, *ctrl, duration);
updateGreetingState(iter->first, *iter->second, timerUpdateHello > 0); updateGreetingState(iter->first, *iter->second, timerUpdateHello > 0);
@ -2074,7 +2074,7 @@ namespace MWMechanics
} }
} }
} }
else if (aiActive && iter->first != player && isConscious(iter->first) && !(luaControls && luaControls->disableAI)) else if (aiActive && iter->first != player && isConscious(iter->first) && !(luaControls && luaControls->mDisableAI))
{ {
CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first);
stats.getAiSequence().execute(iter->first, *ctrl, duration, /*outOfRange*/true); stats.getAiSequence().execute(iter->first, *ctrl, duration, /*outOfRange*/true);
@ -2101,21 +2101,21 @@ namespace MWMechanics
float rotationZ = mov.mRotation[2]; float rotationZ = mov.mRotation[2];
bool jump = mov.mPosition[2] == 1; bool jump = mov.mPosition[2] == 1;
bool runFlag = stats.getMovementFlag(MWMechanics::CreatureStats::Flag_Run); bool runFlag = stats.getMovementFlag(MWMechanics::CreatureStats::Flag_Run);
if (luaControls->controlledFromLua) if (luaControls->mControlledFromLua)
{ {
mov.mPosition[0] = luaControls->sideMovement; mov.mPosition[0] = luaControls->mSideMovement;
mov.mPosition[1] = luaControls->movement; mov.mPosition[1] = luaControls->mMovement;
mov.mPosition[2] = luaControls->jump ? 1 : 0; mov.mPosition[2] = luaControls->mJump ? 1 : 0;
mov.mRotation[1] = 0; mov.mRotation[1] = 0;
mov.mRotation[2] = luaControls->turn; mov.mRotation[2] = luaControls->mTurn;
mov.mSpeedFactor = osg::Vec2(luaControls->movement, luaControls->sideMovement).length(); mov.mSpeedFactor = osg::Vec2(luaControls->mMovement, luaControls->mSideMovement).length();
stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Run, luaControls->run); stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Run, luaControls->mRun);
} }
luaControls->sideMovement = movement.x(); luaControls->mSideMovement = movement.x();
luaControls->movement = movement.y(); luaControls->mMovement = movement.y();
luaControls->turn = rotationZ; luaControls->mTurn = rotationZ;
luaControls->jump = jump; luaControls->mJump = jump;
luaControls->run = runFlag; luaControls->mRun = runFlag;
} }
} }
} }

@ -22,8 +22,9 @@ namespace MWWorld
lastAssignedRefNum.mIndex++; lastAssignedRefNum.mIndex++;
if (lastAssignedRefNum.mIndex == 0) // mIndex overflow, so mContentFile should be changed if (lastAssignedRefNum.mIndex == 0) // mIndex overflow, so mContentFile should be changed
{ {
lastAssignedRefNum.mContentFile--; if (lastAssignedRefNum.mContentFile > std::numeric_limits<int32_t>::min())
if (lastAssignedRefNum.mContentFile > 0) lastAssignedRefNum.mContentFile--;
else
Log(Debug::Error) << "RefNum counter overflow in CellRef::getOrAssignRefNum"; Log(Debug::Error) << "RefNum counter overflow in CellRef::getOrAssignRefNum";
} }
mCellRef.mRefNum = lastAssignedRefNum; mCellRef.mRefNum = lastAssignedRefNum;

@ -23,7 +23,7 @@ enum RefDataFlags
namespace MWWorld namespace MWWorld
{ {
void RefData::setLuaScripts(std::unique_ptr<MWLua::LocalScripts>&& scripts) void RefData::setLuaScripts(std::shared_ptr<MWLua::LocalScripts>&& scripts)
{ {
mChanged = true; mChanged = true;
mLuaScripts = std::move(scripts); mLuaScripts = std::move(scripts);

@ -103,7 +103,7 @@ namespace MWWorld
void setLocals (const ESM::Script& script); void setLocals (const ESM::Script& script);
MWLua::LocalScripts* getLuaScripts() { return mLuaScripts.get(); } MWLua::LocalScripts* getLuaScripts() { return mLuaScripts.get(); }
void setLuaScripts(std::unique_ptr<MWLua::LocalScripts>&&); void setLuaScripts(std::shared_ptr<MWLua::LocalScripts>&&);
void setCount (int count); void setCount (int count);
///< Set object count (an object pile is a simple object with a count >1). ///< Set object count (an object pile is a simple object with a count >1).

@ -20,6 +20,7 @@ if (GTEST_FOUND AND GMOCK_FOUND)
lua/test_utilpackage.cpp lua/test_utilpackage.cpp
lua/test_serialization.cpp lua/test_serialization.cpp
lua/test_querypackage.cpp lua/test_querypackage.cpp
lua/test_omwscriptsparser.cpp
misc/test_stringops.cpp misc/test_stringops.cpp
misc/test_endianness.cpp misc/test_endianness.cpp

@ -0,0 +1,59 @@
#include "gmock/gmock.h"
#include <gtest/gtest.h>
#include <components/lua/omwscriptsparser.hpp>
#include "testing_util.hpp"
namespace
{
using namespace testing;
TestFile file1(
"#comment.lua\n"
"\n"
"script1.lua\n"
"some mod/Some Script.lua"
);
TestFile file2(
"#comment.lua\r\n"
"\r\n"
"script2.lua\r\n"
"some other mod/Some Script.lua\r"
);
TestFile emptyFile("");
TestFile invalidFile("Invalid file");
struct OMWScriptsParserTest : Test
{
std::unique_ptr<VFS::Manager> mVFS = createTestVFS({
{"file1.omwscripts", &file1},
{"file2.omwscripts", &file2},
{"empty.omwscripts", &emptyFile},
{"invalid.lua", &file1},
{"invalid.omwscripts", &invalidFile},
});
};
TEST_F(OMWScriptsParserTest, Basic)
{
internal::CaptureStdout();
std::vector<std::string> res = LuaUtil::parseOMWScriptsFiles(
mVFS.get(), {"file2.omwscripts", "empty.omwscripts", "file1.omwscripts"});
EXPECT_EQ(internal::GetCapturedStdout(), "");
EXPECT_THAT(res, ElementsAre("script2.lua", "some other mod/Some Script.lua",
"script1.lua", "some mod/Some Script.lua"));
}
TEST_F(OMWScriptsParserTest, InvalidFiles)
{
internal::CaptureStdout();
std::vector<std::string> res = LuaUtil::parseOMWScriptsFiles(
mVFS.get(), {"invalid.lua", "invalid.omwscripts"});
EXPECT_EQ(internal::GetCapturedStdout(),
"Script list should have suffix '.omwscripts', got: 'invalid.lua'\n"
"Lua script should have suffix '.lua', got: 'Invalid file'\n");
EXPECT_THAT(res, ElementsAre());
}
}

@ -304,6 +304,7 @@ return {
TEST_F(LuaScriptsContainerTest, Timers) TEST_F(LuaScriptsContainerTest, Timers)
{ {
using TimeUnit = LuaUtil::ScriptsContainer::TimeUnit;
LuaUtil::ScriptsContainer scripts(&mLua, "Test"); LuaUtil::ScriptsContainer scripts(&mLua, "Test");
EXPECT_TRUE(scripts.addNewScript("test1.lua")); EXPECT_TRUE(scripts.addNewScript("test1.lua"));
EXPECT_TRUE(scripts.addNewScript("test2.lua")); EXPECT_TRUE(scripts.addNewScript("test2.lua"));
@ -321,18 +322,18 @@ return {
scripts.processTimers(1, 2); scripts.processTimers(1, 2);
scripts.setupSerializableTimer(false, 10, "test1.lua", "B", sol::make_object(mLua.sol(), 3)); scripts.setupSerializableTimer(TimeUnit::SECONDS, 10, "test1.lua", "B", sol::make_object(mLua.sol(), 3));
scripts.setupSerializableTimer(true, 10, "test2.lua", "B", sol::make_object(mLua.sol(), 4)); scripts.setupSerializableTimer(TimeUnit::HOURS, 10, "test2.lua", "B", sol::make_object(mLua.sol(), 4));
scripts.setupSerializableTimer(false, 5, "test1.lua", "A", sol::make_object(mLua.sol(), 1)); scripts.setupSerializableTimer(TimeUnit::SECONDS, 5, "test1.lua", "A", sol::make_object(mLua.sol(), 1));
scripts.setupSerializableTimer(true, 5, "test2.lua", "A", sol::make_object(mLua.sol(), 2)); scripts.setupSerializableTimer(TimeUnit::HOURS, 5, "test2.lua", "A", sol::make_object(mLua.sol(), 2));
scripts.setupSerializableTimer(false, 15, "test1.lua", "A", sol::make_object(mLua.sol(), 10)); scripts.setupSerializableTimer(TimeUnit::SECONDS, 15, "test1.lua", "A", sol::make_object(mLua.sol(), 10));
scripts.setupSerializableTimer(false, 15, "test1.lua", "B", sol::make_object(mLua.sol(), 20)); scripts.setupSerializableTimer(TimeUnit::SECONDS, 15, "test1.lua", "B", sol::make_object(mLua.sol(), 20));
scripts.setupUnsavableTimer(false, 10, "test2.lua", fn2); scripts.setupUnsavableTimer(TimeUnit::SECONDS, 10, "test2.lua", fn2);
scripts.setupUnsavableTimer(true, 10, "test1.lua", fn2); scripts.setupUnsavableTimer(TimeUnit::HOURS, 10, "test1.lua", fn2);
scripts.setupUnsavableTimer(false, 5, "test2.lua", fn1); scripts.setupUnsavableTimer(TimeUnit::SECONDS, 5, "test2.lua", fn1);
scripts.setupUnsavableTimer(true, 5, "test1.lua", fn1); scripts.setupUnsavableTimer(TimeUnit::HOURS, 5, "test1.lua", fn1);
scripts.setupUnsavableTimer(false, 15, "test2.lua", fn1); scripts.setupUnsavableTimer(TimeUnit::SECONDS, 15, "test2.lua", fn1);
EXPECT_EQ(counter1, 0); EXPECT_EQ(counter1, 0);
EXPECT_EQ(counter3, 0); EXPECT_EQ(counter3, 0);

@ -29,7 +29,7 @@ endif (GIT_CHECKOUT)
# source files # source files
add_component_dir (lua add_component_dir (lua
luastate scriptscontainer utilpackage serialization luastate scriptscontainer utilpackage serialization omwscriptsparser
) )
add_component_dir (settings add_component_dir (settings

@ -48,7 +48,7 @@ void ESM::LuaScripts::load(ESMReader& esm)
{ {
esm.getSubHeader(); esm.getSubHeader();
LuaTimer timer; LuaTimer timer;
esm.getT(timer.mHours); esm.getT(timer.mUnit);
esm.getT(timer.mTime); esm.getT(timer.mTime);
timer.mCallbackName = esm.getHNString("LUAC"); timer.mCallbackName = esm.getHNString("LUAC");
timer.mCallbackArgument = loadLuaBinaryData(esm); timer.mCallbackArgument = loadLuaBinaryData(esm);
@ -68,7 +68,7 @@ void ESM::LuaScripts::save(ESMWriter& esm) const
for (const LuaTimer& timer : script.mTimers) for (const LuaTimer& timer : script.mTimers)
{ {
esm.startSubRecord("LUAT"); esm.startSubRecord("LUAT");
esm.writeT(timer.mHours); esm.writeT(timer.mUnit);
esm.writeT(timer.mTime); esm.writeT(timer.mTime);
esm.endRecord("LUAT"); esm.endRecord("LUAT");
esm.writeHNString("LUAC", timer.mCallbackName); esm.writeHNString("LUAC", timer.mCallbackName);

@ -14,8 +14,14 @@ namespace ESM
struct LuaTimer struct LuaTimer
{ {
enum class TimeUnit : bool
{
SECONDS = 0,
HOURS = 1,
};
TimeUnit mUnit;
double mTime; double mTime;
bool mHours; // false - game seconds, true - game hours
std::string mCallbackName; std::string mCallbackName;
std::string mCallbackArgument; // Serialized Lua table. It is a binary data. Can contain '\0'. std::string mCallbackArgument; // Serialized Lua table. It is a binary data. Can contain '\0'.
}; };

@ -12,9 +12,7 @@ namespace LuaUtil
static std::string packageNameToPath(std::string_view packageName) static std::string packageNameToPath(std::string_view packageName)
{ {
std::string res(packageName); std::string res(packageName);
for (size_t i = 0; i < res.size(); ++i) std::replace(res.begin(), res.end(), '.', '/');
if (res[i] == '.')
res[i] = '/';
res.append(".lua"); res.append(".lua");
return res; return res;
} }
@ -28,7 +26,7 @@ namespace LuaUtil
{ {
mLua.open_libraries(sol::lib::base, sol::lib::coroutine, sol::lib::math, sol::lib::string, sol::lib::table); mLua.open_libraries(sol::lib::base, sol::lib::coroutine, sol::lib::math, sol::lib::string, sol::lib::table);
mLua["math"]["randomseed"](static_cast<unsigned>(time(NULL))); mLua["math"]["randomseed"](static_cast<unsigned>(std::time(nullptr)));
mLua["math"]["randomseed"] = sol::nil; mLua["math"]["randomseed"] = sol::nil;
mLua["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; }; mLua["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; };

@ -0,0 +1,44 @@
#include "omwscriptsparser.hpp"
#include <algorithm>
#include <components/debug/debuglog.hpp>
std::vector<std::string> LuaUtil::parseOMWScriptsFiles(const VFS::Manager* vfs, const std::vector<std::string>& scriptLists)
{
auto endsWith = [](std::string_view s, std::string_view suffix)
{
return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
};
std::vector<std::string> res;
for (const std::string& scriptListFile : scriptLists)
{
if (!endsWith(scriptListFile, ".omwscripts"))
{
Log(Debug::Error) << "Script list should have suffix '.omwscripts', got: '" << scriptListFile << "'";
continue;
}
std::string content(std::istreambuf_iterator<char>(*vfs->get(scriptListFile)), {});
std::string_view view(content);
while (!view.empty())
{
size_t pos = 0;
while (pos < view.size() && view[pos] != '\n')
pos++;
std::string_view line = view.substr(0, pos);
view = view.substr(std::min(pos + 1, view.size()));
if (!line.empty() && line.back() == '\r')
line = line.substr(0, pos - 1);
// Lines starting with '#' are comments.
// TODO: Maybe make the parser more robust. It is a bit inconsistent that 'path/#to/file.lua'
// is a valid path, but '#path/to/file.lua' is considered as a comment and ignored.
if (line.empty() || line[0] == '#')
continue;
if (endsWith(line, ".lua"))
res.push_back(std::string(line));
else
Log(Debug::Error) << "Lua script should have suffix '.lua', got: '" << line.substr(0, 300) << "'";
}
}
return res;
}

@ -0,0 +1,14 @@
#ifndef COMPONENTS_LUA_OMWSCRIPTSPARSER_H
#define COMPONENTS_LUA_OMWSCRIPTSPARSER_H
#include <components/vfs/manager.hpp>
namespace LuaUtil
{
// Parses list of `*.omwscripts` files.
std::vector<std::string> parseOMWScriptsFiles(const VFS::Manager* vfs, const std::vector<std::string>& scriptLists);
}
#endif // COMPONENTS_LUA_OMWSCRIPTSPARSER_H

@ -78,10 +78,10 @@ namespace LuaUtil
bool ScriptsContainer::removeScript(const std::string& path) bool ScriptsContainer::removeScript(const std::string& path)
{ {
auto it = mScripts.find(path); auto scriptIter = mScripts.find(path);
if (it == mScripts.end()) if (scriptIter == mScripts.end())
return false; // no such script return false; // no such script
sol::object& script = it->second.mInterface; sol::object& script = scriptIter->second.mInterface;
if (getFieldOrNil(script, INTERFACE_NAME) != sol::nil) if (getFieldOrNil(script, INTERFACE_NAME) != sol::nil)
{ {
std::string_view interfaceName = getFieldOrNil(script, INTERFACE_NAME).as<std::string_view>(); std::string_view interfaceName = getFieldOrNil(script, INTERFACE_NAME).as<std::string_view>();
@ -110,10 +110,10 @@ namespace LuaUtil
for (auto& [key, value] : sol::table(engineHandlers)) for (auto& [key, value] : sol::table(engineHandlers))
{ {
std::string_view handlerName = key.as<std::string_view>(); std::string_view handlerName = key.as<std::string_view>();
auto it = mEngineHandlers.find(handlerName); auto handlerIter = mEngineHandlers.find(handlerName);
if (it == mEngineHandlers.end()) if (handlerIter == mEngineHandlers.end())
continue; continue;
std::vector<sol::protected_function>& list = it->second->mList; std::vector<sol::protected_function>& list = handlerIter->second->mList;
list.erase(std::find(list.begin(), list.end(), value.as<sol::protected_function>())); list.erase(std::find(list.begin(), list.end(), value.as<sol::protected_function>()));
} }
} }
@ -126,7 +126,7 @@ namespace LuaUtil
list.erase(std::find(list.begin(), list.end(), value.as<sol::protected_function>())); list.erase(std::find(list.begin(), list.end(), value.as<sol::protected_function>()));
} }
} }
mScripts.erase(it); mScripts.erase(scriptIter);
mScriptOrder.erase(std::find(mScriptOrder.begin(), mScriptOrder.end(), path)); mScriptOrder.erase(std::find(mScriptOrder.begin(), mScriptOrder.end(), path));
return true; return true;
} }
@ -201,13 +201,13 @@ namespace LuaUtil
void ScriptsContainer::save(ESM::LuaScripts& data) void ScriptsContainer::save(ESM::LuaScripts& data)
{ {
std::map<std::string, std::vector<ESM::LuaTimer>> timers; std::map<std::string, std::vector<ESM::LuaTimer>> timers;
auto saveTimerFn = [&](const Timer& timer, bool inHours) auto saveTimerFn = [&](const Timer& timer, TimeUnit timeUnit)
{ {
if (!timer.mSerializable) if (!timer.mSerializable)
return; return;
ESM::LuaTimer savedTimer; ESM::LuaTimer savedTimer;
savedTimer.mTime = timer.mTime; savedTimer.mTime = timer.mTime;
savedTimer.mHours = inHours; savedTimer.mUnit = timeUnit;
savedTimer.mCallbackName = std::get<std::string>(timer.mCallback); savedTimer.mCallbackName = std::get<std::string>(timer.mCallback);
savedTimer.mCallbackArgument = timer.mSerializedArg; savedTimer.mCallbackArgument = timer.mSerializedArg;
if (timers.count(timer.mScript) == 0) if (timers.count(timer.mScript) == 0)
@ -215,9 +215,9 @@ namespace LuaUtil
timers[timer.mScript].push_back(std::move(savedTimer)); timers[timer.mScript].push_back(std::move(savedTimer));
}; };
for (const Timer& timer : mSecondsTimersQueue) for (const Timer& timer : mSecondsTimersQueue)
saveTimerFn(timer, false); saveTimerFn(timer, TimeUnit::SECONDS);
for (const Timer& timer : mHoursTimersQueue) for (const Timer& timer : mHoursTimersQueue)
saveTimerFn(timer, true); saveTimerFn(timer, TimeUnit::HOURS);
data.mScripts.clear(); data.mScripts.clear();
for (const std::string& path : mScriptOrder) for (const std::string& path : mScriptOrder)
{ {
@ -291,7 +291,7 @@ namespace LuaUtil
// updates refnums, so timer.mSerializedArg may be not equal to savedTimer.mCallbackArgument. // updates refnums, so timer.mSerializedArg may be not equal to savedTimer.mCallbackArgument.
timer.mSerializedArg = serialize(timer.mArg, mSerializer); timer.mSerializedArg = serialize(timer.mArg, mSerializer);
if (savedTimer.mHours) if (savedTimer.mUnit == TimeUnit::HOURS)
mHoursTimersQueue.push_back(std::move(timer)); mHoursTimersQueue.push_back(std::move(timer));
else else
mSecondsTimersQueue.push_back(std::move(timer)); mSecondsTimersQueue.push_back(std::move(timer));
@ -352,7 +352,7 @@ namespace LuaUtil
std::push_heap(timerQueue.begin(), timerQueue.end()); std::push_heap(timerQueue.begin(), timerQueue.end());
} }
void ScriptsContainer::setupSerializableTimer(bool inHours, double time, const std::string& scriptPath, void ScriptsContainer::setupSerializableTimer(TimeUnit timeUnit, double time, const std::string& scriptPath,
std::string_view callbackName, sol::object callbackArg) std::string_view callbackName, sol::object callbackArg)
{ {
Timer t; Timer t;
@ -362,10 +362,10 @@ namespace LuaUtil
t.mTime = time; t.mTime = time;
t.mArg = callbackArg; t.mArg = callbackArg;
t.mSerializedArg = serialize(t.mArg, mSerializer); t.mSerializedArg = serialize(t.mArg, mSerializer);
insertTimer(inHours ? mHoursTimersQueue : mSecondsTimersQueue, std::move(t)); insertTimer(timeUnit == TimeUnit::HOURS ? mHoursTimersQueue : mSecondsTimersQueue, std::move(t));
} }
void ScriptsContainer::setupUnsavableTimer(bool inHours, double time, const std::string& scriptPath, sol::function callback) void ScriptsContainer::setupUnsavableTimer(TimeUnit timeUnit, double time, const std::string& scriptPath, sol::function callback)
{ {
Timer t; Timer t;
t.mScript = scriptPath; t.mScript = scriptPath;
@ -376,7 +376,7 @@ namespace LuaUtil
getHiddenData(scriptPath)[TEMPORARY_TIMER_CALLBACKS][mTemporaryCallbackCounter] = std::move(callback); getHiddenData(scriptPath)[TEMPORARY_TIMER_CALLBACKS][mTemporaryCallbackCounter] = std::move(callback);
mTemporaryCallbackCounter++; mTemporaryCallbackCounter++;
insertTimer(inHours ? mHoursTimersQueue : mSecondsTimersQueue, std::move(t)); insertTimer(timeUnit == TimeUnit::HOURS ? mHoursTimersQueue : mSecondsTimersQueue, std::move(t));
} }
void ScriptsContainer::callTimer(const Timer& t) void ScriptsContainer::callTimer(const Timer& t)

@ -67,6 +67,7 @@ namespace LuaUtil
ScriptsContainer* mContainer; ScriptsContainer* mContainer;
std::string mPath; std::string mPath;
}; };
using TimeUnit = ESM::LuaTimer::TimeUnit;
// `namePrefix` is a common prefix for all scripts in the container. Used in logs for error messages and `print` output. // `namePrefix` is a common prefix for all scripts in the container. Used in logs for error messages and `print` output.
ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix); ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix);
@ -122,17 +123,17 @@ namespace LuaUtil
void registerTimerCallback(const std::string& scriptPath, std::string_view callbackName, sol::function callback); void registerTimerCallback(const std::string& scriptPath, std::string_view callbackName, sol::function callback);
// Sets up a timer, that can be automatically saved and loaded. // Sets up a timer, that can be automatically saved and loaded.
// inHours - false if time unit is game seconds and true if time unit if game hours. // timeUnit - game seconds (TimeUnit::Seconds) or game hours (TimeUnit::Hours).
// time - the absolute game time (in seconds or in hours) when the timer should be executed. // time - the absolute game time (in seconds or in hours) when the timer should be executed.
// scriptPath - script path in VFS is used as script id. The script with the given path should already present in the container. // scriptPath - script path in VFS is used as script id. The script with the given path should already present in the container.
// callbackName - callback (should be registered in advance) for this timer. // callbackName - callback (should be registered in advance) for this timer.
// callbackArg - parameter for the callback (should be serializable). // callbackArg - parameter for the callback (should be serializable).
void setupSerializableTimer(bool inHours, double time, const std::string& scriptPath, void setupSerializableTimer(TimeUnit timeUnit, double time, const std::string& scriptPath,
std::string_view callbackName, sol::object callbackArg); std::string_view callbackName, sol::object callbackArg);
// Creates a timer. `callback` is an arbitrary Lua function. This type of timers is called "unsavable" // Creates a timer. `callback` is an arbitrary Lua function. This type of timers is called "unsavable"
// because it can not be stored in saves. I.e. loading a saved game will not fully restore the state. // because it can not be stored in saves. I.e. loading a saved game will not fully restore the state.
void setupUnsavableTimer(bool inHours, double time, const std::string& scriptPath, sol::function callback); void setupUnsavableTimer(TimeUnit timeUnit, double time, const std::string& scriptPath, sol::function callback);
protected: protected:
struct EngineHandlerList struct EngineHandlerList

@ -10,7 +10,7 @@ namespace LuaUtil
constexpr unsigned char FORMAT_VERSION = 0; constexpr unsigned char FORMAT_VERSION = 0;
enum class SerializedType enum class SerializedType : char
{ {
NUMBER = 0x0, NUMBER = 0x0,
LONG_STRING = 0x1, LONG_STRING = 0x1,
@ -20,8 +20,10 @@ namespace LuaUtil
VEC2 = 0x10, VEC2 = 0x10,
VEC3 = 0x11, VEC3 = 0x11,
// All values should be lesser than 0x20 (SHORT_STRING_FLAG).
}; };
constexpr unsigned char SHORT_STRING_FLAG = 0x20; // 0x001SSSSS. SSSSS = string length constexpr unsigned char SHORT_STRING_FLAG = 0x20; // 0b001SSSSS. SSSSS = string length
constexpr unsigned char CUSTOM_FULL_FLAG = 0x40; // 0b01TTTTTT + 32bit dataSize constexpr unsigned char CUSTOM_FULL_FLAG = 0x40; // 0b01TTTTTT + 32bit dataSize
constexpr unsigned char CUSTOM_COMPACT_FLAG = 0x80; // 0b1SSSSTTT. SSSS = dataSize, TTT = (typeName size - 1) constexpr unsigned char CUSTOM_COMPACT_FLAG = 0x80; // 0b1SSSSTTT. SSSS = dataSize, TTT = (typeName size - 1)
@ -224,8 +226,8 @@ namespace LuaUtil
sol::stack::push<osg::Vec3f>(lua.lua_state(), osg::Vec3f(x, y, z)); sol::stack::push<osg::Vec3f>(lua.lua_state(), osg::Vec3f(x, y, z));
return; return;
} }
default: throw std::runtime_error("Unknown type: " + std::to_string(type));
} }
throw std::runtime_error("Unknown type: " + std::to_string(type));
} }
BinaryData serialize(const sol::object& obj, const UserdataSerializer* customSerializer) BinaryData serialize(const sol::object& obj, const UserdataSerializer* customSerializer)

Loading…
Cancel
Save