Compare commits

..

No commits in common. "main" and "feat/litenetlib" have entirely different histories.

16 changed files with 56 additions and 249 deletions

2
.vscode/launch.json vendored
View File

@ -6,7 +6,7 @@
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build-client", "preLaunchTask": "build-client",
"program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net9.0/Pong.dll", "program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net9.0/Desktop.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"stopAtEntry": false, "stopAtEntry": false,

2
Engine

@ -1 +1 @@
Subproject commit 03232f72e8f045fa3b5731a2b3839425f8ea9721 Subproject commit 83b155fc5e8fd1892278876cb90e10dcb64e2154

View File

@ -19,10 +19,22 @@ ILogger logger = new FileLogger($"Logs/{DateTime.UtcNow:yyyy-MM-dd_HH-mm-ss-ffff
logger = new LoggerWrapper(logger, new ConsoleLogger()); logger = new LoggerWrapper(logger, new ConsoleLogger());
#endif #endif
universe.InstantiateUniverseObject().SetUniverseObject("Logger").BehaviourController.AddBehaviour<LoggerContainer>().Logger = ILogger.Shared = logger; universe.InstantiateUniverseObject().SetUniverseObject("Logger").BehaviourController.AddBehaviour<LoggerContainer>().Logger = logger;
string settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.yaml"); string settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.yaml");
ClientConfiguration clientConfiguration = GetOrCreateConfiguration(serializer, logger, settingsPath); ClientConfiguration clientConfiguration;
try
{
clientConfiguration = serializer.Deserialize<ClientConfiguration>(File.ReadAllText(settingsPath));
logger.Log(clientConfiguration, $"{settingsPath} read");
}
catch (Exception exception)
{
clientConfiguration = new();
logger.LogError(serializer, $"Error loading {settingsPath}. Creating new settings file");
logger.LogException(serializer, exception);
File.WriteAllText(settingsPath, serializer.Serialize(clientConfiguration));
}
if (clientConfiguration.Host) if (clientConfiguration.Host)
Pong.PongUniverse.ApplyPongServer(universe, clientConfiguration.HostPort); Pong.PongUniverse.ApplyPongServer(universe, clientConfiguration.HostPort);
@ -45,30 +57,3 @@ universe.FindBehaviour<Syntriax.Engine.Network.INetworkCommunicatorClient>()?
); );
monoGameWindow.Run(); monoGameWindow.Run();
static ClientConfiguration GetOrCreateConfiguration(ISerializer serializer, ILogger logger, string settingsPath)
{
ClientConfiguration clientConfiguration;
try
{
clientConfiguration = serializer.Deserialize<ClientConfiguration>(File.ReadAllText(settingsPath));
logger.Log(clientConfiguration, $"Configuration is successfully read");
}
catch (FileNotFoundException)
{
clientConfiguration = new();
logger.Log(clientConfiguration, $"Configuration does not exist");
File.WriteAllText(settingsPath, serializer.Serialize(clientConfiguration));
}
catch (Exception exception)
{
clientConfiguration = new();
logger.LogError(clientConfiguration, $"Error loading configuration");
logger.LogException(clientConfiguration, exception);
File.WriteAllText(settingsPath, serializer.Serialize(clientConfiguration));
}
logger.Log(clientConfiguration, $"Creating new configuration file at {settingsPath}");
return clientConfiguration;
}

View File

@ -8,7 +8,7 @@ Universe universe = new();
FileLogger fileLogger = new($"Logs/{DateTime.UtcNow:yyyy-MM-dd_HH-mm-ss-ffffff}.log"); FileLogger fileLogger = new($"Logs/{DateTime.UtcNow:yyyy-MM-dd_HH-mm-ss-ffffff}.log");
universe.InstantiateUniverseObject().SetUniverseObject("Logger").BehaviourController universe.InstantiateUniverseObject().SetUniverseObject("Logger").BehaviourController
.AddBehaviour<LoggerContainer>().Logger = ILogger.Shared = new LoggerWrapper(fileLogger, new ConsoleLogger()); .AddBehaviour<LoggerContainer>().Logger = new LoggerWrapper(fileLogger, new ConsoleLogger());
Pong.PongUniverse.ApplyPongServer(universe, int.Parse(Environment.GetEnvironmentVariable("PORT") ?? "8888")); Pong.PongUniverse.ApplyPongServer(universe, int.Parse(Environment.GetEnvironmentVariable("PORT") ?? "8888"));
Pong.PongUniverse.ApplyPongUniverse(universe); Pong.PongUniverse.ApplyPongUniverse(universe);

