• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 22044569181

15 Feb 2026 10:50PM UTC coverage: 50.34% (-0.5%) from 50.826%
22044569181

Pull #1720

github

web-flow
Merge 4dbe54b70 into 6db06730b
Pull Request #1720: Add leather addon

274 of 1055 new or added lines in 65 files covered. (25.97%)

286 existing lines in 28 files now uncovered.

23017 of 45723 relevant lines covered (50.34%)

43559.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.76
/libs/s25main/SerializedGameData.cpp
1
// Copyright (C) 2005 - 2024 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "SerializedGameData.h"
6
#include "CatapultStone.h"
7
#include "EventManager.h"
8
#include "FOWObjects.h"
9
#include "Game.h"
10
#include "GameEvent.h"
11
#include "GameObject.h"
12
#include "GamePlayer.h"
13
#include "PointOutput.h"
14
#include "RoadSegment.h"
15
#include "Ware.h"
16
#include "buildings/BurnedWarehouse.h"
17
#include "buildings/noBuildingSite.h"
18
#include "buildings/nobHQ.h"
19
#include "buildings/nobHarborBuilding.h"
20
#include "buildings/nobMilitary.h"
21
#include "buildings/nobShipYard.h"
22
#include "buildings/nobStorehouse.h"
23
#include "buildings/nobTemple.h"
24
#include "figures/nofAggressiveDefender.h"
25
#include "figures/nofArmorer.h"
26
#include "figures/nofAttacker.h"
27
#include "figures/nofBaker.h"
28
#include "figures/nofBrewer.h"
29
#include "figures/nofBuilder.h"
30
#include "figures/nofButcher.h"
31
#include "figures/nofCarpenter.h"
32
#include "figures/nofCarrier.h"
33
#include "figures/nofCatapultMan.h"
34
#include "figures/nofCharburner.h"
35
#include "figures/nofDefender.h"
36
#include "figures/nofDonkeybreeder.h"
37
#include "figures/nofFarmer.h"
38
#include "figures/nofFisher.h"
39
#include "figures/nofForester.h"
40
#include "figures/nofGeologist.h"
41
#include "figures/nofHunter.h"
42
#include "figures/nofIronfounder.h"
43
#include "figures/nofLeatherWorker.h"
44
#include "figures/nofMetalworker.h"
45
#include "figures/nofMiller.h"
46
#include "figures/nofMiner.h"
47
#include "figures/nofMinter.h"
48
#include "figures/nofPassiveSoldier.h"
49
#include "figures/nofPassiveWorker.h"
50
#include "figures/nofPigbreeder.h"
51
#include "figures/nofPlaner.h"
52
#include "figures/nofScout_Free.h"
53
#include "figures/nofScout_LookoutTower.h"
54
#include "figures/nofShipWright.h"
55
#include "figures/nofSkinner.h"
56
#include "figures/nofStonemason.h"
57
#include "figures/nofTanner.h"
58
#include "figures/nofTempleServant.h"
59
#include "figures/nofTradeDonkey.h"
60
#include "figures/nofTradeLeader.h"
61
#include "figures/nofVintner.h"
62
#include "figures/nofWarehouseWorker.h"
63
#include "figures/nofWellguy.h"
64
#include "figures/nofWinegrower.h"
65
#include "figures/nofWoodcutter.h"
66
#include "helpers/containerUtils.h"
67
#include "helpers/format.hpp"
68
#include "helpers/toString.h"
69
#include "world/MapSerializer.h"
70
#include "nodeObjs/noAnimal.h"
71
#include "nodeObjs/noCharburnerPile.h"
72
#include "nodeObjs/noDisappearingMapEnvObject.h"
73
#include "nodeObjs/noEnvObject.h"
74
#include "nodeObjs/noExtension.h"
75
#include "nodeObjs/noFighting.h"
76
#include "nodeObjs/noFire.h"
77
#include "nodeObjs/noFlag.h"
78
#include "nodeObjs/noGrainfield.h"
79
#include "nodeObjs/noGranite.h"
80
#include "nodeObjs/noGrapefield.h"
81
#include "nodeObjs/noShip.h"
82
#include "nodeObjs/noShipBuildingSite.h"
83
#include "nodeObjs/noSign.h"
84
#include "nodeObjs/noSkeleton.h"
85
#include "nodeObjs/noStaticObject.h"
86
#include "nodeObjs/noTree.h"
87
#include "s25util/Log.h"
88

