1
1
using System ;
2
2
using System . Collections . Concurrent ;
3
+ using System . Collections . Frozen ;
3
4
using System . Collections . Generic ;
4
5
using System . Collections . Immutable ;
5
6
using System . Linq ;
10
11
using Barotrauma . LuaCs . Events ;
11
12
using Barotrauma . LuaCs . Services . Processing ;
12
13
using Barotrauma . Networking ;
14
+ using Dynamitey . DynamicObjects ;
13
15
using FluentResults ;
14
16
using Microsoft . Xna . Framework ;
17
+ using OneOf ;
15
18
16
19
namespace Barotrauma . LuaCs . Services ;
17
20
18
- public partial class ConfigService : IConfigService , IEventAssemblyContextUnloading
21
+ public partial class ConfigService : IConfigService
19
22
{
20
23
//--- Internals
21
24
public ConfigService ( IConverterServiceAsync < IConfigProfileResourceInfo , IReadOnlyList < IConfigProfileInfo > > configProfileResourceConverter ,
@@ -34,6 +37,9 @@ public ConfigService(IConverterServiceAsync<IConfigProfileResourceInfo, IReadOnl
34
37
private readonly ConcurrentDictionary < ContentPackage , ConcurrentBag < ( ContentPackage Package , string ConfigName ) > > _packageConfigReverseLookup = new ( ) ;
35
38
private readonly ConcurrentDictionary < ( ContentPackage Package , string ProfileName ) , ImmutableArray < ( string ConfigName , OneOf . OneOf < string , XElement > Value ) > > _configProfiles = new ( ) ;
36
39
private readonly ConcurrentDictionary < ContentPackage , ConcurrentBag < ( ContentPackage Package , string ProfileName ) > > _packageProfilesReverseLookup = new ( ) ;
40
+ private readonly ConcurrentDictionary < string , ContentPackage > _packageNameMap = new ( ) ;
41
+
42
+ private readonly AsyncReaderWriterLock _disposeOpsLock = new ( ) ;
37
43
38
44
// converters
39
45
private readonly IConverterServiceAsync < IConfigResourceInfo , IReadOnlyList < IConfigInfo > > _configResourceConverter ;
@@ -44,15 +50,25 @@ public ConfigService(IConverterServiceAsync<IConfigProfileResourceInfo, IReadOnl
44
50
45
51
public void Dispose ( )
46
52
{
53
+ // stop all ops
54
+ using var lck = _disposeOpsLock . AcquireWriterLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
55
+ // set flag
47
56
ModUtils . Threading . SetBool ( ref _isDisposed , true ) ;
57
+
48
58
_configTypeInitializers . Clear ( ) ;
49
- throw new NotImplementedException ( ) ;
59
+ _configs . Clear ( ) ;
60
+ _configProfiles . Clear ( ) ;
61
+ _packageConfigReverseLookup . Clear ( ) ;
62
+ _packageNameMap . Clear ( ) ;
63
+ _packageProfilesReverseLookup . Clear ( ) ;
50
64
}
51
65
52
66
public FluentResults . Result Reset ( )
53
67
{
68
+
69
+ using var lck = _disposeOpsLock . AcquireWriterLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
54
70
_base . CheckDisposed ( ) ;
55
- throw new NotImplementedException ( ) ;
71
+
56
72
}
57
73
58
74
//--- API contracts
@@ -172,7 +188,9 @@ public void SetConfigList(string packageName, string configName, string value)
172
188
public void RegisterTypeInitializer < TData , TConfig > ( IConfigTypeInitializer < TConfig > initializer , bool replaceIfExists = false )
173
189
where TData : IConvertible , IEquatable < TData > where TConfig : IConfigBase
174
190
{
191
+ using var lck = _disposeOpsLock . AcquireReaderLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
175
192
_base . CheckDisposed ( ) ;
193
+
176
194
Type dataType = typeof ( TData ) ;
177
195
if ( _configTypeInitializers . ContainsKey ( dataType ) && ! replaceIfExists )
178
196
return ;
@@ -182,6 +200,8 @@ public void RegisterTypeInitializer<TData, TConfig>(IConfigTypeInitializer<TConf
182
200
private void AddConfigInstance ( ( ContentPackage Package , string ConfigName ) key , IConfigBase instance )
183
201
{
184
202
_configs [ key ] = instance ;
203
+ if ( ! _packageNameMap . ContainsKey ( key . Package . Name ) )
204
+ _packageNameMap [ key . Package . Name ] = key . Package ;
185
205
if ( ! _packageConfigReverseLookup . TryGetValue ( key . Package , out var list ) )
186
206
{
187
207
list = new ConcurrentBag < ( ContentPackage Package , string ConfigName ) > ( ) ;
@@ -193,6 +213,8 @@ private void AddConfigInstance((ContentPackage Package, string ConfigName) key,
193
213
private void AddProfileInstance ( ( ContentPackage Package , string ProfileName ) key , IConfigProfileInfo profile )
194
214
{
195
215
_configProfiles [ key ] = profile . ProfileValues . ToImmutableArray ( ) ;
216
+ if ( ! _packageNameMap . ContainsKey ( key . Package . Name ) )
217
+ _packageNameMap [ key . Package . Name ] = key . Package ;
196
218
if ( ! _packageProfilesReverseLookup . TryGetValue ( key . Package , out var list ) )
197
219
{
198
220
list = new ConcurrentBag < ( ContentPackage Package , string ProfileName ) > ( ) ;
@@ -203,8 +225,9 @@ private void AddProfileInstance((ContentPackage Package, string ProfileName) key
203
225
204
226
public async Task < FluentResults . Result > LoadConfigsAsync ( ImmutableArray < IConfigResourceInfo > configResources )
205
227
{
228
+ using var lck = await _disposeOpsLock . AcquireReaderLock ( ) ;
206
229
_base . CheckDisposed ( ) ;
207
-
230
+
208
231
if ( configResources . IsDefaultOrEmpty )
209
232
return FluentResults . Result . Fail ( $ "{ nameof ( LoadConfigsAsync ) } : Array is empty.") ;
210
233
@@ -247,8 +270,9 @@ private void AddProfileInstance((ContentPackage Package, string ProfileName) key
247
270
248
271
public async Task < FluentResults . Result > LoadConfigsProfilesAsync ( ImmutableArray < IConfigProfileResourceInfo > configProfileResources )
249
272
{
273
+ using var lck = await _disposeOpsLock . AcquireReaderLock ( ) ;
250
274
_base . CheckDisposed ( ) ;
251
-
275
+
252
276
if ( configProfileResources . IsDefaultOrEmpty )
253
277
return FluentResults . Result . Fail ( $ "{ nameof ( LoadConfigsProfilesAsync ) } : Array is empty.") ;
254
278
@@ -277,60 +301,153 @@ private void AddProfileInstance((ContentPackage Package, string ProfileName) key
277
301
return ret ;
278
302
}
279
303
280
- public Result < TConfig > AddConfig < T , TConfig > ( ContentPackage package , string name , T defaultValue , NetSync syncMode = NetSync . None ,
281
- ClientPermissions permissions = ClientPermissions . None ) where T : IConvertible , IEquatable < T > where TConfig : IConfigBase
304
+ public FluentResults . Result < TConfig > AddConfig < TConfig > ( IConfigInfo configInfo ) where TConfig : IConfigBase
282
305
{
283
- throw new NotImplementedException ( ) ;
306
+ using var lck = _disposeOpsLock . AcquireReaderLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
307
+ _base . CheckDisposed ( ) ;
308
+
309
+ if ( configInfo is null )
310
+ return FluentResults . Result . Fail ( $ "{ nameof ( AddConfig ) } : Config is null.") ;
311
+
312
+ if ( ! _configTypeInitializers . TryGetValue ( configInfo . DataType , out var initializer ) )
313
+ return FluentResults . Result . Fail ( $ "{ nameof ( AddConfig ) } : No type initializer for { configInfo . DataType } ") ;
314
+
315
+ var errList = new List < IError > ( ) ;
316
+
317
+ try
318
+ {
319
+ var cfg = initializer . Initialize ( configInfo ) ;
320
+ if ( cfg . Errors . Any ( ) )
321
+ errList . AddRange ( cfg . Errors ) ;
322
+ if ( cfg . IsFailed || cfg . Value is null )
323
+ return FluentResults . Result . Fail ( $ "Failed to initialize { configInfo . DataType } ") . WithErrors ( errList ) ;
324
+ AddConfigInstance ( ( configInfo . OwnerPackage , configInfo . InternalName ) , cfg . Value ) ;
325
+ return ( TConfig ) cfg . Value ;
326
+ }
327
+ catch ( Exception ex )
328
+ {
329
+ return FluentResults . Result . Fail ( $ "Failed to initialize { configInfo . DataType } ") . WithError ( new ExceptionalError ( ex ) ) ;
330
+ }
284
331
}
285
332
286
333
public FluentResults . Result ApplyProfileSettings ( ContentPackage package , string profileName )
287
334
{
288
- throw new NotImplementedException ( ) ;
335
+ using var lck = _disposeOpsLock . AcquireReaderLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
336
+ _base . CheckDisposed ( ) ;
337
+
338
+ if ( package == null || string . IsNullOrEmpty ( profileName ) )
339
+ return FluentResults . Result . Fail ( $ "{ nameof ( ApplyProfileSettings ) } : ContentPackage and/or name were null or empty.") ;
340
+
341
+ if ( ! _configProfiles . TryGetValue ( ( package , profileName ) , out var list ) )
342
+ return FluentResults . Result . Fail ( $ "No profiles found for package { package . Name } with name { profileName } ") ;
343
+
344
+ if ( list . IsDefaultOrEmpty )
345
+ return FluentResults . Result . Fail ( $ "{ nameof ( ApplyProfileSettings ) } : No stored values for profile { profileName } .") ;
346
+
347
+ var errList = new List < IError > ( ) ;
348
+
349
+ foreach ( var profileVal in list )
350
+ {
351
+ if ( ! _configs . TryGetValue ( ( package , profileVal . ConfigName ) , out var val ) )
352
+ continue ;
353
+
354
+ if ( ! val . TrySetValue ( profileVal . Value ) )
355
+ errList . Add ( new Error ( $ "Failed to apply value from profile named { profileName } to { val . InternalName } ") ) ;
356
+ // continue
357
+ }
358
+
359
+ return FluentResults . Result . Ok ( ) . WithErrors ( errList ) ;
289
360
}
290
361
291
362
public FluentResults . Result DisposePackageData ( ContentPackage package )
292
363
{
364
+ using var lck = _disposeOpsLock . AcquireReaderLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
293
365
_base . CheckDisposed ( ) ;
294
- throw new NotImplementedException ( ) ;
295
- }
366
+
367
+ if ( package is null )
368
+ return FluentResults . Result . Fail ( $ "{ nameof ( DisposePackageData ) } : Package was null.") ;
296
369
297
- public Result < IReadOnlyDictionary < string , IConfigBase > > GetConfigsForPackage ( ContentPackage package )
298
- {
299
- _base . CheckDisposed ( ) ;
300
- throw new NotImplementedException ( ) ;
301
- }
370
+ var errList = new List < IError > ( ) ;
371
+
372
+ if ( _packageConfigReverseLookup . Remove ( package , out var cfgKeys ) )
373
+ {
374
+ if ( cfgKeys . Any ( ) )
375
+ {
376
+ foreach ( var key in cfgKeys )
377
+ {
378
+ try
379
+ {
380
+ _configs . Remove ( key , out var cfg ) ;
381
+ cfg ? . Dispose ( ) ;
382
+ }
383
+ catch ( Exception e )
384
+ {
385
+ errList . Add ( new ExceptionalError ( e ) ) ;
386
+ }
387
+ }
388
+ }
389
+ }
302
390
303
- public Result < IReadOnlyDictionary < string , IConfigBase > > GetConfigsForPackage ( string packageName )
304
- {
305
- _base . CheckDisposed ( ) ;
306
- throw new NotImplementedException ( ) ;
391
+ if ( _packageProfilesReverseLookup . Remove ( package , out var profileKeys ) )
392
+ {
393
+ if ( profileKeys . Any ( ) )
394
+ {
395
+ foreach ( var key in profileKeys )
396
+ {
397
+ _configProfiles . Remove ( key , out _ ) ;
398
+ }
399
+ }
400
+ }
401
+
402
+ return FluentResults . Result . Ok ( ) . WithErrors ( errList ) ;
307
403
}
308
404
309
- public IReadOnlyDictionary < ( ContentPackage , string ) , IConfigBase > GetAllConfigs ( )
405
+ public Result < IReadOnlyDictionary < ( ContentPackage Package , string ConfigName ) , IConfigBase > > GetConfigsForPackage ( ContentPackage package )
310
406
{
407
+ using var lck = _disposeOpsLock . AcquireReaderLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
311
408
_base . CheckDisposed ( ) ;
312
- throw new NotImplementedException ( ) ;
313
- }
314
409
315
- public bool TryGetConfig < T > ( ContentPackage package , string name , out T config ) where T : IConfigBase
316
- {
317
- throw new NotImplementedException ( ) ;
410
+ if ( ! _packageConfigReverseLookup . TryGetValue ( package , out var keys ) || keys . IsEmpty )
411
+ return FluentResults . Result . Fail ( $ "No configs found for package { package . Name } ") ;
412
+
413
+ return _configs . Where ( kvp => keys . Contains ( kvp . Key ) ) . ToFrozenDictionary ( kvp => kvp . Key , kvp => kvp . Value ) ;
318
414
}
319
415
320
- public T GetConfig < T > ( ContentPackage package , string name ) where T : IConfigBase
416
+ public Result < IReadOnlyDictionary < ( ContentPackage Package , string ConfigName ) , ImmutableArray < ( string ConfigName , OneOf < string , XElement > Value ) > > > GetProfilesForPackage ( ContentPackage package )
321
417
{
418
+ using var lck = _disposeOpsLock . AcquireReaderLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
322
419
_base . CheckDisposed ( ) ;
323
- throw new NotImplementedException ( ) ;
420
+
421
+ if ( ! _packageProfilesReverseLookup . TryGetValue ( package , out var keys ) || keys . IsEmpty )
422
+ return FluentResults . Result . Fail ( $ "No profiles found for package { package . Name } ") ;
423
+
424
+ return _configProfiles . Where ( kvp => keys . Contains ( kvp . Key ) ) . ToFrozenDictionary ( kvp => kvp . Key , kvp => kvp . Value ) ;
324
425
}
325
426
326
- public T GetConfig < T > ( string packageName , string name ) where T : IConfigBase
427
+ public IReadOnlyDictionary < ( ContentPackage Package , string Name ) , IConfigBase > GetAllConfigs ( )
327
428
{
429
+ using var lck = _disposeOpsLock . AcquireReaderLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
328
430
_base . CheckDisposed ( ) ;
329
- throw new NotImplementedException ( ) ;
431
+
432
+ return _configs . ToFrozenDictionary ( kvp => kvp . Key , kvp => kvp . Value ) ;
330
433
}
331
434
332
- public void OnAssemblyUnloading ( WeakReference < IAssemblyLoaderService > loaderService )
435
+ public bool TryGetConfig < T > ( ContentPackage package , string name , out T config ) where T : IConfigBase
333
436
{
334
- throw new NotImplementedException ( ) ;
437
+ using var lck = _disposeOpsLock . AcquireReaderLock ( ) . GetAwaiter ( ) . GetResult ( ) ;
438
+ _base . CheckDisposed ( ) ;
439
+
440
+ config = default ;
441
+ if ( ! _configs . TryGetValue ( ( package , name ) , out var value ) )
442
+ return false ;
443
+ try
444
+ {
445
+ config = ( T ) value ;
446
+ return true ;
447
+ }
448
+ catch
449
+ {
450
+ return false ;
451
+ }
335
452
}
336
453
}
0 commit comments