View File

@ -1,15 +1,11 @@
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Integration.MonoGame;
using Syntriax.Engine.Network; using Syntriax.Engine.Network;
using Syntriax.Engine.Physics2D; using Syntriax.Engine.Physics2D;
using Syntriax.Engine.Systems.Tween; using Syntriax.Engine.Systems.Tween;
namespace Pong.Behaviours; namespace Pong.Behaviours;
public class Ball : Behaviour2D, IFirstFrameUpdate, ILoadContent, IPhysicsUpdate, INetworkEntity, public class Ball : Behaviour2D, IFirstFrameUpdate, IPhysicsUpdate, INetworkEntity,
IPacketListenerClient<Ball.BallUpdatePacket>, IPacketListenerClient<Ball.BallUpdatePacket>,
IPacketListenerClient<Ball.BallResetPacket> IPacketListenerClient<Ball.BallResetPacket>
{ {
@ -23,8 +19,6 @@ public class Ball : Behaviour2D, IFirstFrameUpdate, ILoadContent, IPhysicsUpdate
private INetworkCommunicatorServer? networkServer = null; private INetworkCommunicatorServer? networkServer = null;
private ITween? networkTween = null; private ITween? networkTween = null;
private SoundEffect? bounceSoundEffect = null;
public void FirstActiveFrame() public void FirstActiveFrame()
{ {
BehaviourController.GetRequiredBehaviour<ICollider2D>().OnCollisionDetected.AddListener(OnCollisionDetected); BehaviourController.GetRequiredBehaviour<ICollider2D>().OnCollisionDetected.AddListener(OnCollisionDetected);
@ -34,11 +28,6 @@ public class Ball : Behaviour2D, IFirstFrameUpdate, ILoadContent, IPhysicsUpdate
networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>(); networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
} }
public void LoadContent(ContentManager content)
{
bounceSoundEffect = content.Load<SoundEffect>("Audio/Bounce");
}
public void LaunchBall(Vector2D launchDirection) public void LaunchBall(Vector2D launchDirection)
{ {
ResetBall(); ResetBall();
@ -70,9 +59,7 @@ public class Ball : Behaviour2D, IFirstFrameUpdate, ILoadContent, IPhysicsUpdate
RigidBody.Velocity = information.Detected.Transform.Position.FromTo(information.Detector.Transform.Position).Normalized * RigidBody.Velocity.Magnitude; RigidBody.Velocity = information.Detected.Transform.Position.FromTo(information.Detector.Transform.Position).Normalized * RigidBody.Velocity.Magnitude;
else else
RigidBody.Velocity = RigidBody.Velocity.Reflect(information.Normal); RigidBody.Velocity = RigidBody.Velocity.Reflect(information.Normal);
networkServer?.SendToAll(new BallUpdatePacket(this));
if (information.Detected.Transform.BehaviourController.GetBehaviour<ScoreWall>() is null)
networkServer?.SendToAll(new BallUpdatePacket(this));
} }
public Ball() { } public Ball() { }
@ -87,9 +74,6 @@ public class Ball : Behaviour2D, IFirstFrameUpdate, ILoadContent, IPhysicsUpdate
else else
Transform.Position = packet.Position; Transform.Position = packet.Position;
if (RigidBody.Velocity.MagnitudeSquared >= .01f)
bounceSoundEffect?.Play();
RigidBody.Velocity = packet.Velocity; RigidBody.Velocity = packet.Velocity;
physicsEngine2D.StepIndividual(RigidBody, sender.Ping); physicsEngine2D.StepIndividual(RigidBody, sender.Ping);
} }

View File

@ -1,4 +1,4 @@
using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
@ -6,10 +6,9 @@ using Syntriax.Engine.Integration.MonoGame;
namespace Pong.Behaviours; namespace Pong.Behaviours;
public class Label : Behaviour2D, IDrawableSprite, ILoadContent public class Label : Behaviour2D, IDrawableSprite
{ {
public SpriteFont? Font { get; set; } = null; public SpriteFont? Font { get; set; } = null;
public ColorRGBA Color { get; set; } = new ColorRGBA(255, 255, 255, 255);
public int Size { get; set; } = 16; public int Size { get; set; } = 16;
public string Text { get; set; } = string.Empty; public string Text { get; set; } = string.Empty;
@ -18,12 +17,7 @@ public class Label : Behaviour2D, IDrawableSprite, ILoadContent
if (Font is null) if (Font is null)
return; return;
spriteBatch.DrawString(Font, Text, Transform.Position, Color.ToPreMultipliedColor(), Transform.Rotation, Vector2D.One * .5f, Transform.Scale.Magnitude, SpriteEffects.None, 0f); spriteBatch.DrawString(Font, Text, Transform.Position, Color.White, Transform.Rotation, Vector2D.One * .5f, Transform.Scale.Magnitude, SpriteEffects.None, 0f);
}
public void LoadContent(ContentManager content)
{
Font ??= content.Load<SpriteFont>("UbuntuMono");
} }
public Label() { } public Label() { }

