Skip to content

Commit a8fe648

Browse files
committed
start implementing correct gameobject parser
1 parent 9179fc0 commit a8fe648

15 files changed

Lines changed: 312 additions & 47 deletions

src/ModVerify/Reporting/Engine/GameAssertErrorReporter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ protected override ErrorData CreateError(EngineAssert assert)
1717
var context = new List<string>();
1818
context.AddRange(assert.Context);
1919
context.Add($"location='{GetLocation(assert)}'");
20-
return new ErrorData(GetIdFromError(assert.Kind), assert.Message, context, assert.Value, VerificationSeverity.Warning);
20+
return new ErrorData(
21+
GetIdFromError(assert.Kind),
22+
assert.Message,
23+
context,
24+
assert.Value,
25+
VerificationSeverity.Warning);
2126
}
2227

2328
private static string GetLocation(EngineAssert assert)
@@ -41,6 +46,7 @@ private static string GetIdFromError(EngineAssertKind assertKind)
4146
EngineAssertKind.ValueOutOfRange => VerifierErrorCodes.AssertValueOutOfRange,
4247
EngineAssertKind.InvalidValue => VerifierErrorCodes.AssertValueInvalid,
4348
EngineAssertKind.FileNotFound => VerifierErrorCodes.FileNotFound,
49+
EngineAssertKind.DuplicateEntry => VerifierErrorCodes.DuplicateFound,
4450
_ => throw new ArgumentOutOfRangeException(nameof(assertKind), assertKind, null)
4551
};
4652
}

src/PetroglyphTools/PG.StarWarsGame.Engine/ErrorReporting/EngineAssertKind.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ public enum EngineAssertKind
77
InvalidValue,
88
CorruptBinary,
99
FileNotFound,
10+
DuplicateEntry
1011
}

src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObject.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,34 @@ namespace PG.StarWarsGame.Engine.GameObjects;
99

1010
public sealed class GameObject : NamedXmlObject
1111
{
12-
internal GameObject(string type, string name, Crc32 nameCrc, GameObjectType estimatedType, XmlLocationInfo location)
12+
internal GameObject(
13+
string type,
14+
string name,
15+
Crc32 nameCrc,
16+
int index,
17+
GameObjectType estimatedType,
18+
XmlLocationInfo location)
1319
: base(name, nameCrc, location)
1420
{
21+
if (index < 0)
22+
throw new ArgumentOutOfRangeException(nameof(index), "Index must be greater than 0.");
23+
Index = index;
24+
Id = (int)nameCrc;
1525
Type = type ?? throw new ArgumentNullException(nameof(type));
1626
EstimatedType = estimatedType;
1727
LandTerrainModelMapping = new ReadOnlyDictionary<string, string>(InternalLandTerrainModelMapping);
1828
}
1929

30+
internal int Id { get; }
31+
32+
public int Index { get; }
33+
2034
public string Type { get; }
2135

36+
public string VariantOfExistingTypeName { get; internal set; }
37+
38+
public bool IsLoadingComplete { get; internal set; }
39+
2240
public GameObjectType EstimatedType { get; }
2341

2442
public string? GalacticModel { get; internal set; }
@@ -81,4 +99,14 @@ private static void AddNotEmpty(ISet<string> set, string? value, Predicate<strin
8199
if (predicate is null || predicate(value))
82100
set.Add(value);
83101
}
102+
103+
public void PostLoadFixup()
104+
{
105+
// TODO:
106+
// MaxSpeed *= 1.0;
107+
// MaxThrust *= 1.0;
108+
109+
// The engine loads references for scripts, images, hardpoints, etc.,
110+
// but we don't do that here.
111+
}
84112
}

src/PetroglyphTools/PG.StarWarsGame.Engine/GameObjects/GameObjectTypeGameManager.cs

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,95 @@
22
using PG.StarWarsGame.Engine.ErrorReporting;
33
using PG.StarWarsGame.Engine.IO.Repositories;
44
using PG.StarWarsGame.Engine.Xml;
5+
using PG.StarWarsGame.Engine.Xml.Parsers;
56
using System;
7+
using System.Collections.Generic;
8+
using System.Collections.ObjectModel;
69
using System.Linq;
710
using System.Threading;
811
using System.Threading.Tasks;
912

1013
namespace PG.StarWarsGame.Engine.GameObjects;
1114

