chore: updated engine to use MonoGame integrations
This commit is contained in:
parent
1ff26f5be4
commit
dcee3a0ff0
2
Engine
2
Engine
@ -1 +1 @@
|
||||
Subproject commit 7a3202a053b6fd7c9adf9bc210bf8026f3412328
|
||||
Subproject commit 8d49fb467cda79187a1a5677f7d594af4dbaef6e
|
@ -1,110 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Systems.Input;
|
||||
|
||||
namespace Pong.Platforms.Desktop;
|
||||
|
||||
public class KeyboardInputsBehaviour : Behaviour, IButtonInputs<Keys>
|
||||
{
|
||||
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonPressed { get; } = new();
|
||||
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonReleased { get; } = new();
|
||||
|
||||
private readonly Dictionary<Keys, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>> OnPressed = new(256);
|
||||
private readonly Dictionary<Keys, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>> OnReleased = new(256);
|
||||
|
||||
private int cachePressedCurrentlyCount = 0;
|
||||
private readonly Keys[] cachePressedCurrently = new Keys[256];
|
||||
|
||||
private int cachePressedPreviouslyCount = 0;
|
||||
private readonly Keys[] cachePressedPreviously = new Keys[256];
|
||||
|
||||
public void RegisterOnPress(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
|
||||
{
|
||||
if (!OnPressed.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
|
||||
{
|
||||
delegateCallback = new();
|
||||
OnPressed.Add(key, delegateCallback);
|
||||
}
|
||||
|
||||
delegateCallback.AddListener(callback);
|
||||
}
|
||||
|
||||
public void UnregisterOnPress(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
|
||||
{
|
||||
if (OnPressed.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
|
||||
delegateCallback.RemoveListener(callback);
|
||||
}
|
||||
|
||||
public void RegisterOnRelease(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
|
||||
{
|
||||
if (!OnReleased.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
|
||||
{
|
||||
delegateCallback = new();
|
||||
OnReleased.Add(key, delegateCallback);
|
||||
}
|
||||
|
||||
delegateCallback.AddListener(callback);
|
||||
}
|
||||
|
||||
public void UnregisterOnRelease(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
|
||||
{
|
||||
if (OnReleased.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
|
||||
delegateCallback.RemoveListener(callback);
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
KeyboardState keyboardState = Keyboard.GetState();
|
||||
keyboardState.GetPressedKeys(cachePressedCurrently);
|
||||
cachePressedCurrentlyCount = keyboardState.GetPressedKeyCount();
|
||||
|
||||
for (int i = 0; i < cachePressedCurrentlyCount; i++)
|
||||
{
|
||||
Keys currentlyPressedKey = cachePressedCurrently[i];
|
||||
|
||||
if (WasPressed(currentlyPressedKey))
|
||||
continue;
|
||||
|
||||
if (OnPressed.TryGetValue(currentlyPressedKey, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? callback))
|
||||
callback?.Invoke(this, new(currentlyPressedKey));
|
||||
|
||||
OnAnyButtonPressed?.Invoke(this, new(currentlyPressedKey));
|
||||
}
|
||||
|
||||
for (int i = 0; i < cachePressedPreviouslyCount; i++)
|
||||
{
|
||||
Keys previouslyPressedKey = cachePressedPreviously[i];
|
||||
|
||||
if (IsPressed(previouslyPressedKey))
|
||||
continue;
|
||||
|
||||
if (OnReleased.TryGetValue(previouslyPressedKey, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? callback))
|
||||
callback?.Invoke(this, new(previouslyPressedKey));
|
||||
|
||||
OnAnyButtonReleased?.Invoke(this, new(previouslyPressedKey));
|
||||
}
|
||||
|
||||
Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);
|
||||
cachePressedPreviouslyCount = cachePressedCurrentlyCount;
|
||||
}
|
||||
|
||||
public bool IsPressed(Keys key)
|
||||
{
|
||||
for (int i = 0; i < cachePressedCurrentlyCount; i++)
|
||||
if (cachePressedCurrently[i] == key)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool WasPressed(Keys key)
|
||||
{
|
||||
for (int i = 0; i < cachePressedPreviouslyCount; i++)
|
||||
if (cachePressedPreviously[i] == key)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using Pong.Platforms.Desktop;
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Integration.MonoGame;
|
||||
|
||||
Syntriax.Engine.Core.IUniverseObject universeObject = Syntriax.Engine.Core.Factory.UniverseObjectFactory.Instantiate().SetUniverseObject("Desktop HO");
|
||||
IUniverseObject universeObject = Syntriax.Engine.Core.Factory.UniverseObjectFactory.Instantiate().SetUniverseObject("Desktop HO");
|
||||
universeObject.BehaviourController.AddBehaviour<KeyboardInputsBehaviour>();
|
||||
using var game = new Pong.GamePong(universeObject);
|
||||
game.Run();
|
||||
|
70
Pong.sln
70
Pong.sln
@ -19,36 +19,104 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop", "Platforms\Deskto
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Systems", "Engine\Engine.Systems\Engine.Systems.csproj", "{8863A1BA-2E83-419F-BACB-D4A4156EC71C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine.Integration", "Engine.Integration", "{9059393F-4073-9273-0EEC-2B1BA61B620B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.MonoGame", "Engine\Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{990CA10C-1EBB-4395-A43A-456B7029D8C9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0D97F83C-B043-48B1-B155-7354C4E84FC0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969}.Release|x86.Build.0 = Release|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{590E392E-9FB3-49FA-B4DA-6C8F7126FFE5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01}.Release|x86.Build.0 = Release|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -59,5 +127,7 @@ Global
|
||||
{2F6B1E26-1217-4EFD-874C-05ADEE4C7969} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||
{2B627F66-5A61-4F69-B479-62EEAB603D01} = {FECFFD54-338F-4060-9161-1E5770D1DC33}
|
||||
{8863A1BA-2E83-419F-BACB-D4A4156EC71C} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||
{9059393F-4073-9273-0EEC-2B1BA61B620B} = {F7F62670-237A-4C93-A30E-CE661C6FC401}
|
||||
{7CC31BC4-38EE-40F4-BBBA-9FC2F4CF6283} = {9059393F-4073-9273-0EEC-2B1BA61B620B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@ -1,8 +0,0 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public interface IDisplayableSprite
|
||||
{
|
||||
public void Draw(SpriteBatch spriteBatch);
|
||||
}
|
@ -2,7 +2,7 @@ using Apos.Shapes;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public interface IDisplayableShape
|
||||
public interface IDrawableShape : IBehaviour
|
||||
{
|
||||
void Draw(ShapeBatch shapeBatch);
|
||||
}
|
@ -5,7 +5,7 @@ using Syntriax.Engine.Systems.Tween;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class BallBehaviour : Behaviour2D, IPhysicsUpdate, INetworkEntity,
|
||||
public class BallBehaviour : Behaviour2D, IFirstFrameUpdate, IPhysicsUpdate, INetworkEntity,
|
||||
IPacketListenerClient<BallBehaviour.BallUpdatePacket>
|
||||
{
|
||||
public float Speed { get; set; } = 500f;
|
||||
@ -17,7 +17,7 @@ public class BallBehaviour : Behaviour2D, IPhysicsUpdate, INetworkEntity,
|
||||
private ITweenManager tweenManager = null!;
|
||||
private INetworkCommunicatorServer? networkServer = null;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
BehaviourController.GetRequiredBehaviour<ICollider2D>().OnCollisionDetected.AddListener(OnCollisionDetected);
|
||||
physicsEngine2D = Universe.GetRequiredUniverseObject<IPhysicsEngine2D>();
|
||||
|
@ -1,27 +1,28 @@
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Integration.MonoGame;
|
||||
using Syntriax.Engine.Systems.Input;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class CameraController : Behaviour
|
||||
public class CameraController : Behaviour, IFirstFrameUpdate, IUpdate
|
||||
{
|
||||
private MonoGameCamera2DBehaviour cameraBehaviour = null!;
|
||||
private IButtonInputs<Keys> buttonInputs = null!;
|
||||
|
||||
private float defaultZoomLevel = 1f;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
cameraBehaviour ??= BehaviourController.GetRequiredBehaviour<MonoGameCamera2DBehaviour>();
|
||||
cameraBehaviour = BehaviourController.GetRequiredBehaviour<MonoGameCamera2DBehaviour>();
|
||||
buttonInputs = Universe.FindRequiredBehaviour<IButtonInputs<Keys>>();
|
||||
|
||||
buttonInputs.RegisterOnPress(Keys.F, SwitchToFullScreen);
|
||||
buttonInputs.RegisterOnPress(Keys.R, ResetCamera);
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
public void Update()
|
||||
{
|
||||
if (buttonInputs.IsPressed(Keys.U))
|
||||
cameraBehaviour.Zoom += Universe.Time.DeltaTime * 5f;
|
||||
|
@ -6,7 +6,7 @@ using Syntriax.Engine.Core;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class CircleBehaviour : Syntriax.Engine.Physics2D.Collider2DCircleBehaviour, IDisplayableShape
|
||||
public class CircleBehaviour : Syntriax.Engine.Physics2D.Collider2DCircleBehaviour, IDrawableShape
|
||||
{
|
||||
public Color Color { get; set; } = Color.White;
|
||||
public float Thickness { get; set; } = .5f;
|
||||
|
@ -1,101 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class MonoGameCamera2DBehaviour(GraphicsDeviceManager Graphics) : Behaviour2D, ICamera2D
|
||||
{
|
||||
public event OnMatrixTransformChangedDelegate? OnMatrixTransformChanged = null;
|
||||
public event OnViewportChangedDelegate? OnViewportChanged = null;
|
||||
public event OnZoomChangedDelegate? OnZoomChanged = null;
|
||||
|
||||
private Matrix _matrixTransform = Matrix.Identity;
|
||||
|
||||
private Viewport _viewport = default;
|
||||
private float _zoom = 1f;
|
||||
|
||||
public GraphicsDeviceManager Graphics { get; } = Graphics;
|
||||
|
||||
public Matrix MatrixTransform
|
||||
{
|
||||
get => _matrixTransform;
|
||||
set
|
||||
{
|
||||
if (_matrixTransform == value)
|
||||
return;
|
||||
|
||||
_matrixTransform = value;
|
||||
OnMatrixTransformChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2D Position
|
||||
{
|
||||
get => Transform.Position;
|
||||
set => Transform.Position = value;
|
||||
}
|
||||
|
||||
public Viewport Viewport
|
||||
{
|
||||
get => _viewport;
|
||||
set
|
||||
{
|
||||
if (_viewport.Equals(value))
|
||||
return;
|
||||
|
||||
_viewport = value;
|
||||
OnViewportChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get => _zoom;
|
||||
set
|
||||
{
|
||||
float newValue = Math.Max(0.1f, value);
|
||||
|
||||
if (_zoom == newValue)
|
||||
return;
|
||||
|
||||
_zoom = newValue;
|
||||
OnZoomChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get => Transform.Rotation;
|
||||
set => Transform.Rotation = value;
|
||||
}
|
||||
|
||||
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
|
||||
public Vector2D ScreenToWorldPosition(Vector2D screenPosition)
|
||||
{
|
||||
Vector2D worldPosition = Vector2.Transform(screenPosition.ToVector2(), Matrix.Invert(MatrixTransform)).ToVector2D();
|
||||
return worldPosition.Scale(EngineConverter.screenScale);
|
||||
}
|
||||
public Vector2D WorldToScreenPosition(Vector2D worldPosition)
|
||||
{
|
||||
Vector2D screenPosition = Vector2.Transform(worldPosition.ToVector2(), MatrixTransform).ToVector2D();
|
||||
return screenPosition.Scale(EngineConverter.screenScale);
|
||||
}
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
=> Viewport = Graphics.GraphicsDevice.Viewport;
|
||||
|
||||
protected override void OnPreDraw()
|
||||
{
|
||||
MatrixTransform =
|
||||
Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) *
|
||||
Matrix.CreateRotationZ(Rotation * Math.DegreeToRadian) *
|
||||
Matrix.CreateScale(Zoom) *
|
||||
Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f));
|
||||
}
|
||||
|
||||
public delegate void OnMatrixTransformChangedDelegate(MonoGameCamera2DBehaviour sender);
|
||||
public delegate void OnViewportChangedDelegate(MonoGameCamera2DBehaviour sender);
|
||||
public delegate void OnZoomChangedDelegate(MonoGameCamera2DBehaviour sender);
|
||||
}
|
@ -3,7 +3,7 @@ using Syntriax.Engine.Physics2D;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class MovementBallBehaviour : Behaviour2D
|
||||
public class MovementBallBehaviour : Behaviour2D, IFirstFrameUpdate, IUpdate
|
||||
{
|
||||
public Vector2D StartDirection { get; private set; } = Vector2D.Zero;
|
||||
public float Speed { get; set; } = 500f;
|
||||
@ -11,7 +11,7 @@ public class MovementBallBehaviour : Behaviour2D
|
||||
|
||||
private IRigidBody2D rigidBody = null!;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
rigidBody = BehaviourController.GetRequiredBehaviour<IRigidBody2D>();
|
||||
|
||||
@ -19,7 +19,7 @@ public class MovementBallBehaviour : Behaviour2D
|
||||
BehaviourController.GetRequiredBehaviour<ICollider2D>().OnCollisionDetected.AddListener(OnCollisionDetected);
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
public void Update()
|
||||
{
|
||||
if (rigidBody.Velocity.MagnitudeSquared <= 0.01f)
|
||||
return;
|
||||
|
@ -9,7 +9,7 @@ using Syntriax.Engine.Systems.Input;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : Behaviour2D, IPostPhysicsUpdate,
|
||||
public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Speed) : Behaviour2D, IFirstFrameUpdate, IPostPhysicsUpdate,
|
||||
IPacketListenerServer<PaddleBehaviour.PaddleKeyStatePacket>, IPacketListenerClient<PaddleBehaviour.PaddleKeyStatePacket>
|
||||
{
|
||||
private Keys Up { get; } = Up;
|
||||
@ -32,7 +32,7 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
|
||||
Transform.Position = new Vector2D(Transform.Position.X, MathF.Max(MathF.Min(Transform.Position.Y, High), Low));
|
||||
}
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
physicsEngine2D = Universe.GetRequiredUniverseObject<IPhysicsEngine2D>();
|
||||
inputs = Universe.FindRequired<IButtonInputs<Keys>>();
|
||||
@ -99,14 +99,18 @@ public class PaddleBehaviour(Keys Up, Keys Down, float High, float Low, float Sp
|
||||
public bool IsDownPressed { get; set; } = default!;
|
||||
public long Timestamp { get; set; } = 0;
|
||||
|
||||
public PaddleKeyStatePacket() { }
|
||||
public PaddleKeyStatePacket(PaddleBehaviour paddleBehaviour)
|
||||
public PaddleKeyStatePacket Set(PaddleBehaviour paddleBehaviour)
|
||||
{
|
||||
EntityId = paddleBehaviour.Id;
|
||||
Position = paddleBehaviour.Transform.Position;
|
||||
IsUpPressed = paddleBehaviour.isUpPressed;
|
||||
IsDownPressed = paddleBehaviour.isDownPressed;
|
||||
Timestamp = DateTime.UtcNow.Ticks;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaddleKeyStatePacket() { }
|
||||
public PaddleKeyStatePacket(PaddleBehaviour paddleBehaviour) => Set(paddleBehaviour);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using Syntriax.Engine.Systems.Input;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class PongManagerBehaviour : Behaviour, INetworkEntity,
|
||||
public class PongManagerBehaviour : Behaviour, INetworkEntity, IFirstFrameUpdate,
|
||||
IPacketListenerServer<PongManagerBehaviour.RequestStartPacket>
|
||||
{
|
||||
public Action<PongManagerBehaviour>? OnReset { get; set; } = null;
|
||||
@ -30,7 +30,7 @@ public class PongManagerBehaviour : Behaviour, INetworkEntity,
|
||||
public PongManagerBehaviour() => WinScore = 5;
|
||||
public PongManagerBehaviour(int winScore) => WinScore = winScore;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
IButtonInputs<Keys> buttonInputs = Universe.FindRequired<IButtonInputs<Keys>>();
|
||||
buttonInputs.RegisterOnRelease(Keys.Space, (_, _1) => networkClient?.SendToServer(new RequestStartPacket()));
|
||||
|
@ -8,7 +8,7 @@ using Syntriax.Engine.Systems.Input;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class ShapeAABBBehaviour : Behaviour2D, IDisplayableShape
|
||||
public class ShapeAABBBehaviour : Behaviour2D, IDrawableShape, IFirstFrameUpdate
|
||||
{
|
||||
private IShapeCollider2D? shapeCollider = null;
|
||||
|
||||
@ -16,7 +16,7 @@ public class ShapeAABBBehaviour : Behaviour2D, IDisplayableShape
|
||||
public float Thickness { get; set; } = .5f;
|
||||
public bool display = true;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
BehaviourController.TryGetBehaviour(out shapeCollider);
|
||||
|
||||
|
35
Shared/Behaviours/ShapeBatcher.cs
Normal file
35
Shared/Behaviours/ShapeBatcher.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Apos.Shapes;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Integration.MonoGame;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class ShapeBatcher : BehaviourBase, IFirstFrameUpdate, IDraw
|
||||
{
|
||||
private static Comparer<IBehaviour> SortByPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
|
||||
private ShapeBatch shapeBatch = null!;
|
||||
private MonoGameCamera2DBehaviour camera2D = null!;
|
||||
private readonly ActiveBehaviourCollectorSorted<IDrawableShape> drawableShapes = new() { SortBy = SortByPriority() };
|
||||
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
MonoGameWindowContainer windowContainer = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
|
||||
camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameCamera2DBehaviour>();
|
||||
|
||||
shapeBatch = new(windowContainer.Window.GraphicsDevice, windowContainer.Window.Content);
|
||||
|
||||
drawableShapes.Unassign();
|
||||
drawableShapes.Assign(Universe);
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
shapeBatch.Begin(camera2D.MatrixTransform);
|
||||
for (int i = drawableShapes.Count - 1; i >= 0; i--)
|
||||
drawableShapes[i].Draw(shapeBatch);
|
||||
shapeBatch.End();
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ using Syntriax.Engine.Core;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class ShapeBehaviour : Syntriax.Engine.Physics2D.Collider2DShapeBehaviour, IDisplayableShape
|
||||
public class ShapeBehaviour : Syntriax.Engine.Physics2D.Collider2DShapeBehaviour, IDrawableShape
|
||||
{
|
||||
public Color Color { get; set; } = Color.White;
|
||||
public float Thickness { get; set; } = .5f;
|
||||
|
@ -2,10 +2,11 @@ using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Integration.MonoGame;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class TextBehaviour : Behaviour2D, IDisplayableSprite
|
||||
public class TextBehaviour : Behaviour2D, IDrawableSprite
|
||||
{
|
||||
public SpriteFont? Font { get; set; } = null;
|
||||
public int Size { get; set; } = 16;
|
||||
@ -13,7 +14,7 @@ public class TextBehaviour : Behaviour2D, IDisplayableSprite
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!IsActive || Font is null)
|
||||
if (Font is null)
|
||||
return;
|
||||
|
||||
spriteBatch.DrawString(Font, Text, Transform.Position.ToDisplayVector2(), Color.White, Transform.Rotation, Vector2.One * .5f, Transform.Scale.Magnitude, SpriteEffects.None, 0f);
|
||||
|
@ -3,13 +3,13 @@ using Syntriax.Engine.Core;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class TextScoreBehaviour : TextBehaviour
|
||||
public class TextScoreBehaviour : TextBehaviour, IFirstFrameUpdate
|
||||
{
|
||||
public bool IsLeft { get; }
|
||||
|
||||
private PongManagerBehaviour? pongManager = null;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
if (!UniverseObject.Universe.TryFindBehaviour(out pongManager))
|
||||
return;
|
||||
|
@ -5,11 +5,11 @@ using Syntriax.Engine.Physics2D;
|
||||
|
||||
namespace Pong.Behaviours;
|
||||
|
||||
public class WallScoreBehaviour(Action OnCollision) : Behaviour2D
|
||||
public class WallScoreBehaviour(Action OnCollision) : Behaviour2D, IFirstFrameUpdate
|
||||
{
|
||||
private Action OnCollision { get; } = OnCollision;
|
||||
|
||||
protected override void OnFirstActiveFrame()
|
||||
public void FirstActiveFrame()
|
||||
{
|
||||
if (!BehaviourController.TryGetBehaviour(out ICollider2D? collider2D))
|
||||
return;
|
||||
|
@ -5,8 +5,6 @@ using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
using Apos.Shapes;
|
||||
|
||||
using Pong.Behaviours;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
@ -14,53 +12,24 @@ using Syntriax.Engine.Core.Factory;
|
||||
using Syntriax.Engine.Network;
|
||||
using Syntriax.Engine.Physics2D;
|
||||
using Syntriax.Engine.Systems.Tween;
|
||||
using Syntriax.Engine.Integration.MonoGame;
|
||||
|
||||
namespace Pong;
|
||||
|
||||
public class GamePong : Game
|
||||
public class GamePong : MonoGameWindow
|
||||
{
|
||||
private readonly IUniverseObject platformSpecificUniverseObject = null!;
|
||||
private readonly GraphicsDeviceManager graphics = null!;
|
||||
private SpriteBatch spriteBatch = null!;
|
||||
private ShapeBatch shapeBatch = null!;
|
||||
|
||||
private Universe universe = null!;
|
||||
private BehaviourCollector<IDisplayableSprite> displayableCollector = null!;
|
||||
private BehaviourCollector<IDisplayableShape> displayableShapeCollector = null!;
|
||||
private MonoGameCamera2DBehaviour cameraBehaviour = null!;
|
||||
private PongManagerBehaviour pongManager = null!;
|
||||
|
||||
public GamePong(IUniverseObject platformSpecificUniverseObject)
|
||||
{
|
||||
this.platformSpecificUniverseObject = platformSpecificUniverseObject;
|
||||
graphics = new GraphicsDeviceManager(this)
|
||||
{
|
||||
PreferredBackBufferWidth = 1024,
|
||||
PreferredBackBufferHeight = 576,
|
||||
GraphicsProfile = GraphicsProfile.HiDef
|
||||
};
|
||||
|
||||
Content.RootDirectory = "Content";
|
||||
IsMouseVisible = true;
|
||||
Graphics.PreferredBackBufferWidth = 1024;
|
||||
Graphics.PreferredBackBufferHeight = 576;
|
||||
Graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
protected override void PopulateUniverse(IUniverse universe)
|
||||
{
|
||||
// TODO: Add your initialization logic here
|
||||
universe = new();
|
||||
|
||||
universe.Initialize();
|
||||
|
||||
displayableCollector = new(universe);
|
||||
displayableShapeCollector = new(universe);
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
protected override void LoadContent()
|
||||
{
|
||||
spriteBatch = new SpriteBatch(GraphicsDevice);
|
||||
shapeBatch = new ShapeBatch(GraphicsDevice, Content);
|
||||
SpriteFont spriteFont = Content.Load<SpriteFont>("UbuntuMono");
|
||||
|
||||
universe.Register(platformSpecificUniverseObject);
|
||||
@ -79,7 +48,9 @@ public class GamePong : Game
|
||||
client.Connect("localhost", 8888);
|
||||
Window.Title = $"Client";
|
||||
|
||||
universe.InstantiateUniverseObject<DrawManager>().SetUniverseObject("Draw Manager");
|
||||
DrawManager drawManager = universe.InstantiateUniverseObject<DrawManager>().SetUniverseObject("Draw Manager");
|
||||
universe.InstantiateUniverseObject().SetUniverseObject("Shape Batcher", drawManager).BehaviourController.AddBehaviour<ShapeBatcher>();
|
||||
universe.InstantiateUniverseObject().SetUniverseObject("Sprite Batcher", drawManager).BehaviourController.AddBehaviour<SpriteBatcher>();
|
||||
}
|
||||
|
||||
universe.InstantiateUniverseObject<UpdateManager>().SetUniverseObject("Update Manager");
|
||||
@ -89,14 +60,14 @@ public class GamePong : Game
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
cameraBehaviour = universe.InstantiateUniverseObject().SetUniverseObject("Camera")
|
||||
universe.InstantiateUniverseObject().SetUniverseObject("Camera")
|
||||
.BehaviourController.AddBehaviour<Transform2D>()
|
||||
.BehaviourController.AddBehaviour<CameraController>()
|
||||
.BehaviourController.AddBehaviour<MonoGameCamera2DBehaviour>(graphics);
|
||||
.BehaviourController.AddBehaviour<MonoGameCamera2DBehaviour>();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pongManager = universe.InstantiateUniverseObject().SetUniverseObject("Pong Game Manager")
|
||||
PongManagerBehaviour pongManager = universe.InstantiateUniverseObject().SetUniverseObject("Pong Game Manager")
|
||||
.BehaviourController.AddBehaviour<PongManagerBehaviour>(5);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -161,34 +132,4 @@ public class GamePong : Game
|
||||
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(250f, 250f), scale: Vector2D.One * .25f)
|
||||
.BehaviourController.AddBehaviour<TextScoreBehaviour>(false, spriteFont);
|
||||
}
|
||||
|
||||
protected override void Update(GameTime gameTime)
|
||||
{
|
||||
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
|
||||
Exit();
|
||||
|
||||
universe.Update(gameTime.ToUniverseTime());
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
protected override void Draw(GameTime gameTime)
|
||||
{
|
||||
GraphicsDevice.Clear(new Color() { R = 32, G = 32, B = 32 });
|
||||
|
||||
// TODO: Add your drawing code here
|
||||
universe.Draw();
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, transformMatrix: cameraBehaviour.MatrixTransform);
|
||||
for (int i = displayableCollector.Count - 1; i >= 0; i--)
|
||||
displayableCollector[i].Draw(spriteBatch);
|
||||
spriteBatch.End();
|
||||
|
||||
shapeBatch.Begin(cameraBehaviour.MatrixTransform);
|
||||
for (int i = displayableShapeCollector.Count - 1; i >= 0; i--)
|
||||
displayableShapeCollector[i].Draw(shapeBatch);
|
||||
shapeBatch.End();
|
||||
|
||||
base.Draw(gameTime);
|
||||
}
|
||||
}
|
||||
|
325
Shared/Network/NetworkManagerSlo.cs
Normal file
325
Shared/Network/NetworkManagerSlo.cs
Normal file
@ -0,0 +1,325 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Network;
|
||||
|
||||
public class NetworkManagerSlo : UniverseObject, INetworkManager
|
||||
{
|
||||
private readonly List<(Type packetType, Delegate callback)> packetDelegates = [];
|
||||
|
||||
private readonly Dictionary<Type, Dictionary<Type, List<DelegateData>>> clientListenerDelegates = [];
|
||||
private readonly Dictionary<Type, Dictionary<Type, List<DelegateData>>> serverListenerDelegates = [];
|
||||
|
||||
private readonly Dictionary<Type, Dictionary<string, Event<string, object>>> clientPacketListeners = [];
|
||||
private readonly Dictionary<Type, Dictionary<string, Event<string, object>>> serverPacketListeners = [];
|
||||
|
||||
private readonly Dictionary<string, INetworkEntity> _networkEntities = [];
|
||||
private readonly BehaviourCollector<INetworkEntity> _networkEntityCollector = new();
|
||||
|
||||
public IReadOnlyDictionary<string, INetworkEntity> NetworkEntities => _networkEntities;
|
||||
public IBehaviourCollector<INetworkEntity> NetworkEntityCollector => _networkEntityCollector;
|
||||
|
||||
private INetworkCommunicator _networkCommunicator = null!;
|
||||
public INetworkCommunicator NetworkCommunicator
|
||||
{
|
||||
get => _networkCommunicator;
|
||||
set
|
||||
{
|
||||
if (_networkCommunicator == value)
|
||||
return;
|
||||
|
||||
INetworkCommunicator previousCommunicator = _networkCommunicator;
|
||||
_networkCommunicator = value;
|
||||
|
||||
if (previousCommunicator is not null)
|
||||
UnsubscribeDelegates(previousCommunicator);
|
||||
|
||||
if (_networkCommunicator is not null)
|
||||
SubscribeDelegates(_networkCommunicator);
|
||||
}
|
||||
}
|
||||
|
||||
#region Network Communicator Subscriptions
|
||||
private static MethodInfo? GetCommunicatorMethod(string methodName)
|
||||
=> typeof(INetworkCommunicator).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
private void SubscribeDelegates(INetworkCommunicator communicator)
|
||||
{
|
||||
MethodInfo subscribeMethod = GetCommunicatorMethod(nameof(INetworkCommunicator.SubscribeToPackets))!;
|
||||
|
||||
foreach ((Type packetType, Delegate callback) in packetDelegates)
|
||||
{
|
||||
MethodInfo genericMethod = subscribeMethod.MakeGenericMethod(packetType);
|
||||
genericMethod.Invoke(communicator, [callback]);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeDelegates(INetworkCommunicator communicator)
|
||||
{
|
||||
MethodInfo unsubscribeMethod = GetCommunicatorMethod(nameof(INetworkCommunicator.UnsubscribeFromPackets))!;
|
||||
|
||||
foreach ((Type packetType, Delegate callback) in packetDelegates)
|
||||
{
|
||||
MethodInfo genericMethod = unsubscribeMethod.MakeGenericMethod(packetType);
|
||||
genericMethod.Invoke(communicator, [callback]);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Engine Callbacks
|
||||
private void OnEntityCollected(IBehaviourCollector<INetworkEntity> sender, IBehaviourCollector<INetworkEntity>.BehaviourCollectedArguments args)
|
||||
{
|
||||
INetworkEntity entity = args.BehaviourCollected;
|
||||
|
||||
if (!_networkEntities.TryAdd(entity.Id, entity))
|
||||
throw new Exception($"Unable to add {entity.Id} to {nameof(NetworkManager)}");
|
||||
|
||||
RegisterEntityListeners(entity);
|
||||
}
|
||||
|
||||
private void OnEntityRemoved(IBehaviourCollector<INetworkEntity> sender, IBehaviourCollector<INetworkEntity>.BehaviourRemovedArguments args)
|
||||
{
|
||||
INetworkEntity removedBehaviour = args.BehaviourRemoved;
|
||||
if (!_networkEntities.Remove(args.BehaviourRemoved.Id))
|
||||
return;
|
||||
|
||||
UnRegisterEntityListeners(removedBehaviour);
|
||||
}
|
||||
|
||||
protected override void OnExitingUniverse(IUniverse universe)
|
||||
{
|
||||
_networkEntityCollector.Unassign();
|
||||
}
|
||||
|
||||
protected override void OnEnteringUniverse(IUniverse universe)
|
||||
{
|
||||
_networkEntityCollector.Assign(universe);
|
||||
NetworkCommunicator = this.GetRequiredUniverseObjectInParent<INetworkCommunicator>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Packet Retrieval
|
||||
private void OnPacketReceived<T>(string senderClientId, T packet)
|
||||
{
|
||||
if (packet is IEntityNetworkPacket entityPacket)
|
||||
RouteEntityPacket(senderClientId, entityPacket.EntityId, packet);
|
||||
else
|
||||
RouteBroadcastPacket(senderClientId, packet!);
|
||||
}
|
||||
|
||||
private void RouteEntityPacket<T>(string senderClientId, string entityId, T packet)
|
||||
{
|
||||
if (NetworkCommunicator is INetworkCommunicatorClient)
|
||||
InvokeListenerIfExists(clientPacketListeners, entityId, senderClientId, packet!);
|
||||
|
||||
if (NetworkCommunicator is INetworkCommunicatorServer)
|
||||
InvokeListenerIfExists(serverPacketListeners, entityId, senderClientId, packet!);
|
||||
}
|
||||
|
||||
private void RouteBroadcastPacket<T>(string senderClientId, T packet)
|
||||
{
|
||||
if (NetworkCommunicator is INetworkCommunicatorClient)
|
||||
InvokeAllListeners(clientPacketListeners, senderClientId, packet!);
|
||||
|
||||
if (NetworkCommunicator is INetworkCommunicatorServer)
|
||||
InvokeAllListeners(serverPacketListeners, senderClientId, packet!);
|
||||
}
|
||||
|
||||
private static void InvokeListenerIfExists<T>(
|
||||
Dictionary<Type, Dictionary<string, Event<string, object>>> listeners,
|
||||
string entityId,
|
||||
string senderClientId,
|
||||
T packet)
|
||||
{
|
||||
if (!listeners.TryGetValue(packet!.GetType(), out Dictionary<string, Event<string, object>>? entityListeners))
|
||||
return;
|
||||
|
||||
if (entityListeners.TryGetValue(entityId, out Event<string, object>? listenerEvent))
|
||||
listenerEvent.Invoke(senderClientId, packet);
|
||||
}
|
||||
|
||||
private static void InvokeAllListeners<T>(
|
||||
Dictionary<Type, Dictionary<string, Event<string, object>>> listeners,
|
||||
string senderClientId,
|
||||
T packet)
|
||||
{
|
||||
if (!listeners.TryGetValue(packet!.GetType(), out Dictionary<string, Event<string, object>>? entityListeners))
|
||||
return;
|
||||
|
||||
foreach ((string _, Event<string, object> listenerEvent) in entityListeners)
|
||||
listenerEvent.Invoke(senderClientId, packet);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Listener Setups
|
||||
private void RegisterEntityListeners(INetworkEntity entity)
|
||||
{
|
||||
RegisterEntityListenersForSide(entity, clientListenerDelegates, clientPacketListeners, isServer: false);
|
||||
RegisterEntityListenersForSide(entity, serverListenerDelegates, serverPacketListeners, isServer: true);
|
||||
}
|
||||
|
||||
private static void RegisterEntityListenersForSide(
|
||||
INetworkEntity entity,
|
||||
Dictionary<Type, Dictionary<Type, List<DelegateData>>> listenerDelegates,
|
||||
Dictionary<Type, Dictionary<string, Event<string, object>>> packetListeners,
|
||||
bool isServer)
|
||||
{
|
||||
if (!listenerDelegates.TryGetValue(entity.GetType(), out Dictionary<Type, List<DelegateData>>? interfaceDelegates))
|
||||
return;
|
||||
|
||||
foreach ((Type interfaceType, List<DelegateData> delegateDataList) in interfaceDelegates)
|
||||
foreach ((Type parameterType, MethodInfo receiveMethod) in delegateDataList)
|
||||
{
|
||||
Dictionary<string, Event<string, object>> listeners = GetOrCreateListenersForPacketType(packetListeners, parameterType);
|
||||
Event<string, object> listenerEvent = CreateListenerEvent(entity, receiveMethod, isServer);
|
||||
listeners.Add(entity.Id, listenerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnRegisterEntityListeners(INetworkEntity entity)
|
||||
{
|
||||
UnregisterEntityListenersForSide(entity, clientListenerDelegates, clientPacketListeners);
|
||||
UnregisterEntityListenersForSide(entity, serverListenerDelegates, serverPacketListeners);
|
||||
}
|
||||
|
||||
private static void UnregisterEntityListenersForSide(
|
||||
INetworkEntity entity,
|
||||
Dictionary<Type, Dictionary<Type, List<DelegateData>>> listenerDelegates,
|
||||
Dictionary<Type, Dictionary<string, Event<string, object>>> packetListeners)
|
||||
{
|
||||
if (!listenerDelegates.TryGetValue(entity.GetType(), out Dictionary<Type, List<DelegateData>>? interfaceDelegates))
|
||||
return;
|
||||
|
||||
foreach ((Type interfaceType, List<DelegateData> delegateDataList) in interfaceDelegates)
|
||||
foreach ((Type parameterType, MethodInfo receiveMethod) in delegateDataList)
|
||||
{
|
||||
Dictionary<string, Event<string, object>> listeners = GetOrCreateListenersForPacketType(packetListeners, parameterType);
|
||||
|
||||
if (!listeners.TryGetValue(entity.Id, out Event<string, object>? listenerEvent))
|
||||
continue;
|
||||
|
||||
listeners.Remove(entity.Id);
|
||||
listenerDelegates.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, Event<string, object>> GetOrCreateListenersForPacketType(
|
||||
Dictionary<Type, Dictionary<string, Event<string, object>>> packetListeners,
|
||||
Type packetType)
|
||||
{
|
||||
if (!packetListeners.TryGetValue(packetType, out Dictionary<string, Event<string, object>>? listeners))
|
||||
{
|
||||
listeners = [];
|
||||
packetListeners.Add(packetType, listeners);
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
private static Event<string, object> CreateListenerEvent(INetworkEntity entity, MethodInfo receiveMethod, bool isServer)
|
||||
{
|
||||
Event<string, object> listenerEvent = new();
|
||||
|
||||
if (isServer)
|
||||
listenerEvent.AddListener((sender, packet) => receiveMethod.Invoke(entity, [sender, packet]));
|
||||
else
|
||||
listenerEvent.AddListener((sender, packet) => receiveMethod.Invoke(entity, [packet]));
|
||||
|
||||
return listenerEvent;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
private static IEnumerable<Type> DiscoverPacketTypes()
|
||||
=> AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.Where(
|
||||
t => typeof(INetworkPacket).IsAssignableFrom(t)
|
||||
&& !t.IsInterface
|
||||
&& !t.IsAbstract
|
||||
&& !t.IsGenericType
|
||||
);
|
||||
|
||||
private static IEnumerable<Type> DiscoverClassesImplementing(Type interfaceType)
|
||||
=> AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes())
|
||||
.Where(
|
||||
t => t.GetInterfaces().Any(
|
||||
i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType
|
||||
)
|
||||
);
|
||||
|
||||
private static IEnumerable<Type> GetInterfacesOfType(Type type, Type interfaceType)
|
||||
=> type.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType);
|
||||
|
||||
private static void CacheListenerDelegatesForSide(Dictionary<Type, Dictionary<Type, List<DelegateData>>> targetCache, Type listenerInterfaceType, string methodName)
|
||||
{
|
||||
IEnumerable<Type> listenerClasses = DiscoverClassesImplementing(listenerInterfaceType);
|
||||
|
||||
foreach (Type listenerClass in listenerClasses)
|
||||
{
|
||||
Dictionary<Type, List<DelegateData>> interfaceListeners = [];
|
||||
targetCache.Add(listenerClass, interfaceListeners);
|
||||
|
||||
IEnumerable<Type> interfaces = GetInterfacesOfType(listenerClass, listenerInterfaceType);
|
||||
|
||||
foreach (Type listenerInterface in interfaces)
|
||||
{
|
||||
Type packetType = listenerInterface.GetGenericArguments().First();
|
||||
|
||||
List<DelegateData> methods = listenerInterface.GetMethods()
|
||||
.Where(m => m.Name == methodName)
|
||||
.Select(m => new DelegateData(packetType, m))
|
||||
.ToList();
|
||||
|
||||
interfaceListeners.Add(packetType, methods);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CacheListenerDelegates()
|
||||
{
|
||||
CacheListenerDelegatesForSide(clientListenerDelegates, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient<INetworkEntity>.OnClientPacketArrived));
|
||||
CacheListenerDelegatesForSide(serverListenerDelegates, typeof(IPacketListenerServer<>), nameof(IPacketListenerServer<INetworkEntity>.OnServerPacketArrived));
|
||||
}
|
||||
|
||||
private void CachePacketDelegates()
|
||||
{
|
||||
IEnumerable<Type> packetTypes = DiscoverPacketTypes();
|
||||
MethodInfo onPacketReceivedMethod = GetType().GetMethod(
|
||||
nameof(OnPacketReceived),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance
|
||||
)!;
|
||||
|
||||
foreach (Type packetType in packetTypes)
|
||||
{
|
||||
MethodInfo genericMethod = onPacketReceivedMethod.MakeGenericMethod(packetType);
|
||||
Type delegateType = typeof(Event<,>.EventHandler).MakeGenericType(typeof(string), packetType);
|
||||
Delegate packetDelegate = Delegate.CreateDelegate(delegateType, this, genericMethod);
|
||||
|
||||
packetDelegates.Add((packetType, packetDelegate));
|
||||
}
|
||||
}
|
||||
|
||||
public NetworkManagerSlo()
|
||||
{
|
||||
CachePacketDelegates();
|
||||
CacheListenerDelegates();
|
||||
|
||||
_networkEntityCollector.OnCollected.AddListener(OnEntityCollected);
|
||||
_networkEntityCollector.OnRemoved.AddListener(OnEntityRemoved);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private readonly record struct DelegateData(Type ParameterType, MethodInfo ReceiveMethod)
|
||||
{
|
||||
public static implicit operator (Type ParameterType, MethodInfo ReceiveMethod)(DelegateData value)
|
||||
=> (value.ParameterType, value.ReceiveMethod);
|
||||
|
||||
public static implicit operator DelegateData((Type ParameterType, MethodInfo ReceiveMethod) value)
|
||||
=> new(value.ParameterType, value.ReceiveMethod);
|
||||
}
|
||||
}
|
@ -13,5 +13,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Engine/Engine/Engine.csproj" />
|
||||
<ProjectReference Include="../Engine/Engine.Integration/Engine.Integration.MonoGame/Engine.Integration.MonoGame.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
x
Reference in New Issue
Block a user