View File

@ -1,107 +0,0 @@
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Debug;
using Syntriax.Engine.Integration.MonoGame;
using Syntriax.Engine.Network;
using Syntriax.Engine.Systems.Input;
using Syntriax.Engine.Systems.Time;
using Syntriax.Engine.Systems.Tween;
namespace Pong.Behaviours;
public class PongGameStarter : Behaviour, INetworkEntity, IFirstFrameUpdate, ILoadContent,
IPacketListenerServer<PongGameStarter.RequestStartPacket>,
IPacketListenerClient<PongGameStarter.RequestStartPacket>
{
private const float START_COUNTDOWN = 3f;
private INetworkCommunicatorServer? networkServer = null;
private INetworkCommunicatorClient? networkClient = null;
private ITweenManager? tweenManager = null;
private PongManager pongManager = null!;
private ILogger? logger = null;
private Label? label = null;
private TickerTimer timer = null!;
private SoundEffectInstance? tickSoundEffect = null;
private SoundEffectInstance? startSoundEffect = null;
public void FirstActiveFrame()
{
IButtonInputs<Keys>? buttonInputs = Universe.FindBehaviour<IButtonInputs<Keys>>();
buttonInputs?.RegisterOnRelease(Keys.Space, (_, _1) => networkClient?.SendToServer(new RequestStartPacket()));
networkClient = Universe.FindBehaviour<INetworkCommunicatorClient>();
networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
tweenManager = Universe.FindBehaviour<ITweenManager>();
pongManager = BehaviourController.GetRequiredBehaviourInParent<PongManager>();
label = BehaviourController.GetRequiredBehaviour<Label>();
logger = Universe.FindBehaviour<ILogger>();
if (!BehaviourController.TryGetBehaviour(out timer!))
{
timer = BehaviourController.AddBehaviour<TickerTimer>();
timer.OnStarted.AddListener(OnCountdownStart);
timer.OnTick.AddListener(DisplayCountdown);
timer.OnStopped.AddListener(StartPong);
}
}
public void LoadContent(ContentManager content)
{
tickSoundEffect = content.Load<SoundEffect>("Audio/TimerTick").CreateInstance();
startSoundEffect = content.Load<SoundEffect>("Audio/TimerEnd").CreateInstance();
}
private void OnCountdownStart(IReadOnlyTimer sender)
{
pongManager.Reset();
DisplayCountdown(timer);
}
private void DisplayCountdown(ITicker sender)
{
tickSoundEffect?.Play();
if (label != null)
{
label.Text = $"{START_COUNTDOWN - timer.TickCounter}";
label.Color = new ColorRGBA(255, 255, 255, 255);
if (tweenManager is not null)
label.Color.TweenColor(tweenManager, 1f, new ColorRGBA(255, 255, 255, 0), (x) => label.Color = x);
}
}
private void StartPong(IReadOnlyTimer sender)
{
startSoundEffect?.Play();
pongManager.Start();
}
void IPacketListenerServer<RequestStartPacket>.OnServerPacketArrived(IConnection sender, RequestStartPacket packet)
{
logger?.Log(this, $"{sender} requested start");
if (pongManager.IsGameInProgress)
{
logger?.Log(this, $"The game is already in progress");
return;
}
timer.Start(START_COUNTDOWN);
networkServer?.SendToAll(packet);
}
void IPacketListenerClient<RequestStartPacket>.OnClientPacketArrived(IConnection sender, RequestStartPacket packet)
{
logger?.Log(this, $"Server is starting the game");
timer.Start(START_COUNTDOWN - sender.Ping);
}
private class RequestStartPacket : INetworkPacket;
}

View File

