Skip to content

Commit 2dd0f2c

Browse files
committed
fix: ensure conversion loop for level asset type
1 parent 4b9eabc commit 2dd0f2c

15 files changed

Lines changed: 106 additions & 57 deletions

Core.SourceGen/ContentSerializerGenerator.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,9 @@ private static void EmitPropertyDeserialize(CodeStringBuilder cb, XnbPropertyInf
190190

191191
if (prop.UseConverter)
192192
{
193-
cb.Append($"({prop.TypeFullName})reader.ReadContent(typeof({prop.TypeFullName}), ");
194-
cb.Append($"{(prop.SkipIdentifier ? "true" : "false")})!");
193+
var castType = $"{prop.TypeFullName}{(prop.IsNullable ? "?" : "")}";
194+
cb.Append($"({castType})reader.ReadContent(typeof({prop.TypeFullName}), ");
195+
cb.Append($"{(prop.SkipIdentifier ? "true" : "false")}){(prop.IsNullable ? "" : "!")}");
195196
}
196197
else
197198
{

Core/Definitions/Game/Level/ArtObjectActorSettings.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class ArtObjectActorSettings
1313
[XnbProperty(UseConverter = true)]
1414
public ActorType ContainedTrile { get; set; }
1515

16-
[XnbProperty(Optional = true)]
16+
[XnbProperty(UseConverter = true)]
1717
public int? AttachedGroup { get; set; }
1818

1919
[XnbProperty(UseConverter = true)]
@@ -40,7 +40,7 @@ public class ArtObjectActorSettings
4040
[XnbProperty(UseConverter = true)]
4141
public PathSegment Segment { get; set; } = new();
4242

43-
[XnbProperty(Optional = true)]
43+
[XnbProperty(UseConverter = true)]
4444
public int? NextNode { get; set; }
4545

4646
[XnbProperty(UseConverter = true)]

Core/Definitions/Game/Level/BackgroundPlane.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class BackgroundPlane
4040
[XnbProperty]
4141
public float Opacity { get; set; } = 1.0f;
4242

43-
[XnbProperty(Optional = true)]
43+
[XnbProperty(UseConverter = true)]
4444
public int? AttachedGroup { get; set; }
4545

4646
[XnbProperty]
@@ -78,7 +78,7 @@ public class BackgroundPlane
7878
[XnbProperty(UseConverter = true)]
7979
public ActorType ActorType { get; set; }
8080

81-
[XnbProperty(Optional = true)]
81+
[XnbProperty(UseConverter = true)]
8282
public int? AttachedPlane { get; set; }
8383

8484
[XnbProperty]

Core/Definitions/Game/Level/Scripting/Entity.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class Entity
77
[XnbProperty]
88
public string Type { get; set; } = "";
99

10-
[XnbProperty(Optional = true)]
10+
[XnbProperty(UseConverter = true)]
1111
public int? Identifier { get; set; }
1212
}
1313
}

Core/Definitions/Game/Level/Scripting/Script.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class Script
77
[XnbProperty]
88
public string Name { get; set; } = "Untitled";
99

10-
[XnbProperty(Optional = true)]
10+
[XnbProperty(UseConverter = true)]
1111
public TimeSpan? Timeout { get; set; }
1212

1313
[XnbProperty(UseConverter = true)]

Core/Definitions/Game/Level/TrileGroup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class TrileGroup
1010
[XnbProperty(UseConverter = true)]
1111
public List<TrileInstance> Triles { get; set; } = new();
1212

13-
[XnbProperty(UseConverter = true, Optional = true, SkipIdentifier = true)]
13+
[XnbProperty(UseConverter = true)]
1414
public MovementPath? Path { get; set; } = null;
1515

1616
[XnbProperty]

