-
Notifications
You must be signed in to change notification settings - Fork 0
Core.Modules
In the context of your project, ObjectManager modules are a design pattern used to group related service registrations into manageable, reusable components. Instead of cluttering your main entry point (like Program.cs or a Game class) with dozens of individual Register calls, you encapsulate that logic within a "Module" class.
Based on the code provided, there are three primary ways these modules are structured:
This is the simplest form. It uses a static method to perform a batch of standard registrations that don't typically change.
- Usage: Ideal for global services or framework-level setup.
- Example:
// To use it, you just call the static method once
CoreModule.Load();This pattern is used when the module needs configuration before it is finalized. It uses method chaining to "build" the desired state.
-
Key components:
- A
Create()method to start. - Configuration methods that return
this(the module instance). - A
Setup()orLoad()method to perform the actual registration in theObjectManager. - Use WithAutoAssign() to automatically switch between devices. This is ideal for single player sessions. If you don't use this then you need to manually assign it to players based on profileid and playerid.
- A
NOTICE Player ID start always from 1
- Example:
InputModule<GameInput>.Create()
.AddInputMapper(new GenericMapper<GameInput>(GlobalObjectManager.ObjectManager.Register(new RaylibKeyboardBinder<GameInput>()))
.AddInputKeyboard(0, GameInput.Up, KeyboardKey.Up)
.AddInputKeyboard(0, GameInput.Down, KeyboardKey.Down)
.AddInputKeyboard(0, GameInput.Left, KeyboardKey.Left)
.AddInputKeyboard(0, GameInput.Right, KeyboardKey.Right)
.AddInputKeyboard(0, GameInput.Start, KeyboardKey.Enter)
.AddInputKeyboard(0, GameInput.Action, KeyboardKey.Enter)
.AddAxis(0, 1, GameInput.Left, GameInput.Right, GameInput.Up, GameInput.Down)
.AddInputKeyboard(1, GameInput.Up, KeyboardKey.W)
.AddInputKeyboard(1, GameInput.Down, KeyboardKey.S)
.AddInputKeyboard(1, GameInput.Left, KeyboardKey.A)
.AddInputKeyboard(1, GameInput.Right, KeyboardKey.D)
.AddInputKeyboard(1, GameInput.Start, KeyboardKey.F)
.AddInputKeyboard(1, GameInput.Action, KeyboardKey.F)
.AddAxis(1, 1, GameInput.Left, GameInput.Right, GameInput.Up, GameInput.Down))
.AddInputMapper(new ControllerInputMapper<GameInput>(new SDLControllerDeviceManager())
.Map(GameInput.Start, ControllerInputEnum.AorCross)
.Map(GameInput.Action, ControllerInputEnum.Start)
.Map(GameInput.Down, ControllerInputEnum.DPadDown)
.Map(GameInput.Left, ControllerInputEnum.DPadLeft)
.Map(GameInput.Up, ControllerInputEnum.DPadUp)
.Map(GameInput.Right, ControllerInputEnum.DPadRight)
.SetDPadIsAxis(true))
.WithAutoAssign()
.Setup(); // This is where the ObjectManager.Add/Register calls happenThis pattern combines a static factory method with instance-based configuration. It often takes external dependencies (like an ArcadeGame instance) to decide what services to register.
- Usage: Ideal for complex systems that might be optional or have different modes (like "standalone" vs "server").
- Example:
var arcade = ArcadeEmulatorModule.Load(myGame, mqttModule, standaloneMode: true);
arcade.SetIntroScene<MyMenuScene>();If you wanted to create a new module for something like a "Network System," here is how you would implement it following the existing conventions:
using Meatcorps.Engine.Core.ObjectManager;
using Meatcorps.Engine.Core.Interfaces.Services;
namespace Meatcorps.Engine.Core.Modules;
public class NetworkModule
{
private string _ip = "127.0.0.1";
private int _port = 8080;
private readonly ObjectManager.ObjectManager _manager;
private NetworkModule(ObjectManager.ObjectManager? manager)
{
_manager = manager ?? GlobalObjectManager.ObjectManager;
}
public static NetworkModule Create(ObjectManager.ObjectManager? manager = null)
{
return new NetworkModule(manager);
}
public NetworkModule Configure(string ip, int port)
{
_ip = ip;
_port = port;
return this;
}
public void Load()
{
var service = new NetworkService(_ip, _port);
// Register the specific class
_manager.Register<NetworkService>(service);
// Also register it as a background service if it implements IBackgroundService
if (service is IBackgroundService background)
{
_manager.Add<IBackgroundService>(background);
}
}
}-
Readability: Your startup code becomes a high-level list of features (
LoadCore,SetupInput,LoadNetwork) rather than a list of individual classes. -
Dependency Management: The module can handle the order of operations. For example, in
InputModule.Setup(), it creates aPlayerInputRouterfirst because theInputManagerdepends on it. -
Interface Abstraction: Modules often register a concrete class (like
PlayerInputRouter) under multiple interfaces (likeIBackgroundServiceandIInputMapper), ensuring the rest of your app only sees the interface it needs.