89
// clang-format off
90
/// Version of the current game data
91
/// Usage: Always save for the most current version but include loading code that can cope with file format changes
92
/// If a format change occurred that can still be handled increase this version and handle it in the loading code.
93
/// If the change is to big to handle increase the version in Savegame.cpp and remove all code referencing GetGameDataVersion.
94
/// Then reset this number to 0.
95
/// TODO: Let GO_Type start at 0 again when resetting this
96
/// Changelog:
97
/// 2: All player buildings together, variable width size for containers and ship names
98
/// 3: Landscape and terrain names stored as strings
99
/// 4: HunterWaitingForAnimalReady introduced as sub-state of HunterFindingShootingpoint
100
/// 5: Make RoadPathDirection contiguous and use optional for ware in nofBuildingWorker
101
/// 6: Make TradeDirection contiguous, Serialize only nobUsuals in BuildingRegister::buildings,
102
///    include water and fish in geologists resourceFound
103
/// 7: Use helpers::push/popContainer (uses var size)
104
/// 8: noFlag::Wares converted to static_vector
105
/// 9: Drop serialization of node BQ
106
/// 10: troop_limits state introduced to military buildings
107
/// 11: wineaddon added, three new building types and two new goods
108
/// 12:: leatheraddon added, three new building types and three new goods
109
static const unsigned currentGameDataVersion = 12;
110
// clang-format on
111

