Skip to content

Commit

Permalink
libcommon|libdoomsday: Thinker ID mapping for deserializing internal …
Browse files Browse the repository at this point in the history
…map state

When reading internal map state, it is crucial to know which internal
state belongs to which thinker. However, the challenge is that when
thinkers are created, they don’t yet have ThinkerData and therefore
no private IDs either. The thinker ID mapping allows restoring the
old IDs.

The save version was bumped so that the private thinker IDs can be
included in the save data.
  • Loading branch information
skyjake committed Dec 21, 2016
1 parent 3bce705 commit 87f0880
Show file tree
Hide file tree
Showing 23 changed files with 376 additions and 159 deletions.
10 changes: 10 additions & 0 deletions doomsday/apps/libdoomsday/include/doomsday/abstractsession.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "libdoomsday.h"
#include "uri.h"
#include "world/ithinkermapping.h"
#include <de/Error>
#include <de/Observers>
#include <de/String>
Expand Down Expand Up @@ -85,6 +86,15 @@ class LIBDOOMSDAY_PUBLIC AbstractSession
*/
virtual void load(de::String const &saveName) = 0;

world::IThinkerMapping const *thinkerMapping() const;

/**
* Sets the currently used serialization thinker mapping object.
* @param mapping Thinker mapping. Set to @c nullptr when the mapping is not
* available. Caller retains ownership.
*/
void setThinkerMapping(world::IThinkerMapping *mapping);

public:
/**
* Configuration profile.
Expand Down
8 changes: 6 additions & 2 deletions doomsday/apps/libdoomsday/include/doomsday/doomsdayapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,19 +146,23 @@ class LIBDOOMSDAY_PUBLIC DoomsdayApp
* When saving a game session to disk, this method should be called so the
* application gets a chance to include its state in the save as well.
*
* @param session Game session that is being saved.
* @param toFolder Folder where the game state is being written.
*/
virtual void gameSessionWasSaved(AbstractSession const &session, GameStateFolder &toFolder);
virtual void gameSessionWasSaved(AbstractSession const &session,
GameStateFolder &toFolder);

/**
* Loads application state from a save folder.
*
* When loading a game session from disk, this method should be called so that
* the application can restore its state from the save.
*
* @param session Game session that is being loaded.
* @param fromFolder Folder where the game state is being read.
*/
virtual void gameSessionWasLoaded(AbstractSession const &session, GameStateFolder const &fromFolder);
virtual void gameSessionWasLoaded(AbstractSession const &session,
GameStateFolder const &fromFolder);

public:
static DoomsdayApp & app();
Expand Down
3 changes: 2 additions & 1 deletion doomsday/apps/libdoomsday/include/doomsday/gamestatefolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define LIBDOOMSDAY_GAMESTATEFOLDER_H

#include "libdoomsday.h"
#include "world/ithinkermapping.h"

#include <de/Error>
#include <de/Observers>
Expand Down Expand Up @@ -69,7 +70,7 @@ class LIBDOOMSDAY_PUBLIC GameStateFolder : public de::ArchiveFolder
/**
* Abstract base class for serialized, map state readers.
*/
class LIBDOOMSDAY_PUBLIC MapStateReader
class LIBDOOMSDAY_PUBLIC MapStateReader : public world::IThinkerMapping
{
public:
/// Base class for read errors. @ingroup errors
Expand Down
47 changes: 47 additions & 0 deletions doomsday/apps/libdoomsday/include/doomsday/world/ithinkermapping.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/** @file ithinkermapping.h Thinker mapping.
*
* @authors Copyright (c) 2016 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the GNU
* General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#ifndef LIBDOOMSDAY_WORLD_ITHINKERMAPPING_H
#define LIBDOOMSDAY_WORLD_ITHINKERMAPPING_H

#include "../libdoomsday.h"
#include "thinker.h"
#include <de/Id>

namespace world {

/**
* Utility for mapping private identifiers to thinker objects. Used when writing and
* reading save files.
*/
class LIBDOOMSDAY_PUBLIC IThinkerMapping
{
public:
virtual ~IThinkerMapping() {}

/**
* Retrieves the thinker corresponding a private identifier.
* @param id Private identifier.
* @return Thinker instance.
*/
virtual thinker_t *thinkerForPrivateId(de::Id::Type id) const = 0;
};

} // namespace world

