Skip to content

Commit

Permalink
Progressive refactoring (#11)
Browse files Browse the repository at this point in the history
* refactor: factory di resolving working

* refactor: structure & di. works
  • Loading branch information
cherrynik authored Oct 7, 2023
1 parent 516bb58 commit 2484ac7
Show file tree
Hide file tree
Showing 51 changed files with 779 additions and 419 deletions.
7 changes: 7 additions & 0 deletions MonoGame.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "External", "src\Libs\Extern
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Features", "src\Libs\Features\Features.csproj", "{F4C957C8-3B10-4614-99F4-E2F2398F38ED}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entities", "src\Libs\Entities\Entities.csproj", "{8232C7AA-E217-465B-95A3-C42D1000FA3B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -64,6 +66,10 @@ Global
{F4C957C8-3B10-4614-99F4-E2F2398F38ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4C957C8-3B10-4614-99F4-E2F2398F38ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4C957C8-3B10-4614-99F4-E2F2398F38ED}.Release|Any CPU.Build.0 = Release|Any CPU
{8232C7AA-E217-465B-95A3-C42D1000FA3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8232C7AA-E217-465B-95A3-C42D1000FA3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8232C7AA-E217-465B-95A3-C42D1000FA3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8232C7AA-E217-465B-95A3-C42D1000FA3B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{5E3B6238-A92E-4703-8527-1C2410D7906A} = {F5258F7E-B0BA-466D-8CF8-4DAB84722BE3}
Expand All @@ -77,5 +83,6 @@ Global
{FE9CE932-6F98-4455-A80F-2A0CDB6DB46E} = {7AE4DC46-59EB-4F69-9240-C7363F0778AA}
{6EEBFA40-2814-43D0-A9DC-4A98F4837A1B} = {FE9CE932-6F98-4455-A80F-2A0CDB6DB46E}
{F4C957C8-3B10-4614-99F4-E2F2398F38ED} = {7AE4DC46-59EB-4F69-9240-C7363F0778AA}
{8232C7AA-E217-465B-95A3-C42D1000FA3B} = {7AE4DC46-59EB-4F69-9240-C7363F0778AA}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using Components;
using Components.World;
using GameDesktop.Resources.Internal;
using LightInject;
using Microsoft.Xna.Framework;
using MonoGame.Aseprite.Sprites;
using Services.Math;

namespace GameDesktop.CompositionRoots.Components;

public class ComponentsCompositionRoot : ICompositionRoot
{
private static readonly string PlayerSpriteSheetPath = System.IO.Path.Join(
Environment.GetEnvironmentVariable(EnvironmentVariable.AppBaseDirectory),
Resources.SpriteSheet.Player);

public void Compose(IServiceRegistry serviceRegistry)
{
RegisterSpriteComponent(serviceRegistry);
RegisterMovementAnimationComponent(serviceRegistry);
RegisterCameraComponent(serviceRegistry);
RegisterTransformComponent(serviceRegistry);
}

private static void RegisterSpriteComponent(IServiceRegistry serviceRegistry)
{
// todo: put non-game strings in internal resources
serviceRegistry.RegisterSingleton(factory =>
{
var getAnimations =
factory.GetInstance<Func<string, string, Dictionary<Direction, AnimatedSprite>>>("Character");
Dictionary<Direction, AnimatedSprite> idle = getAnimations(PlayerSpriteSheetPath, "Idle");
var defaultSprite = idle[Direction.Down];

return new SpriteComponent(defaultSprite);
});
}

private static void RegisterMovementAnimationComponent(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterTransient(factory =>
{
var getAnimations =
factory.GetInstance<Func<string, string, Dictionary<Direction, AnimatedSprite>>>("Character");

return new MovementAnimationComponent(getAnimations(PlayerSpriteSheetPath, "Idle"),
getAnimations(PlayerSpriteSheetPath, "Walking"));
});
}

private static void RegisterCameraComponent(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterSingleton(_ => new CameraComponent { Size = new Rectangle(0, 0, 640, 480) });

private static void RegisterTransformComponent(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterTransient<TransformComponent>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Entities;
using LightInject;

namespace GameDesktop.CompositionRoots.Entities;

public class PlayerEntityCompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
RegisterEntity(serviceRegistry);
}

private static void RegisterEntity(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterTransient<PlayerEntity>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Entities;
using LightInject;

namespace GameDesktop.CompositionRoots.Entities;

public class StaticEntityCompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
RegisterEntity(serviceRegistry);
}


private static void RegisterEntity(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterTransient<StaticEntity>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using Entitas;
using Features;
using GameDesktop.Resources.Internal;
using LightInject;
using Systems;

namespace GameDesktop.CompositionRoots.Features;

public class CameraFeatureCompositionRoot : ICompositionRoot
{
private static readonly IMatcher<GameEntity>[] Matchers = { GameMatcher.Transform, GameMatcher.Drawable };

public void Compose(IServiceRegistry serviceRegistry)
{
RegisterSystem(serviceRegistry);
RegisterFeature(serviceRegistry);
}

private static void RegisterSystem(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterFallback((type, s) => true, request =>
{
var getGroup =
request.ServiceFactory.GetInstance<Func<IMatcher<GameEntity>[], IGroup<GameEntity>>>(Matcher.AllOf);
IGroup<GameEntity> group = getGroup(Matchers);

// return new CameraFollowingSystem(request.ServiceFactory.GetInstance<Contexts>(), group);
return new DefaultDrawSystem(group);
}, new PerContainerLifetime());
}

private static void RegisterFeature(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterSingleton<CameraFeature>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using Entitas;
using Features;
using GameDesktop.Resources.Internal;
using LightInject;
using Serilog;
using Services.Input;
using Systems;

namespace GameDesktop.CompositionRoots.Features;

public class InputFeatureCompositionRoot : ICompositionRoot
{
private static readonly IMatcher<GameEntity>[] Matchers =
{
GameMatcher.Transform, GameMatcher.Movable, GameMatcher.Player
};

public void Compose(IServiceRegistry serviceRegistry)
{
RegisterImpl(serviceRegistry);
RegisterSystem(serviceRegistry);
RegisterFeature(serviceRegistry);
}

private static void RegisterImpl(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterSingleton<IInputScanner, KeyboardScanner>();

private static void RegisterSystem(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterSingleton(factory =>
{
var inputScanner = factory.GetInstance<IInputScanner>();

var getGroup = factory.GetInstance<Func<IMatcher<GameEntity>[], IGroup<GameEntity>>>(Matcher.AllOf);
IGroup<GameEntity> group = getGroup(Matchers);

var logger = factory.GetInstance<ILogger>();

return new InputSystem(inputScanner, group, logger);
});
}

private static void RegisterFeature(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterSingleton<InputFeature>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using Entitas;
using Features;
using GameDesktop.Resources.Internal;
using LightInject;
using Serilog;
using Services;
using Services.Movement;
using Systems;

namespace GameDesktop.CompositionRoots.Features;

public class MovementFeatureCompositionRoot : ICompositionRoot
{
private static readonly IMatcher<GameEntity>[] MovableMatchers = { GameMatcher.Transform, GameMatcher.Movable };

private static readonly IMatcher<GameEntity>[] AnimatedMovableMatchers =
{
GameMatcher.Movable, GameMatcher.MovementAnimation
};

public void Compose(IServiceRegistry serviceRegistry)
{
RegisterImpl(serviceRegistry);
RegisterSystems(serviceRegistry);
RegisterFeature(serviceRegistry);
}

private static void RegisterImpl(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterSingleton<IMovement, SimpleMovement>();

private static void RegisterSystems(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterSingleton(factory =>
{
var movement = factory.GetInstance<IMovement>();

var getGroup = factory.GetInstance<Func<IMatcher<GameEntity>[], IGroup<GameEntity>>>(Matcher.AllOf);
IGroup<GameEntity> group = getGroup(MovableMatchers);

var logger = factory.GetInstance<ILogger>();

return new MovementSystem(movement, group, logger);
});

serviceRegistry.RegisterSingleton(factory =>
{
var getGroup = factory.GetInstance<Func<IMatcher<GameEntity>[], IGroup<GameEntity>>>(Matcher.AllOf);
IGroup<GameEntity> group = getGroup(AnimatedMovableMatchers);

var logger = factory.GetInstance<ILogger>();

return new AnimatedMovementSystem(group, logger);
});
}

private static void RegisterFeature(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterSingleton<MovementFeature>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Features;
using GameDesktop.CompositionRoots.Components;
using GameDesktop.CompositionRoots.Entities;
using LightInject;

namespace GameDesktop.CompositionRoots.Features;

public class RootFeatureCompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
// Layered registration architecture (horizontally & vertically)
// Hence, it allows async/multi-threaded registration

// If it's split with space-line, then it's the end of a group.
// A group (of registering lines) can be multi-threaded.
// At the end of a group, the whole group has to be resolved successfully,
// before going further.
RegisterFundamental(serviceRegistry);

RegisterComponents(serviceRegistry);
RegisterEntities(serviceRegistry);

RegisterFeatures(serviceRegistry);

// Main entry point
serviceRegistry.RegisterSingleton<RootFeature>();
}

private static void RegisterFundamental(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterFrom<FundamentalCompositionRoot>();
}

private static void RegisterComponents(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterFrom<ComponentsCompositionRoot>();
}

private static void RegisterEntities(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterFrom<PlayerEntityCompositionRoot>();
serviceRegistry.RegisterFrom<StaticEntityCompositionRoot>();
}

private static void RegisterFeatures(IServiceRegistry serviceRegistry)
{
serviceRegistry.RegisterFrom<WorldInitializeFeatureCompositionRoot>();
serviceRegistry.RegisterFrom<InputFeatureCompositionRoot>();
serviceRegistry.RegisterFrom<CameraFeatureCompositionRoot>();
serviceRegistry.RegisterFrom<MovementFeatureCompositionRoot>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Features;
using LightInject;

namespace GameDesktop.CompositionRoots.Features;

public class WorldInitializeFeatureCompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
RegisterFeature(serviceRegistry);
}

private static void RegisterFeature(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterSingleton<WorldInitializeFeature>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Collections.Generic;
using Entitas;
using GameDesktop.Resources.Internal;
using LightInject;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Aseprite.Sprites;
using Services.Factories;
using Services.Math;

namespace GameDesktop.CompositionRoots;

public class FundamentalCompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
RegisterContexts(serviceRegistry);
RegisterAllOfMatcher(serviceRegistry);
RegisterAnyOfMatcher(serviceRegistry);
RegisterAnimationsFactory(serviceRegistry);
serviceRegistry.RegisterSingleton(typeof(AbstractFactory<>));
}

private static void RegisterContexts(IServiceRegistry serviceRegistry) =>
serviceRegistry.RegisterInstance(Contexts.sharedInstance);

private static void RegisterAllOfMatcher(IServiceRegistry serviceRegistry)
{
serviceRegistry.Register<IMatcher<GameEntity>[], IGroup<GameEntity>>((factory, matchers) =>
{
IAllOfMatcher<GameEntity> groupMatcher = GameMatcher.AllOf(matchers);
var contexts = factory.GetInstance<Contexts>();
return contexts.game.GetGroup(groupMatcher);
}, Matcher.AllOf);
}

private static void RegisterAnyOfMatcher(IServiceRegistry serviceRegistry)
{
serviceRegistry.Register<IMatcher<GameEntity>[], IGroup<GameEntity>>((factory, matchers) =>
{
IAnyOfMatcher<GameEntity> groupMatcher = GameMatcher.AnyOf(matchers);
var contexts = factory.GetInstance<Contexts>();
return contexts.game.GetGroup(groupMatcher);
}, Matcher.AnyOf);
}

private static void RegisterAnimationsFactory(IServiceRegistry serviceRegistry)
{
// Warning: binding to <string, T> where T is any type, is dangerous and you should have a different
// binding off of implementation overloading, if you wanna pass through a string as an arg.
// So, such resolving won't work either: Func<string, T>, as it'll get it as your string argument is a
// service name. Thus, I use 3 type args here.
serviceRegistry.Register<string, string, Dictionary<Direction, AnimatedSprite>>((factory, path, action) =>
{
GraphicsDevice graphicsDevice = factory.GetInstance<SpriteBatch>().GraphicsDevice;
SpriteSheet spriteSheet = AnimatedCharactersFactory.LoadSpriteSheet(graphicsDevice, path);

return AnimatedCharactersFactory.CreateAnimations(spriteSheet, action);
}, "Character");
}
}
Loading

0 comments on commit 2484ac7

Please sign in to comment.