112
std::unique_ptr<GameObject> SerializedGameData::Create_GameObject(const GO_Type got, const unsigned obj_id)
69✔
113
{
114
    switch(got)
69✔
115
    {
116
#define RTTR_CREATE_GO(GOT, CLASS) \
117
    case GOT: return std::unique_ptr<GameObject>(new CLASS(*this, obj_id))
118
        RTTR_CREATE_GO(GO_Type::NobHq, nobHQ);
8✔
119
        RTTR_CREATE_GO(GO_Type::NobMilitary, nobMilitary);
×
120
        RTTR_CREATE_GO(GO_Type::NobStorehouse, nobStorehouse);
×
121
        RTTR_CREATE_GO(GO_Type::NobUsual, nobUsual);
3✔
122
        RTTR_CREATE_GO(GO_Type::NobShipyard, nobShipYard);
×
123
        RTTR_CREATE_GO(GO_Type::NobHarborbuilding, nobHarborBuilding);
×
124
        RTTR_CREATE_GO(GO_Type::NofAggressivedefender, nofAggressiveDefender);
×
125
        RTTR_CREATE_GO(GO_Type::NofAttacker, nofAttacker);
×
126
        RTTR_CREATE_GO(GO_Type::NofDefender, nofDefender);
×
127
        RTTR_CREATE_GO(GO_Type::NofPassivesoldier, nofPassiveSoldier);
×
128
        RTTR_CREATE_GO(GO_Type::NofPassiveworker, nofPassiveWorker);
×
129
        RTTR_CREATE_GO(GO_Type::NofWellguy, nofWellguy);
×
130
        RTTR_CREATE_GO(GO_Type::NofCarrier, nofCarrier);
1✔
131
        RTTR_CREATE_GO(GO_Type::NofWoodcutter, nofWoodcutter);
×
132
        RTTR_CREATE_GO(GO_Type::NofFisher, nofFisher);
×
133
        RTTR_CREATE_GO(GO_Type::NofForester, nofForester);
×
134
        RTTR_CREATE_GO(GO_Type::NofCarpenter, nofCarpenter);
×
135
        RTTR_CREATE_GO(GO_Type::NofStonemason, nofStonemason);
×
136
        RTTR_CREATE_GO(GO_Type::NofHunter, nofHunter);
2✔
137
        RTTR_CREATE_GO(GO_Type::NofFarmer, nofFarmer);
×
138
        RTTR_CREATE_GO(GO_Type::NofMiller, nofMiller);
×
139
        RTTR_CREATE_GO(GO_Type::NofBaker, nofBaker);
1✔
140
        RTTR_CREATE_GO(GO_Type::NofButcher, nofButcher);
×
141
        RTTR_CREATE_GO(GO_Type::NofMiner, nofMiner);
×
142
        RTTR_CREATE_GO(GO_Type::NofBrewer, nofBrewer);
×
143
        RTTR_CREATE_GO(GO_Type::NofPigbreeder, nofPigbreeder);
×
144
        RTTR_CREATE_GO(GO_Type::NofDonkeybreeder, nofDonkeybreeder);
×
145
        RTTR_CREATE_GO(GO_Type::NofIronfounder, nofIronfounder);
×
146
        RTTR_CREATE_GO(GO_Type::NofMinter, nofMinter);
×
147
        RTTR_CREATE_GO(GO_Type::NofMetalworker, nofMetalworker);
×
148
        RTTR_CREATE_GO(GO_Type::NofArmorer, nofArmorer);
×
149
        RTTR_CREATE_GO(GO_Type::NofBuilder, nofBuilder);
×
150
        RTTR_CREATE_GO(GO_Type::NofPlaner, nofPlaner);
×
151
        RTTR_CREATE_GO(GO_Type::NofGeologist, nofGeologist);
×
152
        RTTR_CREATE_GO(GO_Type::NofShipwright, nofShipWright);
×
153
        RTTR_CREATE_GO(GO_Type::NofScoutFree, nofScout_Free);
×
154
        RTTR_CREATE_GO(GO_Type::NofScoutLookouttower, nofScout_LookoutTower);
×
155
        RTTR_CREATE_GO(GO_Type::NofWarehouseworker, nofWarehouseWorker);
×
156
        RTTR_CREATE_GO(GO_Type::NofCatapultman, nofCatapultMan);
×
157
        RTTR_CREATE_GO(GO_Type::NofCharburner, nofCharburner);
×
158
        RTTR_CREATE_GO(GO_Type::NofTradedonkey, nofTradeDonkey);
×
159
        RTTR_CREATE_GO(GO_Type::NofTradeleader, nofTradeLeader);
×
160
        RTTR_CREATE_GO(GO_Type::Extension, noExtension);
24✔
161
        RTTR_CREATE_GO(GO_Type::Buildingsite, noBuildingSite);
×
162
        RTTR_CREATE_GO(GO_Type::Envobject, noEnvObject);
×
163
        RTTR_CREATE_GO(GO_Type::Fire, noFire);
3✔
164
        RTTR_CREATE_GO(GO_Type::Burnedwarehouse, BurnedWarehouse);
×
165
        RTTR_CREATE_GO(GO_Type::Flag, noFlag);
11✔
166
        RTTR_CREATE_GO(GO_Type::Grainfield, noGrainfield);
×
167
        RTTR_CREATE_GO(GO_Type::Granite, noGranite);
×
168
        RTTR_CREATE_GO(GO_Type::Sign, noSign);
×
169
        RTTR_CREATE_GO(GO_Type::Skeleton, noSkeleton);
×
170
        RTTR_CREATE_GO(GO_Type::Staticobject, noStaticObject);
×
171
        RTTR_CREATE_GO(GO_Type::Disappearingmapenvobject, noDisappearingMapEnvObject);
×
172
        RTTR_CREATE_GO(GO_Type::Tree, noTree);
×
173
        RTTR_CREATE_GO(GO_Type::Animal, noAnimal);
1✔
174
        RTTR_CREATE_GO(GO_Type::Fighting, noFighting);
×
175
        RTTR_CREATE_GO(GO_Type::Roadsegment, RoadSegment);
12✔
176
        RTTR_CREATE_GO(GO_Type::Ware, Ware);
2✔
177
        RTTR_CREATE_GO(GO_Type::Catapultstone, CatapultStone);
×
178
        RTTR_CREATE_GO(GO_Type::Ship, noShip);
×
179
        RTTR_CREATE_GO(GO_Type::Shipbuildingsite, noShipBuildingSite);
×
180
        RTTR_CREATE_GO(GO_Type::Charburnerpile, noCharburnerPile);
×
181
        RTTR_CREATE_GO(GO_Type::Economymodehandler, EconomyModeHandler);
1✔
182
        RTTR_CREATE_GO(GO_Type::NofWinegrower, nofWinegrower);
×
183
        RTTR_CREATE_GO(GO_Type::NofVintner, nofVintner);
×
184
        RTTR_CREATE_GO(GO_Type::NofTempleservant, nofTempleServant);
×
185
        RTTR_CREATE_GO(GO_Type::Grapefield, noGrapefield);
×
186
        RTTR_CREATE_GO(GO_Type::NobTemple, nobTemple);
×
NEW
187
        RTTR_CREATE_GO(GO_Type::NofSkinner, nofSkinner);
×
NEW
188
        RTTR_CREATE_GO(GO_Type::NofTanner, nofTanner);
×
NEW
189
        RTTR_CREATE_GO(GO_Type::NofLeatherWorker, nofLeatherWorker);
×
190

UNCOV
191
        case GO_Type::Nothing: RTTR_Assert(false); break;
×
192
#undef RTTR_CREATE_GO
193
    }
194
    throw Error("Invalid GameObjectType " + helpers::toString(got) + " for objId=" + helpers::toString(obj_id)
×
195
                + " found!");
×
196
}
197

