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

Return-To-The-Roots / s25client / 24081737514

07 Apr 2026 12:38PM UTC coverage: 50.361% (+0.02%) from 50.337%
24081737514

Pull #1910

github

web-flow
Merge fa2a39fa2 into e4146df45
Pull Request #1910: Fix wrongly shown soldiers & Refactor HQ start wares and inventory handling

197 of 409 new or added lines in 22 files covered. (48.17%)

99 existing lines in 25 files now uncovered.

23063 of 45795 relevant lines covered (50.36%)

42320.64 hits per line

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

58.03
/libs/s25main/SerializedGameData.cpp
1
// Copyright (C) 2005 - 2026 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
/// 13: SeaId & HarborId: World::harborData w/o dummy entry at 0
110
/// 14: Remove "age" field in nobBaseMilitary
111
static const unsigned currentGameDataVersion = 14;
112
// clang-format on
113

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

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

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

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

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

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

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

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

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

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

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

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

290
    GameWorld& gw = game.world_;
3✔
291
    em = &gw.GetEvMgr();
3✔
292

293
    expectedNumObjects = PopUnsignedInt();
3✔
294

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

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

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

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

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

338
void SerializedGameData::PushObject_(const GameObject* go, const bool known)
5,911✔
339
{
340
    RTTR_Assert(!isReading);
5,911✔
341

342
    // Gibts das Objekt gar nich?
343
    if(!go)
5,911✔
344
    {
345
        // Null draufschreiben
346
        PushUnsignedInt(0);
5,589✔
347
        return;
5,772✔
348
    }
349

350
    const unsigned objId = go->GetObjId();
322✔
351

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

360
    PushUnsignedInt(objId);
322✔
361

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

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

373
    // Objekt merken
374
    writtenObjIds.insert(objId);
139✔
375

376
    RTTR_Assert(writtenObjIds.size() < GameObject::GetNumObjs());
139✔
377

378
    // Objekt nich bekannt? Dann Type-ID noch mit drauf
379
    if(!known)
139✔
380
        PushEnum<uint16_t>(go->GetGOT());
108✔
381

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

389
    // Sicherheitscode reinschreiben
390
    PushUnsignedShort(GetSafetyCode(*go));
139✔
391
}
392

393
void SerializedGameData::PushEvent(const GameEvent* event)
137✔
394
{
395
    if(!event)
137✔
396
    {
397
        PushUnsignedInt(0);
82✔
398
        return;
109✔
399
    }
400

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

414
const GameEvent* SerializedGameData::PopEvent()
66✔
415
{
416
    unsigned instanceId = PopUnsignedInt();
66✔
417
    if(!instanceId)
66✔
418
        return nullptr;
39✔
419

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

426
    unsigned short safety_code = PopUnsignedShort();
14✔
427

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

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

448
    // Objekt-Typ
449
    PushEnum<uint8_t>(fowobj->GetType());
×
450

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

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

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

464
    // entsprechendes Objekt erzeugen
465
    return Create_FOWObject(type);
×
466
}
467

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

474
    // Obj-ID = 0 ? Dann Null-Pointer zurueckgeben
475
    if(!objId)
2,946✔
476
        return nullptr;
2,790✔
477

478
    if(GameObject* go = GetReadGameObject(objId))
156✔
479
        return go;
89✔
480

481
    // Objekt nich bekannt? Dann in den heiligen Schriften lesen
482
    if(!got)
67✔
483
        got = Pop<GO_Type>();
52✔
484

485
    // und erzeugen
486
    std::unique_ptr<GameObject> go = Create_GameObject(*got, objId);
134✔
487

488
    // Sicherheitscode auslesen
489
    unsigned short safety_code = PopUnsignedShort();
67✔
490

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

499
    return go.release();
67✔
500
}
501

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

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

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

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

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

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

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

547
GameObject* SerializedGameData::GetReadGameObject(const unsigned obj_id) const
156✔
548
{
549
    RTTR_Assert(isReading);
156✔
550
    RTTR_Assert(obj_id <= GameObject::GetObjIDCounter());
156✔
551
    auto foundObj = readObjects.find(obj_id);
156✔
552
    if(foundObj == readObjects.end())
156✔
553
        return nullptr;
67✔
554
    else
555
        return foundObj->second;
89✔
556
}
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