Core/Definitions/Game/Level/TrileInstanceActorSettings.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Original name in FezEngine: InstanceActorSettings
66
public class TrileInstanceActorSettings
77
{
8-
[XnbProperty(Optional = true)]
8+
[XnbProperty(UseConverter = true)]
99
public int? ContainedTrile { get; set; }
1010

1111
[XnbProperty(UseConverter = true)]
@@ -20,7 +20,7 @@ public class TrileInstanceActorSettings
2020
[XnbProperty(UseConverter = true)]
2121
public string SequenceAlternateSampleName { get; set; } = "";
2222

23-
[XnbProperty(Optional = true)]
23+
[XnbProperty(UseConverter = true)]
2424
public int? HostVolume { get; set; }
2525
}
2626
}

Core/Definitions/Json/LevelJsonModel.cs

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -92,31 +92,8 @@ public Level Deserialize()
9292
ArtObjects = ArtObjects,
9393
};
9494

95-
level.Triles = new OrderedDictionary<TrileEmplacement, TrileInstance>();
96-
97-
foreach (var trileModel in Triles)
98-
{
99-
var trile = trileModel.Deserialize();
100-
101-
if (level.Triles.TryGetValue(trileModel.Emplacement, out var existingTrile))
102-
{
103-
if (existingTrile.OverlappedTriles == null)
104-
{
105-
existingTrile.OverlappedTriles = new List<TrileInstance>();
106-
}
107-
108-
level.Triles[trileModel.Emplacement].OverlappedTriles.Add(trile);
109-
continue;
110-
}
111-
112-
level.Triles[trileModel.Emplacement] = trile;
113-
}
114-
115-
level.Groups = new Dictionary<int, TrileGroup>();
116-
foreach (var groupModel in Groups)
117-
{
118-
level.Groups[groupModel.Key] = groupModel.Value.Deserialize();
119-
}
95+
ArrangeTrilesIntoLevel(level);
96+
RecreateGroupsInLevel(level);
12097

12198
return level;
12299
}
@@ -156,7 +133,18 @@ public void SerializeFrom(Level level)
156133
Volumes = level.Volumes;
157134
ArtObjects = level.ArtObjects;
158135

159-
// sort tiles into modified structures
136+
ExtractTrileListFromLevel(level);
137+
ExtractCleanedGroupsFromLevel(level);
138+
}
139+
140+
private void ExtractTrileListFromLevel(Level level)
141+
{
142+
// OverlappedTriles list in original Level's Trile Instance structure exists as a way to store
143+
// multiple triles under the same TrileEmplacement key in Triles dictionary.
144+
// This is wasteful for JSON format. To avoid this, custom model for TrileInstance containing Emplacement
145+
// is made, so overlapping triles can exist next to one another.
146+
// We're merging them back into OverlappedTriles array in ArrangeTrilesIntoLevel during deconversion.
147+
160148
Triles = new();
161149
foreach (var trileRecord in level.Triles)
162150
{
@@ -174,13 +162,54 @@ public void SerializeFrom(Level level)
174162
Triles.Add(new TrileInstanceJsonModel(pos, overlapping));
175163
}
176164
}
165+
}
177166

