Packet refactoring

This commit is contained in:
Erik Simon 2022-12-14 16:31:38 +01:00
parent 599556334f
commit f04cc984b0
38 changed files with 358 additions and 285 deletions

View File

@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SDL/@EntryIndexedValue">SDL</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SDL/@EntryIndexedValue">SDL</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=interactor/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -1,78 +1,86 @@
using System.Reflection; using System.Reflection;
using Mine2d.engine.networking; using Mine2d.engine.extensions;
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.network.packets;
using Mine2d.game.core.extensions; using Mine2d.game.core.extensions;
namespace Mine2d.engine; namespace Mine2d.engine;
public delegate void InteractionHandler(Packet packet);
public class Publisher public class Publisher
{ {
private readonly Dictionary<string, HashSet<Delegate>> subscribers = private readonly Dictionary<PacketType, HashSet<MethodInfo>> subscribers = new();
new(); private readonly HashSet<PacketType> clientOnlySubscriptions = new();
private readonly HashSet<string> clientOnlySubscriptions = new(); private readonly HashSet<PacketType> serverSubscriptions = new();
private readonly HashSet<string> serverSubscriptions = new();
private readonly InteractorKind kind; private readonly InteractorKind kind;
public Publisher(InteractorKind kind) public Publisher(InteractorKind kind)
{ {
Enum.GetValues<PacketType>()
.ForEach(type => this.subscribers[type] = new HashSet<MethodInfo>());
this.kind = kind; this.kind = kind;
this.Scan(); this.Scan();
} }
private void Scan() private void Scan()
{ {
var types = Assembly Assembly
.GetAssembly(this.GetType())! .GetAssembly(this.GetType())!
.GetTypesSafe(); .GetTypesSafe()
foreach (var type in types) .Where(t => t.HasAttribute<InteractorAttribute>())
{ .SelectMany(t => t.GetMethods())
var classAttrs = type.GetCustomAttributes(typeof(InteractorAttribute), false); .Where(m => m.HasAttribute<InteractionAttribute>())
if (classAttrs.Length == 0) .ForEach(method =>
{ {
continue; var attribute = method.GetCustomAttribute<InteractionAttribute>()!;
} Console.WriteLine($"Registering interaction method {method.Name} declared in {method.DeclaringType}");
var methods = type.GetMethods(); Console.WriteLine($"InteractorKind: {attribute.Kind}");
foreach (var method in methods) Console.WriteLine($"PacketType: {attribute.Type}");
{
var methodAttrs = method.GetCustomAttributes(typeof(InteractionAttribute), false); switch (attribute.Kind)
if (methodAttrs.Length == 0)
{ {
continue; case InteractorKind.Hybrid:
case InteractorKind.Server:
this.serverSubscriptions.Add(attribute.Type);
break;
case InteractorKind.Client:
this.clientOnlySubscriptions.Add(attribute.Type);
break;
default:
throw new ArgumentOutOfRangeException(nameof(attribute.Kind), attribute.Kind, null);
} }
var attr = (InteractionAttribute)methodAttrs[0];
if (attr.Kind is InteractorKind.Server or InteractorKind.Hybrid) if (attribute.Kind == this.kind || this.kind == InteractorKind.Hybrid)
{ {
this.serverSubscriptions.Add(attr.Type); this.subscribers[attribute.Type].Add(method);
Console.WriteLine("Subscribed!");
} }
if (attr.Kind is InteractorKind.Client)
{ Console.WriteLine();
this.clientOnlySubscriptions.Add(attr.Type); });
}
if (attr.Kind != this.kind && this.kind != InteractorKind.Hybrid)
{
continue;
}
var del = method.GetParameters().Length == 0 ?
Delegate.CreateDelegate(typeof(Action), method) :
Delegate.CreateDelegate(
typeof(Action<>).MakeGenericTypeSafely(method.GetParameters()[0].ParameterType),
method
);
this.Subscribe(attr.Type, del);
}
}
this.clientOnlySubscriptions.ExceptWith(this.serverSubscriptions);
} }
private void Subscribe(string type, Delegate callback) public void Publish(Packet packet)
{ {
if (!this.subscribers.ContainsKey(type)) if (packet.Type != PacketType.Tick)
{ Console.WriteLine($"[{nameof(Publisher)}] Publishing {packet.Type}");
this.subscribers[type] = new HashSet<Delegate>();
} this.subscribers[packet.Type]
this.subscribers[type].Add(callback); .ForEach(handler =>
{
var parameterCount = handler.GetParameters().Length;
handler.Invoke(null, parameterCount > 0 ? new object[] { packet } : null);
});
} }
public bool IsClientOnlyPacket(Packet packet)
=> this.clientOnlySubscriptions.Contains(packet.Type);
public bool IsServerPacket(Packet packet)
=> this.serverSubscriptions.Contains(packet.Type);
public void Dump() public void Dump()
{ {
foreach (var pair in this.subscribers) foreach (var pair in this.subscribers)
@ -84,41 +92,4 @@ public class Publisher
} }
} }
} }
public void Publish(ValueType packet)
{
var type = PacketUtils.GetType(packet);
if (type != "tick")
{
Console.WriteLine("Publishing packet: " + type);
}
if (this.subscribers.ContainsKey(type))
{
if (type != "tick")
{
Console.WriteLine("Found " + this.subscribers[type].Count + " subscribers");
}
foreach (var del in this.subscribers[type])
{
if (del.Method.GetParameters().Length == 0)
{
del.DynamicInvoke();
}
else
{
del.DynamicInvoke(packet);
}
}
}
}
public bool IsClientOnlyPacket(string type)
{
return this.clientOnlySubscriptions.Contains(type);
}
public bool IsServerPacket(string type)
{
return this.serverSubscriptions.Contains(type);
}
} }