198
std::unique_ptr<FOWObject> SerializedGameData::Create_FOWObject(const FoW_Type fowtype)
×
199
{
200
    switch(fowtype)
×
201
    {
202
        default: return nullptr;
×
203
        case FoW_Type::Building: return std::make_unique<fowBuilding>(*this);
×
204
        case FoW_Type::Buildingsite: return std::make_unique<fowBuildingSite>(*this);
×
205
        case FoW_Type::Flag: return std::make_unique<fowFlag>(*this);
×
206
        case FoW_Type::Tree: return std::make_unique<fowTree>(*this);
×
207
        case FoW_Type::Granite: return std::make_unique<fowGranite>(*this);
×
208
    }
209
}
210

211
SerializedGameData::SerializedGameData()
13✔
212
    : debugMode(false), expectedNumObjects(0), em(nullptr), writeEm(nullptr), isReading(false)
13✔
213
{}
13✔
214

215
void SerializedGameData::Prepare(bool reading)
9✔
216
{
217
    static const std::array<char, 4> versionID = {"VER"};
218
    if(reading)
9✔
219
    {
220
        std::array<char, 4> versionIDRead;
221
        PopRawData(&versionIDRead.front(), versionIDRead.size());
3✔
222
        if(versionIDRead != versionID)
3✔
223
            throw Error("Invalid file format!");
×
224
        gameDataVersion = PopUnsignedInt();
3✔
225
    } else
226
    {
227
        Clear();
6✔
228
        PushRawData(&versionID.front(), versionID.size());
6✔
229
        PushUnsignedInt(currentGameDataVersion);
6✔
230
        gameDataVersion = currentGameDataVersion;
6✔
231
    }
232
    writtenObjIds.clear();
9✔
233
    readObjects.clear();
9✔
234
    expectedNumObjects = 0;
9✔
235
    isReading = reading;
9✔
236
}
9✔
237

238
void SerializedGameData::MakeSnapshot(const Game& game)
6✔
239
{
240
    Prepare(false);
6✔
241

242
    const GameWorldBase& gw = game.world_;
6✔
243
    writeEm = &gw.GetEvMgr();
6✔
244

245
    // Anzahl Objekte reinschreiben (used for safety checks only)
246
    expectedNumObjects = GameObject::GetNumObjs();
6✔
247
    PushUnsignedInt(expectedNumObjects);
6✔
248

249
    // World and objects
250
    MapSerializer::Serialize(gw, *this);
6✔
251
    // EventManager
252
    writeEm->Serialize(*this);
6✔
253
    if(game.ggs_.objective == GameObjective::EconomyMode)
6✔
254
    {
255
        PushObject(gw.getEconHandler(), true);
1✔
256
    }
257
    // Spieler serialisieren
258
    for(unsigned i = 0; i < gw.GetNumPlayers(); ++i)
23✔
259
    {
260
        if(debugMode)
17✔
261
            LOG.write("Start serializing player %1% at %2%\n") % i % GetLength();
×
262
        gw.GetPlayer(i).Serialize(*this);
17✔
263
        if(debugMode)
17✔
264
            LOG.write("Done serializing player %1% at %2%\n") % i % GetLength();
×
265
    }
266

267
    if(writtenEventIds.size() != writeEm->GetNumActiveEvents())
6✔
268
    {
269
        throw Error(helpers::format("Event count mismatch. Expected: %1%, written: %2%", writeEm->GetNumActiveEvents(),
×
270
                                    writtenEventIds.size()));
×
271
    }
272
    // If this check fails, we missed some objects or some objects were destroyed without decreasing the obj count
273
    if(expectedNumObjects != writtenObjIds.size() + 1) // "Nothing" nodeObj does not get serialized
6✔
274
    {
275
        throw Error(helpers::format("Object count mismatch. Expected: %1%, written: %2%", expectedNumObjects,
×
276
                                    writtenObjIds.size() + 1));
×
277
    }
278

279
    writeEm = nullptr;
6✔
280
    writtenObjIds.clear();
6✔
281
    writtenEventIds.clear();
6✔
282
}
6✔
283

