A Local/NosSmooth.LocalBinding/Errors/CouldNotInitializeModuleError.cs => Local/NosSmooth.LocalBinding/Errors/CouldNotInitializeModuleError.cs +17 -0
@@ 0,0 1,17 @@
+//
+// CouldNotInitializeModuleError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Errors;
+
+/// <summary>
+/// Could not initialize the given NosTale module.
+/// </summary>
+/// <param name="Module">The module type that could not be initialized.</param>
+/// <param name="UnderlyingError">The error why the module could not be initialized.</param>
+public record CouldNotInitializeModuleError
+ (Type Module, IResultError UnderlyingError) : ResultError($"Could initialize a nostale module {Module.FullName}.");<
\ No newline at end of file
A Local/NosSmooth.LocalBinding/Errors/NotNostaleProcessError.cs => Local/NosSmooth.LocalBinding/Errors/NotNostaleProcessError.cs +17 -0
@@ 0,0 1,17 @@
+//
+// NotNostaleProcessError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Errors;
+
+/// <summary>
+/// The process you tried to browse is not a nostale process.
+/// </summary>
+/// <param name="process"></param>
+public record NotNostaleProcessError(Process process) : ResultError
+ ($"The process {process.ProcessName} ({process.Id} is not a NosTale process.");<
\ No newline at end of file
M Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs => Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs +8 -1
@@ 6,6 6,7 @@
using Microsoft.Extensions.DependencyInjection;
using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Structs;
namespace NosSmooth.LocalBinding.Extensions;
@@ 29,8 30,14 @@ public static class ServiceCollectionExtensions
{
return serviceCollection
.AddSingleton<NosBindingManager>()
- .AddSingleton(p => p.GetRequiredService<NosBindingManager>().NosBrowser)
+ .AddSingleton<NosBrowserManager>()
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PlayerManager)
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().SceneManager)
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PetManagerList)
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().SceneManager)
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PetManagerList)
.AddSingleton(p => p.GetRequiredService<NosBindingManager>().PlayerManager)
+ .AddSingleton(p => p.GetRequiredService<NosBindingManager>().PetManager)
.AddSingleton(p => p.GetRequiredService<NosBindingManager>().UnitManager)
.AddSingleton(p => p.GetRequiredService<NosBindingManager>().Network);
}
D Local/NosSmooth.LocalBinding/ExternalNosBrowser.cs => Local/NosSmooth.LocalBinding/ExternalNosBrowser.cs +0 -126
@@ 1,126 0,0 @@
-//
-// ExternalNosBrowser.cs
-//
-// Copyright (c) František Boháček. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System.Diagnostics;
-using Microsoft.Extensions.Options;
-using NosSmooth.LocalBinding.Options;
-using NosSmooth.LocalBinding.Structs;
-using Reloaded.Memory.Sigscan;
-using Reloaded.Memory.Sources;
-using Remora.Results;
-
-namespace NosSmooth.LocalBinding;
-
-/// <summary>
-/// Used for browsing a nostale process data.
-/// </summary>
-public class ExternalNosBrowser
-{
- private readonly PlayerManagerOptions _playerManagerOptions;
- private readonly SceneManagerOptions _sceneManagerOptions;
- private readonly PetManagerOptions _petManagerOptions;
- private PlayerManager? _playerManager;
- private SceneManager? _sceneManager;
- private PetManagerList? _petManagerList;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ExternalNosBrowser"/> class.
- /// </summary>
- /// <param name="process">The process to browse.</param>
- /// <param name="playerManagerOptions">The options for obtaining player manager.</param>
- /// <param name="sceneManagerOptions">The scene manager options.</param>
- /// <param name="petManagerOptions">The pet manager options.</param>
- public ExternalNosBrowser
- (
- Process process,
- PlayerManagerOptions playerManagerOptions,
- SceneManagerOptions sceneManagerOptions,
- PetManagerOptions petManagerOptions
- )
- {
- _playerManagerOptions = playerManagerOptions;
- _sceneManagerOptions = sceneManagerOptions;
- _petManagerOptions = petManagerOptions;
- Process = process;
- Memory = new ExternalMemory(process);
- Scanner = new Scanner(process, process.MainModule);
- }
-
- /// <summary>
- /// The NosTale process.
- /// </summary>
- public Process Process { get; }
-
- /// <summary>
- /// Gets the memory scanner.
- /// </summary>
- internal Scanner Scanner { get; }
-
- /// <summary>
- /// Gets the current process memory.
- /// </summary>
- internal IMemory Memory { get; }
-
- /// <summary>
- /// Get the player manager.
- /// </summary>
- /// <returns>The player manager or an error.</returns>
- public Result<PlayerManager> GetPlayerManager()
- {
- if (_playerManager is null)
- {
- var playerManagerResult = PlayerManager.Create(this, _playerManagerOptions);
- if (!playerManagerResult.IsSuccess)
- {
- return playerManagerResult;
- }
-
- _playerManager = playerManagerResult.Entity;
- }
-
- return _playerManager;
- }
-
- /// <summary>
- /// Get the player manager.
- /// </summary>
- /// <returns>The player manager or an error.</returns>
- public Result<SceneManager> GetSceneManager()
- {
- if (_sceneManager is null)
- {
- var sceneManagerResult = SceneManager.Create(this, _sceneManagerOptions);
- if (!sceneManagerResult.IsSuccess)
- {
- return sceneManagerResult;
- }
-
- _sceneManager = sceneManagerResult.Entity;
- }
-
- return _sceneManager;
- }
-
- /// <summary>
- /// Get the pet manager list.
- /// </summary>
- /// <returns>The player manager or an error.</returns>
- public Result<PetManagerList> GetPetManagerList()
- {
- if (_petManagerList is null)
- {
- var petManagerResult = PetManagerList.Create(this, _petManagerOptions);
- if (!petManagerResult.IsSuccess)
- {
- return petManagerResult;
- }
-
- _petManagerList = petManagerResult.Entity;
- }
-
- return _petManagerList;
- }
-}>
\ No newline at end of file
M Local/NosSmooth.LocalBinding/NosBindingManager.cs => Local/NosSmooth.LocalBinding/NosBindingManager.cs +204 -44
@@ 6,6 6,7 @@
using System.Diagnostics;
using Microsoft.Extensions.Options;
+using NosSmooth.LocalBinding.Errors;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Options;
using Reloaded.Hooks;
@@ 21,6 22,8 @@ namespace NosSmooth.LocalBinding;
/// </summary>
public class NosBindingManager : IDisposable
{
+ private readonly NosBrowserManager _browserManager;
+ private readonly PetManagerBindingOptions _petManagerBindingOptions;
private readonly CharacterBindingOptions _characterBindingOptions;
private readonly NetworkBindingOptions _networkBindingOptions;
private UnitManagerBindingOptions _unitManagerBindingOptions;
@@ 28,47 31,36 @@ public class NosBindingManager : IDisposable
private NetworkBinding? _networkBinding;
private PlayerManagerBinding? _characterBinding;
private UnitManagerBinding? _unitManagerBinding;
+ private PetManagerBinding? _petManagerBinding;
/// <summary>
/// Initializes a new instance of the <see cref="NosBindingManager"/> class.
/// </summary>
+ /// <param name="browserManager">The NosTale browser manager.</param>
/// <param name="characterBindingOptions">The character binding options.</param>
/// <param name="networkBindingOptions">The network binding options.</param>
/// <param name="sceneManagerBindingOptions">The scene manager binding options.</param>
- /// <param name="playerManagerOptions">The player manager options.</param>
- /// <param name="sceneManagerOptions">The scene manager options.</param>
- /// <param name="petManagerOptions">The pet manager options.</param>
+ /// <param name="petManagerBindingOptions">The pet manager binding options.</param>
public NosBindingManager
(
+ NosBrowserManager browserManager,
IOptions<CharacterBindingOptions> characterBindingOptions,
IOptions<NetworkBindingOptions> networkBindingOptions,
IOptions<UnitManagerBindingOptions> sceneManagerBindingOptions,
- IOptions<PlayerManagerOptions> playerManagerOptions,
- IOptions<SceneManagerOptions> sceneManagerOptions,
- IOptions<PetManagerOptions> petManagerOptions
+ IOptions<PetManagerBindingOptions> petManagerBindingOptions
)
{
+ _browserManager = browserManager;
Hooks = new ReloadedHooks();
Memory = new Memory();
Scanner = new Scanner(Process.GetCurrentProcess(), Process.GetCurrentProcess().MainModule);
_characterBindingOptions = characterBindingOptions.Value;
_networkBindingOptions = networkBindingOptions.Value;
_unitManagerBindingOptions = sceneManagerBindingOptions.Value;
- NosBrowser = new ExternalNosBrowser
- (
- Process.GetCurrentProcess(),
- playerManagerOptions.Value,
- sceneManagerOptions.Value,
- petManagerOptions.Value
- );
+ _petManagerBindingOptions = petManagerBindingOptions.Value;
}
/// <summary>
- /// Gets the nos browser.
- /// </summary>
- public ExternalNosBrowser NosBrowser { get; }
-
- /// <summary>
/// Gets the memory scanner.
/// </summary>
internal Scanner Scanner { get; }
@@ 144,48 136,174 @@ public class NosBindingManager : IDisposable
}
/// <summary>
+ /// Gets the pet manager binding.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
+ public PetManagerBinding PetManager
+ {
+ get
+ {
+ if (_petManagerBinding is null)
+ {
+ throw new InvalidOperationException
+ (
+ "Could not get pet manager. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?"
+ );
+ }
+
+ return _petManagerBinding;
+ }
+ }
+
+ /// <summary>
/// Initialize the existing bindings and hook NosTale functions.
/// </summary>
/// <returns>A result that may or may not have succeeded.</returns>
- public Result Initialize()
+ public IResult Initialize()
{
- var network = NetworkBinding.Create(this, _networkBindingOptions);
- if (!network.IsSuccess)
+ List<IResult> errorResults = new List<IResult>();
+ var browserInitializationResult = _browserManager.Initialize();
+ if (!browserInitializationResult.IsSuccess)
{
- return Result.FromError(network);
+ if (browserInitializationResult.Error is NotNostaleProcessError)
+ {
+ return browserInitializationResult;
+ }
+
+ errorResults.Add(browserInitializationResult);
}
- _networkBinding = network.Entity;
- var playerManager = NosBrowser.GetPlayerManager();
- if (!playerManager.IsSuccess)
+ try
+ {
+ var network = NetworkBinding.Create(this, _networkBindingOptions);
+ if (!network.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(NetworkBinding), network.Error),
+ network
+ )
+ );
+ }
+
+ _networkBinding = network.Entity;
+ }
+ catch (Exception e)
+ {
+ errorResults.Add(
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(NetworkBinding), new ExceptionError(e)),
+ (Result)new ExceptionError(e)
+ ));
+ }
+
+ try
+ {
+ var playerManagerBinding = PlayerManagerBinding.Create
+ (
+ this,
+ _browserManager.PlayerManager,
+ _characterBindingOptions
+ );
+ if (!playerManagerBinding.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(PlayerManagerBinding), playerManagerBinding.Error),
+ playerManagerBinding
+ )
+ );
+ }
+ _characterBinding = playerManagerBinding.Entity;
+ }
+ catch (Exception e)
{
- return Result.FromError(playerManager);
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(PlayerManagerBinding), new ExceptionError(e)),
+ (Result)new ExceptionError(e)
+ )
+ );
}
- var playerManagerBinding = PlayerManagerBinding.Create
- (
- this,
- playerManager.Entity,
- _characterBindingOptions
- );
- if (!playerManagerBinding.IsSuccess)
+ try
+ {
+ var unitManagerBinding = UnitManagerBinding.Create
+ (
+ this,
+ _unitManagerBindingOptions
+ );
+ if (!unitManagerBinding.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(UnitManagerBinding), unitManagerBinding.Error),
+ unitManagerBinding
+ )
+ );
+ }
+ _unitManagerBinding = unitManagerBinding.Entity;
+ }
+ catch (Exception e)
{
- return Result.FromError(playerManagerBinding);
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(UnitManagerBinding), new ExceptionError(e)),
+ (Result)new ExceptionError(e)
+ )
+ );
}
- _characterBinding = playerManagerBinding.Entity;
- var unitManagerBinding = UnitManagerBinding.Create
- (
- this,
- _unitManagerBindingOptions
- );
- if (!unitManagerBinding.IsSuccess)
+ try
{
- return Result.FromError(unitManagerBinding);
+ var petManagerBinding = PetManagerBinding.Create
+ (
+ this,
+ _browserManager.PetManagerList,
+ _petManagerBindingOptions
+ );
+ if (!petManagerBinding.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(PetManagerBinding), petManagerBinding.Error),
+ petManagerBinding
+ )
+ );
+ }
+ _petManagerBinding = petManagerBinding.Entity;
+ }
+ catch (Exception e)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(UnitManagerBinding), new ExceptionError(e)),
+ (Result)new ExceptionError(e)
+ )
+ );
}
- _unitManagerBinding = unitManagerBinding.Entity;
- return Result.FromSuccess();
+ return errorResults.Count switch
+ {
+ 0 => Result.FromSuccess(),
+ 1 => errorResults[0],
+ _ => (Result)new AggregateError(errorResults)
+ };
}
/// <summary>
@@ 212,4 330,46 @@ public class NosBindingManager : IDisposable
Scanner.Dispose();
DisableHooks();
}
+
+ /// <summary>
+ /// Create a hook object for the given pattern.
+ /// </summary>
+ /// <param name="name">The name of the binding.</param>
+ /// <param name="callbackFunction">The callback function to call instead of the original one.</param>
+ /// <param name="pattern">The pattern.</param>
+ /// <param name="offset">The offset from the pattern.</param>
+ /// <param name="hook">Whether to activate the hook.</param>
+ /// <typeparam name="TFunction">The type of the function.</typeparam>
+ /// <returns>The hook object or an error.</returns>
+ internal Result<IHook<TFunction>> CreateHookFromPattern<TFunction>
+ (
+ string name,
+ TFunction callbackFunction,
+ string pattern,
+ int offset = 0,
+ bool hook = true
+ )
+ {
+ var walkFunctionAddress = Scanner.CompiledFindPattern(pattern);
+ if (!walkFunctionAddress.Found)
+ {
+ return new BindingNotFoundError(pattern, "PetManagerBinding.PetWalk");
+ }
+
+ try
+ {
+ return Result<IHook<TFunction>>.FromSuccess
+ (
+ Hooks.CreateHook
+ (
+ callbackFunction,
+ walkFunctionAddress.Offset + (int)_browserManager.Process.MainModule!.BaseAddress
+ )
+ );
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
}=
\ No newline at end of file
A Local/NosSmooth.LocalBinding/NosBrowserManager.cs => Local/NosSmooth.LocalBinding/NosBrowserManager.cs +279 -0
@@ 0,0 1,279 @@
+//
+// NosBrowserManager.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using Microsoft.Extensions.Options;
+using NosSmooth.LocalBinding.Errors;
+using NosSmooth.LocalBinding.Options;
+using NosSmooth.LocalBinding.Structs;
+using Reloaded.Memory.Sigscan;
+using Reloaded.Memory.Sources;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding;
+
+/// <summary>
+/// Used for browsing a nostale process data.
+/// </summary>
+public class NosBrowserManager
+{
+ /// <summary>
+ /// Checks whether the given process is a NosTale client process.
+ /// </summary>
+ /// <remarks>
+ /// This is just a guess based on presence of "NostaleData" directory.
+ /// </remarks>
+ /// <param name="process">The process to check.</param>
+ /// <returns>Whether the process is a NosTale client.</returns>
+ public static bool IsProcessNostaleProcess(Process process)
+ {
+ if (process.MainModule is null)
+ {
+ return false;
+ }
+
+ var processDirectory = Path.GetDirectoryName(process.MainModule.FileName);
+ if (processDirectory is null)
+ {
+ return false;
+ }
+
+ return Directory.Exists(Path.Combine(processDirectory, "NostaleData"));
+ }
+
+ /// <summary>
+ /// Get all running nostale processes.
+ /// </summary>
+ /// <returns>The nostale processes.</returns>
+ public static IEnumerable<Process> GetAllNostaleProcesses()
+ => Process
+ .GetProcesses()
+ .Where(IsProcessNostaleProcess);
+
+ private readonly PlayerManagerOptions _playerManagerOptions;
+ private readonly SceneManagerOptions _sceneManagerOptions;
+ private readonly PetManagerOptions _petManagerOptions;
+ private PlayerManager? _playerManager;
+ private SceneManager? _sceneManager;
+ private PetManagerList? _petManagerList;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NosBrowserManager"/> class.
+ /// </summary>
+ /// <param name="playerManagerOptions">The options for obtaining player manager.</param>
+ /// <param name="sceneManagerOptions">The scene manager options.</param>
+ /// <param name="petManagerOptions">The pet manager options.</param>
+ public NosBrowserManager
+ (
+ IOptions<PlayerManagerOptions> playerManagerOptions,
+ IOptions<SceneManagerOptions> sceneManagerOptions,
+ IOptions<PetManagerOptions> petManagerOptions
+ )
+ : this
+ (
+ Process.GetCurrentProcess(),
+ playerManagerOptions.Value,
+ sceneManagerOptions.Value,
+ petManagerOptions.Value
+ )
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NosBrowserManager"/> class.
+ /// </summary>
+ /// <param name="process">The process to browse.</param>
+ /// <param name="playerManagerOptions">The options for obtaining player manager.</param>
+ /// <param name="sceneManagerOptions">The scene manager options.</param>
+ /// <param name="petManagerOptions">The pet manager options.</param>
+ public NosBrowserManager
+ (
+ Process process,
+ PlayerManagerOptions playerManagerOptions,
+ SceneManagerOptions sceneManagerOptions,
+ PetManagerOptions petManagerOptions
+ )
+ {
+ _playerManagerOptions = playerManagerOptions;
+ _sceneManagerOptions = sceneManagerOptions;
+ _petManagerOptions = petManagerOptions;
+ Process = process;
+ Memory = new ExternalMemory(process);
+ Scanner = new Scanner(process, process.MainModule);
+ }
+
+ /// <summary>
+ /// The NosTale process.
+ /// </summary>
+ public Process Process { get; }
+
+ /// <summary>
+ /// Gets the memory scanner.
+ /// </summary>
+ internal Scanner Scanner { get; }
+
+ /// <summary>
+ /// Gets the current process memory.
+ /// </summary>
+ internal IMemory Memory { get; }
+
+ /// <summary>
+ /// Gets whether this is a NosTale process or not.
+ /// </summary>
+ public bool IsNostaleProcess => NosBrowserManager.IsProcessNostaleProcess(Process);
+
+ /// <summary>
+ /// Gets whether the player is currently in game.
+ /// </summary>
+ /// <remarks>
+ /// It may be unsafe to access some data if the player is not in game.
+ /// </remarks>
+ public bool IsInGame
+ {
+ get
+ {
+ var player = PlayerManager.Player;
+ return player.Address != IntPtr.Zero;
+ }
+ }
+
+ /// <summary>
+ /// Gets the player manager.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of player manager.</exception>
+ public PlayerManager PlayerManager
+ {
+ get
+ {
+ if (_playerManager is null)
+ {
+ throw new InvalidOperationException
+ (
+ "Could not get player manager. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
+ );
+ }
+
+ return _playerManager;
+ }
+ }
+
+ /// <summary>
+ /// Gets the scene manager.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of scene manager.</exception>
+ public SceneManager SceneManager
+ {
+ get
+ {
+ if (_sceneManager is null)
+ {
+ throw new InvalidOperationException
+ (
+ "Could not get scene manager. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
+ );
+ }
+
+ return _sceneManager;
+ }
+ }
+
+ /// <summary>
+ /// Gets the pet manager list.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of pet manager list.</exception>
+ public PetManagerList PetManagerList
+ {
+ get
+ {
+ if (_petManagerList is null)
+ {
+ throw new InvalidOperationException
+ (
+ "Could not get pet manager list. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
+ );
+ }
+
+ return _petManagerList;
+ }
+ }
+
+ /// <summary>
+ /// Initialize the nos browser modules.
+ /// </summary>
+ /// <remarks>
+ /// Needed to use all of the classes from NosTale.
+ /// </remarks>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public IResult Initialize()
+ {
+ if (!IsNostaleProcess)
+ {
+ return (Result)new NotNostaleProcessError(Process);
+ }
+
+ List<IResult> errorResults = new List<IResult>();
+ if (_playerManager is null)
+ {
+ var playerManagerResult = PlayerManager.Create(this, _playerManagerOptions);
+ if (!playerManagerResult.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(PlayerManager), playerManagerResult.Error),
+ playerManagerResult
+ )
+ );
+ }
+
+ _playerManager = playerManagerResult.Entity;
+ }
+
+ if (_sceneManager is null)
+ {
+ var sceneManagerResult = SceneManager.Create(this, _sceneManagerOptions);
+ if (!sceneManagerResult.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(SceneManager), sceneManagerResult.Error),
+ sceneManagerResult
+ )
+ );
+ }
+
+ _sceneManager = sceneManagerResult.Entity;
+ }
+
+ if (_petManagerList is null)
+ {
+ var petManagerResult = PetManagerList.Create(this, _petManagerOptions);
+ if (!petManagerResult.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(PetManagerList), petManagerResult.Error),
+ petManagerResult
+ )
+ );
+ }
+
+ _petManagerList = petManagerResult.Entity;
+ }
+
+ return errorResults.Count switch
+ {
+ 0 => Result.FromSuccess(),
+ 1 => errorResults[0],
+ _ => (Result)new AggregateError(errorResults)
+ };
+ }
+}<
\ No newline at end of file