From 40806deaa67a1d2de36675e8dd7174d802d7f638 Mon Sep 17 00:00:00 2001 From: Travis Shears Date: Sat, 11 Apr 2026 22:36:53 +0200 Subject: [PATCH 1/3] get camera working --- two_player_cleaning_game/main.fnl | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/two_player_cleaning_game/main.fnl b/two_player_cleaning_game/main.fnl index 6d666bc..586c65f 100644 --- a/two_player_cleaning_game/main.fnl +++ b/two_player_cleaning_game/main.fnl @@ -8,14 +8,27 @@ (var walls-sprite nil) (var walls-batch nil) (var wall-quads nil) +(local camera {:x 0 :y 0}) (local debug true) (local collider-debug-boxes []) (local bump-world (bump.newWorld 25)) (local player { :x 50 :y 50 :w 25 :h 25 :speed 80 }) +;; Screen Size +(local screen-width 800) +(local screen-height 600) +; 16:9 (Modern Widescreen - Recommended)** +; - **640x360** ← Best middle ground +; - 800x450 +; - 1024x576 +; - 1280x720 +; **4:3 (Classic/Arcade Feel)** +; - **320x240** (very retro, but small) +; - **800x600** (spacious, classic 4:3) +; - 1024x768 (fn love.load [] - (love.window.setMode 600 640) + (love.window.setMode screen-width screen-height) (bump-world:add player player.x player.y player.w player.h) ;; load world images @@ -90,12 +103,17 @@ while 1 do love.event.push('stdin', io.read('*line')) end") :start)) (let [(x y) (bump-world:move player (+ player.x deltas.dx) (+ player.y deltas.dy))] ; (print (fennel.view { :msg "Moving player" :x x :y y})) (tset player :x x) - (tset player :y y))))) + (tset player :y y)))) + + ;; Update camera to follow player (keep player centered on screen) + (tset camera :x (- player.x (/ screen-width 2))) + (tset camera :y (- player.y (/ screen-height 2))) + ) (fn love.draw [] - ;; clear the screen and set bg to off white (love.graphics.clear) + (love.graphics.translate (* -1 camera.x) (* -1 camera.y)) (draw-world) ;; draw player (love.graphics.draw From d2621e2aec463c92d5471f5fa9000e9bbc341709 Mon Sep 17 00:00:00 2001 From: Travis Shears Date: Sun, 12 Apr 2026 21:37:53 +0200 Subject: [PATCH 2/3] init c# backend --- backend/.gitignore | 50 ++++++++++++++++++++++++++ backend/Backend.csproj | 16 +++++++++ backend/Program.cs | 12 +++++++ backend/README.md | 82 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 backend/.gitignore create mode 100644 backend/Backend.csproj create mode 100644 backend/Program.cs create mode 100644 backend/README.md diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..43ccafb --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,50 @@ +# Build results +bin/ +obj/ +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ + +# Visual Studio +.vs/ +.vscode/ +*.sln.iml +*.sln.docstates +*.user +*.userprefs +*.userosscache +*.sln.user + +# ReSharper +.idea/ +*.resharper +*.resharper.user +*.DotSettings.user +_ReSharper*/ +*.[Rr]e[Ss]harper +*.sln.iml + +# Local environment +.env +.env.local +appsettings.Development.json + +# NuGet +.nuget/ +*.nupkg +*.snupkg +packages/ + +# Rider +.idea/ +*.sln.iml +*.log + +# macOS +.DS_Store diff --git a/backend/Backend.csproj b/backend/Backend.csproj new file mode 100644 index 0000000..748c613 --- /dev/null +++ b/backend/Backend.csproj @@ -0,0 +1,16 @@ + + + + Exe + net10.0 + latest + enable + Love2DBackend + Backend + + + + + + + diff --git a/backend/Program.cs b/backend/Program.cs new file mode 100644 index 0000000..d9f4eb9 --- /dev/null +++ b/backend/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace Love2DBackend +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello from LÖVE2D Backend!"); + } + } +} diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..3bedb6f --- /dev/null +++ b/backend/README.md @@ -0,0 +1,82 @@ +# ENET Gaming Backend + +A multiplayer game server written in C# using ENet for networking. +Designed to support mutiple of my LÖVE2D game experiments at once. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ LÖVE2D Game Clients │ +│ (Fennel/Lua with Love2D Framework) │ +└────────────────────────────────┬────────────────────────────┘ + │ (UDP via ENet) + │ + ┌────────────┴────────────┐ + │ ENet-CSharp Server │ + │ (Multiplayer Backend) │ + └────────────┬────────────┘ + │ + ┌────────────┴────────────┐ + │ Game State Manager │ + │ Session Storage │ + │ Player Coordination │ + └────────────────────────┘ +``` + +## Technology Stack + +- **Language**: C# (.NET) +- **Networking**: [ENet-CSharp](https://github.com/nxrighthere/ENet-CSharp) - reliable UDP multiplayer protocol +- **Deployment**: Docker + Nomad +- **Port**: `7777` (UDP, configurable) + +## Quick Start + +### Prerequisites + +- .NET 8.0 or later +- ENet-CSharp (vendored or via NuGet) +- Docker (for containerization) + +### Local Development + +```bash +cd backend +dotnet build +dotnet run +``` + +The server will start and listen on `127.0.0.1:7777` by default. + +### Testing Connection + +With a LÖVE2D client configured to connect to `localhost:7777`: + +```bash +love ../two_player_cleaning_game +``` + +## Configuration + +Server behavior is controlled via environment variables: + +```bash +ENET_HOST=0.0.0.0 # Bind address +ENET_PORT=7777 # UDP port +``` + +## Resources + +- [Learn X In Y C#](https://learnxinyminutes.com/csharp/) + +## Next Steps + +- [ ] Set up C# project structure +- [ ] Integrate ENet-CSharp dependency +- [ ] Implement basic server startup +- [ ] Define message protocol +- [ ] Create player session management +- [ ] Build Docker image +- [ ] Test Nomad deployment +- [ ] Implement game state synchronization From 4420b4e06196695513f775c9de865e81e1aeae68 Mon Sep 17 00:00:00 2001 From: Travis Shears Date: Sun, 12 Apr 2026 21:56:05 +0200 Subject: [PATCH 3/3] get udp example working --- backend/Backend.csproj | 3 --- backend/Dockerfile | 14 ++++++++++++++ backend/Program.cs | 44 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 backend/Dockerfile diff --git a/backend/Backend.csproj b/backend/Backend.csproj index 748c613..cc4ddb6 100644 --- a/backend/Backend.csproj +++ b/backend/Backend.csproj @@ -9,8 +9,5 @@ Backend - - - diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..985edbb --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,14 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /build +COPY Backend.csproj . +RUN dotnet restore +COPY . . +RUN dotnet build -c Release + +FROM mcr.microsoft.com/dotnet/runtime:10.0 +WORKDIR /app +COPY --from=build /build/bin/Release/net10.0 . +ENV ENET_HOST=0.0.0.0 +ENV ENET_PORT=7777 +EXPOSE 7777/udp +ENTRYPOINT ["./Backend"] diff --git a/backend/Program.cs b/backend/Program.cs index d9f4eb9..805a683 100644 --- a/backend/Program.cs +++ b/backend/Program.cs @@ -1,12 +1,52 @@ using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; namespace Love2DBackend { class Program { - static void Main(string[] args) + static async Task Main(string[] args) { - Console.WriteLine("Hello from LÖVE2D Backend!"); + Console.WriteLine("LÖVE2D Backend - UDP Server"); + Console.WriteLine("Starting server on port 7777..."); + + using (var udpServer = new UdpClient(7777)) + { + Console.WriteLine("Server listening on 0.0.0.0:7777"); + + var cts = new CancellationTokenSource(); + + // Handle Ctrl+C gracefully + Console.CancelKeyPress += (s, e) => + { + e.Cancel = true; + cts.Cancel(); + }; + + try + { + while (!cts.Token.IsCancellationRequested) + { + var result = await udpServer.ReceiveAsync(cts.Token); + var message = Encoding.UTF8.GetString(result.Buffer); + var clientEndpoint = result.RemoteEndPoint; + + Console.WriteLine($"[{clientEndpoint}] {message}"); + + // Echo back + var response = Encoding.UTF8.GetBytes($"Echo: {message}"); + await udpServer.SendAsync(response, response.Length, clientEndpoint); + } + } + catch (OperationCanceledException) + { + Console.WriteLine("Server stopped."); + } + } } } }