284
void SerializedGameData::ReadSnapshot(Game& game, ILocalGameState& localGameState)
3✔
285
{
286
    Prepare(true);
3✔
287

288
    GameWorld& gw = game.world_;
3✔
289
    em = &gw.GetEvMgr();
3✔
290

291
    expectedNumObjects = PopUnsignedInt();
3✔
292

293
    MapSerializer::Deserialize(gw, *this, game, localGameState);
3✔
294
    em->Deserialize(*this);
3✔
295
    if(gw.GetGGS().objective == GameObjective::EconomyMode)
3✔
296
    {
297
        gw.setEconHandler(
1✔
298
          std::unique_ptr<EconomyModeHandler>(PopObject<EconomyModeHandler>(GO_Type::Economymodehandler)));
2✔
299
    }
300

301
    for(unsigned i = 0; i < gw.GetNumPlayers(); ++i)
11✔
302
        gw.GetPlayer(i).Deserialize(*this);
8✔
303

304
    // If this check fails, we did not serialize all objects or there was an async
305
    if(readEvents.size() != em->GetNumActiveEvents())
3✔
306
    {
307
        throw Error(helpers::format("Event count mismatch. Expected: %1%, read: %2%", em->GetNumActiveEvents(),
×
308
                                    readEvents.size()));
×
309
    }
310
    if(expectedNumObjects != GameObject::GetNumObjs())
3✔
311
    {
312
        throw Error(helpers::format("Object count mismatch. Expected: %1%, Existing: %2%", expectedNumObjects,
×
313
                                    GameObject::GetNumObjs()));
×
314
    }
315
    if(expectedNumObjects != readObjects.size() + 1) // "Nothing" nodeObj does not get serialized
3✔
316
    {
317
        throw Error(helpers::format("Object count mismatch. Expected: %1%, read: %2%", expectedNumObjects,
×
318
                                    readObjects.size() + 1));
×
319
    }
320

321
    // Sanity check for flag workers. See bug #1449
322
    for(const auto& entry : readObjects)
72✔
323
    {
324
        const auto* worker = dynamic_cast<const nofFlagWorker*>(entry.second);
69✔
325
        if(worker && worker->GetFlag() && worker->GetPlayer() != worker->GetFlag()->GetPlayer())
69✔
326
        {
327
            throw Error(helpers::format("Invalid flag worker at %1%", worker->GetPos()));
×
328
        }
329
    }
330

331
    em = nullptr;
3✔
332
    readObjects.clear();
3✔
333
    readEvents.clear();
3✔
334
}
3✔
335

