diff --git a/multiplayer-game/assets/stone.png b/multiplayer-game/assets/stone.png
new file mode 100644
index 0000000..496c4ac
Binary files /dev/null and b/multiplayer-game/assets/stone.png differ
diff --git a/multiplayer-game/multiplayer-game.csproj b/multiplayer-game/multiplayer-game.csproj
index f8c4950..f62c379 100644
--- a/multiplayer-game/multiplayer-game.csproj
+++ b/multiplayer-game/multiplayer-game.csproj
@@ -9,7 +9,7 @@
link
true
true
- enable
+ disable
diff --git a/multiplayer-game/src/Context.cs b/multiplayer-game/src/Context.cs
index 6f5f2c5..7f40153 100644
--- a/multiplayer-game/src/Context.cs
+++ b/multiplayer-game/src/Context.cs
@@ -7,7 +7,9 @@ class Context
public FrontendGameState FrontendGameState { get; set; }
public Window Window { get; set; }
public Renderer Renderer { get; set; }
- public static Context? instance { get; set; }
+ public TileRegistry TileRegistry { get; set; }
+ public ResourceLoader ResourceLoader { get; set; }
+ public static Context instance { get; set; }
public Context(
bool isHost,
@@ -26,6 +28,8 @@ class Context
this.FrontendGameState = frontendGameState;
this.Renderer = renderer;
this.Window = window;
+ this.TileRegistry = new TileRegistry();
+ this.ResourceLoader = new ResourceLoader();
Context.instance = this;
}
diff --git a/multiplayer-game/src/MultiPlayerGame.cs b/multiplayer-game/src/MultiPlayerGame.cs
index e2b76fa..2796e17 100644
--- a/multiplayer-game/src/MultiPlayerGame.cs
+++ b/multiplayer-game/src/MultiPlayerGame.cs
@@ -29,6 +29,7 @@ class MultiPlayerGame : Game
window
);
}
+ Bootstrapper.Bootstrap();
ctx.Backend.Init();
ctx.Frontend.Init();
}
diff --git a/multiplayer-game/src/backend/Backend.cs b/multiplayer-game/src/backend/Backend.cs
index 226ae6d..2156339 100644
--- a/multiplayer-game/src/backend/Backend.cs
+++ b/multiplayer-game/src/backend/Backend.cs
@@ -1,4 +1,3 @@
-using System.Numerics;
using System.Text;
using Newtonsoft.Json;
using WatsonTcp;
@@ -8,20 +7,17 @@ class Backend : IBackend
private WatsonTcpServer server;
private Publisher publisher;
private Queue pendingPackets = new Queue();
+ private uint tick = 0;
public void Process(double dt)
{
var ctx = Context.Get();
+ this.ProcessPacket(new TickPacket(tick++));
while (pendingPackets.Count > 0)
{
var packet = pendingPackets.Dequeue();
this.ProcessPacket(packet);
}
- ctx.GameState.PlayerPositions.ForEach(player =>
- {
- player.position += player.movement;
- });
- // this.sendGameState();
}
public void ProcessPacket(ValueType packet)
@@ -33,9 +29,7 @@ class Backend : IBackend
public void Init()
{
Task.Run(Run);
- this.publisher = new Publisher(
- Context.Get().IsHost ? InteractorKind.Server : InteractorKind.Client
- );
+ this.publisher = new Publisher(InteractorKind.Server);
}
public void Run()
@@ -47,7 +41,7 @@ class Backend : IBackend
server.Start();
}
- private void clientConnected(object? sender, ConnectionEventArgs args)
+ private void clientConnected(object sender, ConnectionEventArgs args)
{
Console.WriteLine("Client connected: " + args.IpPort);
var gameState = Context.Get().GameState;
@@ -58,12 +52,12 @@ class Backend : IBackend
}
}
- private void clientDisconnected(object? sender, DisconnectionEventArgs args)
+ private void clientDisconnected(object sender, DisconnectionEventArgs args)
{
Console.WriteLine("Client disconnected: " + args.IpPort);
}
- private void messageReceived(object? sender, MessageReceivedEventArgs args)
+ private void messageReceived(object sender, MessageReceivedEventArgs args)
{
var time = DateTime.Now;
Console.WriteLine("Message Received: " + args.IpPort);
diff --git a/multiplayer-game/src/backend/RemoteBackend.cs b/multiplayer-game/src/backend/RemoteBackend.cs
index f4cf16e..0584e22 100644
--- a/multiplayer-game/src/backend/RemoteBackend.cs
+++ b/multiplayer-game/src/backend/RemoteBackend.cs
@@ -5,18 +5,24 @@ using System.Text;
class RemoteBackend : IBackend
{
private WatsonTcpClient client;
+ private Publisher publisher;
+ private Queue pendingPackets = new Queue();
+ private uint tick = 0;
public void Process(double dt)
{
var ctx = Context.Get();
- ctx.GameState.PlayerPositions.ForEach(player =>
+ this.ProcessPacket(new TickPacket(tick++));
+ while (pendingPackets.Count > 0)
{
- player.position += player.movement;
- });
+ var packet = pendingPackets.Dequeue();
+ this.ProcessPacket(packet);
+ }
}
public void ProcessPacket(ValueType packet)
{
+ this.publisher.Publish(packet);
var json = JsonConvert.SerializeObject(packet);
var bytes = Encoding.UTF8.GetBytes(json);
client.Send(bytes);
@@ -25,6 +31,7 @@ class RemoteBackend : IBackend
public void Init()
{
Task.Run(this.Run);
+ this.publisher = new Publisher(InteractorKind.Client);
}
public void Run()
diff --git a/multiplayer-game/src/backend/data/Packet.cs b/multiplayer-game/src/backend/data/Packet.cs
index d906260..beeeb3a 100644
--- a/multiplayer-game/src/backend/data/Packet.cs
+++ b/multiplayer-game/src/backend/data/Packet.cs
@@ -17,9 +17,22 @@ readonly struct ConnectPacket
{
public readonly string type = "connect";
public readonly string playerName;
+ public readonly Guid playerGuid;
- public ConnectPacket(string playerName)
+ public ConnectPacket(string playerName, Guid playerGuid)
{
this.playerName = playerName;
+ this.playerGuid = playerGuid;
+ }
+}
+
+readonly struct TickPacket
+{
+ public readonly string type = "tick";
+ public readonly uint tick;
+
+ public TickPacket(uint tick)
+ {
+ this.tick = tick;
}
}
diff --git a/multiplayer-game/src/backend/interactor/Connect.cs b/multiplayer-game/src/backend/interactor/Connect.cs
index e214e1c..22b4208 100644
--- a/multiplayer-game/src/backend/interactor/Connect.cs
+++ b/multiplayer-game/src/backend/interactor/Connect.cs
@@ -14,6 +14,7 @@ class Connect
new Player
{
name = packet.playerName,
+ guid = packet.playerGuid,
position = new Vector2(0, 0),
movement = new Vector2(0, 0)
}
diff --git a/multiplayer-game/src/backend/interactor/Move.cs b/multiplayer-game/src/backend/interactor/Move.cs
index 14ec141..c6ce3ae 100644
--- a/multiplayer-game/src/backend/interactor/Move.cs
+++ b/multiplayer-game/src/backend/interactor/Move.cs
@@ -11,4 +11,14 @@ class Move
player.movement = packet.movement * 4;
}
}
+
+ [Interaction(InteractorKind.Hybrid, "tick")]
+ public static void TickHybrid(TickPacket packet)
+ {
+ var ctx = Context.Get();
+ ctx.GameState.PlayerPositions.ForEach(player =>
+ {
+ player.position += player.movement;
+ });
+ }
}
diff --git a/multiplayer-game/src/core/Bootstrapper.cs b/multiplayer-game/src/core/Bootstrapper.cs
new file mode 100644
index 0000000..9cd4490
--- /dev/null
+++ b/multiplayer-game/src/core/Bootstrapper.cs
@@ -0,0 +1,9 @@
+class Bootstrapper
+{
+ public static void Bootstrap()
+ {
+ var ctx = Context.Get();
+ ctx.GameState.World = new World();
+ ctx.GameState.World.AddChunk(ChunkGenerator.CreateFilledChunk(0, 0, Tiles.stone));
+ }
+}
diff --git a/multiplayer-game/src/core/Constants.cs b/multiplayer-game/src/core/Constants.cs
new file mode 100644
index 0000000..0eeda54
--- /dev/null
+++ b/multiplayer-game/src/core/Constants.cs
@@ -0,0 +1,4 @@
+class Constants
+{
+ public const int ChunkSize = 32;
+}
diff --git a/multiplayer-game/src/core/data/Chunk.cs b/multiplayer-game/src/core/data/Chunk.cs
new file mode 100644
index 0000000..157ba87
--- /dev/null
+++ b/multiplayer-game/src/core/data/Chunk.cs
@@ -0,0 +1,21 @@
+class Chunk
+{
+ public int[][] Tiles { get; set; } = new int[Constants.ChunkSize][];
+ public int X { get; set; }
+ public int Y { get; set; }
+
+ public Chunk(int x, int y)
+ {
+ X = x;
+ Y = y;
+ for (int i = 0; i < Constants.ChunkSize; i++)
+ {
+ Tiles[i] = new int[Constants.ChunkSize];
+ }
+ }
+
+ public void SetTile(int x, int y, int tile)
+ {
+ Tiles[x][y] = tile;
+ }
+}
diff --git a/multiplayer-game/src/core/data/World.cs b/multiplayer-game/src/core/data/World.cs
new file mode 100644
index 0000000..526cc0d
--- /dev/null
+++ b/multiplayer-game/src/core/data/World.cs
@@ -0,0 +1,19 @@
+class World
+{
+ public Dictionary Chunks { get; set; } = new Dictionary();
+
+ public void AddChunk(Chunk chunk)
+ {
+ Chunks.Add(chunk.X + "," + chunk.Y, chunk);
+ }
+
+ public Chunk GetChunk(int x, int y)
+ {
+ return Chunks[x + "," + y];
+ }
+
+ public bool ChunkExists(int x, int y)
+ {
+ return Chunks.ContainsKey(x + "," + y);
+ }
+}
diff --git a/multiplayer-game/src/core/tiles/Tile.cs b/multiplayer-game/src/core/tiles/Tile.cs
new file mode 100644
index 0000000..c17b0fc
--- /dev/null
+++ b/multiplayer-game/src/core/tiles/Tile.cs
@@ -0,0 +1,33 @@
+using static SDL2.SDL;
+using static SDL2.SDL_image;
+
+class Tile
+{
+ public string Name { get; set; }
+ public IntPtr Texture { get; set; }
+
+ public Tile(string name, string textureName)
+ {
+ this.Name = name;
+
+ var rl = Context.Get().ResourceLoader;
+ var res = rl.LoadToIntPtr("assets." + textureName + ".png");
+ var sdlBuffer = SDL_RWFromMem(res.ptr, res.size);
+ var surface = IMG_Load_RW(sdlBuffer, 1);
+ var texture = Context.Get().Renderer.CreateTextureFromSurface(surface);
+ this.Texture = texture;
+ SDL_FreeSurface(surface);
+ }
+
+ ~Tile()
+ {
+ SDL_DestroyTexture(this.Texture);
+ }
+
+ public void Render(int x, int y)
+ {
+ var renderer = Context.Get().Renderer;
+ var scale = Context.Get().GameState.Settings.GameScale;
+ renderer.DrawTexture(this.Texture, x * scale, y * scale, 16 * scale, 16 * scale);
+ }
+}
diff --git a/multiplayer-game/src/core/tiles/TileRegistry.cs b/multiplayer-game/src/core/tiles/TileRegistry.cs
new file mode 100644
index 0000000..61341ec
--- /dev/null
+++ b/multiplayer-game/src/core/tiles/TileRegistry.cs
@@ -0,0 +1,19 @@
+enum Tiles : int
+{
+ stone = 1,
+}
+
+class TileRegistry
+{
+ public Dictionary Tiles { get; set; } = new Dictionary();
+
+ public void RegisterTile()
+ {
+ Tiles.Add(1, new Tile("stone", "stone"));
+ }
+
+ public Tile GetTile(int id)
+ {
+ return Tiles[id];
+ }
+}
diff --git a/multiplayer-game/src/core/world/ChunkGenerator.cs b/multiplayer-game/src/core/world/ChunkGenerator.cs
new file mode 100644
index 0000000..5f932ea
--- /dev/null
+++ b/multiplayer-game/src/core/world/ChunkGenerator.cs
@@ -0,0 +1,20 @@
+class ChunkGenerator
+{
+ public static Chunk CreateFilledChunk(int x, int y, int fill)
+ {
+ Chunk chunk = new Chunk(x, y);
+ for (int i = 0; i < Constants.ChunkSize; i++)
+ {
+ for (int j = 0; j < Constants.ChunkSize; j++)
+ {
+ chunk.SetTile(i, j, fill);
+ }
+ }
+ return chunk;
+ }
+
+ public static Chunk CreateFilledChunk(int x, int y, Tiles fill)
+ {
+ return CreateFilledChunk(x, y, (int)fill);
+ }
+}
diff --git a/multiplayer-game/src/engine/Renderer.cs b/multiplayer-game/src/engine/Renderer.cs
index a47574c..f694ec3 100644
--- a/multiplayer-game/src/engine/Renderer.cs
+++ b/multiplayer-game/src/engine/Renderer.cs
@@ -108,4 +108,15 @@ class Renderer
{
return SDL_CreateTextureFromSurface(renderer, surface);
}
+
+ public void DrawTexture(IntPtr texture, int x, int y, int w, int h)
+ {
+ SDL_Rect rect = new SDL_Rect();
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+
+ SDL_RenderCopy(renderer, texture, IntPtr.Zero, ref rect);
+ }
}
diff --git a/multiplayer-game/src/engine/Window.cs b/multiplayer-game/src/engine/Window.cs
index 671a375..64e2aa3 100644
--- a/multiplayer-game/src/engine/Window.cs
+++ b/multiplayer-game/src/engine/Window.cs
@@ -38,4 +38,9 @@ class Window
SDL_GetWindowSize(this.window, out w, out h);
return (w, h);
}
+
+ public IntPtr GetRaw()
+ {
+ return this.window;
+ }
}
diff --git a/multiplayer-game/src/frontend/Frontend.cs b/multiplayer-game/src/frontend/Frontend.cs
index 0b5646d..d61b1be 100644
--- a/multiplayer-game/src/frontend/Frontend.cs
+++ b/multiplayer-game/src/frontend/Frontend.cs
@@ -4,12 +4,16 @@ using static SDL2.SDL;
class Frontend : IFrontend
{
private string playerName = "Player";
+ private bool fullscreen = false;
public void Init()
{
this.playerName = Context.Get().IsHost ? "Host" : "Client";
- var conntectPacket = new ConnectPacket(playerName);
- Context.Get().Backend.ProcessPacket(conntectPacket);
+ var guid = Guid.NewGuid();
+ Context.Get().FrontendGameState.PlayerGuid = guid;
+ var connectPacket = new ConnectPacket(playerName, guid);
+ Context.Get().Backend.ProcessPacket(connectPacket);
+ Context.Get().TileRegistry.RegisterTile();
}
public void Process()
@@ -21,9 +25,33 @@ class Frontend : IFrontend
{
System.Environment.Exit(0);
}
+ if (e.type == SDL_EventType.SDL_WINDOWEVENT)
+ {
+ if (e.window.windowEvent == SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED)
+ {
+ ctx.FrontendGameState.WindowWidth = e.window.data1;
+ ctx.FrontendGameState.WindowHeight = e.window.data2;
+ Console.WriteLine($"Window resized to {e.window.data1}x{e.window.data2}");
+ }
+ }
if (e.type == SDL_EventType.SDL_KEYDOWN && e.key.repeat == 0)
{
- var movementInput = ctx.FrontendGameState.movementInput;
+ var movementInput = ctx.FrontendGameState.MovementInput;
+ if (e.key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_F11)
+ {
+ if (!fullscreen)
+ {
+ SDL_SetWindowFullscreen(
+ ctx.Window.GetRaw(),
+ (uint)SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP
+ );
+ }
+ else
+ {
+ SDL_SetWindowFullscreen(ctx.Window.GetRaw(), 0);
+ }
+ fullscreen = !fullscreen;
+ }
if (e.key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_A)
{
movementInput.X -= 1;
@@ -40,11 +68,11 @@ class Frontend : IFrontend
{
movementInput.Y += 1;
}
- ctx.FrontendGameState.movementInput = movementInput;
+ ctx.FrontendGameState.MovementInput = movementInput;
}
if (e.type == SDL_EventType.SDL_KEYUP && e.key.repeat == 0)
{
- var movementInput = ctx.FrontendGameState.movementInput;
+ var movementInput = ctx.FrontendGameState.MovementInput;
if (e.key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_A)
{
movementInput.X += 1;
@@ -61,7 +89,7 @@ class Frontend : IFrontend
{
movementInput.Y -= 1;
}
- ctx.FrontendGameState.movementInput = movementInput;
+ ctx.FrontendGameState.MovementInput = movementInput;
}
if (
e.key.keysym.scancode
@@ -75,7 +103,7 @@ class Frontend : IFrontend
{
if (e.key.repeat == 1)
continue;
- var movement = ctx.FrontendGameState.movementInput;
+ var movement = ctx.FrontendGameState.MovementInput;
if (movement.Length() > 0)
movement = Vector2.Normalize(movement);
ctx.Backend.ProcessPacket(new MovePacket(playerName, movement));
@@ -87,6 +115,7 @@ class Frontend : IFrontend
}
ctx.Renderer.Clear();
+ new WorldRenderer().Render();
ctx.GameState.PlayerPositions.ForEach(player =>
{
if (player.name == playerName)
diff --git a/multiplayer-game/src/frontend/renderer/IRenderer.cs b/multiplayer-game/src/frontend/renderer/IRenderer.cs
new file mode 100644
index 0000000..7423328
--- /dev/null
+++ b/multiplayer-game/src/frontend/renderer/IRenderer.cs
@@ -0,0 +1,4 @@
+interface IRenderer
+{
+ public void Render();
+}
diff --git a/multiplayer-game/src/frontend/renderer/WorldRenderer.cs b/multiplayer-game/src/frontend/renderer/WorldRenderer.cs
new file mode 100644
index 0000000..3c9a5c8
--- /dev/null
+++ b/multiplayer-game/src/frontend/renderer/WorldRenderer.cs
@@ -0,0 +1,22 @@
+class WorldRenderer : IRenderer
+{
+ public void Render()
+ {
+ var ctx = Context.Get();
+ var world = ctx.GameState.World;
+ var renderer = ctx.Renderer;
+ var tileRegistry = ctx.TileRegistry;
+ foreach (var (_, chunk) in world.Chunks)
+ {
+ for (int y = 0; y < 16; y++)
+ {
+ for (int x = 0; x < 16; x++)
+ {
+ var tileId = chunk.Tiles[x][y];
+ var tile = tileRegistry.GetTile(tileId);
+ tile.Render(x * 16, y * 16);
+ }
+ }
+ }
+ }
+}
diff --git a/multiplayer-game/src/state/GameState.cs b/multiplayer-game/src/state/GameState.cs
index f3f8131..d2abb3b 100644
--- a/multiplayer-game/src/state/GameState.cs
+++ b/multiplayer-game/src/state/GameState.cs
@@ -5,14 +5,27 @@ class Player
public string name;
public Vector2 position;
public Vector2 movement;
+ public Guid guid;
}
class FrontendGameState
{
- public Vector2 movementInput;
+ public Vector2 MovementInput;
+ public Vector2 CameraPosition;
+ public int WindowWidth;
+ public int WindowHeight;
+ public Guid PlayerGuid;
+}
+
+class Settings
+{
+ public int GameScale = 4;
+ public int UIScale = 4;
}
class GameState
{
public List PlayerPositions { get; set; } = new List();
+ public World World { get; set; }
+ public Settings Settings { get; set; } = new Settings();
}