@ -1,17 +1,16 @@
using System; using System;
using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Debug; using Syntriax.Engine.Core.Debug;
using Syntriax.Engine.Integration.MonoGame;
using Syntriax.Engine.Network; using Syntriax.Engine.Network;
using Syntriax.Engine.Systems.Input;
namespace Pong.Behaviours; namespace Pong.Behaviours;
public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate, ILoadContent, public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate,
IPacketListenerClient<PongManager.StartPacket>, IPacketListenerServer<PongManager.RequestStartPacket>,
IPacketListenerClient<PongManager.ScorePacket> IPacketListenerClient<PongManager.ScorePacket>
{ {
public Action<PongManager>? OnReset { get; set; } = null; public Action<PongManager>? OnReset { get; set; } = null;
@ -19,78 +18,60 @@ public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate, ILoadCo
public Action<PongManager>? OnScoreUpdated { get; set; } = null; public Action<PongManager>? OnScoreUpdated { get; set; } = null;
private Random random = new(); private Random random = new();
private Ball ball = null!;
private INetworkCommunicatorClient? networkClient = null!;
private INetworkCommunicatorServer? networkServer = null; private INetworkCommunicatorServer? networkServer = null;
private ILogger? logger = null; private ILogger? logger = null;
private SoundEffectInstance? scoreSoundEffect = null;
private SoundEffectInstance? gameEndSoundEffect = null;
public int ScoreLeft { get; private set; } = 0; public int ScoreLeft { get; private set; } = 0;
public int ScoreRight { get; private set; } = 0; public int ScoreRight { get; private set; } = 0;
public int ScoreSum => ScoreLeft + ScoreRight; public int ScoreSum => ScoreLeft + ScoreRight;
public int WinScore { get; } = 5; public int WinScore { get; } = 5;
public Ball Ball { get; private set; } = null!;
public bool IsGameInProgress { get; private set; } = false;
public PongManager() => WinScore = 5; public PongManager() => WinScore = 5;
public PongManager(int winScore) => WinScore = winScore; public PongManager(int winScore) => WinScore = winScore;
public void FirstActiveFrame() public void FirstActiveFrame()
{ {
Ball = Universe.FindRequiredBehaviour<Ball>(); IButtonInputs<Keys>? buttonInputs = Universe.FindBehaviour<IButtonInputs<Keys>>();
buttonInputs?.RegisterOnRelease(Keys.Space, (_, _1) => networkClient?.SendToServer(new RequestStartPacket()));
ball = Universe.FindRequiredBehaviour<Ball>();
networkClient = Universe.FindBehaviour<INetworkCommunicatorClient>();
networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>(); networkServer = Universe.FindBehaviour<INetworkCommunicatorServer>();
logger = Universe.FindBehaviour<ILogger>(); logger = Universe.FindBehaviour<ILogger>();
} }
public void LoadContent(ContentManager content)
{
gameEndSoundEffect = content.Load<SoundEffect>("Audio/Win").CreateInstance();
scoreSoundEffect = content.Load<SoundEffect>("Audio/Score").CreateInstance();
}
public void ScoreToLeft() public void ScoreToLeft()
{ {
ScoreLeft++; ScoreLeft++;
OnScoreUpdated?.Invoke(this);
PostScoreUpdate(); PostScoreUpdate();
} }
public void ScoreToRight() public void ScoreToRight()
{ {
ScoreRight++; ScoreRight++;
OnScoreUpdated?.Invoke(this);
PostScoreUpdate(); PostScoreUpdate();
} }
public bool Start()
{
if (networkServer is null)
return false;
if (Ball.RigidBody.Velocity.MagnitudeSquared > 0.01f)
return false;
Reset();
IsGameInProgress = true;
PostScoreUpdate();
networkServer.SendToAll(new StartPacket());
logger?.Log(this, $"Game started");
return true;
}
public void Reset() public void Reset()
{ {
ScoreLeft = ScoreRight = 0; ScoreLeft = ScoreRight = 0;
IsGameInProgress = false;
Ball.ResetBall(); PostScoreUpdate();
OnReset?.Invoke(this); OnReset?.Invoke(this);
} }
private void PostScoreUpdate() private void PostScoreUpdate()
{ {
OnScoreUpdated?.Invoke(this); ball.ResetBall();
Ball.ResetBall();
if (networkServer is not null) if (networkServer is not null)
{ {
@ -101,14 +82,12 @@ public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate, ILoadCo
int halfwayScore = (int)(WinScore * .5f); int halfwayScore = (int)(WinScore * .5f);
if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore) if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore)
{ {
IsGameInProgress = false;
gameEndSoundEffect?.Play();
OnFinished?.Invoke(this); OnFinished?.Invoke(this);
logger?.Log(this, $"Game finished: {ScoreLeft} - {ScoreRight}"); logger?.Log(this, $"Game finished");
return; return;
} }
Ball.LaunchBall(GetBallLaunchDirection()); ball.LaunchBall(GetBallLaunchDirection());
} }
private Vector2D GetBallLaunchDirection() private Vector2D GetBallLaunchDirection()
@ -124,21 +103,22 @@ public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate, ILoadCo
ScoreLeft = packet.Left; ScoreLeft = packet.Left;
ScoreRight = packet.Right; ScoreRight = packet.Right;
PostScoreUpdate(); OnScoreUpdated?.Invoke(this);
if (IsGameInProgress && (ScoreLeft != 0 || ScoreRight != 0))
scoreSoundEffect?.Play();
logger?.Log(this, $"Client score update packet arrived: {packet.Left} - {packet.Right}"); logger?.Log(this, $"Client score update packet arrived: {packet.Left} - {packet.Right}");
} }
void IPacketListenerClient<StartPacket>.OnClientPacketArrived(IConnection sender, StartPacket packet) void IPacketListenerServer<RequestStartPacket>.OnServerPacketArrived(IConnection sender, RequestStartPacket packet)
{ {
IsGameInProgress = true; logger?.Log(this, $"{sender} requested start");
if (ball.RigidBody.Velocity.MagnitudeSquared > 0.01f)
return;
Reset();
ball.LaunchBall(GetBallLaunchDirection());
logger?.Log(this, $"Game started"); logger?.Log(this, $"Game started");
} }
private class StartPacket : INetworkPacket; private class RequestStartPacket : INetworkPacket;
private class ScorePacket : INetworkPacket private class ScorePacket : INetworkPacket
{ {
public int Left { get; set; } public int Left { get; set; }

View File

@ -1,4 +1,7 @@
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core; using Syntriax.Engine.Core;
using Syntriax.Engine.Integration.MonoGame;
namespace Pong.Behaviours; namespace Pong.Behaviours;
@ -10,6 +13,9 @@ public class ScoreLabel(bool IsLeft) : Label, IFirstFrameUpdate
public void FirstActiveFrame() public void FirstActiveFrame()
{ {
MonoGameWindow monoGameWindow = Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window;
Font = monoGameWindow.Content.Load<SpriteFont>("UbuntuMono");
pongManager = Universe.FindRequiredBehaviour<PongManager>(); pongManager = Universe.FindRequiredBehaviour<PongManager>();
pongManager.OnScoreUpdated += UpdateScores; pongManager.OnScoreUpdated += UpdateScores;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -13,36 +13,6 @@
#---------------------------------- Content ---------------------------------# #---------------------------------- Content ---------------------------------#
#begin Audio/Bounce.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:Audio/Bounce.wav
#begin Audio/Score.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:Audio/Score.wav
#begin Audio/Win.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:Audio/Win.wav
#begin Audio/TimerTick.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:Audio/TimerTick.wav
#begin Audio/TimerEnd.wav
/importer:WavImporter
/processor:SoundEffectProcessor
/processorParam:Quality=Best
/build:Audio/TimerEnd.wav
#begin UbuntuMono.spritefont #begin UbuntuMono.spritefont
/importer:FontDescriptionImporter /importer:FontDescriptionImporter
/processor:FontDescriptionProcessor /processor:FontDescriptionProcessor

View File

@ -66,11 +66,6 @@ public static class PongUniverse
PongManager pongManager = universe.InstantiateUniverseObject().SetUniverseObject("Pong Game Manager") PongManager pongManager = universe.InstantiateUniverseObject().SetUniverseObject("Pong Game Manager")
.BehaviourController.AddBehaviour<PongManager>(5); .BehaviourController.AddBehaviour<PongManager>(5);
universe.InstantiateUniverseObject().SetUniverseObject("Pong Game Starter", parent: pongManager.UniverseObject)
.BehaviourController.AddBehaviour<PongGameStarter>()
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new Vector2D(-24, 250f), scale: Vector2D.One * .5f)
.BehaviourController.AddBehaviour<Label>();
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
universe.InstantiateUniverseObject().SetUniverseObject("Ball") universe.InstantiateUniverseObject().SetUniverseObject("Ball")