refactoring WIP

This commit is contained in:
Erik Simon 2022-11-09 19:29:50 +01:00
parent 2742e0100c
commit 7eea232196
49 changed files with 474 additions and 251 deletions

13
.idea/.idea.mine2d.dir/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/.idea.mine2d.iml
/contentModel.xml
/projectSettingsUpdater.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

2
Folder.DotSettings Normal file
View File

@ -0,0 +1,2 @@
<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:Boolean x:Key="/Default/UserDictionary/Words/=trimmable/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -1,4 +1,6 @@
class Program
namespace mine2d;
class Program
{
static void Main(string[] args)
{

View File

@ -1,3 +1,11 @@
using mine2d.backend;
using mine2d.core.tiles;
using mine2d.engine;
using mine2d.frontend;
using mine2d.state;
namespace mine2d;
class Context
{
public bool IsHost { get; set; }
@ -9,7 +17,7 @@ class Context
public Renderer Renderer { get; set; }
public TileRegistry TileRegistry { get; set; }
public ResourceLoader ResourceLoader { get; set; }
public static Context instance { get; set; }
public static Context Instance { get; set; }
public Context(
bool isHost,
@ -30,16 +38,16 @@ class Context
this.Window = window;
this.TileRegistry = new TileRegistry();
this.ResourceLoader = new ResourceLoader();
Context.instance = this;
Context.Instance = this;
}
public static Context Get()
{
if (Context.instance == null)
if (Context.Instance == null)
{
throw new Exception("Context not initialized");
}
return Context.instance;
return Context.Instance;
}
}

View File

@ -1,13 +1,13 @@
using static SDL2.SDL;
namespace mine2d;
enum Control
{
UP,
DOWN,
LEFT,
RIGHT,
STAY,
CONFIRM,
Up,
Down,
Left,
Right,
Stay,
Confirm,
}
static class ControlKeyExtension
@ -16,17 +16,17 @@ static class ControlKeyExtension
{
switch (c)
{
case Control.UP:
case Control.Up:
return SDL_Keycode.SDLK_w;
case Control.DOWN:
case Control.Down:
return SDL_Keycode.SDLK_s;
case Control.LEFT:
case Control.Left:
return SDL_Keycode.SDLK_a;
case Control.RIGHT:
case Control.Right:
return SDL_Keycode.SDLK_d;
case Control.STAY:
case Control.Stay:
return SDL_Keycode.SDLK_LCTRL;
case Control.CONFIRM:
case Control.Confirm:
return SDL_Keycode.SDLK_SPACE;
default:
throw new ArgumentException("Invalid control");

View File

@ -1,3 +1,11 @@
using mine2d.backend;
using mine2d.core;
using mine2d.engine;
using mine2d.frontend;
using mine2d.state;
namespace mine2d;
class Mine2d : Game
{
private readonly Context ctx;
@ -29,12 +37,12 @@ class Mine2d : Game
this.ctx.Frontend.Init();
}
protected override void draw()
protected override void Draw()
{
this.ctx.Frontend.Process();
}
protected override void update(double dt)
protected override void Update(double dt)
{
this.ctx.Backend.Process(dt);
}

View File

@ -1,13 +1,18 @@
using System.Text;
using mine2d.backend.data;
using mine2d.engine.system.annotations;
using mine2d.network;
using Newtonsoft.Json;
using WatsonTcp;
namespace mine2d.backend;
class Backend : IBackend
{
private WatsonTcpServer server;
private Publisher publisher;
private Queue<ValueType> pendingPackets = new();
private uint tick = 0;
private uint tick;
public void Process(double dt)
{
@ -17,7 +22,7 @@ class Backend : IBackend
var packet = this.pendingPackets.Dequeue();
this.publisher.Publish(packet);
}
this.sendGameState();
this.SendGameState();
}
public void ProcessPacket(ValueType packet)
@ -34,13 +39,13 @@ class Backend : IBackend
public void Run()
{
this.server = new WatsonTcpServer("127.0.0.1", 42069);
this.server.Events.ClientConnected += this.clientConnected;
this.server.Events.ClientDisconnected += this.clientDisconnected;
this.server.Events.MessageReceived += this.messageReceived;
this.server.Events.ClientConnected += this.ClientConnected;
this.server.Events.ClientDisconnected += this.ClientDisconnected;
this.server.Events.MessageReceived += this.MessageReceived;
this.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;
@ -51,12 +56,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);
@ -64,24 +69,24 @@ class Backend : IBackend
Console.WriteLine("Received packet: " + packet);
if (packet != null)
{
pendingPackets.Enqueue(packet);
this.pendingPackets.Enqueue(packet);
}
Console.WriteLine(DateTime.Now - time);
}
private void sendGameState()
private void SendGameState()
{
if (server == null)
if (this.server == null)
return;
var clients = server.ListClients();
if (clients.Count() == 0)
var clients = this.server.ListClients().ToArray();
if (!clients.Any())
return;
var gameState = Context.Get().GameState;
var json = JsonConvert.SerializeObject(gameState);
var bytes = Encoding.UTF8.GetBytes(json);
foreach (var client in clients)
{
server.Send(client, bytes);
this.server.Send(client, bytes);
}
}
}

View File

@ -1,3 +1,5 @@
namespace mine2d.backend;
interface IBackend
{
public void Process(double dt);

View File

@ -1,3 +1,10 @@
using System.Reflection;
using mine2d.core.extensions;
using mine2d.engine;
using mine2d.engine.system.annotations;
namespace mine2d.backend;
class Publisher
{
private readonly Dictionary<string, HashSet<Delegate>> subscribers =
@ -7,13 +14,14 @@ class Publisher
public Publisher(InteractorKind kind)
{
this.kind = kind;
this.scan();
this.Scan();
}
private void scan()
private void Scan()
{
var assembly = this.GetType().Assembly;
var types = assembly.GetTypes();
var types = Assembly
.GetAssembly(this.GetType())!
.GetTypesSafe();
foreach (var type in types)
{
var attrs = type.GetCustomAttributes(typeof(Interactor), false);
@ -35,15 +43,15 @@ class Publisher
continue;
}
var del = Delegate.CreateDelegate(
typeof(Action<>).MakeGenericType(method.GetParameters()[0].ParameterType),
typeof(Action<>).MakeGenericTypeSafely(method.GetParameters()[0].ParameterType),
method
);
this.subscribe(attr.Type, del);
this.Subscribe(attr.Type, del);
}
}
}
private void subscribe(string type, Delegate callback)
private void Subscribe(string type, Delegate callback)
{
if (!this.subscribers.ContainsKey(type))
{

View File

@ -1,21 +1,25 @@
using WatsonTcp;
using Newtonsoft.Json;
using System.Text;
using mine2d.backend.data;
using mine2d.engine.system.annotations;
using mine2d.state;
using Newtonsoft.Json;
using WatsonTcp;
namespace mine2d.backend;
class RemoteBackend : IBackend
{
private WatsonTcpClient client;
private Publisher publisher;
private Queue<ValueType> pendingPackets = new Queue<ValueType>();
private readonly Queue<ValueType> pendingPackets = new();
private uint tick = 0;
public void Process(double dt)
{
var ctx = Context.Get();
this.ProcessPacket(new TickPacket(tick++));
while (pendingPackets.Count > 0)
this.ProcessPacket(new TickPacket(this.tick++));
while (this.pendingPackets.Count > 0)
{
var packet = pendingPackets.Dequeue();
var packet = this.pendingPackets.Dequeue();
this.ProcessPacket(packet);
}
}
@ -25,7 +29,7 @@ class RemoteBackend : IBackend
this.publisher.Publish(packet);
var json = JsonConvert.SerializeObject(packet);
var bytes = Encoding.UTF8.GetBytes(json);
client.Send(bytes);
this.client.Send(bytes);
}
public void Init()
@ -36,8 +40,8 @@ class RemoteBackend : IBackend
public void Run()
{
client = new WatsonTcpClient("127.0.0.1", 42069);
client.Events.MessageReceived += (sender, args) =>
this.client = new WatsonTcpClient("127.0.0.1", 42069);
this.client.Events.MessageReceived += (_, args) =>
{
var ctx = Context.Get();
var message = Encoding.UTF8.GetString(args.Data);
@ -47,6 +51,6 @@ class RemoteBackend : IBackend
ctx.GameState = packet;
}
};
client.Connect();
this.client.Connect();
}
}

View File

@ -1,49 +1,55 @@
using System.Numerics;
namespace mine2d.backend.data;
readonly struct MovePacket
public interface IPacket
{
readonly public string type = "move";
readonly public string playerName;
readonly public Vector2 movement;
string Type { get; }
}
readonly struct MovePacket : IPacket
{
public string Type => "move";
readonly public string PlayerName;
readonly public Vector2 Movement;
public MovePacket(string playerName, Vector2 movement)
{
this.playerName = playerName;
this.movement = movement;
this.PlayerName = playerName;
this.Movement = movement;
}
}
readonly struct ConnectPacket
{
public readonly string type = "connect";
public readonly string playerName;
public readonly Guid playerGuid;
public readonly string Type = "connect";
public readonly string PlayerName;
public readonly Guid PlayerGuid;
public ConnectPacket(string playerName, Guid playerGuid)
{
this.playerName = playerName;
this.playerGuid = playerGuid;
this.PlayerName = playerName;
this.PlayerGuid = playerGuid;
}
}
readonly struct TickPacket
{
public readonly string type = "tick";
public readonly uint tick;
public readonly string Type = "tick";
public readonly uint Tick;
public TickPacket(uint tick)
{
this.tick = tick;
this.Tick = tick;
}
}
readonly struct SelfMovedPacket
{
public readonly string type = "selfMoved";
public readonly Vector2 target;
public readonly string Type = "selfMoved";
public readonly Vector2 Target;
public SelfMovedPacket(Vector2 target)
{
this.target = target;
this.Target = target;
}
}

View File

@ -1,3 +1,8 @@
using mine2d.backend.data;
using mine2d.engine.system.annotations;
namespace mine2d.backend.interactor;
[Interactor]
class Breaking
{
@ -7,8 +12,8 @@ class Breaking
var ctx = Context.Get();
ctx.GameState.Players.ForEach(player =>
{
Math.Max(0, player.MiningCooldown -= 1);
if (player.Mining != null && player.MiningCooldown == 0)
player.MiningCooldown = Math.Max(0, player.MiningCooldown - 1);
if (player.Mining != Vector2.Zero && player.MiningCooldown == 0)
{
var chunk = ctx.GameState.World.GetChunkAt(player.Mining);
// chunk.SetTileAt(player.Mining, tile with { Hits = tile.Hits + 1 });

View File

@ -1,3 +1,9 @@
using mine2d.backend.data;
using mine2d.engine.system.annotations;
using mine2d.state;
namespace mine2d.backend.interactor;
[Interactor]
class Connect
{
@ -5,14 +11,14 @@ class Connect
public static void ConnectServer(ConnectPacket packet)
{
var ctx = Context.Get();
var player = ctx.GameState.Players.Find(p => p.Name == packet.playerName);
var player = ctx.GameState.Players.Find(p => p.Name == packet.PlayerName);
if (player == null)
{
ctx.GameState.Players.Add(
new Player
{
Name = packet.playerName,
Guid = packet.playerGuid,
Name = packet.PlayerName,
Guid = packet.PlayerGuid,
Position = new Vector2(20, 16 * 16),
Movement = new Vector2(0, 0)
}

View File

@ -1,3 +1,9 @@
using mine2d.backend.data;
using mine2d.core;
using mine2d.engine.system.annotations;
namespace mine2d.backend.interactor;
[Interactor]
class Move
{
@ -5,10 +11,10 @@ class Move
public static void MoveHybrid(MovePacket packet)
{
var ctx = Context.Get();
var player = ctx.GameState.Players.Find(p => p.Name == packet.playerName);
var player = ctx.GameState.Players.Find(p => p.Name == packet.PlayerName);
if (player != null)
{
player.Movement = packet.movement * 4;
player.Movement = packet.Movement * 4;
}
}

View File

@ -1,11 +1,17 @@
using mine2d.core.data;
using mine2d.core.tiles;
using mine2d.core.world;
namespace mine2d.core;
class Bootstrapper
{
public static void Bootstrap()
{
var ctx = Context.Get();
ctx.GameState.World = new World();
ctx.GameState.World.AddChunk(ChunkGenerator.CreateFilledChunk(0, 1, STile.From(Tiles.stone)));
ctx.GameState.World.AddChunk(ChunkGenerator.CreateFilledChunk(1, 1, STile.From(Tiles.stone)));
ctx.GameState.World.AddChunk(ChunkGenerator.CreateFilledChunk(1, 0, STile.From(Tiles.stone)));
ctx.GameState.World.AddChunk(ChunkGenerator.CreateFilledChunk(0, 1, STile.From(Tiles.Stone)));
ctx.GameState.World.AddChunk(ChunkGenerator.CreateFilledChunk(1, 1, STile.From(Tiles.Stone)));
ctx.GameState.World.AddChunk(ChunkGenerator.CreateFilledChunk(1, 0, STile.From(Tiles.Stone)));
}
}

View File

@ -1,10 +1,12 @@
namespace mine2d.core;
class Camera
{
public Vector2 position;
public Vector2 Position;
public Camera()
{
position = Vector2.Zero;
this.Position = Vector2.Zero;
}
public void CenterOn(Vector2 target)
@ -13,6 +15,6 @@ class Camera
var scale = ctx.FrontendGameState.Settings.GameScale;
var windowWidth = ctx.FrontendGameState.WindowWidth;
var windowHeight = ctx.FrontendGameState.WindowHeight;
position = target - (new Vector2(windowWidth / 2, windowHeight / 2)) / scale;
this.Position = target - (new Vector2(windowWidth, windowHeight) / 2) / scale;
}
}

View File

@ -1,6 +1,8 @@
namespace mine2d.core;
class Constants
{
public const int ChunkSize = 32;
public const int TileSize = 16;
public static Vector2 gravity = new Vector2(0, 0.1f);
public static Vector2 Gravity = new Vector2(0, 0.1f);
}

View File

@ -1,6 +1,10 @@
using mine2d.state;
namespace mine2d.core;
class PlayerEntity
{
public static bool isSelf(Player p)
public static bool IsSelf(Player p)
{
return p.Guid == GetSelf().Guid;
}
@ -16,7 +20,7 @@ class PlayerEntity
public static void Move(Player p)
{
p.Movement += Constants.gravity;
p.Movement += Constants.Gravity;
p.Position += p.Movement;
}
@ -28,7 +32,7 @@ class PlayerEntity
{
var pL = p.Position + new Vector2(0, -8);
hasCollision =
world.HasChunkAt(pL) && world.GetChunkAt(pL).hasTileAt(pL);
world.HasChunkAt(pL) && world.GetChunkAt(pL).HasTileAt(pL);
if (hasCollision)
{
p.Movement = p.Movement with { X = 0 };
@ -39,7 +43,7 @@ class PlayerEntity
{
var pR = p.Position + new Vector2(16, -8);
hasCollision =
world.HasChunkAt(pR) && world.GetChunkAt(pR).hasTileAt(pR);
world.HasChunkAt(pR) && world.GetChunkAt(pR).HasTileAt(pR);
if (hasCollision)
{
p.Movement = p.Movement with { X = 0 };
@ -51,8 +55,8 @@ class PlayerEntity
var pL = p.Position;
var pR = p.Position + new Vector2(16, 0);
hasCollision =
world.HasChunkAt(pL) && world.GetChunkAt(pL).hasTileAt(pL)
|| world.HasChunkAt(pR) && world.GetChunkAt(pR).hasTileAt(pR);
world.HasChunkAt(pL) && world.GetChunkAt(pL).HasTileAt(pL)
|| world.HasChunkAt(pR) && world.GetChunkAt(pR).HasTileAt(pR);
if (hasCollision)
{
p.Movement = p.Movement with { Y = 0 };
@ -64,8 +68,8 @@ class PlayerEntity
var pL = p.Position + new Vector2(0, -32);
var pR = p.Position + new Vector2(16, -32);
hasCollision =
world.HasChunkAt(pL) && world.GetChunkAt(pL).hasTileAt(pL)
|| world.HasChunkAt(pR) && world.GetChunkAt(pR).hasTileAt(pR);
world.HasChunkAt(pL) && world.GetChunkAt(pL).HasTileAt(pL)
|| world.HasChunkAt(pR) && world.GetChunkAt(pR).HasTileAt(pR);
if (hasCollision)
{
p.Movement = p.Movement with { Y = 0 };

View File

@ -1,3 +1,5 @@
namespace mine2d.core.data;
class Chunk
{
public STile[,] Tiles { get; set; } = new STile[Constants.ChunkSize, Constants.ChunkSize];
@ -20,17 +22,17 @@ class Chunk
return this.Tiles[x, y];
}
public bool hasTileAt(Vector2 pos)
public bool HasTileAt(Vector2 pos)
{
return this.hasTileAt((int)pos.X, (int)pos.Y);
return this.HasTileAt((int)pos.X, (int)pos.Y);
}
public bool hasTileAt(int x, int y)
public bool HasTileAt(int x, int y)
{
var posInChunk = this.GetPositionInChunk(new Vector2(x, y));
var tileX = (int)Math.Floor(posInChunk.X / Constants.TileSize);
var tileY = (int)Math.Floor(posInChunk.Y / Constants.TileSize);
return this.hasTile(tileX, tileY);
return this.HasTile(tileX, tileY);
}
public STile GetTileAt(Vector2 pos)
@ -59,14 +61,14 @@ class Chunk
this.SetTile(tileX, tileY, tile);
}
public bool hasTile(int x, int y)
public bool HasTile(int x, int y)
{
return x >= 0 && x < this.Tiles.Length && y >= 0 && y < this.Tiles.Length && this.Tiles[x, y].Id != 0;
}
public bool hasTile(Vector2 pos)
public bool HasTile(Vector2 pos)
{
return this.hasTile((int)pos.X, (int)pos.Y);
return this.HasTile((int)pos.X, (int)pos.Y);
}
public Vector2 GetPositionInChunk(Vector2 pos)

View File

@ -1,3 +1,7 @@
using mine2d.core.tiles;
namespace mine2d.core.data;
struct STile
{
public int Id { get; set; }

View File

@ -1,3 +1,5 @@
namespace mine2d.core.data;
class World
{
public Dictionary<string, Chunk> Chunks { get; set; } = new Dictionary<string, Chunk>();
@ -75,6 +77,6 @@ class World
public bool HasTileAt(int x, int y)
{
return this.HasChunkAt(x, y) && this.GetChunkAt(x, y).hasTileAt(new Vector2(x, y));
return this.HasChunkAt(x, y) && this.GetChunkAt(x, y).HasTileAt(new Vector2(x, y));
}
}

View File

@ -0,0 +1,20 @@
using System.Reflection;
namespace mine2d.core.extensions;
public static class AssemblyExtensions
{
public static Type[] GetTypesSafe(this Assembly assembly)
{
try
{
#pragma warning disable IL2026
return assembly.GetTypes();
#pragma warning restore IL2026
}
catch (ReflectionTypeLoadException ex)
{
return ex.Types.Where(t => t != null).ToArray();
}
}
}

View File

@ -0,0 +1,23 @@
using System.Reflection;
namespace mine2d.core.extensions;
public static class TypeExtensions
{
public static Type MakeGenericTypeSafely(this Type type, params Type[] typeArguments)
{
try
{
#pragma warning disable IL2026
return type.MakeGenericType(typeArguments);
#pragma warning restore IL2026
}
catch (ReflectionTypeLoadException e)
{
var missingTypes = e.Types
.Where(t => typeArguments.Contains(t) && t != null);
throw new Exception($"Failed to make generic type {type} with arguments {string.Join(", ", missingTypes)}", e);
}
}
}

View File

@ -1,3 +1,7 @@
using mine2d.core.data;
namespace mine2d.core.tiles;
class Tile
{
public string Name { get; set; }
@ -14,11 +18,10 @@ class Tile
var (ptr, size) = rl.LoadToIntPtr("assets." + textureName + ".png");
var sdlBuffer = SDL_RWFromMem(ptr, size);
var surface = IMG_Load_RW(sdlBuffer, 1);
var texture = Context.Get().Renderer.CreateTextureFromSurface(surface);
this.texture = texture;
this.texture = Context.Get().Renderer.CreateTextureFromSurface(surface);
if (breakingTexture == IntPtr.Zero)
{
loadBreakingTexture();
LoadBreakingTexture();
}
SDL_FreeSurface(surface);
}
@ -35,8 +38,8 @@ class Tile
var camera = Context.Get().FrontendGameState.Camera;
renderer.DrawTexture(
this.texture,
(x - (int)camera.position.X) * scale,
(y - (int)camera.position.Y) * scale,
(x - (int)camera.Position.X) * scale,
(y - (int)camera.Position.Y) * scale,
Constants.TileSize * scale,
Constants.TileSize * scale
);
@ -45,8 +48,8 @@ class Tile
var breakingOffset = (int)((double)tile.Hits / this.Hardness * 4);
renderer.DrawTexture(
breakingTexture,
(x - (int)camera.position.X) * scale,
(y - (int)camera.position.Y) * scale,
(x - (int)camera.Position.X) * scale,
(y - (int)camera.Position.Y) * scale,
Constants.TileSize * scale,
Constants.TileSize * scale,
breakingOffset,
@ -56,7 +59,7 @@ class Tile
}
}
private static void loadBreakingTexture()
private static void LoadBreakingTexture()
{
var rl = Context.Get().ResourceLoader;
var (ptr, size) = rl.LoadToIntPtr("assets.breaking.png");

View File

@ -1,11 +1,13 @@
enum Tiles : int
namespace mine2d.core.tiles;
enum Tiles
{
stone = 1,
Stone = 1,
}
class TileRegistry
{
public Dictionary<int, Tile> Tiles { get; set; } = new Dictionary<int, Tile>();
public Dictionary<int, Tile> Tiles { get; set; } = new();
public void RegisterTile()
{

View File

@ -1,3 +1,7 @@
using mine2d.core.data;
namespace mine2d.core.world;
class ChunkGenerator
{
public static Chunk CreateFilledChunk(int x, int y, STile fill)

View File

@ -1,3 +1,5 @@
namespace mine2d.engine;
enum Sound { }
class AudioPlayer
@ -12,7 +14,7 @@ class AudioPlayer
public void Register(Sound name, string path)
{
var buffer = resourceLoader.LoadBytes(path);
var buffer = this.resourceLoader.LoadBytes(path);
this.audioFiles.Add(name, buffer);
}

View File

@ -1,5 +1,4 @@
using static SDL2.SDL_ttf;
using static SDL2.SDL;
namespace mine2d.engine;
class FontManager
{
@ -17,20 +16,20 @@ class FontManager
public void RegisterFont(string name, string path, int fontSize)
{
if (fonts.ContainsKey(name))
if (this.fonts.ContainsKey(name))
return;
var res = resourceLoader.LoadToIntPtr(path);
var res = this.resourceLoader.LoadToIntPtr(path);
var sdlBuffer = SDL_RWFromConstMem(res.ptr, res.size);
var font = TTF_OpenFontRW(sdlBuffer, 1, fontSize);
if (font == IntPtr.Zero)
{
throw new Exception("TTF_OpenFont failed");
}
fonts.Add(name, font);
this.fonts.Add(name, font);
}
public IntPtr GetFont(string name)
{
return fonts[name];
return this.fonts[name];
}
}

View File

@ -1,30 +1,34 @@
namespace mine2d.engine;
abstract class Game
{
public const int TPS = 128;
private Queue<int> fpsQueue = new Queue<int>();
protected abstract void update(double dt);
protected abstract void draw();
public const int Tps = 128;
private bool running = true;
private Queue<int> fpsQueue = new();
protected abstract void Update(double dt);
protected abstract void Draw();
public void Run()
{
var tLast = DateTime.Now;
var tAcc = TimeSpan.Zero;
while (true)
while (this.running)
{
var dt = DateTime.Now - tLast;
tLast = DateTime.Now;
tAcc += dt;
var fps = (int)(1 / dt.TotalSeconds);
fpsQueue.Enqueue(fps);
while (fpsQueue.Count > fps)
fpsQueue.Dequeue();
while (tAcc >= TimeSpan.FromSeconds(1.0 / TPS))
this.fpsQueue.Enqueue(fps);
while (this.fpsQueue.Count > fps)
this.fpsQueue.Dequeue();
while (tAcc >= TimeSpan.FromSeconds(1.0 / Tps))
{
update(dt.TotalSeconds);
tAcc -= TimeSpan.FromSeconds(1.0 / TPS);
this.Update(dt.TotalSeconds);
tAcc -= TimeSpan.FromSeconds(1.0 / Tps);
}
draw();
this.Draw();
}
}
}

View File

@ -1,4 +1,8 @@
class PacketUtils
using mine2d.backend.data;
namespace mine2d.engine;
public static class PacketUtils
{
public static string GetType(ValueType packet)
{
@ -7,15 +11,15 @@ class PacketUtils
{
Console.WriteLine(pp.Name);
}
var p = t.GetField("type");
var p = t.GetField(nameof(IPacket.Type));
if (p == null)
{
throw new Exception("p undef");
throw new ArgumentNullException(nameof(p), "p undef");
}
var v = p.GetValue(packet);
if (v == null)
{
throw new Exception("v undef");
throw new ArgumentNullException(nameof(v), "v undef");
}
return (string)v;
}

View File

@ -1,6 +1,10 @@
using mine2d.engine.utils;
namespace mine2d.engine;
class Renderer
{
private IntPtr renderer;
private readonly IntPtr renderer;
private IntPtr font;
private SDL_Color color;
@ -15,13 +19,13 @@ class Renderer
public void Clear()
{
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(this.renderer, 0, 0, 0, 255);
SDL_RenderClear(this.renderer);
}
public void Present()
{
SDL_RenderPresent(renderer);
SDL_RenderPresent(this.renderer);
}
public void DrawRect(double x, double y, int w, int h)
@ -31,13 +35,15 @@ class Renderer
public void DrawRect(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;
var rect = new SDL_Rect
{
x = x,
y = y,
w = w,
h = h
};
SDL_RenderFillRect(renderer, ref rect);
SDL_RenderFillRect(this.renderer, ref rect);
}
public void DrawOutline(int x, int y, int w, int h)
@ -50,24 +56,24 @@ class Renderer
h = h
};
_ = SDL_RenderDrawRect(renderer, ref rect);
_ = SDL_RenderDrawRect(this.renderer, ref rect);
}
public void DrawLines(double[][] points)
{
SDL_Point[] sdlPoints = new SDL_Point[points.Length];
for (int i = 0; i < points.Length; i++)
var sdlPoints = new SDL_Point[points.Length];
for (var i = 0; i < points.Length; i++)
{
sdlPoints[i].x = (int)points[i][0];
sdlPoints[i].y = (int)points[i][1];
}
SDL_RenderDrawLines(renderer, sdlPoints, points.Length);
SDL_RenderDrawLines(this.renderer, sdlPoints, points.Length);
}
public void SetColor(int r, int g, int b, int a = 255)
{
SDL_SetRenderDrawColor(renderer, (byte)r, (byte)g, (byte)b, (byte)a);
SDL_SetRenderDrawColor(this.renderer, (byte)r, (byte)g, (byte)b, (byte)a);
}
public void SetFont(IntPtr font, SDL_Color color)
@ -79,24 +85,23 @@ class Renderer
public void SetFont(IntPtr font, Color color)
{
this.font = font;
this.color = color.toSDLColor();
this.color = color.ToSdlColor();
}
public void DrawText(string text, int x, int y, bool center = false)
{
var surfaceMessage = TTF_RenderText_Solid(this.font, text, this.color);
var texture = SDL_CreateTextureFromSurface(this.renderer, surfaceMessage);
int width;
int height;
SDL_QueryTexture(texture, out _, out _, out width, out height);
SDL_QueryTexture(texture, out _, out _, out var width, out var height);
SDL_Rect rect = new SDL_Rect();
rect.x = x;
rect.y = y;
rect.w = width;
rect.h = height;
var rect = new SDL_Rect
{
x = x,
y = y,
w = width,
h = height
};
if (center)
{
@ -111,12 +116,12 @@ class Renderer
public IntPtr GetRaw()
{
return renderer;
return this.renderer;
}
public IntPtr CreateTextureFromSurface(IntPtr surface)
{
return SDL_CreateTextureFromSurface(renderer, surface);
return SDL_CreateTextureFromSurface(this.renderer, surface);
}
public void DrawTexture(IntPtr texture, int x, int y, int w, int h)

View File

@ -1,5 +1,7 @@
using System.Runtime.InteropServices;
namespace mine2d.engine;
class ResourceLoader
{
private string assemblyName;
@ -14,11 +16,12 @@ class ResourceLoader
#if (DEBUG)
Console.WriteLine("Loading resource: " + resourceName);
return File.ReadAllText(ToPath(resourceName));
#endif
#else
using var stream = this.GetType()
.Assembly.GetManifestResourceStream($"{this.assemblyName}.{resourceName}");
using var reader = new StreamReader(stream!);
return reader.ReadToEnd();
#endif
}
public byte[] LoadBytes(string resourceName)

View File

@ -1,11 +1,13 @@
namespace mine2d.engine;
struct Line
{
public Vector2 start;
public Vector2 end;
public Vector2 Start;
public Vector2 End;
public Line(Vector2 start, Vector2 end)
{
this.start = start;
this.end = end;
this.Start = start;
this.End = end;
}
}

View File

@ -1,10 +1,12 @@
namespace mine2d.engine;
class Window
{
IntPtr window;
public Window(string title, int w, int h)
{
window = SDL_CreateWindow(
this.window = SDL_CreateWindow(
title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,

View File

@ -1,4 +1,6 @@
enum EventPriority : int
namespace mine2d.engine.system;
enum EventPriority
{
Lowest = 0,
Low = 1,

View File

@ -1,3 +1,5 @@
namespace mine2d.engine.system.annotations;
enum InteractorKind
{
Client,

View File

@ -1,33 +1,35 @@
namespace mine2d.engine.utils;
class Color
{
public int r,
g,
b,
a;
public int R,
G,
B,
A;
public Color(int r, int g, int b, int a)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
this.R = r;
this.G = g;
this.B = b;
this.A = a;
}
public Color(int r, int g, int b)
{
this.r = r;
this.g = g;
this.b = b;
this.a = 255;
this.R = r;
this.G = g;
this.B = b;
this.A = 255;
}
public SDL_Color toSDLColor()
public SDL_Color ToSdlColor()
{
SDL_Color color = new();
color.r = (byte)r;
color.g = (byte)g;
color.b = (byte)b;
color.a = (byte)a;
color.r = (byte)this.R;
color.g = (byte)this.G;
color.b = (byte)this.B;
color.a = (byte)this.A;
return color;
}
}

View File

@ -1,3 +1,5 @@
namespace mine2d.engine.utils;
class Point
{
public static double Distance(double x1, double y1, double x2, double y2)

View File

@ -1,3 +1,10 @@
using mine2d.backend.data;
using mine2d.core;
using mine2d.core.data;
using mine2d.frontend.renderer;
namespace mine2d.frontend;
class Frontend : IFrontend
{
public void Init()
@ -44,7 +51,7 @@ class Frontend : IFrontend
}
if (e.type == SDL_EventType.SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT)
{
var amp = ctx.FrontendGameState.MousePosition / ctx.FrontendGameState.Settings.GameScale + ctx.FrontendGameState.Camera.position;
var amp = ctx.FrontendGameState.MousePosition / ctx.FrontendGameState.Settings.GameScale + ctx.FrontendGameState.Camera.Position;
if (ctx.GameState.World.HasChunkAt(amp))
{
var chunk = ctx.GameState.World.GetChunkAt(amp);
@ -156,29 +163,29 @@ class Frontend : IFrontend
{
if (player.Name == ctx.FrontendGameState.PlayerName)
{
ctx.Renderer.SetColor(0, 0, 255, 255);
ctx.Renderer.SetColor(0, 0, 255);
}
else
{
ctx.Renderer.SetColor(255, 0, 0, 255);
ctx.Renderer.SetColor(255, 0, 0);
}
ctx.Renderer.DrawRect(
(player.Position.X - (int)camera.position.X) * scale,
(player.Position.Y - (int)camera.position.Y) * scale - 32 * scale,
(player.Position.X - (int)camera.Position.X) * scale,
(player.Position.Y - (int)camera.Position.Y) * scale - 32 * scale,
16 * scale,
32 * scale
);
});
var absoluteMousePos = ctx.FrontendGameState.MousePosition / ctx.FrontendGameState.Settings.GameScale + camera.position;
var absoluteMousePos = ctx.FrontendGameState.MousePosition / ctx.FrontendGameState.Settings.GameScale + camera.Position;
if (ctx.GameState.World.HasTileAt((int)absoluteMousePos.X, (int)absoluteMousePos.Y))
{
var a = Constants.TileSize;
var tilePos = new Vector2(absoluteMousePos.X - absoluteMousePos.X % a, absoluteMousePos.Y - absoluteMousePos.Y % a);
ctx.Renderer.SetColor(255, 255, 255, 255);
ctx.Renderer.SetColor(255, 255, 255);
ctx.Renderer.DrawOutline(
(int)tilePos.X * scale - (int)camera.position.X * scale,
(int)tilePos.Y * scale - (int)camera.position.Y * scale,
(int)tilePos.X * scale - (int)camera.Position.X * scale,
(int)tilePos.Y * scale - (int)camera.Position.Y * scale,
16 * scale,
16 * scale
);

View File

@ -1,3 +1,5 @@
namespace mine2d.frontend;
interface IFrontend
{
public void Process();

View File

@ -1,3 +1,5 @@
namespace mine2d.frontend.renderer;
interface IRenderer
{
public void Render();

View File

@ -1,3 +1,7 @@
using mine2d.core;
namespace mine2d.frontend.renderer;
class WorldRenderer : IRenderer
{
public void Render()

View File

@ -1,6 +1,9 @@
using System.Text;
using Newtonsoft.Json.Linq;
using mine2d.backend.data;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace mine2d.network;
class Converter
{

View File

@ -1,3 +1,8 @@
using mine2d.core;
using mine2d.core.data;
namespace mine2d.state;
class FrontendGameState
{
public Vector2 MovementInput;
@ -14,7 +19,7 @@ class FrontendGameState
class Settings
{
public int GameScale = 4;
public int UIScale = 4;
public int UiScale = 4;
public bool ShowCollision = true;
public bool Fullscreen = false;
}

View File

@ -1,3 +1,7 @@
using mine2d.engine;
namespace mine2d.state;
class Player
{
public string Name;