178-
// create groups of modified paths
167+
private void ArrangeTrilesIntoLevel(Level level)
168+
{
169+
level.Triles = new OrderedDictionary<TrileEmplacement, TrileInstance>();
170+
foreach (var trileModel in Triles)
171+
{
172+
var trile = trileModel.Deserialize();
173+
174+
if (level.Triles.TryGetValue(trileModel.Emplacement, out var existingTrile))
175+
{
176+
if (existingTrile.OverlappedTriles == null)
177+
{
178+
existingTrile.OverlappedTriles = new List<TrileInstance>();
179+
}
180+
181+
level.Triles[trileModel.Emplacement].OverlappedTriles.Add(trile);
182+
continue;
183+
}
184+
185+
level.Triles[trileModel.Emplacement] = trile;
186+
}
187+
}
188+
189+
private void ExtractCleanedGroupsFromLevel(Level level)
190+
{
191+
// Groups store exact copies of contained TrileInstances. This is very wasteful, and even the game
192+
// dumps them and looks them up again using TrileEmplacement calculated from trile's position
193+
// (see FezEngine.Structure.Level.OnDeserialization())
194+
// We're dumping all trile data using custom model for TrileGroup,
195+
// and then rebuilding it in RecreateGroupsInLevel.
196+
179197
Groups = new Dictionary<int, TrileGroupJsonModel>();
180198
foreach (var pair in level.Groups)
181199
{
182200
Groups[pair.Key] = new TrileGroupJsonModel(pair.Value);
183201
}
184202
}
203+
204+
private void RecreateGroupsInLevel(Level level)
205+
{
206+
level.Groups = new Dictionary<int, TrileGroup>();
207+
foreach (var groupModel in Groups)
208+
{
209+
var group = groupModel.Value.Deserialize();
210+
groupModel.Value.ReconstructTrilesInGroup(group, level);
211+
level.Groups[groupModel.Key] = group;
212+
}
213+
}
185214
}
186215
}

Core/Definitions/Json/TrileGroupJsonModel.cs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,30 @@ public TrileGroup Deserialize()
5858
AssociatedSound = AssociatedSound,
5959
};
6060

61-
trileGroup.Triles = Triles
62-
.Select(x => new TrileInstance() { Position = new(x.X, x.Y, x.Z) })
63-
.ToList();
64-
6561
return trileGroup;
6662
}
6763

64+
public void ReconstructTrilesInGroup(TrileGroup trileGroup, Level level)
65+
{
66+
trileGroup.Triles = new List<TrileInstance>();
67+
foreach (var emplacement in Triles)
68+
{
69+
if (!level.Triles.TryGetValue(emplacement, out var existingTrile))
70+
{
71+
// If we couldn't find linked trile instance in level, then something went
72+
// terribly wrong with the level file. This fallback is here to make sure
73+
// the game tries to find it as well and fail miserably so user knows about it
74+
trileGroup.Triles.Add(new TrileInstance()
75+
{
76+
Position = new (emplacement.X, emplacement.Y, emplacement.Z)
77+
});
78+
continue;
79+
}
80+
81+
trileGroup.Triles.Add(existingTrile);
82+
}
83+
}
84+
6885
private void CopyBasicPropertiesOverFrom(TrileGroup trileGroup)
6986
{
7087
Path = trileGroup.Path!;
@@ -88,6 +105,8 @@ public void SerializeFrom(TrileGroup trileGroup)
88105
{
89106
CopyBasicPropertiesOverFrom(trileGroup);
90107

108+
// leaving only emplacements, as triles are most likely exact copies of triles from level
109+
// see LevelJsonModel.ExtractCleanedGroupsFromLevel for details
91110
Triles = trileGroup.Triles.Select(x => new TrileEmplacement(x.Position)).ToList();
92111
}
93112
}

Core/Helpers/Json/CustomConverters/ScriptPropertiesJsonConverters.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ public override ScriptAction Read(
151151
action.Arguments = beforeFuncSplit[1].Split(',');
152152
for (int i = 0; i < action.Arguments.Length; i++)
153153
{
154-
action.Arguments[i] = action.Arguments[i].Trim();
154+
var arg = action.Arguments[i].Trim();
155+
action.Arguments[i] = arg == "null" ? "" : arg;
155156
}
156157
}
157158

@@ -170,7 +171,8 @@ public override void Write(
170171
for (int i = 0; i < value.Arguments.Count(); i++)
171172
{
172173
if (i > 0) output += ", ";
173-
output += value.Arguments[i];
174+
var arg = value.Arguments[i];
175+
output += string.IsNullOrEmpty(arg) ? "null" : arg;
174176
}
175177
output += ")";
176178
if (value.Blocking) output = "#" + output;

0 commit comments

Comments
 (0)