mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 07:09:40 +00:00
Saving/loading for Lua scripts (saves format is changed)
This commit is contained in:
parent
914e604e06
commit
8c6d303730
16 changed files with 248 additions and 8 deletions
|
@ -56,7 +56,7 @@ add_openmw_dir (mwscript
|
|||
)
|
||||
|
||||
add_openmw_dir (mwlua
|
||||
luamanagerimp localscripts object worldview luabindings userdataserializer
|
||||
luamanagerimp localscripts object worldview luabindings userdataserializer eventqueue
|
||||
objectbindings asyncbindings
|
||||
)
|
||||
|
||||
|
|
|
@ -8,6 +8,18 @@ namespace MWWorld
|
|||
class Ptr;
|
||||
}
|
||||
|
||||
namespace Loading
|
||||
{
|
||||
class Listener;
|
||||
}
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
class LuaScripts;
|
||||
}
|
||||
|
||||
namespace MWBase
|
||||
{
|
||||
|
||||
|
@ -35,6 +47,18 @@ namespace MWBase
|
|||
|
||||
virtual void clear() = 0;
|
||||
virtual void setupPlayer(const MWWorld::Ptr&) = 0;
|
||||
|
||||
// Saving
|
||||
int countSavedGameRecords() const { return 1; };
|
||||
virtual void write(ESM::ESMWriter& writer, Loading::Listener& progress) = 0;
|
||||
virtual void saveLocalScripts(const MWWorld::Ptr& ptr, ESM::LuaScripts& data) = 0;
|
||||
|
||||
// Loading from a save
|
||||
virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0;
|
||||
virtual void loadLocalScripts(const MWWorld::Ptr& ptr, const ESM::LuaScripts& data) = 0;
|
||||
|
||||
// Should be called before loading. The map is used to fix refnums if the order of content files was changed.
|
||||
virtual void setContentFileMapping(const std::map<int, int>&) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
63
apps/openmw/mwlua/eventqueue.cpp
Normal file
63
apps/openmw/mwlua/eventqueue.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include "eventqueue.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/luascripts.hpp>
|
||||
|
||||
#include <components/lua/serialization.hpp>
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
template <typename Event>
|
||||
void saveEvent(ESM::ESMWriter& esm, const ObjectId& dest, const Event& event)
|
||||
{
|
||||
esm.writeHNString("LUAE", event.eventName);
|
||||
dest.save(esm, true);
|
||||
if (!event.eventData.empty())
|
||||
saveLuaBinaryData(esm, event.eventData);
|
||||
}
|
||||
|
||||
void loadEvents(sol::state& lua, ESM::ESMReader& esm, GlobalEventQueue& globalEvents, LocalEventQueue& localEvents,
|
||||
const std::map<int, int>& contentFileMapping, const LuaUtil::UserdataSerializer* serializer)
|
||||
{
|
||||
while (esm.isNextSub("LUAE"))
|
||||
{
|
||||
std::string name = esm.getHString();
|
||||
ObjectId dest;
|
||||
dest.load(esm, true);
|
||||
std::string data = loadLuaBinaryData(esm);
|
||||
try
|
||||
{
|
||||
data = LuaUtil::serialize(LuaUtil::deserialize(lua, data, serializer), serializer);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "loadEvent: invalid event data: " << e.what();
|
||||
}
|
||||
if (dest.isSet())
|
||||
{
|
||||
auto it = contentFileMapping.find(dest.mContentFile);
|
||||
if (it != contentFileMapping.end())
|
||||
dest.mContentFile = it->second;
|
||||
localEvents.push_back({dest, std::move(name), std::move(data)});
|
||||
}
|
||||
else
|
||||
globalEvents.push_back({std::move(name), std::move(data)});
|
||||
}
|
||||
}
|
||||
|
||||
void saveEvents(ESM::ESMWriter& esm, const GlobalEventQueue& globalEvents, const LocalEventQueue& localEvents)
|
||||
{
|
||||
ObjectId globalId;
|
||||
globalId.unset(); // Used as a marker of a global event.
|
||||
|
||||
for (const GlobalEvent& e : globalEvents)
|
||||
saveEvent(esm, globalId, e);
|
||||
for (const LocalEvent& e : localEvents)
|
||||
saveEvent(esm, e.dest, e);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,22 @@
|
|||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
}
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
class UserdataSerializer;
|
||||
}
|
||||
|
||||
namespace sol
|
||||
{
|
||||
class state;
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
struct GlobalEvent
|
||||
|
@ -18,6 +34,10 @@ namespace MWLua
|
|||
};
|
||||
using GlobalEventQueue = std::vector<GlobalEvent>;
|
||||
using LocalEventQueue = std::vector<LocalEvent>;
|
||||
|
||||
void loadEvents(sol::state& lua, ESM::ESMReader& esm, GlobalEventQueue&, LocalEventQueue&,
|
||||
const std::map<int, int>& contentFileMapping, const LuaUtil::UserdataSerializer* serializer);
|
||||
void saveEvents(ESM::ESMWriter& esm, const GlobalEventQueue&, const LocalEventQueue&);
|
||||
}
|
||||
|
||||
#endif // MWLUA_EVENTQUEUE_H
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#include "luamanagerimp.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/luascripts.hpp>
|
||||
|
||||
#include <components/lua/utilpackage.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -15,8 +20,12 @@ namespace MWLua
|
|||
LuaManager::LuaManager(const VFS::Manager* vfs) : mLua(vfs)
|
||||
{
|
||||
Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion();
|
||||
|
||||
mGlobalSerializer = createUserdataSerializer(false, mWorldView.getObjectRegistry());
|
||||
mLocalSerializer = createUserdataSerializer(true, mWorldView.getObjectRegistry());
|
||||
mGlobalLoader = createUserdataSerializer(false, mWorldView.getObjectRegistry(), &mContentFileMapping);
|
||||
mLocalLoader = createUserdataSerializer(true, mWorldView.getObjectRegistry(), &mContentFileMapping);
|
||||
|
||||
mGlobalScripts.setSerializer(mGlobalSerializer.get());
|
||||
|
||||
Context context;
|
||||
|
@ -210,4 +219,56 @@ namespace MWLua
|
|||
return refData.getLuaScripts();
|
||||
}
|
||||
|
||||
void LuaManager::write(ESM::ESMWriter& writer, Loading::Listener& progress)
|
||||
{
|
||||
writer.startRecord(ESM::REC_LUAM);
|
||||
|
||||
mWorldView.save(writer);
|
||||
ESM::LuaScripts globalScripts;
|
||||
mGlobalScripts.save(globalScripts);
|
||||
globalScripts.save(writer);
|
||||
saveEvents(writer, mGlobalEvents, mLocalEvents);
|
||||
|
||||
writer.endRecord(ESM::REC_LUAM);
|
||||
}
|
||||
|
||||
void LuaManager::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||
{
|
||||
if (type != ESM::REC_LUAM)
|
||||
throw std::runtime_error("ESM::REC_LUAM is expected");
|
||||
|
||||
mWorldView.load(reader);
|
||||
ESM::LuaScripts globalScripts;
|
||||
globalScripts.load(reader);
|
||||
loadEvents(mLua.sol(), reader, mGlobalEvents, mLocalEvents, mContentFileMapping, mGlobalLoader.get());
|
||||
|
||||
mGlobalScripts.setSerializer(mGlobalLoader.get());
|
||||
mGlobalScripts.load(globalScripts, false);
|
||||
mGlobalScripts.setSerializer(mGlobalSerializer.get());
|
||||
}
|
||||
|
||||
void LuaManager::saveLocalScripts(const MWWorld::Ptr& ptr, ESM::LuaScripts& data)
|
||||
{
|
||||
if (ptr.getRefData().getLuaScripts())
|
||||
ptr.getRefData().getLuaScripts()->save(data);
|
||||
else
|
||||
data.mScripts.clear();
|
||||
}
|
||||
|
||||
void LuaManager::loadLocalScripts(const MWWorld::Ptr& ptr, const ESM::LuaScripts& data)
|
||||
{
|
||||
if (data.mScripts.empty())
|
||||
{
|
||||
if (ptr.getRefData().getLuaScripts())
|
||||
ptr.getRefData().setLuaScripts(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
mWorldView.getObjectRegistry()->registerPtr(ptr);
|
||||
LocalScripts* scripts = createLocalScripts(ptr);
|
||||
|
||||
scripts->setSerializer(mLocalLoader.get());
|
||||
scripts->load(data, true);
|
||||
scripts->setSerializer(mLocalSerializer.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,15 @@ namespace MWLua
|
|||
// Used only in luabindings.cpp
|
||||
void addLocalScript(const MWWorld::Ptr&, const std::string& scriptPath);
|
||||
|
||||
// Saving
|
||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) override;
|
||||
void saveLocalScripts(const MWWorld::Ptr& ptr, ESM::LuaScripts& data) override;
|
||||
|
||||
// Loading from a save
|
||||
void readRecord(ESM::ESMReader& reader, uint32_t type) override;
|
||||
void loadLocalScripts(const MWWorld::Ptr& ptr, const ESM::LuaScripts& data) override;
|
||||
void setContentFileMapping(const std::map<int, int>& mapping) override { mContentFileMapping = mapping; }
|
||||
|
||||
private:
|
||||
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr);
|
||||
|
||||
|
@ -69,6 +78,10 @@ namespace MWLua
|
|||
std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalSerializer;
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> mLocalSerializer;
|
||||
|
||||
std::map<int, int> mContentFileMapping;
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalLoader;
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> mLocalLoader;
|
||||
|
||||
std::vector<SDL_Keysym> mKeyPressEvents;
|
||||
std::vector<ObjectId> mActorAddedEvents;
|
||||
};
|
||||
|
|
|
@ -11,8 +11,8 @@ namespace MWLua
|
|||
class Serializer final : public LuaUtil::UserdataSerializer
|
||||
{
|
||||
public:
|
||||
explicit Serializer(bool localSerializer, ObjectRegistry* registry)
|
||||
: mLocalSerializer(localSerializer), mObjectRegistry(registry) {}
|
||||
explicit Serializer(bool localSerializer, ObjectRegistry* registry, std::map<int, int>* contentFileMapping)
|
||||
: mLocalSerializer(localSerializer), mObjectRegistry(registry), mContentFileMapping(contentFileMapping) {}
|
||||
|
||||
private:
|
||||
// Appends serialized sol::userdata to the end of BinaryData.
|
||||
|
@ -43,6 +43,12 @@ namespace MWLua
|
|||
std::memcpy(&id, binaryData.data(), sizeof(ObjectId));
|
||||
id.mIndex = Misc::fromLittleEndian(id.mIndex);
|
||||
id.mContentFile = Misc::fromLittleEndian(id.mContentFile);
|
||||
if (id.hasContentFile() && mContentFileMapping)
|
||||
{
|
||||
auto iter = mContentFileMapping->find(id.mContentFile);
|
||||
if (iter != mContentFileMapping->end())
|
||||
id.mContentFile = iter->second;
|
||||
}
|
||||
if (mLocalSerializer)
|
||||
sol::stack::push<LObject>(lua, LObject(id, mObjectRegistry));
|
||||
else
|
||||
|
@ -54,11 +60,13 @@ namespace MWLua
|
|||
|
||||
bool mLocalSerializer;
|
||||
ObjectRegistry* mObjectRegistry;
|
||||
std::map<int, int>* mContentFileMapping;
|
||||
};
|
||||
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> createUserdataSerializer(bool local, ObjectRegistry* registry)
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> createUserdataSerializer(
|
||||
bool local, ObjectRegistry* registry, std::map<int, int>* contentFileMapping)
|
||||
{
|
||||
return std::make_unique<Serializer>(local, registry);
|
||||
return std::make_unique<Serializer>(local, registry, contentFileMapping);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,10 @@ namespace MWLua
|
|||
// UserdataSerializer is an extension for components/lua/serialization.hpp
|
||||
// Needed to serialize references to objects.
|
||||
// If local=true, then during deserialization creates LObject, otherwise creates GObject.
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> createUserdataSerializer(bool local, ObjectRegistry* registry);
|
||||
// contentFileMapping is used only for deserialization. Needed to fix references if the order
|
||||
// of content files was changed.
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> createUserdataSerializer(
|
||||
bool local, ObjectRegistry* registry, std::map<int, int>* contentFileMapping = nullptr);
|
||||
}
|
||||
|
||||
#endif // MWLUA_USERDATASERIALIZER_H
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "worldview.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/timestamp.hpp"
|
||||
|
||||
|
@ -43,6 +46,20 @@ namespace MWLua
|
|||
return static_cast<double>(timeStamp.getDay()) * 24 + timeStamp.getHour();
|
||||
}
|
||||
|
||||
void WorldView::load(ESM::ESMReader& esm)
|
||||
{
|
||||
esm.getHNT(mGameSeconds, "LUAW");
|
||||
ObjectId lastAssignedId;
|
||||
lastAssignedId.load(esm, true);
|
||||
mObjectRegistry.setLastAssignedId(lastAssignedId);
|
||||
}
|
||||
|
||||
void WorldView::save(ESM::ESMWriter& esm) const
|
||||
{
|
||||
esm.writeHNT("LUAW", mGameSeconds);
|
||||
mObjectRegistry.getLastAssignedId().save(esm, true);
|
||||
}
|
||||
|
||||
void WorldView::ObjectGroup::updateList()
|
||||
{
|
||||
if (mChanged)
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMWriter;
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
|
@ -31,6 +37,9 @@ namespace MWLua
|
|||
void objectAddedToScene(const MWWorld::Ptr& ptr);
|
||||
void objectRemovedFromScene(const MWWorld::Ptr& ptr);
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
void save(ESM::ESMWriter& esm) const;
|
||||
|
||||
private:
|
||||
struct ObjectGroup
|
||||
{
|
||||
|
|
|
@ -251,6 +251,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
|||
|
||||
int recordCount = 1 // saved game header
|
||||
+MWBase::Environment::get().getJournal()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getLuaManager()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getWorld()->countSavedGameRecords()
|
||||
+MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords()
|
||||
+MWBase::Environment::get().getDialogueManager()->countSavedGameRecords()
|
||||
|
@ -274,6 +275,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
|||
|
||||
MWBase::Environment::get().getJournal()->write (writer, listener);
|
||||
MWBase::Environment::get().getDialogueManager()->write (writer, listener);
|
||||
// LuaManager::write should be called before World::write because world also saves
|
||||
// local scripts that depend on LuaManager.
|
||||
MWBase::Environment::get().getLuaManager()->write(writer, listener);
|
||||
MWBase::Environment::get().getWorld()->write (writer, listener);
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener);
|
||||
MWBase::Environment::get().getWindowManager()->write(writer, listener);
|
||||
|
@ -384,6 +388,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
|||
throw std::runtime_error("This save file was created using a newer version of OpenMW and is thus not supported. Please upgrade to the newest OpenMW version to load this file.");
|
||||
|
||||
std::map<int, int> contentFileMap = buildContentFileIndexMap (reader);
|
||||
MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap);
|
||||
|
||||
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||
|
||||
|
@ -482,6 +487,10 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
|||
MWBase::Environment::get().getInputManager()->readRecord(reader, n.intval);
|
||||
break;
|
||||
|
||||
case ESM::REC_LUAM:
|
||||
MWBase::Environment::get().getLuaManager()->readRecord(reader, n.intval);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
// ignore invalid records
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "class.hpp"
|
||||
|
@ -52,6 +53,8 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state)
|
|||
Log(Debug::Warning) << "Soul '" << mRef.getSoul() << "' not found, removing the soul from soul gem";
|
||||
mRef.setSoul(std::string());
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getLuaManager()->loadLocalScripts(ptr, state.mLuaScripts);
|
||||
}
|
||||
|
||||
void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const
|
||||
|
@ -61,6 +64,7 @@ void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const
|
|||
ConstPtr ptr (this);
|
||||
|
||||
mData.write (state, mClass->getScript (ptr));
|
||||
MWBase::Environment::get().getLuaManager()->saveLocalScripts(Ptr(const_cast<LiveCellRefBase*>(this)), state.mLuaScripts);
|
||||
|
||||
mClass->writeAdditionalState (ptr, state);
|
||||
}
|
||||
|
|
|
@ -164,7 +164,10 @@ enum RecNameInts
|
|||
|
||||
// format 1
|
||||
REC_FILT = FourCC<'F','I','L','T'>::value,
|
||||
REC_DBGP = FourCC<'D','B','G','P'>::value ///< only used in project files
|
||||
REC_DBGP = FourCC<'D','B','G','P'>::value, ///< only used in project files
|
||||
|
||||
// format 16 - Lua scripts in saved games
|
||||
REC_LUAM = FourCC<'L','U','A','M'>::value, // LuaManager data
|
||||
};
|
||||
|
||||
/// Common subrecords
|
||||
|
|
|
@ -20,6 +20,8 @@ void ESM::ObjectState::load (ESMReader &esm)
|
|||
if (mHasLocals)
|
||||
mLocals.load (esm);
|
||||
|
||||
mLuaScripts.load(esm);
|
||||
|
||||
mEnabled = 1;
|
||||
esm.getHNOT (mEnabled, "ENAB");
|
||||
|
||||
|
@ -56,6 +58,8 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
|
|||
mLocals.save (esm);
|
||||
}
|
||||
|
||||
mLuaScripts.save(esm);
|
||||
|
||||
if (!mEnabled && !inInventory)
|
||||
esm.writeHNT ("ENAB", mEnabled);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "cellref.hpp"
|
||||
#include "locals.hpp"
|
||||
#include "luascripts.hpp"
|
||||
#include "animationstate.hpp"
|
||||
|
||||
namespace ESM
|
||||
|
@ -27,6 +28,7 @@ namespace ESM
|
|||
|
||||
unsigned char mHasLocals;
|
||||
Locals mLocals;
|
||||
LuaScripts mLuaScripts;
|
||||
unsigned char mEnabled;
|
||||
int mCount;
|
||||
ESM::Position mPosition;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "esmwriter.hpp"
|
||||
|
||||
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
||||
int ESM::SavedGame::sCurrentFormat = 15;
|
||||
int ESM::SavedGame::sCurrentFormat = 16;
|
||||
|
||||
void ESM::SavedGame::load (ESMReader &esm)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue