Compare commits
3 commits
e3a7182de1
...
4420b4e061
| Author | SHA1 | Date | |
|---|---|---|---|
| 4420b4e061 | |||
| d2621e2aec | |||
| 40806deaa6 |
6 changed files with 232 additions and 3 deletions
50
backend/.gitignore
vendored
Normal file
50
backend/.gitignore
vendored
Normal 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
13
backend/Backend.csproj
Normal 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
14
backend/Dockerfile
Normal 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
52
backend/Program.cs
Normal 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
82
backend/README.md
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue