Skip to content

Commit 5ff5195

Browse files
committed
Fix circular dependency during deserialization
1 parent 8aed2e2 commit 5ff5195

4 files changed

Lines changed: 40 additions & 19 deletions

File tree

‎libs/s25main/figures/noFigure.cpp‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ const unsigned short WANDER_RADIUS_SOLDIERS = 15;
4848
noFigure::noFigure(const Job job, const MapPoint pos, const unsigned char player, noRoadNode* const goal)
4949
: noMovable(NodalObjectType::Figure, pos), fs(FigureState::GotToGoal), job_(job), player(player), cur_rs(nullptr),
5050
rs_pos(0), rs_dir(false), on_ship(false), goal_(goal), waiting_for_free_node(false), wander_way(0),
51-
wander_tryings(0), flagPos_(MapPoint::Invalid()), flag_obj_id(0), burned_wh_id(0xFFFFFFFF), last_id(0xFFFFFFFF)
51+
wander_tryings(0), flagPos_(MapPoint::Invalid()), flag_obj_id(0), burned_wh_id(0xFFFFFFFF), last_id(0xFFFFFFFF),
52+
armor(false)
5253
{
5354
// Haben wir ein Ziel?
5455
// Gehen wir in ein Lagerhaus? Dann dürfen wir da nicht unsere Arbeit ausführen, sondern
@@ -62,7 +63,8 @@ noFigure::noFigure(const Job job, const MapPoint pos, const unsigned char player
6263
noFigure::noFigure(const Job job, const MapPoint pos, const unsigned char player)
6364
: noMovable(NodalObjectType::Figure, pos), fs(FigureState::Job), job_(job), player(player), cur_rs(nullptr),
6465
rs_pos(0), rs_dir(false), on_ship(false), goal_(nullptr), waiting_for_free_node(false), wander_way(0),
65-
wander_tryings(0), flagPos_(MapPoint::Invalid()), flag_obj_id(0), burned_wh_id(0xFFFFFFFF), last_id(0xFFFFFFFF)
66+
wander_tryings(0), flagPos_(MapPoint::Invalid()), flag_obj_id(0), burned_wh_id(0xFFFFFFFF), last_id(0xFFFFFFFF),
67+
armor(false)
6668
{}
6769

6870
void noFigure::Destroy()
@@ -85,6 +87,7 @@ void noFigure::Serialize(SerializedGameData& sgd) const
8587
sgd.PushUnsignedShort(rs_pos);
8688
sgd.PushBool(rs_dir);
8789
sgd.PushBool(on_ship);
90+
sgd.PushBool(armor);
8891

8992
if(fs == FigureState::GotToGoal || fs == FigureState::GoHome)
9093
sgd.PushObject(goal_);
@@ -104,7 +107,7 @@ void noFigure::Serialize(SerializedGameData& sgd) const
104107
noFigure::noFigure(SerializedGameData& sgd, const unsigned obj_id)
105108
: noMovable(sgd, obj_id), fs(sgd.Pop<FigureState>()), job_(sgd.Pop<Job>()), player(sgd.PopUnsignedChar()),
106109
cur_rs(sgd.PopObject<RoadSegment>(GO_Type::Roadsegment)), rs_pos(sgd.PopUnsignedShort()), rs_dir(sgd.PopBool()),
107-
on_ship(sgd.PopBool()), last_id(0xFFFFFFFF)
110+
on_ship(sgd.PopBool()), last_id(0xFFFFFFFF), armor(sgd.GetGameDataVersion() >= 12 ? sgd.PopBool() : false)
108111
{
109112
if(fs == FigureState::GotToGoal || fs == FigureState::GoHome)
110113
goal_ = sgd.PopObject<noRoadNode>();

‎libs/s25main/figures/noFigure.h‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,31 @@ class noFigure : public noMovable
7474
/// Speichert letzten Animationsframes (zum Abspielen von Sounds)
7575
unsigned last_id;
7676

77+
/*
78+
The armor variable should be a member of nofArmored. But this is not possible due to a circular dependency
79+
between objects during object deserialization. If a soldier is sent to a military building from a warehouse, the
80+
soldier is inserted into the ordered_troops list of the military building. This soldier is also in the leave
81+
queue of the warehouse after some time. When the game is stored in this state a circular dependency exists.
82+
83+
What happens during deserialization?
84+
85+
The warehouse is deserialized first. And therefore deserialization of the soldier in the leave queue will start.
86+
The deserialization is done by going up the class hierarchy. In the top base class GameObject the new obejct is
87+
inserted into the líst of already deserialized objects. But at this time the object is not yet fully constructed.
88+
In the base class noFigure the goal for the soldier is deserialized. The goal is a military building and holds
89+
the ordered_troops list. When this list is deserialized we try to deserialize the soldier, we are currently
90+
creating, again. Because the soldier is in the líst of already deserialized objects we get back a reference to
91+
this instance from the list instead of creating it new. The soldier is inserted into the ordered_troops list.
92+
Because this list is a sorted list a comparator is called to do the insertion. The comparator in this case is the
93+
ComparatorSoldiersByRank. This comparator uses the rank of the soldier, the objectId and the armor for
94+
comparison. But at this time the armor variable in the nofArmored subclass of the soldier is not yet deserialized
95+
and has a random value. This leads to wrong insertion into the sorted list. And later on to a wrong application
96+
state. Because after loading, when in the game the ordered soldier reaches the building the removing from the
97+
ordered_troops list will fail. This problem does not exist for the rank of the soldier. Because the rank of a
98+
soldier depends on the job id, which is deserialized in noFigure before the goal is deserialized.
99+
*/
100+
bool armor;
101+
77102
explicit noFigure(const noFigure&) = default;
78103

79104
private:

‎libs/s25main/figures/nofArmored.cpp‎

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,22 @@
1414
#include "gameTypes/JobTypes.h"
1515

1616
nofArmored::nofArmored(Job job, MapPoint pos, unsigned char player, noRoadNode* goal, bool armor)
17-
: noFigure(job, pos, player, goal), armor(armor)
18-
{}
17+
: noFigure(job, pos, player, goal)
18+
{
19+
this->armor = armor;
20+
}
1921

20-
nofArmored::nofArmored(Job job, MapPoint pos, unsigned char player, bool armor)
21-
: noFigure(job, pos, player), armor(armor)
22-
{}
22+
nofArmored::nofArmored(Job job, MapPoint pos, unsigned char player, bool armor) : noFigure(job, pos, player)
23+
{
24+
this->armor = armor;
25+
}
2326

2427
void nofArmored::Serialize(SerializedGameData& sgd) const
2528
{
2629
noFigure::Serialize(sgd);
27-
sgd.PushBool(armor);
2830
}
2931

30-
nofArmored::nofArmored(SerializedGameData& sgd, const unsigned obj_id) : noFigure(sgd, obj_id)
31-
{
32-
if(sgd.GetGameDataVersion() >= 12)
33-
armor = sgd.PopBool();
34-
else
35-
armor = false;
36-
}
32+
nofArmored::nofArmored(SerializedGameData& sgd, const unsigned obj_id) : noFigure(sgd, obj_id) {}
3733

3834
void nofArmored::DrawArmorWalking(DrawPoint drawPt)
3935
{

‎libs/s25main/figures/nofArmored.h‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ class SerializedGameData;
1313
class nofArmored : public noFigure
1414
{
1515
protected:
16-
/// Armor
17-
bool armor;
18-
1916
explicit nofArmored(const nofArmored&) = default;
2017

2118
void DrawArmor(DrawPoint drawPt);

0 commit comments

Comments
 (0)