Skip to content

Commit b0b026f

Browse files
committed
UniqueID and Age are now persisted through game save/load
1 parent 085aa11 commit b0b026f

File tree

12 files changed

+114
-19
lines changed

12 files changed

+114
-19
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4242

4343
- The Signal Hunt activity no longer has a preview image, as it was not formatted correctly and spoiled the interior structure of the cave.
4444

45+
- `MovableObject`'s `UniqueID` and `Age` fields are now persisted through game save/load.
46+
4547
</details>
4648

4749
<details><summary><b>Fixed</b></summary>

Source/Entities/MovableObject.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ using namespace RTE;
2323

2424
AbstractClassInfo(MovableObject, SceneObject);
2525

26-
std::atomic<long> MovableObject::m_UniqueIDCounter = 1;
2726
std::string MovableObject::ms_EmptyString = "";
2827

2928
MovableObject::MovableObject() {
@@ -141,14 +140,17 @@ int MovableObject::Create() {
141140
if (SceneObject::Create() < 0)
142141
return -1;
143142

144-
m_AgeTimer.Reset();
145143
m_RestTimer.Reset();
146144

147145
// If the stop time hasn't been assigned, just make the same as the life time.
148146
if (m_EffectStopTime <= 0)
149147
m_EffectStopTime = m_Lifetime;
150148

151-
m_UniqueID = MovableObject::GetNextUniqueID();
149+
if (m_UniqueID == 0)
150+
{
151+
m_UniqueID = g_MovableMan.GetNextUniqueID();
152+
m_AgeTimer.Reset();
153+
}
152154

153155
m_MOIDHit = g_NoMOID;
154156
m_TerrainMatHit = g_MaterialAir;
@@ -177,7 +179,7 @@ int MovableObject::Create(const float mass,
177179
m_HitsMOs = hitMOs;
178180
m_GetsHitByMOs = getHitByMOs;
179181

180-
m_UniqueID = MovableObject::GetNextUniqueID();
182+
m_UniqueID = g_MovableMan.GetNextUniqueID();
181183

182184
m_MOIDHit = g_NoMOID;
183185
m_TerrainMatHit = g_MaterialAir;
@@ -203,9 +205,6 @@ int MovableObject::Create(const MovableObject& reference) {
203205
m_RestThreshold = reference.m_RestThreshold;
204206
// m_Force = reference.m_Force;
205207
// m_ImpulseForce = reference.m_ImpulseForce;
206-
// Should reset age instead??
207-
// m_AgeTimer = reference.m_AgeTimer;
208-
m_AgeTimer.Reset();
209208
m_RestTimer.Reset();
210209
m_Lifetime = reference.m_Lifetime;
211210
m_Sharpness = reference.m_Sharpness;
@@ -266,7 +265,17 @@ int MovableObject::Create(const MovableObject& reference) {
266265
m_NumberValueMap = reference.m_NumberValueMap;
267266
m_ObjectValueMap = reference.m_ObjectValueMap;
268267

269-
m_UniqueID = MovableObject::GetNextUniqueID();
268+
// If we have a -1 unique ID, then we're currently performing a game save or load
269+
// In this case, we actually want to persist our existing IDs
270+
if (g_MovableMan.ShouldPersistUniqueIDs()) {
271+
m_UniqueID = reference.m_UniqueID;
272+
m_AgeTimer = reference.m_AgeTimer;
273+
} else {
274+
// Otherwise we're copying from a preset normally and ought to create a new object
275+
m_UniqueID = g_MovableMan.GetNextUniqueID();
276+
m_AgeTimer.Reset();
277+
}
278+
270279
g_MovableMan.RegisterObject(this);
271280

272281
return 0;
@@ -374,6 +383,11 @@ int MovableObject::ReadProperty(const std::string_view& propName, Reader& reader
374383
MatchProperty("SimUpdatesBetweenScriptedUpdates", { reader >> m_SimUpdatesBetweenScriptedUpdates; });
375384
MatchProperty("AddCustomValue", { ReadCustomValueProperty(reader); });
376385
MatchProperty("ForceIntoMasterLuaState", { reader >> m_ForceIntoMasterLuaState; });
386+
MatchProperty("SpecialBehaviour_SetUniqueID", {
387+
long oldID = m_UniqueID;
388+
reader >> m_UniqueID;
389+
g_MovableMan.ReregisterObjectIfApplicable(this, oldID);
390+
});
377391

378392
EndPropertyList;
379393
}

Source/Entities/MovableObject.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -979,10 +979,6 @@ namespace RTE {
979979
/// @param newRestThreshold New rest threshold value
980980
void SetRestThreshold(int newRestThreshold) { m_RestThreshold = newRestThreshold; }
981981

982-
/// Returns the next unique id for MO's and increments unique ID counter
983-
/// @return Returns the next unique id.
984-
static long GetNextUniqueID() { return ++m_UniqueIDCounter; }
985-
986982
/// Returns this MO's unique persistent ID
987983
/// @return Returns this MO's unique persistent ID
988984
long GetUniqueID() const { return m_UniqueID; }
@@ -1139,8 +1135,7 @@ namespace RTE {
11391135
/// @param A MovableObject object which is passed in by reference.
11401136
// Member variables
11411137
static Entity::ClassInfo m_sClass;
1142-
// Global counter with unique ID's
1143-
static std::atomic<long> m_UniqueIDCounter;
1138+
11441139
// The type of MO this is, either Actor, Item, or Particle
11451140
int m_MOType;
11461141
float m_Mass; // In metric kilograms (kg).

Source/Entities/Scene.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,7 @@ void Scene::SaveSceneObject(Writer& writer, const SceneObject* sceneObjectToSave
11721172
writer.NewPropertyWithValue("LifeTime", movableObjectToSave->GetLifetime());
11731173
writer.NewPropertyWithValue("Age", movableObjectToSave->GetAge());
11741174
writer.NewPropertyWithValue("PinStrength", movableObjectToSave->GetPinStrength());
1175+
writer.NewPropertyWithValue("SpecialBehaviour_SetUniqueID", movableObjectToSave->GetUniqueID());
11751176
}
11761177

11771178
if (const MOSprite* moSpriteToSave = dynamic_cast<const MOSprite*>(sceneObjectToSave)) {

Source/GUI/GUIReader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,12 @@ GUIReader& GUIReader::operator>>(unsigned long& var) {
359359
return *this;
360360
}
361361

362+
GUIReader& GUIReader::operator>>(long long& var) {
363+
DiscardEmptySpace();
364+
*m_Stream >> var;
365+
return *this;
366+
}
367+
362368
// Yeah, this is dumb - read as double and cast.
363369
// This is because, for whatever fucking reason, iostream can save out floats at a precision that it's then unable to read...
364370
GUIReader& GUIReader::operator>>(float& var) {

Source/GUI/GUIReader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ namespace RTE {
9292
GUIReader& operator>>(unsigned int& var);
9393
GUIReader& operator>>(long& var);
9494
GUIReader& operator>>(unsigned long& var);
95+
GUIReader& operator>>(long long& var);
9596
GUIReader& operator>>(float& var);
9697
GUIReader& operator>>(double& var);
9798
GUIReader& operator>>(std::string& var);

Source/Main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ void PollSDLEvents() {
245245
void RunMenuLoop() {
246246
g_UInputMan.DisableKeys(false);
247247
g_UInputMan.TrapMousePos(false);
248+
g_TimerMan.PauseSim(true);
248249

249250
while (!System::IsSetToQuit()) {
250251
g_WindowMan.ClearRenderer();

Source/Managers/ActivityMan.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
9393
return false;
9494
}
9595

96+
long currentMaxID = g_MovableMan.GetMaxUniqueID();
97+
g_MovableMan.SetShouldPersistUniqueIDs(true);
98+
9699
// We need a copy of our scene, because we have to do some fixup to remove PLACEONLOAD items and only keep the current MovableMan state.
97100
std::unique_ptr<Scene> modifiableScene(dynamic_cast<Scene*>(scene->Clone()));
98101

@@ -121,6 +124,8 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
121124
}
122125
}
123126

127+
writer->NewPropertyWithValue("MaxUniqueID", currentMaxID);
128+
writer->NewPropertyWithValue("CurrentSimTicks", g_TimerMan.GetSimTickCount());
124129
writer->NewPropertyWithValue("OriginalScenePresetName", scene->GetPresetName());
125130
writer->NewPropertyWithValue("PlaceObjectsIfSceneIsRestarted", g_SceneMan.GetPlaceObjectsOnLoad());
126131
writer->NewPropertyWithValue("PlaceUnitsIfSceneIsRestarted", g_SceneMan.GetPlaceUnitsOnLoad());
@@ -137,6 +142,8 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
137142
// We didn't transfer ownership, so we must be very careful that sceneAltered's deletion doesn't touch the stuff we got from MovableMan.
138143
modifiableScene->ClearPlacedObjectSet(Scene::PlacedObjectSets::PLACEONLOAD, false);
139144

145+
g_MovableMan.SetShouldPersistUniqueIDs(false);
146+
140147
g_ConsoleMan.PrintString("SYSTEM: Game saved to \"" + fileName + "\"!");
141148
return true;
142149
}
@@ -156,13 +163,20 @@ bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) {
156163
std::unique_ptr<Scene> scene(std::make_unique<Scene>());
157164
std::unique_ptr<GAScripted> activity(std::make_unique<GAScripted>());
158165

166+
long maxUniqueID = 0;
167+
long long simTimeTicks = 0;
159168
std::string originalScenePresetName = fileName;
160169
bool placeObjectsIfSceneIsRestarted = true;
161170
bool placeUnitsIfSceneIsRestarted = true;
162171
while (reader.NextProperty()) {
163172
std::string propName = reader.ReadPropName();
164173
if (propName == "Activity") {
165174
reader >> activity.get();
175+
} else if (propName == "MaxUniqueID") {
176+
reader >> maxUniqueID;
177+
g_MovableMan.SetShouldPersistUniqueIDs(true);
178+
} else if (propName == "CurrentSimTicks") {
179+
reader >> simTimeTicks;
166180
} else if (propName == "OriginalScenePresetName") {
167181
reader >> originalScenePresetName;
168182
} else if (propName == "PlaceObjectsIfSceneIsRestarted") {
@@ -181,6 +195,15 @@ bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) {
181195
scene->SetPresetName(originalScenePresetName);
182196
// For starting Activity, we need to directly clone the Activity we want to start.
183197
StartActivity(dynamic_cast<GAScripted*>(activity->Clone()));
198+
199+
// Set the max unique ID to our loaded maximum so we don't stomp over any existing ones
200+
if (maxUniqueID != 0) {
201+
g_MovableMan.SetMaxUniqueID(maxUniqueID);
202+
}
203+
204+
g_MovableMan.SetShouldPersistUniqueIDs(false);
205+
g_TimerMan.SetSimTickCount(simTimeTicks);
206+
184207
// When this method exits, our Scene object will be destroyed, which will cause problems if you try to restart it. To avoid this, set the Scene to load to the preset object with the same name.
185208
g_SceneMan.SetSceneToLoad(originalScenePresetName, placeObjectsIfSceneIsRestarted, placeUnitsIfSceneIsRestarted);
186209

Source/Managers/MovableMan.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct MOXPosComparison {
4343

4444
MovableMan::MovableMan() {
4545
Clear();
46+
m_UniqueIDCounter = 1;
4647
}
4748

4849
MovableMan::~MovableMan() {
@@ -75,6 +76,7 @@ void MovableMan::Clear() {
7576
m_MaxDroppedItems = 100;
7677
m_SettlingEnabled = true;
7778
m_MOSubtractionEnabled = true;
79+
m_ShouldPersistUniqueIDs = false;
7880
}
7981

8082
int MovableMan::Initialize() {
@@ -181,6 +183,17 @@ void MovableMan::UnregisterObject(MovableObject* mo) {
181183
m_KnownObjects.erase(mo->GetUniqueID());
182184
}
183185

186+
void MovableMan::ReregisterObjectIfApplicable(MovableObject* mo, long oldUniqueId) {
187+
if (mo == nullptr || m_KnownObjects.find(oldUniqueId) == m_KnownObjects.end()) {
188+
// Old ID was never registered, don't need to do anything
189+
return;
190+
}
191+
192+
std::lock_guard<std::mutex> guard(m_ObjectRegisteredMutex);
193+
m_KnownObjects[oldUniqueId] = nullptr;
194+
m_KnownObjects[mo->GetUniqueID()] = mo;
195+
}
196+
184197
const std::vector<MovableObject*>* MovableMan::GetMOsInBox(const Box& box, int ignoreTeam, bool getsHitByMOsOnly) const {
185198
std::vector<MovableObject*>* vectorForLua = new std::vector<MovableObject*>();
186199
*vectorForLua = std::move(g_SceneMan.GetMOIDGrid().GetMOsInBox(box, ignoreTeam, getsHitByMOsOnly));

Source/Managers/MovableMan.h

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -482,14 +482,40 @@ namespace RTE {
482482
/// @param mo MO to remove.
483483
void UnregisterObject(MovableObject* mo);
484484

485+
/// Reregisters an object in a global Map collection to handle if it's unique ID changes
486+
/// @param mo MO to reregister if necessary.
487+
/// @param oldUniqueID The MO's old UniqueID.
488+
void ReregisterObjectIfApplicable(MovableObject* mo, long oldUniqueId);
489+
490+
/// Returns the next unique id for MO's and increments unique ID counter
491+
/// @return Returns the next unique id.
492+
long GetNextUniqueID() { if (m_ShouldPersistUniqueIDs) { return 0; } return ++m_UniqueIDCounter; }
493+
494+
/// Returns the max unique id for MO's
495+
/// @return Returns the current max unique id.
496+
long GetMaxUniqueID() { return m_UniqueIDCounter; }
497+
498+
/// Set the max unique id for MO's. This is used so we can consistently save/load unique IDs so they're consistent within a game session
499+
/// @param id Unique Id to set.
500+
void SetMaxUniqueID(long newMaxID) { m_UniqueIDCounter = newMaxID; }
501+
502+
/// Returns whether we should be persisting uniqueIDs for save/load
503+
/// @return Returns whether we are persisting uniqueIDs.
504+
bool ShouldPersistUniqueIDs() { return m_ShouldPersistUniqueIDs; }
505+
506+
/// Sets whether we should be persisting uniqueIDs for save/load
507+
/// @param newValue Whether we should be persisting uniqueIDs.
508+
void SetShouldPersistUniqueIDs(bool newValue) { m_ShouldPersistUniqueIDs = newValue; }
509+
485510
/// Uses a global lookup map to find an object by it's unique id.
486511
/// @param id Unique Id to look for.
487512
/// @return Object found or 0 if not found any.
488-
MovableObject* FindObjectByUniqueID(long int id) {
489-
if (m_KnownObjects.count(id) > 0)
513+
MovableObject* FindObjectByUniqueID(long id) {
514+
if (m_KnownObjects.count(id) > 0) {
490515
return m_KnownObjects[id];
491-
else
516+
} else {
492517
return 0;
518+
}
493519
}
494520

495521
/// Returns the size of the object registry collection
@@ -551,6 +577,10 @@ namespace RTE {
551577
std::deque<Actor*> m_Actors;
552578
// A map to give a unique contiguous identifier per-actor. This is re-created per frame.
553579
std::unordered_map<const Actor*, int> m_ContiguousActorIDs;
580+
// Global counter with unique ID's
581+
std::atomic<long> m_UniqueIDCounter;
582+
// Whether or not UniqueIDs should be persisted (for save/load)
583+
bool m_ShouldPersistUniqueIDs;
554584
// List of items that are pickup-able by actors
555585
std::deque<MovableObject*> m_Items;
556586
// List of free, dead particles flying around
@@ -621,8 +651,8 @@ namespace RTE {
621651

622652
unsigned int m_SimUpdateFrameNumber;
623653

624-
// Global map which stores all objects so they could be foud by their unique ID
625-
std::map<long int, MovableObject*> m_KnownObjects;
654+
// Global map which stores all objects so they could be found by their unique ID
655+
std::map<long, MovableObject*> m_KnownObjects;
626656

627657
/// Private member variable and method declarations
628658
private:

0 commit comments

Comments
 (0)