Skip to content

Commit 104f264

Browse files
committed
[Save/Sync] Work on ConfigService.
1 parent 41d1c61 commit 104f264

File tree

3 files changed

+158
-39
lines changed

3 files changed

+158
-39
lines changed
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
using System;
2+
using System.Xml.Linq;
23
using Barotrauma.LuaCs.Data;
34
using Barotrauma.LuaCs.Services;
45
using Barotrauma.Networking;
56

67
namespace Barotrauma.LuaCs.Configuration;
78

8-
public partial interface IConfigBase : IEquatable<IConfigBase>
9+
public partial interface IConfigBase : IDataInfo, IEquatable<IConfigBase>, IDisposable
910
{
1011
Type GetValueType();
1112
string GetValue();
12-
bool TrySetValue(string value);
13-
bool IsAssignable(string value);
13+
bool TrySetValue(OneOf.OneOf<string, XElement> value);
14+
bool IsAssignable(OneOf.OneOf<string, XElement> value);
1415
}

Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/ConfigService.cs

Lines changed: 149 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Concurrent;
3+
using System.Collections.Frozen;
34
using System.Collections.Generic;
45
using System.Collections.Immutable;
56
using System.Linq;
@@ -10,12 +11,14 @@
1011
using Barotrauma.LuaCs.Events;
1112
using Barotrauma.LuaCs.Services.Processing;
1213
using Barotrauma.Networking;
14+
using Dynamitey.DynamicObjects;
1315
using FluentResults;
1416
using Microsoft.Xna.Framework;
17+
using OneOf;
1518

1619
namespace Barotrauma.LuaCs.Services;
1720

18-
public partial class ConfigService : IConfigService, IEventAssemblyContextUnloading
21+
public partial class ConfigService : IConfigService
1922
{
2023
//--- Internals
2124
public ConfigService(IConverterServiceAsync<IConfigProfileResourceInfo, IReadOnlyList<IConfigProfileInfo>> configProfileResourceConverter,
@@ -34,6 +37,9 @@ public ConfigService(IConverterServiceAsync<IConfigProfileResourceInfo, IReadOnl
3437
private readonly ConcurrentDictionary<ContentPackage, ConcurrentBag<(ContentPackage Package, string ConfigName)>> _packageConfigReverseLookup = new();
3538
private readonly ConcurrentDictionary<(ContentPackage Package, string ProfileName), ImmutableArray<(string ConfigName, OneOf.OneOf<string, XElement> Value)>> _configProfiles = new();
3639
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();
3743

3844
// converters
3945
private readonly IConverterServiceAsync<IConfigResourceInfo, IReadOnlyList<IConfigInfo>> _configResourceConverter;
@@ -44,15 +50,25 @@ public ConfigService(IConverterServiceAsync<IConfigProfileResourceInfo, IReadOnl
4450

4551
public void Dispose()
4652
{
53+
// stop all ops
54+
using var lck = _disposeOpsLock.AcquireWriterLock().GetAwaiter().GetResult();
55+
// set flag
4756
ModUtils.Threading.SetBool(ref _isDisposed, true);
57+
4858
_configTypeInitializers.Clear();
49-
throw new NotImplementedException();
59+
_configs.Clear();
60+
_configProfiles.Clear();
61+
_packageConfigReverseLookup.Clear();
62+
_packageNameMap.Clear();
63+
_packageProfilesReverseLookup.Clear();
5064
}
5165

5266
public FluentResults.Result Reset()
5367
{
68+
69+
using var lck = _disposeOpsLock.AcquireWriterLock().GetAwaiter().GetResult();
5470
_base.CheckDisposed();
55-
throw new NotImplementedException();
71+
5672
}
5773

5874
//--- API contracts
@@ -172,7 +188,9 @@ public void SetConfigList(string packageName, string configName, string value)
172188
public void RegisterTypeInitializer<TData, TConfig>(IConfigTypeInitializer<TConfig> initializer, bool replaceIfExists = false)
173189
where TData : IConvertible, IEquatable<TData> where TConfig : IConfigBase
174190
{
191+
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
175192
_base.CheckDisposed();
193+
176194
Type dataType = typeof(TData);
177195
if (_configTypeInitializers.ContainsKey(dataType) && !replaceIfExists)
178196
return;
@@ -182,6 +200,8 @@ public void RegisterTypeInitializer<TData, TConfig>(IConfigTypeInitializer<TConf
182200
private void AddConfigInstance((ContentPackage Package, string ConfigName) key, IConfigBase instance)
183201
{
184202
_configs[key] = instance;
203+
if (!_packageNameMap.ContainsKey(key.Package.Name))
204+
_packageNameMap[key.Package.Name] = key.Package;
185205
if (!_packageConfigReverseLookup.TryGetValue(key.Package, out var list))
186206
{
187207
list = new ConcurrentBag<(ContentPackage Package, string ConfigName)>();
@@ -193,6 +213,8 @@ private void AddConfigInstance((ContentPackage Package, string ConfigName) key,
193213
private void AddProfileInstance((ContentPackage Package, string ProfileName) key, IConfigProfileInfo profile)
194214
{
195215
_configProfiles[key] = profile.ProfileValues.ToImmutableArray();
216+
if (!_packageNameMap.ContainsKey(key.Package.Name))
217+
_packageNameMap[key.Package.Name] = key.Package;
196218
if (!_packageProfilesReverseLookup.TryGetValue(key.Package, out var list))
197219
{
198220
list = new ConcurrentBag<(ContentPackage Package, string ProfileName)>();
@@ -203,8 +225,9 @@ private void AddProfileInstance((ContentPackage Package, string ProfileName) key
203225

204226
public async Task<FluentResults.Result> LoadConfigsAsync(ImmutableArray<IConfigResourceInfo> configResources)
205227
{
228+
using var lck = await _disposeOpsLock.AcquireReaderLock();
206229
_base.CheckDisposed();
207-
230+
208231
if (configResources.IsDefaultOrEmpty)
209232
return FluentResults.Result.Fail($"{nameof(LoadConfigsAsync)}: Array is empty.");
210233

@@ -247,8 +270,9 @@ private void AddProfileInstance((ContentPackage Package, string ProfileName) key
247270

248271
public async Task<FluentResults.Result> LoadConfigsProfilesAsync(ImmutableArray<IConfigProfileResourceInfo> configProfileResources)
249272
{
273+
using var lck = await _disposeOpsLock.AcquireReaderLock();
250274
_base.CheckDisposed();
251-
275+
252276
if (configProfileResources.IsDefaultOrEmpty)
253277
return FluentResults.Result.Fail($"{nameof(LoadConfigsProfilesAsync)}: Array is empty.");
254278

@@ -277,60 +301,153 @@ private void AddProfileInstance((ContentPackage Package, string ProfileName) key
277301
return ret;
278302
}
279303

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
282305
{
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+
}
284331
}
285332

286333
public FluentResults.Result ApplyProfileSettings(ContentPackage package, string profileName)
287334
{
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);
289360
}
290361

291362
public FluentResults.Result DisposePackageData(ContentPackage package)
292363
{
364+
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
293365
_base.CheckDisposed();
294-
throw new NotImplementedException();
295-
}
366+
367+
if (package is null)
368+
return FluentResults.Result.Fail($"{nameof(DisposePackageData)}: Package was null.");
296369

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+
}
302390

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);
307403
}
308404

309-
public IReadOnlyDictionary<(ContentPackage, string), IConfigBase> GetAllConfigs()
405+
public Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), IConfigBase>> GetConfigsForPackage(ContentPackage package)
310406
{
407+
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
311408
_base.CheckDisposed();
312-
throw new NotImplementedException();
313-
}
314409

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);
318414
}
319415

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)
321417
{
418+
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
322419
_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);
324425
}
325426

326-
public T GetConfig<T>(string packageName, string name) where T : IConfigBase
427+
public IReadOnlyDictionary<(ContentPackage Package, string Name), IConfigBase> GetAllConfigs()
327428
{
429+
using var lck = _disposeOpsLock.AcquireReaderLock().GetAwaiter().GetResult();
328430
_base.CheckDisposed();
329-
throw new NotImplementedException();
431+
432+
return _configs.ToFrozenDictionary(kvp => kvp.Key, kvp => kvp.Value);
330433
}
331434

332-
public void OnAssemblyUnloading(WeakReference<IAssemblyLoaderService> loaderService)
435+
public bool TryGetConfig<T>(ContentPackage package, string name, out T config) where T : IConfigBase
333436
{
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+
}
335452
}
336453
}

Barotrauma/BarotraumaShared/SharedSource/LuaCs/Services/_Interfaces/IConfigService.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Immutable;
44
using System.Diagnostics.CodeAnalysis;
55
using System.Threading.Tasks;
6+
using System.Xml.Linq;
67
using Barotrauma.LuaCs.Configuration;
78
using Barotrauma.LuaCs.Data;
89
using Barotrauma.LuaCs.Services;
@@ -29,14 +30,14 @@ void RegisterTypeInitializer<TData, TConfig>(IConfigTypeInitializer<TConfig> ini
2930
Task<FluentResults.Result> LoadConfigsProfilesAsync(ImmutableArray<IConfigProfileResourceInfo> configProfileResources);
3031

3132
// Immediate Mode
32-
FluentResults.Result<TConfig> AddConfig<T, TConfig>(ContentPackage package, string name,
33-
T defaultValue, NetSync syncMode = NetSync.None, ClientPermissions permissions = ClientPermissions.None)
34-
where T : IConvertible, IEquatable<T> where TConfig : IConfigBase;
33+
FluentResults.Result<TConfig> AddConfig<TConfig>(IConfigInfo configInfo) where TConfig : IConfigBase;
3534

3635
// Utility
3736
FluentResults.Result ApplyProfileSettings(ContentPackage package, string profileName);
3837
FluentResults.Result DisposePackageData(ContentPackage package);
39-
FluentResults.Result<IReadOnlyDictionary<string, IConfigBase>> GetConfigsForPackage(ContentPackage package);
38+
FluentResults.Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), IConfigBase>> GetConfigsForPackage(ContentPackage package);
39+
FluentResults.Result<IReadOnlyDictionary<(ContentPackage Package, string ConfigName), ImmutableArray<(string ConfigName, OneOf.OneOf<string, XElement> Value)>>>
40+
GetProfilesForPackage(ContentPackage package);
4041
IReadOnlyDictionary<(ContentPackage Package, string Name), IConfigBase> GetAllConfigs();
4142
bool TryGetConfig<T>(ContentPackage package, string name, out T config) where T : IConfigBase;
4243
}

0 commit comments

Comments
 (0)