Compare commits

..

3 commits

Author SHA1 Message Date
4420b4e061
get udp example working 2026-04-12 22:49:25 +02:00
d2621e2aec
init c# backend 2026-04-12 21:48:24 +02:00
40806deaa6
get camera working 2026-04-12 12:34:04 +02:00
6 changed files with 232 additions and 3 deletions

50
backend/.gitignore vendored Normal file
View file

@ -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

13
backend/Backend.csproj Normal file
View file

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<RootNamespace>Love2DBackend</RootNamespace>
<AssemblyName>Backend</AssemblyName>
</PropertyGroup>
</Project>

14
backend/Dockerfile Normal file
View file

@ -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"]

52
backend/Program.cs Normal file
View file

@ -0,0 +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 async Task Main(string[] args)
{
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.");
}
}
}
}
}

82
backend/README.md Normal file
View file

@ -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

View file

@ -8,14 +8,27 @@
(var walls-sprite nil) (var walls-sprite nil)
(var walls-batch nil) (var walls-batch nil)
(var wall-quads nil) (var wall-quads nil)
(local camera {:x 0 :y 0})
(local debug true) (local debug true)
(local collider-debug-boxes []) (local collider-debug-boxes [])
(local bump-world (bump.newWorld 25)) (local bump-world (bump.newWorld 25))
(local player { :x 50 :y 50 :w 25 :h 25 :speed 80 }) (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 [] (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) (bump-world:add player player.x player.y player.w player.h)
;; load world images ;; 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))] (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})) ; (print (fennel.view { :msg "Moving player" :x x :y y}))
(tset player :x x) (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 [] (fn love.draw []
;; clear the screen and set bg to off white
(love.graphics.clear) (love.graphics.clear)
(love.graphics.translate (* -1 camera.x) (* -1 camera.y))
(draw-world) (draw-world)
;; draw player ;; draw player
(love.graphics.draw (love.graphics.draw