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

Return-To-The-Roots / s25client / 28242163216

26 Jun 2026 01:46PM UTC coverage: 50.449% (+0.07%) from 50.377%
28242163216

Pull #1949

github

web-flow
Merge 02cc02719 into d90bf9ac6
Pull Request #1949: Fix and extend AI-Battle

1 of 3 new or added lines in 2 files covered. (33.33%)

534 existing lines in 14 files now uncovered.

23270 of 46126 relevant lines covered (50.45%)

47704.69 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
/// 15: Add sea to HarborPos::Neighbor
112
static const unsigned currentGameDataVersion = 15;
113
// clang-format on
114

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

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

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

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

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

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

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

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

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

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

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

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

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

294
    expectedNumObjects = PopUnsignedInt();
3✔
295

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

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

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

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

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

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

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

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

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

361
    PushUnsignedInt(objId);
322✔
362

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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