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">
<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 Mine2d.engine.networking;
using Mine2d.engine.extensions;
using Mine2d.engine.system.annotations;
using Mine2d.game.backend.network.packets;
using Mine2d.game.core.extensions;
namespace Mine2d.engine;
public delegate void InteractionHandler(Packet packet);
public class Publisher
{
private readonly Dictionary<string, HashSet<Delegate>> subscribers =
new();
private readonly HashSet<string> clientOnlySubscriptions = new();
private readonly HashSet<string> serverSubscriptions = new();
private readonly Dictionary<PacketType, HashSet<MethodInfo>> subscribers = new();
private readonly HashSet<PacketType> clientOnlySubscriptions = new();
private readonly HashSet<PacketType> serverSubscriptions = new();
private readonly InteractorKind kind;
public Publisher(InteractorKind kind)
{
Enum.GetValues<PacketType>()
.ForEach(type => this.subscribers[type] = new HashSet<MethodInfo>());
this.kind = kind;
this.Scan();
}
private void Scan()
{
var types = Assembly
Assembly
.GetAssembly(this.GetType())!
.GetTypesSafe();
foreach (var type in types)
{
var classAttrs = type.GetCustomAttributes(typeof(InteractorAttribute), false);
if (classAttrs.Length == 0)
.GetTypesSafe()
.Where(t => t.HasAttribute<InteractorAttribute>())
.SelectMany(t => t.GetMethods())
.Where(m => m.HasAttribute<InteractionAttribute>())
.ForEach(method =>
{
continue;
}
var methods = type.GetMethods();
foreach (var method in methods)
{
var methodAttrs = method.GetCustomAttributes(typeof(InteractionAttribute), false);
if (methodAttrs.Length == 0)
var attribute = method.GetCustomAttribute<InteractionAttribute>()!;
Console.WriteLine($"Registering interaction method {method.Name} declared in {method.DeclaringType}");
Console.WriteLine($"InteractorKind: {attribute.Kind}");
Console.WriteLine($"PacketType: {attribute.Type}");
switch (attribute.Kind)
{
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)
{
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);
Console.WriteLine();
});
}
private void Subscribe(string type, Delegate callback)
public void Publish(Packet packet)
{
if (!this.subscribers.ContainsKey(type))
{
this.subscribers[type] = new HashSet<Delegate>();
}
this.subscribers[type].Add(callback);
if (packet.Type != PacketType.Tick)
Console.WriteLine($"[{nameof(Publisher)}] Publishing {packet.Type}");
this.subscribers[packet.Type]
.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()
{
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;
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 Mine2d.engine.system.annotations;
using Mine2d.game;
using Mine2d.game.backend.data;
using Mine2d.game.backend.network;
using Mine2d.game.backend.network.packets;
using Newtonsoft.Json;
using WatsonTcp;
@ -12,22 +12,25 @@ public class Backend : IBackend
{
private WatsonTcpServer server;
private Publisher publisher;
private readonly Queue<ValueType> pendingPackets = new();
private readonly Queue<Packet> pendingPackets = new();
private uint tick;
public void Process(double dt)
{
this.ProcessPacket(new TickPacket(this.tick++));
this.ProcessPacket(new TickPacket
{
Tick = ++this.tick
});
Context.Get().GameState.Tick = this.tick;
while (this.pendingPackets.Count > 0)
{
var packet = this.pendingPackets.Dequeue();
this.publisher.Publish(packet);
this.publisher.Publish(this.pendingPackets.Dequeue());
}
this.SendGameState();
}
public void ProcessPacket(ValueType packet)
public void ProcessPacket(Packet packet)
{
this.pendingPackets.Enqueue(packet);
}
@ -36,6 +39,7 @@ public class Backend : IBackend
{
Task.Run(this.Run);
this.publisher = new Publisher(InteractorKind.Hybrid);
this.publisher.Dump();
}
public void Run()
@ -65,15 +69,12 @@ public class Backend : IBackend
private void MessageReceived(object sender, MessageReceivedEventArgs args)
{
var time = DateTime.Now;
Console.WriteLine("Message Received: " + args.IpPort);
var packet = Converter.ParsePacket(args.Data);
Console.WriteLine("Received packet: " + packet);
if (packet != null)
{
this.pendingPackets.Enqueue(packet);
}
Console.WriteLine(DateTime.Now - time);
Console.WriteLine($"Received packet: {packet.Type}");
this.pendingPackets.Enqueue(packet);
}
private void SendGameState()

View File

@ -1,5 +1,5 @@
using Mine2d.game;
using Mine2d.game.backend.data;
using Mine2d.game.backend.network.packets;
using Mine2d.game.frontend.renderer;
namespace Mine2d.engine.networking;
@ -14,7 +14,11 @@ public class Frontend : IFrontend
ctx.FrontendGameState.PlayerName = ctx.IsHost ? "Host" : "Client";
var guid = Guid.NewGuid();
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.TileRegistry.RegisterTile();
ctx.ItemRegistry.RegisterItems();

View File

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

View File

@ -1,3 +1,5 @@
using Mine2d.game.backend.network.packets;
namespace Mine2d.engine.system.annotations;
public enum InteractorKind
@ -13,10 +15,10 @@ public class InteractorAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class InteractionAttribute : Attribute
{
public string Type { get; set; }
public PacketType Type { get; set; }
public InteractorKind Kind { get; set; }
public InteractionAttribute(InteractorKind kind, string type)
public InteractionAttribute(InteractorKind kind, PacketType type)
{
this.Type = type;
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.system.annotations;
using Mine2d.game.backend.network.packets;
namespace Mine2d.game.backend.interactor;
[Interactor]
public class Audio
{
[Interaction(InteractorKind.Client, "blockBroken")]
[Interaction(InteractorKind.Client, PacketType.BlockBroken)]
public static void BlockBroken()
{
var ctx = Context.Get();

View File

@ -1,6 +1,5 @@
using Mine2d.engine;
using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data;
using Mine2d.game.backend.network.packets;
using Mine2d.game.core;
using Mine2d.game.core.data;
@ -9,7 +8,7 @@ namespace Mine2d.game.backend.interactor;
[Interactor]
public class Breaking
{
[Interaction(InteractorKind.Hybrid, "tick")]
[Interaction(InteractorKind.Hybrid, PacketType.Tick)]
public static void TickHybrid()
{
var ctx = Context.Get();
@ -45,7 +44,12 @@ public class Breaking
if (tile.Hits >= hardness)
{
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));
}
}
@ -53,7 +57,7 @@ public class Breaking
}
}
[Interaction(InteractorKind.Server, "break")]
[Interaction(InteractorKind.Server, PacketType.Break)]
public static void BreakServer(BreakPacket packet)
{
var ctx = Context.Get();
@ -65,7 +69,7 @@ public class Breaking
player.Mining = packet.Target;
}
[Interaction(InteractorKind.Server, "blockBroken")]
[Interaction(InteractorKind.Server, PacketType.BlockBroken)]
public static void BreakServer(BlockBrokenPacket packet)
{
var ctx = Context.Get();

View File

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

View File

@ -1,6 +1,6 @@
using Mine2d.engine;
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.entities;
@ -10,8 +10,8 @@ namespace Mine2d.game.backend.interactor;
public class ItemPhysics
{
[Interaction(InteractorKind.Hybrid, "tick")]
public static void TickHybrid()
[Interaction(InteractorKind.Hybrid, PacketType.Tick)]
public static void TickHybrid(TickPacket packet)
{
var gameState = Context.Get().GameState;
var world = gameState.World;
@ -36,8 +36,8 @@ public class ItemPhysics
}
}
[Interaction(InteractorKind.Hybrid, "tick")]
public static void Pickup(TickPacket tickPacket)
[Interaction(InteractorKind.Hybrid, PacketType.Tick)]
public static void Pickup(TickPacket packet)
{
var gameState = Context.Get().GameState;
var world = gameState.World;
@ -51,12 +51,12 @@ public class ItemPhysics
Console.WriteLine("Where");
return e is ItemEntity itemEntity &&
(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())
{
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));
}

View File

@ -1,5 +1,5 @@
using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data;
using Mine2d.game.backend.network.packets;
using Mine2d.game.core;
namespace Mine2d.game.backend.interactor;
@ -7,7 +7,7 @@ namespace Mine2d.game.backend.interactor;
[Interactor]
public class Move
{
[Interaction(InteractorKind.Hybrid, "move")]
[Interaction(InteractorKind.Hybrid, PacketType.Move)]
public static void MoveHybrid(MovePacket packet)
{
var ctx = Context.Get();
@ -18,7 +18,7 @@ public class Move
}
}
[Interaction(InteractorKind.Hybrid, "tick")]
[Interaction(InteractorKind.Hybrid, PacketType.Tick)]
public static void TickHybrid()
{
var ctx = Context.Get();
@ -33,12 +33,16 @@ public class Move
{
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()
{
var camera = Context.Get().FrontendGameState.Camera;

View File

@ -1,5 +1,5 @@
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.world;
@ -19,7 +19,7 @@ public class WorldGeneration
new(-1, 1)
};
[Interaction(InteractorKind.Server, "playerMoved")]
[Interaction(InteractorKind.Server, PacketType.PlayerMoved)]
public static void PlayerMovedServer(PlayerMovedPacket packet)
{
var ctx = Context.Get();
@ -28,7 +28,7 @@ public class WorldGeneration
{
var generationTarget = packet.Target + direction * 16 * 32;
var hasChunkGenerated = world.HasChunkAt(generationTarget);
if (!hasChunkGenerated)
{
var chunkPos = World.ToChunkPos(generationTarget);

View File

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

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;
namespace Mine2d.game.core.tiles;

View File

@ -1,6 +1,6 @@
using Mine2d.engine.system;
using Mine2d.engine.system.annotations;
using Mine2d.game.backend.data;
using Mine2d.game.backend.network.packets;
namespace Mine2d.game.frontend.events;
@ -18,7 +18,12 @@ public class PlayerBreakInput
var amp = ctx.FrontendGameState.MousePosition
/ ctx.FrontendGameState.Settings.GameScale
+ 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 amp = ctx.FrontendGameState.MousePosition / ctx.FrontendGameState.Settings.GameScale + ctx.FrontendGameState.Camera.Position;
ctx.Backend.ProcessPacket(new BreakPacket(ctx.FrontendGameState.PlayerGuid, amp));
var amp = ctx.FrontendGameState.MousePosition
/ ctx.FrontendGameState.Settings.GameScale
+ ctx.FrontendGameState.Camera.Position;
ctx.Backend.ProcessPacket(new BreakPacket
{
PlayerGuid = ctx.FrontendGameState.PlayerGuid,
Target = amp
});
}
[EventListener(EventType.MouseButtonUp)]
@ -44,6 +56,10 @@ public class PlayerBreakInput
}
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.annotations;
using Mine2d.game.backend.data;
using Mine2d.game.backend.network.packets;
namespace Mine2d.game.frontend.events;
@ -80,6 +80,10 @@ public class PlayerInput
{
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 player = PlayerEntity.GetSelf();
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)
{
continue;

View File

@ -12,23 +12,23 @@ public class ItemRenderer : IRenderer
var world = gameState.World;
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;
foreach (var entity in entities)
{
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 position = itemEntity.Position;

View File

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