12-
internal class GameObjectTypeGameManager(GameRepository repository, GameEngineErrorReporterWrapper errorReporter, IServiceProvider serviceProvider)
13-
: GameManagerBase<GameObject>(repository, errorReporter, serviceProvider), IGameObjectTypeGameManager
15+
internal class GameObjectTypeGameManager : GameManagerBase<GameObject>, IGameObjectTypeGameManager
1416
{
17+
private readonly List<GameObject> _gameObjects;
18+
19+
public GameObjectTypeGameManager(
20+
GameRepository repository,
21+
GameEngineErrorReporterWrapper errorReporter,
22+
IServiceProvider serviceProvider)
23+
: base(repository, errorReporter, serviceProvider)
24+
{
25+
_gameObjects = new List<GameObject>();
26+
GameObjects = new ReadOnlyCollection<GameObject>(_gameObjects);
27+
}
28+
29+
30+
public IReadOnlyList<GameObject> GameObjects
31+
{
32+
get
33+
{
34+
ThrowIfNotInitialized();
35+
return field;
36+
}
37+
}
38+
1539
protected override async Task InitializeCoreAsync(CancellationToken token)
1640
{
1741
Logger?.LogInformation("Parsing GameObjects...");
1842
await Task.Run(ParseGameObjectDatabases, token);
1943
}
2044

21-
2245
private void ParseGameObjectDatabases()
2346
{
24-
var parser = new PetroglyphStarWarsGameXmlParser(GameRepository,
47+
var gameParser = new PetroglyphStarWarsGameXmlParser(GameRepository,
2548
new PetroglyphStarWarsGameXmlParseSettings
2649
{
2750
GameManager = ToString(),
2851
InvalidFilesListXmlFailsInitialization = true,
2952
InvalidObjectXmlFailsInitialization = false,
3053
}, ServiceProvider, ErrorReporter);
3154

32-
var xmlFileList = parser.ParseFileList(@"DATA\XML\GAMEOBJECTFILES.XML").Files
33-
.Select(x =>
34-
{
35-
var filePath = FileSystem.Path.Combine(@".\DATA\XML\", x);
36-
VerifyFilePathLength(filePath);
37-
return filePath;
38-
}).ToList();
39-
55+
var xmlFileList = gameParser.ParseFileList(@"DATA\XML\GAMEOBJECTFILES.XML").Files
56+
.Select(x => FileSystem.Path.Combine(@".\DATA\XML\", x))
57+
.Where(VerifyFilePathLength)
58+
.ToList();
4059

41-
//var gameObjectFileParser = new GameObjectFileParser(serviceProvider, errorReporter);
60+
var gameObjectFileParser = new GameObjectFileParser(ServiceProvider, ErrorReporter);
4261

4362
var allLoaded = false;
63+
64+
// This also acts a guard against infinite loops in case of unexpected circular dependencies or
65+
// when a unit declares itself as its own <Variant_Of_Existing_Type>
4466
for (var passNumber = 0; !allLoaded && passNumber < 10; passNumber++)
4567
{
68+
Logger?.LogDebug("***** Parsing game object types - pass {PassNumber} *****", passNumber);
69+
70+
if (passNumber != 0)
71+
gameObjectFileParser.OverlayLoad = true;
72+
4673
foreach (var gameObjectXmlFile in xmlFileList)
4774
{
4875
if (passNumber == 0)
4976
{
50-
//ParseSingleGameObjectFile(gameObjectXmlFile, parser, gameObjectFileParser);
77+
try
78+
{
79+
gameObjectFileParser.GameObjectParsed += OnGameObjectParsed;
80+
ParseSingleGameObjectFile(gameObjectXmlFile, gameParser, gameObjectFileParser);
81+
}
82+
finally
83+
{
84+
gameObjectFileParser.GameObjectParsed -= OnGameObjectParsed;
85+
}
5186
}
5287
else
5388
{
89+
foreach (var gameObject in _gameObjects)
90+
{
91+
if (!gameObject.IsLoadingComplete && gameObject.Location.XmlFile == gameObjectXmlFile)
92+
ParseSingleGameObjectFile(gameObjectXmlFile, gameParser, gameObjectFileParser);
93+
}
5494
}
5595
}
5696

@@ -65,12 +105,38 @@ private void ParseGameObjectDatabases()
65105
}
66106
}
67107

68-
//private void ParseSingleGameObjectFile(string file, EngineXmlParser engineParser, GameObjectFileParser gameObjectFileParser)
69-
//{
70-
// engineParser.ParseEntriesFromContainerFile(gameObjectFileParser, file, NamedEntries);
71-
//}
108+
private void OnGameObjectParsed(object sender, GameObjectParsedEventArgs e)
109+
{
110+
if (!e.Unique)
111+
{
112+
var entries = NamedEntries.GetValues(e.ParsedElement.Crc32)
113+
.Select(x => x.Name);
114+
ErrorReporter.Assert(EngineAssert.Create(
115+
EngineAssertKind.DuplicateEntry,
116+
e.ParsedElement.Crc32, entries,
117+
$"Error: Game object type {e.ParsedElement.Name} is defined multiple times."));
118+
}
72119

73-
private void VerifyFilePathLength(string filePath)
120+
if (NamedEntries.ValueCount >= 0x10000)
121+
{
122+
ErrorReporter.Assert(
123+
EngineAssert.Create(
124+
EngineAssertKind.ValueOutOfRange,
125+
NamedEntries.ValueCount,
126+
[ToString()],
127+
"Too many game object types defined."));
128+
}
129+
}
130+
131+
private void ParseSingleGameObjectFile(
132+
string file,
133+
PetroglyphStarWarsGameXmlParser gameParser,
134+
GameObjectFileParser gameObjectFileParser)
135+
{
136+
gameParser.ParseObjectsFromContainerFile(file, gameObjectFileParser, NamedEntries);
137+
}
138+
139+
private bool VerifyFilePathLength(string filePath)
74140
{
75141
if (filePath.Length > PGConstants.MaxGameObjectDatabaseFileName)
76142
{
@@ -81,6 +147,9 @@ private void VerifyFilePathLength(string filePath)
81147
GameManager = ToString(),
82148
Message = $"Game object file '{filePath}' is longer than {PGConstants.MaxGameObjectDatabaseFileName} characters."
83149
});
150+
return false;
84151
}
152+
153+
return true;
85154
}
86155
}
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
namespace PG.StarWarsGame.Engine.GameObjects;
1+
using System.Collections.Generic;
22

3-
public interface IGameObjectTypeGameManager : IGameManager<GameObject>;
3+
namespace PG.StarWarsGame.Engine.GameObjects;
4+
5+
public interface IGameObjectTypeGameManager : IGameManager<GameObject>
6+
{
7+
// List represent XML load order
8+
IReadOnlyList<GameObject> GameObjects { get; }
9+
}

src/PetroglyphTools/PG.StarWarsGame.Engine/Xml/Parsers/NamedObjects/CommandBarComponentParser.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1-
using System;
2-
using System.Collections.ObjectModel;
3-
using System.Xml.Linq;
1+
using AnakinRaW.CommonUtilities.Collections;
42
using PG.StarWarsGame.Engine.CommandBar.Xml;
53
using PG.StarWarsGame.Files.XML;
64
using PG.StarWarsGame.Files.XML.ErrorHandling;
75
using PG.StarWarsGame.Files.XML.Parsers;
6+
using System;
7+
using System.Collections.ObjectModel;
8+
using System.Xml.Linq;
89
using Crc32 = PG.Commons.Hashing.Crc32;
910

1011
namespace PG.StarWarsGame.Engine.Xml.Parsers;
1112

1213
internal class CommandBarComponentParser(IServiceProvider serviceProvider, IXmlParserErrorReporter? errorReporter = null)
1314
: NamedXmlObjectParser<CommandBarComponentData>(serviceProvider, new CommandBarComponentDataXmlTagMapper(serviceProvider), errorReporter)
1415
{
15-
protected override CommandBarComponentData CreateXmlObject(string name, Crc32 nameCrc, XElement element, XmlLocationInfo location)
16+
protected override CommandBarComponentData CreateXmlObject(
17+
string name,
18+
Crc32 nameCrc,
19+
XElement element,
20+
IReadOnlyFrugalValueListDictionary<Crc32, CommandBarComponentData> parsedEntries,
21+
XmlLocationInfo location)
1622
{
1723
return new CommandBarComponentData(name, nameCrc, location);
1824
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using AnakinRaW.CommonUtilities.Collections;
2+
using PG.Commons.Hashing;
3+
using PG.StarWarsGame.Engine.ErrorReporting;
4+
using PG.StarWarsGame.Engine.GameObjects;
5+
using PG.StarWarsGame.Files.XML.Parsers;
6+
using System;
7+
using System.IO;
8+
9+
namespace PG.StarWarsGame.Engine.Xml.Parsers;
10+
11+
internal sealed class GameObjectParsedEventArgs : EventArgs
12+
{
13+
public bool Unique { get; }
14+
15+
public GameObject ParsedElement { get; }
16+
17+
internal GameObjectParsedEventArgs(GameObject parsedElement, bool unique)
18+
{
19+
Unique = unique;
20+
ParsedElement = parsedElement ?? throw new ArgumentNullException(nameof(parsedElement));
21+
}
22+
}
23+
24+
25+
internal class GameObjectFileParser(IServiceProvider serviceProvider, IGameEngineErrorReporter? errorReporter)
26+
: PetroglyphXmlFileParserBase(serviceProvider, errorReporter), IXmlContainerFileParser<GameObject>
27+
{
28+
public event EventHandler<GameObjectParsedEventArgs>? GameObjectParsed;
29+
30+
private readonly GameObjectParser _gameObjectParser = new(serviceProvider, errorReporter);
31+
32+
public NamedXmlObjectParser<GameObject> ElementParser => _gameObjectParser;
33+
34+
public bool OverlayLoad
35+
{
36+
get;
37+
set
38+
{
39+
field = value;
40+
_gameObjectParser.OverlayLoad = value;
41+
}
42+
}
43+
44+
public void ParseFile(Stream xmlStream, IFrugalValueListDictionary<Crc32, GameObject> parsedEntries)
45+
{
46+
var root = GetRootElement(xmlStream, out _);
47+
foreach (var xElement in root.Elements())
48+
{
49+
var parsedElement = _gameObjectParser.Parse(xElement, parsedEntries, out var entryCrc);
50+
if (!OverlayLoad)
51+
{
52+
parsedEntries.Add(entryCrc, parsedElement);
53+
GameObjectParsed?.Invoke(this, new GameObjectParsedEventArgs(parsedElement, true));
54+
}
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)