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

zorbathut / dec / 11709205202

06 Nov 2024 05:57PM UTC coverage: 90.198% (-0.4%) from 90.637%
11709205202

push

github

zorbathut
Added the ability to reference class objects contained within Decs.

4748 of 5264 relevant lines covered (90.2%)

194623.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.87
/src/ParserModular.cs
1
using System;
2
using System.Collections.Generic;
3
using System.IO;
4
using System.Linq;
5
using System.Reflection;
6
using System.Runtime.CompilerServices;
7

8
namespace Dec
9
{
10
    /// <summary>
11
    /// Handles all parsing and initialization of dec structures.
12
    ///
13
    /// Intended for moddable games; use Parser for non-moddable or prototype games.
14
    /// </summary>
15
    public class ParserModular
16
    {
17
        public class Module : IParser
18
        {
19
            internal string name;
20
            internal readonly List<ReaderFileDec> readers = new List<ReaderFileDec>();
13,675✔
21
            internal Recorder.IUserSettings userSettings;
22

23
            internal Module(Recorder.IUserSettings userSettings)
13,675✔
24
            {
13,675✔
25
                this.userSettings = userSettings;
13,675✔
26
            }
13,675✔
27

28
            /// <summary>
29
            /// Pass a directory in for recursive processing.
30
            /// </summary>
31
            /// <remarks>
32
            /// This function will ignore dot-prefixed directory names and files, which are common for development tools to create.
33
            /// </remarks>
34
            /// <param name="directory">The directory to look for files in.</param>
35
            public void AddDirectory(string directory)
36
            {
185✔
37
                if (directory.IsNullOrEmpty())
185✔
38
                {
40✔
39
                    Dbg.Err("Attempted to add a null or empty directory to the parser; this is probably wrong");
40✔
40
                    return;
40✔
41
                }
42

43
                foreach (var file in Directory.GetFiles(directory, "*.xml"))
965✔
44
                {
265✔
45
                    if (!System.IO.Path.GetFileName(file).StartsWith("."))
265✔
46
                    {
205✔
47
                        AddFile(Parser.FileType.Xml, file);
205✔
48
                    }
205✔
49
                }
265✔
50

51
                foreach (var subdir in Directory.GetDirectories(directory))
620✔
52
                {
100✔
53
                    if (!System.IO.Path.GetFileName(subdir).StartsWith("."))
100✔
54
                    {
40✔
55
                        AddDirectory(subdir);
40✔
56
                    }
40✔
57
                }
100✔
58
            }
185✔
59

60
            /// <summary>
61
            /// Pass a file in for processing.
62
            /// </summary>
63
            /// <param name="stringName">A human-readable identifier useful for debugging. Generally, the name of the file that the string was read from. Not required; will be derived from filename automatically.</param>
64
            public void AddFile(Parser.FileType fileType, string filename, string identifier = null)
65
            {
285✔
66
                if (filename.IsNullOrEmpty())
285✔
67
                {
40✔
68
                    Dbg.Err("Attempted to add a null or empty filename to the parser; this is probably wrong");
40✔
69
                    return;
40✔
70
                }
71

72
                if (identifier == null)
245✔
73
                {
245✔
74
                    // This is imperfect, but good enough. People can pass their own identifier in if they want something clever.
75
                    identifier = System.IO.Path.GetFileName(filename);
245✔
76
                }
245✔
77

78
                using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
245✔
79
                {
245✔
80
                    AddStream(fileType, fs, identifier);
245✔
81
                }
245✔
82
            }
285✔
83

84
            /// <summary>
85
            /// Pass a stream in for processing.
86
            /// </summary>
87
            /// <param name="identifier">A human-readable identifier useful for debugging. Generally, the name of the file that the stream was built from. Not required; will be derived from filename automatically</param>
88
            public void AddStream(Parser.FileType fileType, Stream stream, string identifier = "(unnamed)")
89
            {
265✔
90
                if (stream == null)
265✔
91
                {
20✔
92
                    Dbg.Err("Attempted to add a null stream to the parser; this is probably wrong");
20✔
93
                    return;
20✔
94
                }
95

96
                using (var reader = new StreamReader(stream))
245✔
97
                {
245✔
98
                    AddTextReader(fileType, reader, identifier);
245✔
99
                }
245✔
100
            }
265✔
101

102
            /// <summary>
103
            /// Pass a string in for processing.
104
            /// </summary>
105
            /// <param name="identifier">A human-readable identifier useful for debugging. Generally, the name of the file that the string was built from. Not required, but helpful.</param>
106
            public void AddString(Parser.FileType fileType, string contents, string identifier = "(unnamed)")
107
            {
12,810✔
108
                if (contents.IsNullOrEmpty())
12,810✔
109
                {
60✔
110
                    Dbg.Err("Attempted to add a null or empty string to the parser; this is probably wrong");
60✔
111
                    return;
60✔
112
                }
113

114
                // This is a really easy error to make; we might as well handle it.
115
                if (contents.EndsWith(".xml"))
12,750✔
116
                {
20✔
117
                    Dbg.Err($"It looks like you've passed the filename `{contents}` to AddString instead of the actual XML file. Either use AddFile() or pass the file contents in.");
20✔
118
                }
20✔
119

120
                using (var reader = new StringReader(contents))
12,750✔
121
                {
12,750✔
122
                    AddTextReader(fileType, reader, identifier);
12,750✔
123
                }
12,750✔
124
            }
12,810✔
125

126
            private void AddTextReader(Parser.FileType fileType, TextReader textReader, string identifier = "(unnamed)")
127
            {
12,995✔
128
                if (s_Status != Status.Accumulating)
12,995✔
129
                {
5✔
130
                    Dbg.Err($"Adding data while while the world is in {s_Status} state; should be {Status.Accumulating} state");
5✔
131
                }
5✔
132

133
                string bakedIdentifier = (name == "core") ? identifier : $"{name}:{identifier}";
12,995✔
134

135
                ReaderFileDec reader;
136

137
                if (fileType == Parser.FileType.Xml)
12,995✔
138
                {
12,995✔
139
                    reader = ReaderFileDecXml.Create(textReader, bakedIdentifier, userSettings);
12,995✔
140
                }
12,995✔
141
                else
142
                {
×
143
                    Dbg.Err($"{bakedIdentifier}: Only XML files are supported at this time");
×
144
                    return;
×
145
                }
146

147
                if (reader != null)
12,995✔
148
                {
12,935✔
149
                    readers.Add(reader);
12,935✔
150
                }
12,935✔
151

152
                // otherwise, error has already been printed
153
            }
12,995✔
154
        }
155

156
        // Global status
157
        private enum Status
158
        {
159
            Uninitialized,
160
            Accumulating,
161
            Processing,
162
            Distributing,
163
            Finalizing,
164
            Finished,
165
        }
166
        private static Status s_Status = Status.Uninitialized;
167

168
        // Data stored from initialization parameters
169
        private List<Type> staticReferences = new List<Type>();
12,895✔
170
        private Recorder.IUserSettings userSettings;
171

172
        // Modules
173
        internal List<Module> modules = new List<Module>();
12,895✔
174

175
        // Used for static reference validation
176
        private static Action s_StaticReferenceHandler = null;
177

178
        /// <summary>
179
        /// Creates a Parser.
180
        /// </summary>
181
        public ParserModular(Recorder.IUserSettings userSettings = null)
12,895✔
182
        {
12,895✔
183
            this.userSettings = userSettings;
12,895✔
184

185
            if (s_Status != Status.Uninitialized)
12,895✔
186
            {
10✔
187
                Dbg.Err($"Parser created while the world is in {s_Status} state; should be {Status.Uninitialized} state");
10✔
188
            }
10✔
189
            s_Status = Status.Accumulating;
12,895✔
190

191
            bool unitTestMode = Config.TestParameters != null;
12,895✔
192

193
            {
12,895✔
194
                IEnumerable<Type> staticRefs;
195
                if (!unitTestMode)
12,895✔
196
                {
5✔
197
                    staticRefs = UtilReflection.GetAllUserTypes().Where(t => t.GetCustomAttribute<StaticReferencesAttribute>() != null);
6,800✔
198
                }
5✔
199
                else if (Config.TestParameters.explicitStaticRefs != null)
12,890✔
200
                {
535✔
201
                    staticRefs = Config.TestParameters.explicitStaticRefs;
535✔
202
                }
535✔
203
                else
204
                {
12,355✔
205
                    staticRefs = Enumerable.Empty<Type>();
12,355✔
206
                }
12,355✔
207

208
                foreach (var type in staticRefs)
39,445✔
209
                {
380✔
210
                    if (type.GetCustomAttribute<StaticReferencesAttribute>() == null)
380✔
211
                    {
30✔
212
                        Dbg.Err($"{type} is not tagged as StaticReferences");
30✔
213
                    }
30✔
214

215
                    if (!type.IsAbstract || !type.IsSealed)
380✔
216
                    {
30✔
217
                        Dbg.Err($"{type} is not static");
30✔
218
                    }
30✔
219

220
                    staticReferences.Add(type);
380✔
221
                }
380✔
222
            }
12,895✔
223

224
            Serialization.Initialize();
12,895✔
225
        }
12,895✔
226

227
        /// <summary>
228
        /// Creates and registers a new module with a given name.
229
        /// </summary>
230
        public Module CreateModule(string name)
231
        {
13,675✔
232
            var module = new Module(userSettings);
13,675✔
233
            module.name = name;
13,675✔
234

235
            if (modules.Any(mod => mod.name == name))
14,570✔
236
            {
20✔
237
                Dbg.Err($"Created duplicate module {name}");
20✔
238
            }
20✔
239

240
            modules.Add(module);
13,675✔
241
            return module;
13,675✔
242
        }
13,675✔
243

244
        /// <summary>
245
        /// Finish all parsing.
246
        /// </summary>
247
        /// <remarks>
248
        /// The `dependencies` parameter can be used to feed in dependencies for the PostLoad function.
249
        /// This is a placeholder and is probably going to be replaced at some point, though only with something more capable.
250
        /// </remarks>
251
        public void Finish()
252
        {
12,880✔
253
            using (var _ = new CultureInfoScope(Config.CultureInfo))
12,880✔
254
            {
12,880✔
255
                if (s_Status != Status.Accumulating)
12,880✔
256
                {
5✔
257
                    Dbg.Err($"Finishing while the world is in {s_Status} state; should be {Status.Accumulating} state");
5✔
258
                }
5✔
259
                s_Status = Status.Processing;
12,880✔
260

261
                var readerContext = new ReaderGlobals() { allowReflection = true, allowRefs = false, decPathLookup = Database.DecPathLookup };
12,880✔
262

263
                // Collate reader decs
264
                var registeredDecs = new Dictionary<(Type, string), List<ReaderFileDec.ReaderDec>>();
12,880✔
265
                foreach (var module in modules)
65,960✔
266
                {
13,660✔
267
                    var seenDecs = new Dictionary<(Type, string), Context>();
13,660✔
268
                    foreach (var reader in module.readers)
66,840✔
269
                    {
12,930✔
270
                        foreach (var readerDec in reader.ParseDecs())
132,410✔
271
                        {
46,810✔
272
                            var id = (readerDec.type.GetDecRootType(), readerDec.name);
46,810✔
273

274
                            if (seenDecs.TryGetValue(id, out var collidingDec))
46,810✔
275
                            {
100✔
276
                                Dbg.Err($"{collidingDec} / {readerDec.context}: Dec [{id.Item1}:{id.name}] defined twice");
100✔
277

278
                                // If the already-parsed one is abstract, we throw it away and go with the non-abstract one, because it's arguably more likely to be the one the user wants.
279
                                if (!(registeredDecs[id].Select(dec => dec.abstrct).LastOrDefault(abstrct => abstrct.HasValue) ?? false))
300✔
280
                                {
60✔
281
                                    continue;
60✔
282
                                }
283

284
                                registeredDecs.Remove(id);
40✔
285
                            }
40✔
286

287
                            seenDecs[id] = readerDec.context;
46,750✔
288

289
                            if (!registeredDecs.TryGetValue(id, out var list))
46,750✔
290
                            {
45,630✔
291
                                list = new List<ReaderFileDec.ReaderDec>();
45,630✔
292
                                registeredDecs[id] = list;
45,630✔
293
                            }
45,630✔
294
                            list.Add(readerDec);
46,750✔
295
                        }
46,750✔
296
                    }
12,930✔
297
                }
13,660✔
298

299
                // Compile reader decs into Order assemblies
300
                var registeredDecOrders = new Dictionary<(Type, string), List<ReaderFileDec.ReaderDec>>();
12,880✔
301
                foreach (var seenDec in registeredDecs)
129,820✔
302
                {
45,590✔
303
                    var orders = Serialization.CompileDecOrders(seenDec.Value);
45,590✔
304

305
                    // If we have no orders, this probably ended up deleted.
306
                    if (orders.Count > 0)
45,590✔
307
                    {
45,250✔
308
                        registeredDecOrders[seenDec.Key] = orders;
45,250✔
309
                    }
45,250✔
310
                }
45,590✔
311

312
                // Instantiate all decs
313
                var toParseDecOrders = new Dictionary<(Type, string), List<ReaderFileDec.ReaderDec>>();
12,880✔
314
                foreach (var (id, orders) in registeredDecOrders)
129,140✔
315
                {
45,250✔
316
                    // It's currently sort of unclear how we should be deriving the type.
317
                    // I'm choosing, for now, to go with "the most derived type in the list, assuming all types are in the same inheritance sequence".
318
                    // Thankfully this isn't too hard to do.
319
                    var typeDeterminor = orders[0];
45,250✔
320
                    bool abstrct = typeDeterminor.abstrct ?? false;
45,250✔
321

322
                    foreach (var order in orders.Skip(1))
136,680✔
323
                    {
480✔
324
                        // Since we're iterating over this anyway, yank the abstract updates out as we go.
325
                        if (order.abstrct.HasValue)
480✔
326
                        {
80✔
327
                            abstrct = order.abstrct.Value;
80✔
328
                        }
80✔
329

330
                        if (order.type == typeDeterminor.type)
480✔
331
                        {
320✔
332
                            // fast case
333
                            continue;
320✔
334
                        }
335

336
                        if (order.type.IsSubclassOf(typeDeterminor.type))
160✔
337
                        {
100✔
338
                            typeDeterminor = order;
100✔
339
                            continue;
100✔
340
                        }
341

342
                        if (typeDeterminor.type.IsSubclassOf(order.type))
60✔
343
                        {
40✔
344
                            continue;
40✔
345
                        }
346

347
                        // oops, they're not subclasses of each other
348
                        Dbg.Err($"{typeDeterminor.context} / {order.context}: Modded dec with tree-identifier [{id.Item1}:{id.Item2}] has conflicting types without a simple subclass relationship ({typeDeterminor.type}/{order.type}); deferring to {order.type}");
20✔
349
                        typeDeterminor = order;
20✔
350
                    }
20✔
351

352
                    // We don't actually want an instance of this.
353
                    if (abstrct)
45,250✔
354
                    {
755✔
355
                        continue;
755✔
356
                    }
357

358
                    // Not an abstract dec instance, so create our instance
359
                    // also, this invocation of NodeFactory is unnecessarily slow and should be fixed at some point
360
                    var decInstance = (Dec)typeDeterminor.type.CreateInstanceSafe("dec", typeDeterminor.nodeFactory(new PathDec(id.Item1, id.Item2)));
44,495✔
361

362
                    // Error reporting happens within CreateInstanceSafe; if we get null out, we just need to clean up elegantly
363
                    if (decInstance != null)
44,495✔
364
                    {
44,455✔
365
                        decInstance.DecName = id.Item2;
44,455✔
366

367
                        Database.Register(decInstance);
44,455✔
368

369
                        // create a new map that filters out abstract objects and failed instantiations
370
                        toParseDecOrders.Add(id, orders);
44,455✔
371
                    }
44,455✔
372
                }
44,495✔
373

374
                // It's time to actually pull references out of the database, so let's stop spitting out empty warnings.
375
                Database.SuppressEmptyWarning();
12,880✔
376

377
                foreach (var (id, orders) in toParseDecOrders)
127,550✔
378
                {
44,455✔
379
                    // Accumulate our orders
380
                    var completeOrders = orders;
44,455✔
381

382
                    var currentOrder = orders;
44,455✔
383
                    while (true)
45,490✔
384
                    {
45,490✔
385
                        // See if we have a parent
386
                        var decWithParent = currentOrder.Where(dec => dec.parent != null).LastOrDefault();
91,640✔
387
                        if (decWithParent.parent == null || decWithParent.parent == "")
45,490✔
388
                        {
44,395✔
389
                            break;
44,395✔
390
                        }
391

392
                        var parentId = (id.Item1, decWithParent.parent);
1,095✔
393
                        if (!registeredDecOrders.TryGetValue(parentId, out var parentDec))
1,095✔
394
                        {
60✔
395
                            Dbg.Err($"{decWithParent.context}: Dec [{decWithParent.type}:{id.Item2}] is attempting to use parent `[{parentId.Item1}:{parentId.parent}]`, but no such dec exists");
60✔
396
                            // guess we'll just try to build it from nothing
397
                            break;
60✔
398
                        }
399

400
                        completeOrders.InsertRange(0, parentDec);
1,035✔
401

402
                        currentOrder = parentDec;
1,035✔
403
                    }
1,035✔
404

405
                    var generatedOrders = completeOrders.Select(order => order.nodeFactory(new PathDec(id.Item1, id.Item2))).ToList();
90,605✔
406

407
                    var targetDec = Database.Get(id.Item1, id.Item2);
44,455✔
408
                    Serialization.ParseElement(generatedOrders, targetDec.GetType(), targetDec, readerContext, new Recorder.Settings(), isRootDec: true, ordersOverride: generatedOrders.Select(order => (Serialization.ParseCommand.Patch, node: order)).ToList());
90,605✔
409
                }
44,455✔
410

411
                if (s_Status != Status.Processing)
12,880✔
412
                {
×
413
                    Dbg.Err($"Distributing while the world is in {s_Status} state; should be {Status.Processing} state");
×
414
                }
×
415
                s_Status = Status.Distributing;
12,880✔
416

417
                foreach (var stat in staticReferences)
39,400✔
418
                {
380✔
419
                    if (!StaticReferencesAttribute.StaticReferencesFilled.Contains(stat))
380✔
420
                    {
100✔
421
                        s_StaticReferenceHandler = () =>
100✔
422
                        {
160✔
423
                            s_StaticReferenceHandler = null;
160✔
424
                            StaticReferencesAttribute.StaticReferencesFilled.Add(stat);
160✔
425
                        };
160✔
426
                    }
100✔
427

428
                    bool touched = false;
380✔
429
                    foreach (var field in stat.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static))
2,600✔
430
                    {
730✔
431
                        var dec = Database.Get(field.FieldType, field.Name);
730✔
432
                        if (dec == null)
730✔
433
                        {
30✔
434
                            Dbg.Err($"Static reference class {stat} has member `{field.FieldType} {field.Name}` that does not correspond to any loaded Dec");
30✔
435
                            field.SetValue(null, null); // this is unnecessary, but it does kick the static constructor just in case we wouldn't do it otherwise
30✔
436
                        }
30✔
437
                        else if (!field.FieldType.IsAssignableFrom(dec.GetType()))
700✔
438
                        {
30✔
439
                            Dbg.Err($"Static reference class {stat} has member `{field.FieldType} {field.Name}` that is not compatible with actual {dec.GetType()} {dec}");
30✔
440
                            field.SetValue(null, null); // this is unnecessary, but it does kick the static constructor just in case we wouldn't do it otherwise
30✔
441
                        }
30✔
442
                        else
443
                        {
670✔
444
                            field.SetValue(null, dec);
670✔
445
                        }
670✔
446

447
                        touched = true;
730✔
448
                    }
730✔
449

450
                    if (s_StaticReferenceHandler != null)
380✔
451
                    {
40✔
452
                        if (touched)
40✔
453
                        {
35✔
454
                            // Otherwise we shouldn't even expect this to have been registered, but at least there's literally no fields in it so it doesn't matter
455
                            Dbg.Err($"Failed to properly register {stat}; you may be missing a call to Dec.StaticReferencesAttribute.Initialized() in its static constructor, or the class may already have been initialized elsewhere (this should have thrown an error)");
35✔
456
                        }
35✔
457

458
                        s_StaticReferenceHandler = null;
40✔
459
                    }
40✔
460
                }
380✔
461

462
                // Invert the dec path lookup tables
463
                foreach (var (decObject, decPath) in Database.DecPathLookup)
256,007✔
464
                {
108,691✔
465
                    var pathSerialized = decPath.Serialize();
108,691✔
466

467
                    if (Database.DecPathLookupReverse.ContainsKey(pathSerialized))
108,691✔
468
                    {
27,740✔
469
                        Database.DecPathLookupConflicts.Add(pathSerialized);    // this is currently considered OK because our path system is highly incomplete
27,740✔
470
                    }
27,740✔
471

472
                    if (!decPath.IsValidForWriting())
108,691✔
473
                    {
49,915✔
474
                        Database.DecPathLookupInvalid.Add(pathSerialized);
49,915✔
475
                    }
49,915✔
476

477
                    Database.DecPathLookupReverse[pathSerialized] = decObject;
108,691✔
478
                }
108,691✔
479

480
                if (s_Status != Status.Distributing)
12,880✔
481
                {
×
482
                    Dbg.Err($"Finalizing while the world is in {s_Status} state; should be {Status.Distributing} state");
×
483
                }
×
484
                s_Status = Status.Finalizing;
12,880✔
485

486
                // figure out our config/postload order; first we just run over all the Decs and collate classes
487
                var decTypes = new Dictionary<Type, List<Dec>>();
12,880✔
488
                foreach (var dec in Database.List)
127,550✔
489
                {
44,455✔
490
                    var list = decTypes.TryGetValue(dec.GetType());
44,455✔
491
                    if (list == null)
44,455✔
492
                    {
13,150✔
493
                        list = new List<Dec>();
13,150✔
494
                        decTypes[dec.GetType()] = list;
13,150✔
495
                    }
13,150✔
496

497
                    list.Add(dec);
44,455✔
498
                }
44,455✔
499

500
                List<Dag<Type>.Dependency> postLoadDependencies = new List<Dag<Type>.Dependency>();
12,880✔
501
                foreach (var type in decTypes)
64,940✔
502
                {
13,150✔
503
                    foreach (var rsaa in type.Key.GetCustomAttributes<SetupDependsOnAttribute>())
39,575✔
504
                    {
70✔
505
                        var dependsOn = rsaa.Type;
70✔
506

507
                        // make sure it inherits from Dec.Dec
508
                        if (!dependsOn.IsSubclassOf(typeof(Dec)))
70✔
509
                        {
×
510
                            Dbg.Err($"{type.Key} has a SetupDependsOnAttribute on {dependsOn}, but {dependsOn} is not a Dec type");
×
511
                            continue;
×
512
                        }
513

514
                        if (!decTypes.ContainsKey(rsaa.Type))
70✔
515
                        {
5✔
516
                            Dbg.Err($"{type.Key} has a SetupDependsOnAttribute on {dependsOn}, but {dependsOn} is not a known Dec type; this might result in weird behavior, either create an instance of {dependsOn} or pester ZorbaTHut on Discord if you need this fixed");
5✔
517
                            continue;
5✔
518
                        }
519

520
                        postLoadDependencies.Add(new Dag<Type>.Dependency { before = rsaa.Type, after = type.Key });
65✔
521
                    }
65✔
522
                }
13,150✔
523

524
                var postprocessOrder = Dag<Type>.CalculateOrder(decTypes.Keys, postLoadDependencies, t => t.Name);
26,030✔
525

526
                foreach (var type in postprocessOrder)
64,940✔
527
                {
13,150✔
528
                    foreach (var dec in decTypes[type])
128,360✔
529
                    {
44,455✔
530
                        try
531
                        {
44,455✔
532
                            dec.ConfigErrors(err => Dbg.Err($"{dec}: {err}"));
44,505✔
533
                        }
44,395✔
534
                        catch (Exception e)
60✔
535
                        {
60✔
536
                            Dbg.Ex(e);
60✔
537
                        }
60✔
538

539
                        try
540
                        {
44,455✔
541
                            dec.PostLoad(err => Dbg.Err($"{dec}: {err}"));
44,505✔
542
                        }
44,395✔
543
                        catch (Exception e)
60✔
544
                        {
60✔
545
                            Dbg.Ex(e);
60✔
546
                        }
60✔
547
                    }
44,455✔
548
                }
13,150✔
549

550
                if (s_Status != Status.Finalizing)
12,880✔
551
                {
×
552
                    Dbg.Err($"Completing while the world is in {s_Status} state; should be {Status.Finalizing} state");
×
553
                }
×
554
                s_Status = Status.Finished;
12,880✔
555
            }
12,880✔
556
        }
12,880✔
557

558
        internal static void Clear()
559
        {
27,285✔
560
            if (s_Status != Status.Finished && s_Status != Status.Uninitialized)
27,285✔
561
            {
15✔
562
                Dbg.Err($"Clearing while the world is in {s_Status} state; should be {Status.Uninitialized} state or {Status.Finished} state");
15✔
563
            }
15✔
564
            s_Status = Status.Uninitialized;
27,285✔
565
        }
27,285✔
566

567
        [MethodImpl(MethodImplOptions.NoInlining)]
568
        internal static void StaticReferencesInitialized()
569
        {
80✔
570
            if (s_StaticReferenceHandler != null)
80✔
571
            {
60✔
572
                s_StaticReferenceHandler();
60✔
573
                return;
60✔
574
            }
575

576
            Dbg.Err($"Initializing static reference class at an inappropriate time. Either you forgot to add [StaticReferences] to the class, or you accessed it before it was ready.");
20✔
577
        }
80✔
578
    }
579
}
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