#endif // LIBDOOMSDAY_WORLD_ITHINKERMAPPING_H
4 changes: 3 additions & 1 deletion doomsday/apps/libdoomsday/include/doomsday/world/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class EntityDatabase;

namespace world {

class IThinkerMapping;

/**
* Base class for world maps.
*/
Expand Down Expand Up @@ -76,7 +78,7 @@ class LIBDOOMSDAY_PUBLIC BaseMap

virtual void serializeInternalState(de::Writer &to) const;

virtual void deserializeInternalState(de::Reader &from);
virtual void deserializeInternalState(de::Reader &from, IThinkerMapping const &);

DENG2_AS_IS_METHODS()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
class LIBDOOMSDAY_PUBLIC MobjThinkerData : public ThinkerData
{
public:
MobjThinkerData();
MobjThinkerData(de::Id const &id = de::Id::none());
MobjThinkerData(MobjThinkerData const &other);

IData *duplicate() const override;
Expand Down
27 changes: 26 additions & 1 deletion doomsday/apps/libdoomsday/include/doomsday/world/thinkerdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,23 @@ class LIBDOOMSDAY_PUBLIC ThinkerData
DENG2_DEFINE_AUDIENCE2(Deletion, void thinkerBeingDeleted(thinker_s &))

public:
ThinkerData();
ThinkerData(de::Id const &id = de::Id::none());
ThinkerData(ThinkerData const &other);

/**
* Returns the unique and persistent ID of the thinker.
*
* Note that due to historical reasons game-side mobj IDs are separately enumerated
* 16-bit numbers.
*
* @return Thinker ID.
*
* @todo Use this for identifying all thinkers everywhere, including mobjs.
*/
de::Id const &id() const;

void setId(de::Id const &id);

void setThinker(thinker_s *thinker) override;
void think() override;
IData *duplicate() const override;
Expand All @@ -66,6 +80,17 @@ class LIBDOOMSDAY_PUBLIC ThinkerData
void operator >> (de::Writer &to) const override;
void operator << (de::Reader &from) override;

public:
/*
* Finds a thinker based on its unique identifier. This searches all ThinkerData
* instances in existence at the moment. If there happens to be multiple ThinkerData
* instances with the same ID, returns the most recently created instance.
*
* @param id Identifier.
* @return Thinker or @c nullptr.
*/
//static ThinkerData *find(de::Id const &id);

private:
DENG2_PRIVATE(d)

Expand Down
12 changes: 11 additions & 1 deletion doomsday/apps/libdoomsday/src/abstractsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ static AbstractSession::Profile currentProfile;
DENG2_PIMPL_NOREF(AbstractSession)
{
bool inProgress = false; ///< @c true: session is in progress / internal.save exists.

de::Uri mapUri;
world::IThinkerMapping *thinkerMapping = nullptr;
};

AbstractSession::AbstractSession()
Expand Down Expand Up @@ -64,6 +64,16 @@ de::Uri AbstractSession::mapUri() const
return hasBegun()? d->mapUri : de::Uri("Maps:", RC_NULL);
}

world::IThinkerMapping const *AbstractSession::thinkerMapping() const
{
return d->thinkerMapping;
}

void AbstractSession::setThinkerMapping(world::IThinkerMapping *mapping)
{
d->thinkerMapping = mapping;
}

void AbstractSession::setMapUri(Uri const &uri)
{
d->mapUri = uri;
Expand Down
2 changes: 1 addition & 1 deletion doomsday/apps/libdoomsday/src/world/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ EntityDatabase &BaseMap::entityDatabase() const
void BaseMap::serializeInternalState(Writer &) const
{}

void BaseMap::deserializeInternalState(Reader &)
void BaseMap::deserializeInternalState(Reader &, IThinkerMapping const &)
{}

} // namespace world
5 changes: 3 additions & 2 deletions doomsday/apps/libdoomsday/src/world/mobjthinkerdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ static String const VAR_ID("__id__");
DENG2_PIMPL_NOREF(MobjThinkerData)
{};

MobjThinkerData::MobjThinkerData()
: d(new Impl)
MobjThinkerData::MobjThinkerData(de::Id const &id)
: ThinkerData(id)
, d(new Impl)
{}

MobjThinkerData::MobjThinkerData(MobjThinkerData const &other)
Expand Down
60 changes: 54 additions & 6 deletions doomsday/apps/libdoomsday/src/world/thinkerdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,35 @@
#include "doomsday/world/thinkerdata.h"
#include "doomsday/world/map.h"

#include <QMultiHash>

using namespace de;

//static QMultiHash<Id::Type, ThinkerData *> thinkerLookup;

DENG2_PIMPL(ThinkerData)
{
thinker_s *think;
Id id; ///< Internal unique ID.
Record names;

Impl(Public *i) : Base(i), think(0) {}
Impl(Public *i, Id const &id)
: Base(i)
, think(0)
, id(id)
{}

Impl(Public *i, Impl const &other)
: Base(i)
, think(other.think)
, id(other.id)
, names(other.names)
{}

~Impl()
{
//thinkerLookup.remove(id, &self());

DENG2_FOR_PUBLIC_AUDIENCE2(Deletion, i)
{
i->thinkerBeingDeleted(*think);
Expand All @@ -48,11 +60,31 @@ DENG2_PIMPL(ThinkerData)

DENG2_AUDIENCE_METHOD(ThinkerData, Deletion)

ThinkerData::ThinkerData() : d(new Impl(this))
{}
ThinkerData::ThinkerData(Id const &id)
: d(new Impl(this, id))
{
DENG2_ASSERT(!d->id.isNone());
//thinkerLookup.insert(d->id, this);
}

ThinkerData::ThinkerData(ThinkerData const &other) : d(new Impl(this, *other.d))
{}
{
DENG2_ASSERT(!d->id.isNone());
//thinkerLookup.insert(d->id, this);
}

Id const &ThinkerData::id() const
{
return d->id;
}

void ThinkerData::setId(Id const &id)
{
//thinkerLookup.remove(d->id, this);
//thinkerLookup.insert(id, this);

d->id = id;
}

void ThinkerData::setThinker(thinker_s *thinker)
{
Expand Down Expand Up @@ -97,27 +129,43 @@ void ThinkerData::initBindings()
void ThinkerData::operator >> (Writer &to) const
{
to << world::InternalSerialId(world::THINKER_DATA)
<< d->names;
<< d->id
<< Record(d->names, Record::IgnoreDoubleUnderscoreMembers);
}

void ThinkerData::operator << (Reader &from)
{
//thinkerLookup.remove(d->id, this);

world::InternalSerialId sid;
from >> sid;

switch (sid)
{
case world::THINKER_DATA:
from >> d->names;
from >> d->id >> d->names;
break;

default:
throw DeserializationError("ThinkerData::operator <<",
"Invalid serial identifier " +
String::number(sid));
}

// The thinker has a new ID.
//thinkerLookup.insert(d->id, this);
}

/*ThinkerData *ThinkerData::find(Id const &id)
{
auto found = thinkerLookup.constFind(id);
if (found != thinkerLookup.constEnd())
{
return found.value();
}
return nullptr;
}*/

#ifdef DENG2_DEBUG
duint32 ThinkerData::DebugCounter::total = 0;
ThinkerData::DebugValidator ensureAllPrivateDataIsReleased;
Expand Down
3 changes: 3 additions & 0 deletions doomsday/apps/plugins/common/include/mapstatereader.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ class MapStateReader : public GameStateFolder::MapStateReader
*/
struct player_s *player(int serialId) const;

// Implements IThinkerMapping.
thinker_t *thinkerForPrivateId(de::Id::Type id) const override;

public: /// @todo refactor away:
void addMobjToThingArchive(struct mobj_s *mobj, ThingArchive::SerialId);

Expand Down
7 changes: 6 additions & 1 deletion doomsday/apps/plugins/common/include/mapstatewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "common.h"
#include "thingarchive.h"
#include <doomsday/world/ithinkermapping.h>

namespace world { class Material; }

Expand Down Expand Up @@ -69,10 +70,14 @@ class MapStateWriter
*/
Writer1 *writer();

ThingArchive::SerialId serialIdFor(struct mobj_s *mobj);
ThingArchive::SerialId serialIdFor(struct mobj_s const *mobj);
materialarchive_serialid_t serialIdFor(world::Material *material);
materialarchive_serialid_t serialIdFor(struct material_s *material);

// Implements world::IThinkerMapping.
//int mappedNumberfromThinkerId(de::Id const &thinkerId) const override;
//de::Id thinkerIdFromMappedNumber(int mappedNumber) const override;

private:
DENG2_PRIVATE(d)
};
Expand Down
Loading

0 comments on commit 87f0880

Please sign in to comment.