336
void SerializedGameData::PushObject_(const GameObject* go, const bool known)
5,950✔
337
{
338
    RTTR_Assert(!isReading);
5,950✔
339

340
    // Gibts das Objekt gar nich?
341
    if(!go)
5,950✔
342
    {
343
        // Null draufschreiben
344
        PushUnsignedInt(0);
5,587✔
345
        return;
5,807✔
346
    }
347

348
    const unsigned objId = go->GetObjId();
363✔
349

350
    RTTR_Assert(objId <= GameObject::GetObjIDCounter());
363✔
351
    if(objId > GameObject::GetObjIDCounter())
363✔
352
    {
353
        LOG.write("%s\n") % _("An error occured while saving which was suppressed!");
×
354
        PushUnsignedInt(0);
×
355
        return;
×
356
    }
357

358
    PushUnsignedInt(objId);
363✔
359

360
    // If the object was already serialized skip the data
361
    if(IsObjectSerialized(objId))
363✔
362
    {
363
        if(debugMode)
220✔
364
            LOG.write("Saved known objId %u\n") % objId;
×
365
        return;
220✔
366
    }
367

368
    if(debugMode)
143✔
369
        LOG.write("Saving objId %u, obj#=%u\n") % objId % writtenObjIds.size();
×
370

371
    // Objekt merken
372
    writtenObjIds.insert(objId);
143✔
373

374
    RTTR_Assert(writtenObjIds.size() < GameObject::GetNumObjs());
143✔
375

376
    // Objekt nich bekannt? Dann Type-ID noch mit drauf
377
    if(!known)
143✔
378
        PushEnum<uint16_t>(go->GetGOT());
110✔
379

380
    // Objekt serialisieren
381
    if(debugMode)
143✔
382
        LOG.write("Start serializing %1% at %2%\n") % objId % GetLength();
×
383
    go->Serialize(*this);
143✔
384
    if(debugMode)
143✔
385
        LOG.write("Done serializing %1% at %2%\n") % objId % GetLength();
×
386

387
    // Sicherheitscode reinschreiben
388
    PushUnsignedShort(GetSafetyCode(*go));
143✔
389
}
390

391
void SerializedGameData::PushEvent(const GameEvent* event)
168✔
392
{
393
    if(!event)
168✔
394
    {
395
        PushUnsignedInt(0);
63✔
396
        return;
115✔
397
    }
398

399
    unsigned instanceId = event->GetInstanceId();
105✔
400
    PushUnsignedInt(instanceId);
105✔
401
    if(IsEventSerialized(instanceId))
105✔
402
        return;
52✔
403
    writtenEventIds.insert(instanceId);
53✔
404
    if(debugMode)
53✔
405
        LOG.write("Start serializing event %1% at %2%\n") % instanceId % GetLength();
×
406
    event->Serialize(*this);
53✔
407
    if(debugMode)
53✔
408
        LOG.write("Done serializing event %1% at %2%\n") % instanceId % GetLength();
×
409
    PushUnsignedShort(GetSafetyCode(*event));
53✔
410
}
411

412
const GameEvent* SerializedGameData::PopEvent()
81✔
413
{
414
    unsigned instanceId = PopUnsignedInt();
81✔
415
    if(!instanceId)
81✔
416
        return nullptr;
30✔
417

418
    // Note: em->GetEventInstanceCtr() might not be set yet
419
    const auto foundObj = readEvents.find(instanceId);
51✔
420
    if(foundObj != readEvents.end())
51✔
421
        return foundObj->second;
25✔
422
    std::unique_ptr<GameEvent> ev = std::make_unique<GameEvent>(*this, instanceId);
52✔
423

424
    unsigned short safety_code = PopUnsignedShort();
26✔
425

426
    if(safety_code != GetSafetyCode(*ev))
26✔
427
    {
428
        LOG.write("SerializedGameData::PopEvent: ERROR: After loading Event(instanceId = %1%); Code is wrong!\n")
×
429
          % instanceId;
×
430
        throw Error("Invalid safety code after PopEvent");
×
431
    }
432
    return ev.release();
26✔
433
}
434

435
/// FoW-Objekt
436
void SerializedGameData::PushFOWObject(const FOWObject* fowobj)
×
437
{
438
    // Gibts das Objekt gar nich?
439
    if(!fowobj)
×
440
    {
441
        // Null draufschreiben
442
        PushUnsignedChar(0);
×
443
        return;
×
444
    }
445

446
    // Objekt-Typ
447
    PushEnum<uint8_t>(fowobj->GetType());
×
448

449
    // Objekt serialisieren
450
    fowobj->Serialize(*this);
×
451
}
452

453
std::unique_ptr<FOWObject> SerializedGameData::PopFOWObject()
×
454
{
455
    // Typ auslesen
456
    auto type = Pop<FoW_Type>();
×
457

458
    // Kein Objekt?
459
    if(type == FoW_Type::Nothing)
×
460
        return nullptr;
×
461

462
    // entsprechendes Objekt erzeugen
463
    return Create_FOWObject(type);
×
464
}
465

