@@ -15,19 +15,45 @@ namespace Yafc.Parser;
1515internal partial class FactorioDataDeserializer {
1616 private static readonly ILogger logger = Logging . GetLogger < FactorioDataDeserializer > ( ) ;
1717 private LuaTable raw = null ! ; // null-forgiving: Initialized at the beginning of LoadData.
18- private bool GetRef < T > ( LuaTable table , string key , [ NotNullWhen ( true ) ] out T ? result ) where T : FactorioObject , new ( ) {
18+
19+ /// <summary>
20+ /// Gets or creates an object with the specified type (or a derived type), based on the specified value in the lua table. If the specified key
21+ /// does not exist, this method does not get or create an object and returns <see langword="false"/>.
22+ /// </summary>
23+ /// <typeparam name="TReturn">The (compile-time) type of the return value.</typeparam>
24+ /// <param name="table">The <see cref="LuaTable"/> to read to get the object's name.</param>
25+ /// <param name="key">The table key to read to get the object's name.</param>
26+ /// <param name="result">When successful, the new or pre-existing object described by <typeparamref name="TReturn"/> and
27+ /// <c><paramref name="table"/>[<paramref name="key"/>]</c>. Otherwise, <see langword="null"/>.</param>
28+ /// <returns><see langword="true"/>, if the specified key was present in the table, or <see langword="false"/> otherwise.</returns>
29+ /// <exception cref="ArgumentException">Thrown if <c><paramref name="table"/>[<paramref name="key"/>]</c> does not describe an object in
30+ /// <c>data.raw</c> that can be loaded as a <typeparamref name="TReturn"/>.</exception>
31+ /// <seealso cref="GetObject{TReturn}(string)"/>
32+ private bool GetRef < TReturn > ( LuaTable table , string key , [ NotNullWhen ( true ) ] out TReturn ? result ) where TReturn : FactorioObject , new ( ) {
1933 result = null ;
2034 if ( ! table . Get ( key , out string ? name ) ) {
2135 return false ;
2236 }
2337
24- result = GetObject < T > ( name ) ;
38+ result = GetObject < TReturn > ( name ) ;
2539
2640 return true ;
2741 }
2842
29- private T ? GetRef < T > ( LuaTable table , string key ) where T : FactorioObject , new ( ) {
30- _ = GetRef < T > ( table , key , out var result ) ;
43+ /// <summary>
44+ /// Gets or creates an object with the specified type (or a derived type), based on the specified value in the lua table. If the specified key
45+ /// does not exist, this method does not get or create an object and returns <see langword="null"/>.
46+ /// </summary>
47+ /// <typeparam name="TReturn">The (compile-time) type of the return value.</typeparam>
48+ /// <param name="table">The <see cref="LuaTable"/> to read to get the object's name.</param>
49+ /// <param name="key">The table key to read to get the object's name.</param>
50+ /// <returns>If the specified key was present in the table, the new or pre-existing object described by <typeparamref name="TReturn"/> and
51+ /// <c><paramref name="table"/>[<paramref name="key"/>]</c>. Otherwise, <see langword="null"/>.</returns>
52+ /// <exception cref="ArgumentException">Thrown if <c><paramref name="table"/>[<paramref name="key"/>]</c> does not describe an object in
53+ /// <c>data.raw</c> that can be loaded as a <typeparamref name="TReturn"/>.</exception>
54+ /// <seealso cref="GetObject{TReturn}(string)"/>
55+ private TReturn ? GetRef < TReturn > ( LuaTable table , string key ) where TReturn : FactorioObject , new ( ) {
56+ _ = GetRef < TReturn > ( table , key , out var result ) ;
3157
3258 return result ;
3359 }
@@ -118,11 +144,9 @@ public Project LoadData(string projectPath, LuaTable data, LuaTable prototypes,
118144 raw = ( LuaTable ? ) data [ "raw" ] ?? throw new ArgumentException ( "Could not load data.raw from data argument" , nameof ( data ) ) ;
119145 LuaTable itemPrototypes = ( LuaTable ? ) prototypes ? [ "item" ] ?? throw new ArgumentException ( "Could not load prototypes.item from data argument" , nameof ( prototypes ) ) ;
120146
121- foreach ( object prototypeName in Item . ExplicitPrototypeLoadOrder . Intersect ( itemPrototypes . ObjectElements . Keys ) ) {
122- DeserializePrototypes ( raw , ( string ) prototypeName , DeserializeItem , progress , errorCollector ) ;
123- }
147+ LoadPrototypes ( raw , prototypes ) ;
124148
125- foreach ( object prototypeName in itemPrototypes . ObjectElements . Keys . Except ( Item . ExplicitPrototypeLoadOrder ) ) {
149+ foreach ( object prototypeName in itemPrototypes . ObjectElements . Keys ) {
126150 DeserializePrototypes ( raw , ( string ) prototypeName , DeserializeItem , progress , errorCollector ) ;
127151 }
128152
@@ -320,6 +344,37 @@ private unsafe Icon CreateIconFromSpec(Dictionary<(string mod, string path), Int
320344 return IconCollection . AddIcon ( targetSurface ) ;
321345 }
322346
347+ /// <summary>
348+ /// A lookup table from (prototype, name) (e.g. ("item", "speed-module")) to subtype (e.g. "module").
349+ /// This is used to determine the correct concrete type when constructing an item or entity by name.
350+ /// Most values are unused, but they're all stored for simplicity and future-proofing.
351+ /// </summary>
352+ private readonly Dictionary < ( string prototype , string name ) , string > prototypes = new ( ) {
353+ [ ( "item" , "science" ) ] = "item" ,
354+ [ ( "item" , "item-total-input" ) ] = "item" ,
355+ [ ( "item" , "item-total-output" ) ] = "item" ,
356+ } ;
357+
358+ /// <summary>
359+ /// Load all keys (object names) from <c>data.raw[<em>type</em>]</c>, for all <em>type</em>s. Create a lookup from
360+ /// (<em>prototype</em>, <em>name</em>) to <em>type</em>, where <c>defines.prototypes[<em>prototype</em>]</c> contains the key <em>type</em>.
361+ /// </summary>
362+ /// <param name="raw">The <c>data.raw</c> table.</param>
363+ /// <param name="prototypes">The <c>defines.prototypes</c> table.</param>
364+ /// <remarks>This stored data allows requests like "I need the 'cerys-radioactive-module-decayed' item" to correctly respond with a module,
365+ /// regardless of where the item name is first encountered.</remarks>
366+ private void LoadPrototypes ( LuaTable raw , LuaTable prototypes ) {
367+ foreach ( ( object p , object ? t ) in prototypes . ObjectElements ) {
368+ if ( p is string prototype && t is LuaTable types ) { // here, 'prototype' is item, entity, equipment, etc.
369+ foreach ( string type in types . ObjectElements . Keys . Cast < string > ( ) ) { // 'type' is module, accumulator, solar-panel-equipment, etc.
370+ foreach ( string name in raw . Get < LuaTable > ( type ) ? . ObjectElements . Keys . Cast < string > ( ) ?? [ ] ) {
371+ this . prototypes [ ( prototype , name ) ] = type ;
372+ }
373+ }
374+ }
375+ }
376+ }
377+
323378 private static void DeserializePrototypes ( LuaTable data , string type , Action < LuaTable , ErrorCollector > deserializer ,
324379 IProgress < ( string , string ) > progress , ErrorCollector errorCollector ) {
325380
@@ -391,7 +446,7 @@ private static EffectReceiver ParseEffectReceiver(LuaTable? table) {
391446
392447 private void DeserializeItem ( LuaTable table , ErrorCollector _1 ) {
393448 if ( table . Get ( "type" , "" ) == "module" && table . Get ( "effect" , out LuaTable ? moduleEffect ) ) {
394- Module module = GetObject < Item , Module > ( table ) ;
449+ Module module = GetObject < Module > ( table ) ;
395450 var effect = ParseEffect ( moduleEffect ) ;
396451 module . moduleSpecification = new ModuleSpecification {
397452 category = table . Get ( "category" , "" ) ,
@@ -403,7 +458,7 @@ private void DeserializeItem(LuaTable table, ErrorCollector _1) {
403458 } ;
404459 }
405460 else if ( table . Get ( "type" , "" ) == "ammo" && table [ "ammo_type" ] is LuaTable ammo_type ) {
406- Ammo ammo = GetObject < Item , Ammo > ( table ) ;
461+ Ammo ammo = GetObject < Ammo > ( table ) ;
407462 ammo_type . ReadObjectOrArray ( readAmmoType ) ;
408463
409464 if ( ammo_type [ "target_filter" ] is LuaTable targets ) {
0 commit comments