View File

@ -1,5 +1,3 @@
using Mine2d.game;
namespace Mine2d.engine; namespace Mine2d.engine;
public class TextureFactory public class TextureFactory

View File

@ -0,0 +1,12 @@
namespace Mine2d.engine.extensions;
public static class EnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
}
}
}

View File

@ -0,0 +1,9 @@
using System.Reflection;
namespace Mine2d.engine.extensions;
public static class MethodInfoExtensions
{
public static bool HasAttribute<T>(this MethodInfo methodInfo) where T : Attribute
=> methodInfo.GetCustomAttributes(typeof(T), false).Length > 0;
}

View File

@ -0,0 +1,7 @@
namespace Mine2d.engine.extensions;
public static class TypeExtensions
{
public static bool HasAttribute<T>(this Type type) where T : Attribute
=> type.GetCustomAttributes(typeof(T), false).Length > 0;
}

View File

@ -1,8 +1,8 @@
using System.Text; using System.Text;
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game; using Mine2d.game;
using Mine2d.game.backend.data;
using Mine2d.game.backend.network; using Mine2d.game.backend.network;
using Mine2d.game.backend.network.packets;
using Newtonsoft.Json; using Newtonsoft.Json;
using WatsonTcp; using WatsonTcp;
@ -12,22 +12,25 @@ public class Backend : IBackend
{ {
private WatsonTcpServer server; private WatsonTcpServer server;
private Publisher publisher; private Publisher publisher;
private readonly Queue<ValueType> pendingPackets = new(); private readonly Queue<Packet> pendingPackets = new();
private uint tick; private uint tick;
public void Process(double dt) public void Process(double dt)
{ {
this.ProcessPacket(new TickPacket(this.tick++)); this.ProcessPacket(new TickPacket
{
Tick = ++this.tick
});
Context.Get().GameState.Tick = this.tick; Context.Get().GameState.Tick = this.tick;
while (this.pendingPackets.Count > 0) while (this.pendingPackets.Count > 0)
{ {
var packet = this.pendingPackets.Dequeue(); this.publisher.Publish(this.pendingPackets.Dequeue());
this.publisher.Publish(packet);
} }
this.SendGameState(); this.SendGameState();
} }
public void ProcessPacket(ValueType packet) public void ProcessPacket(Packet packet)
{ {
this.pendingPackets.Enqueue(packet); this.pendingPackets.Enqueue(packet);
} }
@ -36,6 +39,7 @@ public class Backend : IBackend
{ {
Task.Run(this.Run); Task.Run(this.Run);
this.publisher = new Publisher(InteractorKind.Hybrid); this.publisher = new Publisher(InteractorKind.Hybrid);
this.publisher.Dump();
} }
public void Run() public void Run()
@ -65,15 +69,12 @@ public class Backend : IBackend
private void MessageReceived(object sender, MessageReceivedEventArgs args) private void MessageReceived(object sender, MessageReceivedEventArgs args)
{ {
var time = DateTime.Now;
Console.WriteLine("Message Received: " + args.IpPort); Console.WriteLine("Message Received: " + args.IpPort);
var packet = Converter.ParsePacket(args.Data); var packet = Converter.ParsePacket(args.Data);
Console.WriteLine("Received packet: " + packet); Console.WriteLine($"Received packet: {packet.Type}");
if (packet != null)
{ this.pendingPackets.Enqueue(packet);
this.pendingPackets.Enqueue(packet);
}
Console.WriteLine(DateTime.Now - time);
} }
private void SendGameState() private void SendGameState()

View File

@ -1,5 +1,5 @@
using Mine2d.game; using Mine2d.game;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
using Mine2d.game.frontend.renderer; using Mine2d.game.frontend.renderer;
namespace Mine2d.engine.networking; namespace Mine2d.engine.networking;
@ -14,7 +14,11 @@ public class Frontend : IFrontend
ctx.FrontendGameState.PlayerName = ctx.IsHost ? "Host" : "Client"; ctx.FrontendGameState.PlayerName = ctx.IsHost ? "Host" : "Client";
var guid = Guid.NewGuid(); var guid = Guid.NewGuid();
ctx.FrontendGameState.PlayerGuid = guid; ctx.FrontendGameState.PlayerGuid = guid;
var connectPacket = new ConnectPacket(ctx.FrontendGameState.PlayerName, guid); var connectPacket = new ConnectPacket
{
PlayerName = ctx.FrontendGameState.PlayerName,
PlayerGuid = guid
};
ctx.Backend.ProcessPacket(connectPacket); ctx.Backend.ProcessPacket(connectPacket);
ctx.TileRegistry.RegisterTile(); ctx.TileRegistry.RegisterTile();
ctx.ItemRegistry.RegisterItems(); ctx.ItemRegistry.RegisterItems();

View File

@ -1,8 +1,10 @@
using Mine2d.game.backend.network.packets;
namespace Mine2d.engine.networking; namespace Mine2d.engine.networking;
public interface IBackend public interface IBackend
{ {
public void Process(double dt); public void Process(double dt);
public void ProcessPacket(ValueType packet); public void ProcessPacket(Packet packet);
public void Init(); public void Init();
} }

View File

@ -1,22 +0,0 @@
using Mine2d.game.backend.data;
namespace Mine2d.engine.networking;
public static class PacketUtils
{
public static string GetType(ValueType packet)
{
var t = packet.GetType();
var p = t.GetProperty(nameof(IPacket.Type));
if (p == null)
{
throw new ArgumentNullException(nameof(p), "p undef");
}
var v = p.GetValue(packet);
if (v == null)
{
throw new ArgumentNullException(nameof(v), "v undef");
}
return (string)v;
}
}

View File

@ -1,7 +1,7 @@
using System.Text; using System.Text;
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game; using Mine2d.game;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
using Mine2d.game.state; using Mine2d.game.state;
using Newtonsoft.Json; using Newtonsoft.Json;
using WatsonTcp; using WatsonTcp;
@ -12,12 +12,16 @@ public class RemoteBackend : IBackend
{ {
private WatsonTcpClient client; private WatsonTcpClient client;
private Publisher publisher; private Publisher publisher;
private readonly Queue<ValueType> pendingPackets = new(); private readonly Queue<Packet> pendingPackets = new();
private uint tick; private uint tick;
public void Process(double dt) public void Process(double dt)
{ {
this.ProcessPacket(new TickPacket(this.tick++)); this.ProcessPacket(new TickPacket
{
Tick = ++this.tick
});
while (this.pendingPackets.Count > 0) while (this.pendingPackets.Count > 0)
{ {
var packet = this.pendingPackets.Dequeue(); var packet = this.pendingPackets.Dequeue();
@ -25,13 +29,12 @@ public class RemoteBackend : IBackend
} }
} }
public void ProcessPacket(ValueType packet) public void ProcessPacket(Packet packet)
{ {
this.publisher.Publish(packet); this.publisher.Publish(packet);
if (this.publisher.IsClientOnlyPacket(PacketUtils.GetType(packet))) if (this.publisher.IsClientOnlyPacket(packet))
{
return; return;
}
var json = JsonConvert.SerializeObject(packet); var json = JsonConvert.SerializeObject(packet);
var bytes = Encoding.UTF8.GetBytes(json); var bytes = Encoding.UTF8.GetBytes(json);
this.client.Send(bytes); this.client.Send(bytes);

View File

@ -1,3 +1,5 @@
using Mine2d.game.backend.network.packets;
namespace Mine2d.engine.system.annotations; namespace Mine2d.engine.system.annotations;
public enum InteractorKind public enum InteractorKind
@ -13,10 +15,10 @@ public class InteractorAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class InteractionAttribute : Attribute public class InteractionAttribute : Attribute
{ {
public string Type { get; set; } public PacketType Type { get; set; }
public InteractorKind Kind { get; set; } public InteractorKind Kind { get; set; }
public InteractionAttribute(InteractorKind kind, string type) public InteractionAttribute(InteractorKind kind, PacketType type)
{ {
this.Type = type; this.Type = type;
this.Kind = kind; this.Kind = kind;

View File

@ -1,98 +0,0 @@
using Mine2d.game.core.data;
namespace Mine2d.game.backend.data;
public interface IPacket
{
public string Type { get; }
}
public struct MovePacket : IPacket
{
public readonly string Type => "move";
public readonly string PlayerName { get; }
public readonly Vector2 Movement { get; }
public MovePacket(string playerName, Vector2 movement)
{
this.PlayerName = playerName;
this.Movement = movement;
}
}
public readonly struct ConnectPacket : IPacket
{
public readonly string Type => "connect";
public readonly string PlayerName { get; }
public readonly Guid PlayerGuid { get; }
public ConnectPacket(string playerName, Guid playerGuid)
{
this.PlayerName = playerName;
this.PlayerGuid = playerGuid;
}
}
public readonly struct TickPacket : IPacket
{
public readonly string Type => "tick";
public readonly uint Tick { get; }
public TickPacket(uint tick)
{
this.Tick = tick;
}
}
public readonly struct SelfMovedPacket : IPacket
{
public readonly string Type => "selfMoved";
public readonly Vector2 Target { get; }
public SelfMovedPacket(Vector2 target)
{
this.Target = target;
}
}
public readonly struct BreakPacket : IPacket
{
public readonly string Type => "break";
public readonly Guid PlayerGuid { get; }
public readonly Vector2 Target { get; }
public BreakPacket(Guid playerGuid, Vector2 target)
{
this.PlayerGuid = playerGuid;
this.Target = target;
}
}
public readonly struct PlayerMovedPacket : IPacket
{
public readonly string Type => "playerMoved";
public readonly Guid PlayerId { get; }
public readonly Vector2 Target { get; }
public PlayerMovedPacket(Guid playerId, Vector2 target)
{
this.PlayerId = playerId;
this.Target = target;
}
}
public readonly struct BlockBrokenPacket : IPacket
{
public readonly string Type => "blockBroken";
public readonly Guid PlayerId { get; }
public readonly Vector2 Target { get; }
public readonly STile Tile { get; }
public BlockBrokenPacket(Guid playerId, Vector2 target, STile tile)
{
this.PlayerId = playerId;
this.Target = target;
this.Tile = tile;
}
}

View File

@ -1,12 +1,13 @@
using Mine2d.engine; using Mine2d.engine;
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.network.packets;
namespace Mine2d.game.backend.interactor; namespace Mine2d.game.backend.interactor;
[Interactor] [Interactor]
public class Audio public class Audio
{ {
[Interaction(InteractorKind.Client, "blockBroken")] [Interaction(InteractorKind.Client, PacketType.BlockBroken)]
public static void BlockBroken() public static void BlockBroken()
{ {
var ctx = Context.Get(); var ctx = Context.Get();

View File

@ -1,6 +1,5 @@
using Mine2d.engine;
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
using Mine2d.game.core; using Mine2d.game.core;
using Mine2d.game.core.data; using Mine2d.game.core.data;
@ -9,7 +8,7 @@ namespace Mine2d.game.backend.interactor;
[Interactor] [Interactor]
public class Breaking public class Breaking
{ {
[Interaction(InteractorKind.Hybrid, "tick")] [Interaction(InteractorKind.Hybrid, PacketType.Tick)]
public static void TickHybrid() public static void TickHybrid()
{ {
var ctx = Context.Get(); var ctx = Context.Get();
@ -45,7 +44,12 @@ public class Breaking
if (tile.Hits >= hardness) if (tile.Hits >= hardness)
{ {
var blockPos = new Vector2((int)Math.Floor(player.Mining.X / 16) * 16, (int)Math.Floor(player.Mining.Y / 16) * 16); var blockPos = new Vector2((int)Math.Floor(player.Mining.X / 16) * 16, (int)Math.Floor(player.Mining.Y / 16) * 16);
ctx.Backend.ProcessPacket(new BlockBrokenPacket(player.Id, blockPos, tile)); ctx.Backend.ProcessPacket(new BlockBrokenPacket
{
PlayerGuid = player.Id,
Target = blockPos,
Tile = tile
});
chunk.SetTileAt(player.Mining, STile.From(0)); chunk.SetTileAt(player.Mining, STile.From(0));
} }
} }
@ -53,7 +57,7 @@ public class Breaking
} }
} }
[Interaction(InteractorKind.Server, "break")] [Interaction(InteractorKind.Server, PacketType.Break)]
public static void BreakServer(BreakPacket packet) public static void BreakServer(BreakPacket packet)
{ {
var ctx = Context.Get(); var ctx = Context.Get();
@ -65,7 +69,7 @@ public class Breaking
player.Mining = packet.Target; player.Mining = packet.Target;
} }
[Interaction(InteractorKind.Server, "blockBroken")] [Interaction(InteractorKind.Server, PacketType.BlockBroken)]
public static void BreakServer(BlockBrokenPacket packet) public static void BreakServer(BlockBrokenPacket packet)
{ {
var ctx = Context.Get(); var ctx = Context.Get();

View File

@ -1,5 +1,5 @@
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
using Mine2d.game.state; using Mine2d.game.state;
namespace Mine2d.game.backend.interactor; namespace Mine2d.game.backend.interactor;
@ -7,7 +7,7 @@ namespace Mine2d.game.backend.interactor;
[Interactor] [Interactor]
public class Connect public class Connect
{ {
[Interaction(InteractorKind.Server, "connect")] [Interaction(InteractorKind.Server, PacketType.Connect)]
public static void ConnectServer(ConnectPacket packet) public static void ConnectServer(ConnectPacket packet)
{ {
var ctx = Context.Get(); var ctx = Context.Get();

View File

@ -1,6 +1,6 @@
using Mine2d.engine; using Mine2d.engine;
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
using Mine2d.game.core.data; using Mine2d.game.core.data;
using Mine2d.game.core.data.entities; using Mine2d.game.core.data.entities;
@ -10,8 +10,8 @@ namespace Mine2d.game.backend.interactor;
public class ItemPhysics public class ItemPhysics
{ {
[Interaction(InteractorKind.Hybrid, "tick")] [Interaction(InteractorKind.Hybrid, PacketType.Tick)]
public static void TickHybrid() public static void TickHybrid(TickPacket packet)
{ {
var gameState = Context.Get().GameState; var gameState = Context.Get().GameState;
var world = gameState.World; var world = gameState.World;
@ -36,8 +36,8 @@ public class ItemPhysics
} }
} }
[Interaction(InteractorKind.Hybrid, "tick")] [Interaction(InteractorKind.Hybrid, PacketType.Tick)]
public static void Pickup(TickPacket tickPacket) public static void Pickup(TickPacket packet)
{ {
var gameState = Context.Get().GameState; var gameState = Context.Get().GameState;
var world = gameState.World; var world = gameState.World;
@ -51,12 +51,12 @@ public class ItemPhysics
Console.WriteLine("Where"); Console.WriteLine("Where");
return e is ItemEntity itemEntity && return e is ItemEntity itemEntity &&
(player.Position + new Vector2(7, 3) - itemEntity.Position).LengthSquared() < 8 * 8 && (player.Position + new Vector2(7, 3) - itemEntity.Position).LengthSquared() < 8 * 8 &&
player.inventory.PickupItemStack(new ItemStack { Id = itemEntity.ItemId, Count = 1 }); player.Inventory.PickupItemStack(new ItemStack { Id = itemEntity.ItemId, Count = 1 });
}); }).ToList();
if (items.Any()) if (items.Any())
{ {
Context.Get().GameAudio.Play(Sound.ItemPickup); Context.Get().GameAudio.Play(Sound.ItemPickup);
Console.WriteLine(tickPacket.Tick + " " + items.Count()); Console.WriteLine(packet.Tick + " " + items.Count());
} }
_ = chunk.Value.Entities.RemoveAll(e => items.Contains(e)); _ = chunk.Value.Entities.RemoveAll(e => items.Contains(e));
} }

View File

@ -1,5 +1,5 @@
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
using Mine2d.game.core; using Mine2d.game.core;
namespace Mine2d.game.backend.interactor; namespace Mine2d.game.backend.interactor;
@ -7,7 +7,7 @@ namespace Mine2d.game.backend.interactor;
[Interactor] [Interactor]
public class Move public class Move
{ {
[Interaction(InteractorKind.Hybrid, "move")] [Interaction(InteractorKind.Hybrid, PacketType.Move)]
public static void MoveHybrid(MovePacket packet) public static void MoveHybrid(MovePacket packet)
{ {
var ctx = Context.Get(); var ctx = Context.Get();
@ -18,7 +18,7 @@ public class Move
} }
} }
[Interaction(InteractorKind.Hybrid, "tick")] [Interaction(InteractorKind.Hybrid, PacketType.Tick)]
public static void TickHybrid() public static void TickHybrid()
{ {
var ctx = Context.Get(); var ctx = Context.Get();
@ -33,12 +33,16 @@ public class Move
{ {
if (player.Position != fromPositions[player.Id]) if (player.Position != fromPositions[player.Id])
{ {
ctx.Backend.ProcessPacket(new PlayerMovedPacket(player.Id, player.Position)); ctx.Backend.ProcessPacket(new PlayerMovedPacket
{
PlayerGuid = player.Id,
Target = player.Position
});
} }
} }
} }
[Interaction(InteractorKind.Client, "tick")] [Interaction(InteractorKind.Client, PacketType.Tick)]
public static void SelfMovedClient() public static void SelfMovedClient()
{ {
var camera = Context.Get().FrontendGameState.Camera; var camera = Context.Get().FrontendGameState.Camera;

View File

@ -1,5 +1,5 @@
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
using Mine2d.game.core.data; using Mine2d.game.core.data;
using Mine2d.game.core.world; using Mine2d.game.core.world;
@ -19,7 +19,7 @@ public class WorldGeneration
new(-1, 1) new(-1, 1)
}; };
[Interaction(InteractorKind.Server, "playerMoved")] [Interaction(InteractorKind.Server, PacketType.PlayerMoved)]
public static void PlayerMovedServer(PlayerMovedPacket packet) public static void PlayerMovedServer(PlayerMovedPacket packet)
{ {
var ctx = Context.Get(); var ctx = Context.Get();

View File

@ -1,34 +1,43 @@
using System.Text; using System.Text;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using ConnectPacket = Mine2d.game.backend.network.packets.ConnectPacket;
using TickPacket = Mine2d.game.backend.network.packets.TickPacket;
namespace Mine2d.game.backend.network; namespace Mine2d.game.backend.network;
public class Converter public class Converter
{ {
public static ValueType ParsePacket(byte[] bytes) public static Packet ParsePacket(byte[] bytes)
{ {
var jsonString = Encoding.UTF8.GetString(bytes); var jsonString = Encoding.UTF8.GetString(bytes);
return ParsePacket(jsonString); return ParsePacket(jsonString);
} }
public static ValueType ParsePacket(string jsonString) public static Packet ParsePacket(string jsonString)
{ {
var parsedRaw = JObject.Parse(jsonString); var parsedRaw = JObject.Parse(jsonString);
var type = parsedRaw.GetValue("type"); var packetType = parsedRaw
if (type == null) .GetValue("type")?
{ .Value<PacketType>();
if (packetType == null)
throw new PacketException("Packet has no type"); throw new PacketException("Packet has no type");
}
var packetType = type.Value<string>();
Console.WriteLine("Packet type: " + packetType); Console.WriteLine("Packet type: " + packetType);
return packetType switch Packet packet = packetType switch
{ {
"move" => parsedRaw.ToObject<MovePacket>(), PacketType.Connect => parsedRaw.ToObject<ConnectPacket>(),
"connect" => parsedRaw.ToObject<ConnectPacket>(), PacketType.Disconnect => parsedRaw.ToObject<DisconnectPacket>(),
_ => throw new PacketException("Unknown packet type") PacketType.Tick => parsedRaw.ToObject<TickPacket>(),
_ => throw new PacketException($"Unknown packet type: {packetType}")
}; };
if (packet is null)
throw new PacketException($"Failed to parse packet of type: {packetType}");
return packet;
} }
public static byte[] SerializePacket(ValueType packet) public static byte[] SerializePacket(ValueType packet)

View File

@ -0,0 +1,12 @@
using Mine2d.game.core.data;
namespace Mine2d.game.backend.network.packets;
public class BlockBrokenPacket : Packet
{
public override PacketType Type => PacketType.BlockBroken;
public Guid PlayerGuid { get; init; }
public Vector2 Target { get; init; }
public STile Tile { get; init; }
}

View File

@ -0,0 +1,9 @@
namespace Mine2d.game.backend.network.packets;
public class BreakPacket : Packet
{
public override PacketType Type => PacketType.Break;
public Guid PlayerGuid { get; init; }
public Vector2 Target { get; init; }
}

View File

@ -0,0 +1,9 @@
namespace Mine2d.game.backend.network.packets;
public class ConnectPacket : Packet
{
public override PacketType Type => PacketType.Connect;
public string PlayerName { get; init; }
public Guid PlayerGuid { get; init; }
}

View File

@ -0,0 +1,9 @@
namespace Mine2d.game.backend.network.packets;
public class DisconnectPacket : Packet
{
public override PacketType Type => PacketType.Disconnect;
public string PlayerName { get; init; }
public string PlayerGuid { get; init; }
}

View File

@ -0,0 +1,9 @@
namespace Mine2d.game.backend.network.packets;
public class MovePacket : Packet
{
public override PacketType Type => PacketType.Move;
public string PlayerName { get; init; }
public Vector2 Movement { get; init; }
}

View File

@ -0,0 +1,6 @@
namespace Mine2d.game.backend.network.packets;
public abstract class Packet
{
public abstract PacketType Type { get; }
}

View File

@ -0,0 +1,13 @@
namespace Mine2d.game.backend.network.packets;
public enum PacketType
{
Connect,
Disconnect,
Tick,
Move,
SelfMoved,
Break,
PlayerMoved,
BlockBroken
}

View File

@ -0,0 +1,6 @@
namespace Mine2d.game.backend.network.packets;
public class PlayerInputPacket
{
public Vector2 InputVector { get; set; }
}

View File

@ -0,0 +1,9 @@
namespace Mine2d.game.backend.network.packets;
public class PlayerMovedPacket : Packet
{
public override PacketType Type => PacketType.PlayerMoved;
public Guid PlayerGuid { get; init; }
public Vector2 Target { get; init; }
}

View File

@ -0,0 +1,8 @@
namespace Mine2d.game.backend.network.packets;
public class SelfMovedPacked : Packet
{
public override PacketType Type => PacketType.SelfMoved;
public Vector2 Target { get; init; }
}

View File

@ -0,0 +1,8 @@
namespace Mine2d.game.backend.network.packets;
public class TickPacket : Packet
{
public override PacketType Type => PacketType.Tick;
public uint Tick { get; init; }
}

View File

@ -1,4 +1,3 @@
using Mine2d.engine;
using Mine2d.game.core.data; using Mine2d.game.core.data;
namespace Mine2d.game.core.tiles; namespace Mine2d.game.core.tiles;

View File

@ -1,6 +1,6 @@
using Mine2d.engine.system; using Mine2d.engine.system;
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
namespace Mine2d.game.frontend.events; namespace Mine2d.game.frontend.events;
@ -18,7 +18,12 @@ public class PlayerBreakInput
var amp = ctx.FrontendGameState.MousePosition var amp = ctx.FrontendGameState.MousePosition
/ ctx.FrontendGameState.Settings.GameScale / ctx.FrontendGameState.Settings.GameScale
+ ctx.FrontendGameState.Camera.Position; + ctx.FrontendGameState.Camera.Position;
ctx.Backend.ProcessPacket(new BreakPacket(ctx.FrontendGameState.PlayerGuid, amp));
ctx.Backend.ProcessPacket(new BreakPacket
{
PlayerGuid = ctx.FrontendGameState.PlayerGuid,
Target = amp
});
} }
} }
@ -31,8 +36,15 @@ public class PlayerBreakInput
} }
var ctx = Context.Get(); var ctx = Context.Get();
var amp = ctx.FrontendGameState.MousePosition / ctx.FrontendGameState.Settings.GameScale + ctx.FrontendGameState.Camera.Position; var amp = ctx.FrontendGameState.MousePosition
ctx.Backend.ProcessPacket(new BreakPacket(ctx.FrontendGameState.PlayerGuid, amp)); / ctx.FrontendGameState.Settings.GameScale
+ ctx.FrontendGameState.Camera.Position;
ctx.Backend.ProcessPacket(new BreakPacket
{
PlayerGuid = ctx.FrontendGameState.PlayerGuid,
Target = amp
});
} }
[EventListener(EventType.MouseButtonUp)] [EventListener(EventType.MouseButtonUp)]
@ -44,6 +56,10 @@ public class PlayerBreakInput
} }
var ctx = Context.Get(); var ctx = Context.Get();
ctx.Backend.ProcessPacket(new BreakPacket(ctx.FrontendGameState.PlayerGuid, Vector2.Zero)); ctx.Backend.ProcessPacket(new BreakPacket
{
PlayerGuid = ctx.FrontendGameState.PlayerGuid,
Target = Vector2.Zero
});
} }
} }

View File

@ -0,0 +1,48 @@
using Mine2d.engine.system;
using Mine2d.engine.system.annotations;
namespace Mine2d.game.frontend.events;
public class PlayerController
{
[EventListener(EventType.KeyDown)]
public static void OnKeyDown(SDL_Event e)
{
if (!IsMovementKey(e.key.keysym.scancode))
return;
// Context
// .Get()
// .Backend
// .ProcessPacket(new PlayerInputPacket
// {
// InputVector = e.key.keysym.scancode switch
// {
// SDL_Scancode.SDL_SCANCODE_A
// => new Vector2(-1, 0),
// SDL_Scancode.SDL_SCANCODE_D
// => new Vector2(1, 0),
// SDL_Scancode.SDL_SCANCODE_W or SDL_Scancode.SDL_SCANCODE_SPACE
// => new Vector2(0, 1),
// SDL_Scancode.SDL_SCANCODE_S
// => new Vector2(0, -1)
// }
// });
}
[EventListener(EventType.KeyUp)]
public static void OnKeyUp(SDL_Event e)
{
if (!IsMovementKey(e.key.keysym.scancode))
return;
}
private static bool IsMovementKey(SDL_Scancode scancode)
{
return scancode
is SDL_Scancode.SDL_SCANCODE_A
or SDL_Scancode.SDL_SCANCODE_D
or SDL_Scancode.SDL_SCANCODE_W
or SDL_Scancode.SDL_SCANCODE_S;
}
}

View File

@ -1,6 +1,6 @@
using Mine2d.engine.system; using Mine2d.engine.system;
using Mine2d.engine.system.annotations; using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data; using Mine2d.game.backend.network.packets;
namespace Mine2d.game.frontend.events; namespace Mine2d.game.frontend.events;
@ -80,6 +80,10 @@ public class PlayerInput
{ {
movement = Vector2.Normalize(movement); movement = Vector2.Normalize(movement);
} }
ctx.Backend.ProcessPacket(new MovePacket(ctx.FrontendGameState.PlayerName, movement)); ctx.Backend.ProcessPacket(new MovePacket
{
PlayerName = ctx.FrontendGameState.PlayerName,
Movement = movement
});
} }
} }

View File

@ -30,9 +30,9 @@ public class HudRenderer : IRenderer
var (hotbarWidth, hotbarHeight) = renderer.GetTextureSize(this.hotbarTexture); var (hotbarWidth, hotbarHeight) = renderer.GetTextureSize(this.hotbarTexture);
var player = PlayerEntity.GetSelf(); var player = PlayerEntity.GetSelf();
renderer.DrawTexture(this.hotbarTexture, 0, 0, hotbarWidth * uiScale, hotbarHeight * uiScale); renderer.DrawTexture(this.hotbarTexture, 0, 0, hotbarWidth * uiScale, hotbarHeight * uiScale);
for (var i = 0; i < player?.inventory.Hotbar.Length; i++) for (var i = 0; i < player?.Inventory.Hotbar.Length; i++)
{ {
var stack = player.inventory.Hotbar[i]; var stack = player.Inventory.Hotbar[i];
if (stack == null) if (stack == null)
{ {
continue; continue;

View File

@ -12,23 +12,23 @@ public class ItemRenderer : IRenderer
var world = gameState.World; var world = gameState.World;
foreach (var chunk in world.Chunks) foreach (var chunk in world.Chunks)
{ {
renderChunk(chunk.Value); RenderChunk(chunk.Value);
} }
} }
private static void renderChunk(Chunk chunk) private static void RenderChunk(Chunk chunk)
{ {
var entities = chunk.Entities; var entities = chunk.Entities;
foreach (var entity in entities) foreach (var entity in entities)
{ {
if (entity is ItemEntity itemEntity) if (entity is ItemEntity itemEntity)
{ {
renderItem(itemEntity); RenderItem(itemEntity);
} }
} }
} }
private static void renderItem(ItemEntity itemEntity) private static void RenderItem(ItemEntity itemEntity)
{ {
var item = itemEntity.ItemId; var item = itemEntity.ItemId;
var position = itemEntity.Position; var position = itemEntity.Position;

View File

@ -8,7 +8,7 @@ public class Player
public Guid Id { get; set; } public Guid Id { get; set; }
public Vector2 Mining { get; set; } public Vector2 Mining { get; set; }
public int MiningCooldown { get; set; } public int MiningCooldown { get; set; }
public PlayerInventory inventory { get; set; } = new(); public PlayerInventory Inventory { get; set; } = new();
public Vector2 GetCenter() public Vector2 GetCenter()
{ {