466
GameObject* SerializedGameData::PopObject_(helpers::OptionalEnum<GO_Type> got)
2,965✔
467
{
468
    RTTR_Assert(isReading);
2,965✔
469
    // Obj-ID holen
470
    const unsigned objId = PopUnsignedInt();
2,965✔
471

472
    // Obj-ID = 0 ? Dann Null-Pointer zurueckgeben
473
    if(!objId)
2,965✔
474
        return nullptr;
2,789✔
475

476
    if(GameObject* go = GetReadGameObject(objId))
176✔
477
        return go;
107✔
478

479
    // Objekt nich bekannt? Dann in den heiligen Schriften lesen
480
    if(!got)
69✔
481
        got = Pop<GO_Type>();
53✔
482

483
    // und erzeugen
484
    std::unique_ptr<GameObject> go = Create_GameObject(*got, objId);
138✔
485

486
    // Sicherheitscode auslesen
487
    unsigned short safety_code = PopUnsignedShort();
69✔
488

489
    if(safety_code != GetSafetyCode(*go))
69✔
490
    {
491
        LOG.write(
×
492
          "SerializedGameData::PopObject_: ERROR: After loading Object(obj_id = %u, got = %u); Code is wrong!\n")
493
          % objId % rttr::enum_cast(*got);
×
494
        throw Error("Invalid safety code after PopObject");
×
495
    }
496

497
    return go.release();
69✔
498
}
499

500
unsigned short SerializedGameData::GetSafetyCode(const GameObject& go)
212✔
501
{
502
    return 0xFFFF ^ rttr::enum_cast(go.GetGOT()) ^ go.GetObjId();
212✔
503
}
504

505
unsigned short SerializedGameData::GetSafetyCode(const GameEvent& ev)
79✔
506
{
507
    return 0xFFFF ^ ev.GetInstanceId();
79✔
508
}
509

510
SerializedGameData::Error SerializedGameData::makeOutOfRange(unsigned value, unsigned maxValue)
×
511
{
512
    return Error(helpers::format("%s is out of range. Maximum allowed value: %s", value, maxValue));
×
513
}
514

515
void SerializedGameData::AddObject(GameObject* go)
69✔
516
{
517
    RTTR_Assert(isReading);
69✔
518
    RTTR_Assert(!readObjects[go->GetObjId()]); // Do not call this multiple times per GameObject
69✔
519
    readObjects[go->GetObjId()] = go;
69✔
520
    RTTR_Assert(readObjects.size() < expectedNumObjects);
69✔
521
}
69✔
522

523
unsigned SerializedGameData::AddEvent(unsigned instanceId, GameEvent* ev)
26✔
524
{
525
    RTTR_Assert(isReading);
26✔
526
    RTTR_Assert(!readEvents[instanceId]); // Do not call this multiple times per GameObject
26✔
527
    readEvents[instanceId] = ev;
26✔
528
    return instanceId;
26✔
529
}
530

531
bool SerializedGameData::IsObjectSerialized(unsigned obj_id) const
363✔
532
{
533
    RTTR_Assert(!isReading);
363✔
534
    RTTR_Assert(obj_id <= GameObject::GetObjIDCounter());
363✔
535
    return helpers::contains(writtenObjIds, obj_id);
363✔
536
}
537

538
bool SerializedGameData::IsEventSerialized(unsigned evInstanceid) const
105✔
539
{
540
    RTTR_Assert(!isReading);
105✔
541
    RTTR_Assert(evInstanceid < writeEm->GetEventInstanceCtr());
105✔
542
    return helpers::contains(writtenEventIds, evInstanceid);
105✔
543
}
544

545
GameObject* SerializedGameData::GetReadGameObject(const unsigned obj_id) const
176✔
546
{
547
    RTTR_Assert(isReading);
176✔
548
    RTTR_Assert(obj_id <= GameObject::GetObjIDCounter());
176✔
549
    auto foundObj = readObjects.find(obj_id);
176✔
550
    if(foundObj == readObjects.end())
176✔
551
        return nullptr;
69✔
552
    else
553
        return foundObj->second;
107✔
554
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc