diff --git a/README.md b/README.md index 5e66054..0b5beff 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,56 @@ -### Backend +## Building and running + +### With Nix + +Using the power of nix, you can just `nix run git+https://git.berlin.ccc.de/vinzenz/servicepoint-tanks`! + +To build from local source: + +```bash +# no submodules required +git clone https://github.com/kaesaecracker/servicepoint-tanks.git +cd servicepoint-tanks +nix build .# +result/bin/TanksServer +``` + +You can also use the provided devShells (`nix develop .#`) and follow the manual steps. + +For proper IDE suggestions, you may need to initialize the submodules. They are not used for building by Nix though. + +### The hard way + +```bash +# initialize the submodules +git clone https://github.com/kaesaecracker/servicepoint-tanks.git +cd servicepoint-tanks +git submodule update --init --recursive + +# build +cd tank-frontent +npm install +npm run build +cd ../tanks-backend +dotnet build +``` + +The Docker builds are currently broken and need some updates for the new library repo structure. + + +## Backend @@ -18,7 +67,7 @@ - some values (like tank speed) can be configured but are fixed at run time - By default, the backend also hosts the frontend -### Frontend +## Frontend @@ -28,9 +77,9 @@ - Sends user input to server - real time communication via WebSockets, HTTP for the REST -### Binary formats +## Binary formats -#### Controls WebSocket +### Controls WebSocket - Client sends 2 byte messages. - on or off: `0x01` or `0x02` @@ -55,31 +104,3 @@ - 10: bullet - 11: (reserved) - client responds with empty message to request the next frame - -# Building - -```bash -# checkout repo and submodules -git clone https://github.com/kaesaecracker/servicepoint-tanks.git -cd servicepoint-tanks -git submodule update --init -cd tanks-backend/servicepoint -git submodule update --init -cd ../.. - -# build with nix-shell -nix-shell -cd tank-frontent -npm install -npm run build -cd ../tanks-backend -dotnet build - -# build with docker/podman -cd tanks-backend -docker build . -cd ../tank-frontend -docker build . -cd .. -docker build . -``` diff --git a/flake.nix b/flake.nix index eaa0969..ba0d31e 100644 --- a/flake.nix +++ b/flake.nix @@ -5,13 +5,17 @@ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-25.05"; binding = { - url = "git+https://git.berlin.ccc.de/servicepoint/servicepoint-binding-csharp.git"; - inputs.nixpkgs.follows = "nixpkgs"; + url = "git+https://git.berlin.ccc.de/servicepoint/servicepoint-binding-csharp.git"; + inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = - { self, nixpkgs, binding }: + { + self, + nixpkgs, + binding, + }: let supported-systems = [ "x86_64-linux" @@ -31,6 +35,20 @@ ); in { + apps = forAllSystems ( + { + pkgs, + lib, + selfPkgs, + ... + }: + { + default = { + type = "app"; + program = "${lib.getBin selfPkgs.servicepoint-tanks}/bin/TanksServer"; + }; + } + ); devShells = forAllSystems ( { pkgs, @@ -47,7 +65,7 @@ ]; }; backend-set = { - inputsFrom = [ selfPkgs.servicepoint-tanks-backend ]; + inputsFrom = [ selfPkgs.servicepoint-tanks ]; packages = with pkgs; [ nuget-to-json cargo-tarpaulin @@ -71,7 +89,7 @@ }: { servicepoint-tanks-frontend = pkgs.buildNpmPackage (finalAttrs: { - pname = "tank-frontend"; + pname = "servicepoint-tanks-frontend"; version = "0.0.0"; src = ./tank-frontend; @@ -82,20 +100,29 @@ ''; }); - servicepoint-tanks-backend = pkgs.buildDotnetModule { - pname = "servicepoint-tanks-backend"; + servicepoint-tanks-assets = ./tanks-backend/TanksServer/assets; + + servicepoint-tanks = pkgs.buildDotnetModule { + pname = "servicepoint-tanks"; version = "0.0.0"; dotnet-sdk = pkgs.dotnetCorePackages.sdk_8_0; dotnet-runtime = pkgs.dotnetCorePackages.runtime_8_0; src = ./tanks-backend; - projectFile = "TanksServer/TanksServer.csproj"; + projectFile = "TanksServer/TanksServer.csproj"; nugetDeps = ./tanks-backend/deps.json; selfContainedBuild = true; buildInputs = [ bindingPkgs.servicepoint-binding-csharp ]; + + runtimeDeps = [ bindingPkgs.servicepoint-binding-uniffi ]; + + makeWrapperArgs = [ + "--set-default TANKSSERVER_CLIENT ${selfPkgs.servicepoint-tanks-frontend}" + "--set-default TANKSSERVER_ASSETS ${selfPkgs.servicepoint-tanks-assets}" + ]; }; } ); diff --git a/tanks-backend/TanksServer/GameLogic/CollideBullets.cs b/tanks-backend/TanksServer/GameLogic/CollideBullets.cs index 05dfe5a..bff8ea5 100644 --- a/tanks-backend/TanksServer/GameLogic/CollideBullets.cs +++ b/tanks-backend/TanksServer/GameLogic/CollideBullets.cs @@ -4,7 +4,7 @@ namespace TanksServer.GameLogic; internal sealed class CollideBullets : ITickStep { - private readonly Sprite _explosiveSprite = Sprite.FromImageFile("assets/explosion.png"); + private readonly Sprite _explosiveSprite = Sprite.FromImageFile(Path.Combine(Program.AssetsDir, "explosion.png")); private readonly Predicate _removeBulletsPredicate; private readonly MapEntityManager _entityManager; private readonly MapService _map; diff --git a/tanks-backend/TanksServer/GameLogic/MapService.cs b/tanks-backend/TanksServer/GameLogic/MapService.cs index a79bd94..e82b9c6 100644 --- a/tanks-backend/TanksServer/GameLogic/MapService.cs +++ b/tanks-backend/TanksServer/GameLogic/MapService.cs @@ -22,9 +22,10 @@ internal sealed class MapService public MapService() { - foreach (var file in Directory.EnumerateFiles("./assets/maps/", "*.txt")) + var dir = Path.Combine(Program.AssetsDir, "maps"); + foreach (var file in Directory.EnumerateFiles(dir, "*.txt")) LoadMapString(file); - foreach (var file in Directory.EnumerateFiles("./assets/maps/", "*.png")) + foreach (var file in Directory.EnumerateFiles(dir, "*.png")) LoadMapPng(file); Current = GetRandomMap(); } diff --git a/tanks-backend/TanksServer/GlobalUsings.cs b/tanks-backend/TanksServer/GlobalUsings.cs index 5c674f0..5c10eda 100644 --- a/tanks-backend/TanksServer/GlobalUsings.cs +++ b/tanks-backend/TanksServer/GlobalUsings.cs @@ -1,6 +1,7 @@ global using System; global using System.Collections.Concurrent; global using System.Collections.Generic; +global using System.IO; global using System.Linq; global using System.Threading; global using System.Threading.Tasks; diff --git a/tanks-backend/TanksServer/Graphics/DrawPowerUpsStep.cs b/tanks-backend/TanksServer/Graphics/DrawPowerUpsStep.cs index 87076f8..f35094b 100644 --- a/tanks-backend/TanksServer/Graphics/DrawPowerUpsStep.cs +++ b/tanks-backend/TanksServer/Graphics/DrawPowerUpsStep.cs @@ -4,11 +4,11 @@ namespace TanksServer.Graphics; internal sealed class DrawPowerUpsStep(MapEntityManager entityManager) : IDrawStep { - private readonly Sprite _genericSprite = Sprite.FromImageFile("assets/powerup_generic.png"); - private readonly Sprite _smartSprite = Sprite.FromImageFile("assets/powerup_smart.png"); - private readonly Sprite _magazineSprite = Sprite.FromImageFile("assets/powerup_magazine.png"); - private readonly Sprite _explosiveSprite = Sprite.FromImageFile("assets/powerup_explosive.png"); - private readonly Sprite _fastSprite = Sprite.FromImageFile("assets/powerup_fastbullet.png"); + private readonly Sprite _genericSprite = Sprite.FromImageFile(Path.Combine(Program.AssetsDir, "powerup_generic.png")); + private readonly Sprite _smartSprite = Sprite.FromImageFile(Path.Combine(Program.AssetsDir, "powerup_smart.png")); + private readonly Sprite _magazineSprite = Sprite.FromImageFile(Path.Combine(Program.AssetsDir, "powerup_magazine.png")); + private readonly Sprite _explosiveSprite = Sprite.FromImageFile(Path.Combine(Program.AssetsDir, "powerup_explosive.png")); + private readonly Sprite _fastSprite = Sprite.FromImageFile(Path.Combine(Program.AssetsDir, "powerup_fastbullet.png")); public void Draw(GamePixelGrid pixels) { diff --git a/tanks-backend/TanksServer/Graphics/DrawTanksStep.cs b/tanks-backend/TanksServer/Graphics/DrawTanksStep.cs index 2b592db..daec4f0 100644 --- a/tanks-backend/TanksServer/Graphics/DrawTanksStep.cs +++ b/tanks-backend/TanksServer/Graphics/DrawTanksStep.cs @@ -5,7 +5,7 @@ namespace TanksServer.Graphics; internal sealed class DrawTanksStep(MapEntityManager entityManager) : IDrawStep { private readonly SpriteSheet _tankSprites = - SpriteSheet.FromImageFile("assets/tank.png", (int)MapService.TileSize, (int)MapService.TileSize); + SpriteSheet.FromImageFile(Path.Combine(Program.AssetsDir, "tank.png"), (int)MapService.TileSize, (int)MapService.TileSize); public void Draw(GamePixelGrid pixels) { diff --git a/tanks-backend/TanksServer/Program.cs b/tanks-backend/TanksServer/Program.cs index e794589..cbac46a 100644 --- a/tanks-backend/TanksServer/Program.cs +++ b/tanks-backend/TanksServer/Program.cs @@ -11,22 +11,44 @@ namespace TanksServer; public static class Program { + internal static string AssetsDir = (Environment.GetEnvironmentVariable("TANKSSERVER_ASSETS") ?? "./assets") + "/"; + [RequiresUnreferencedCode("Calls Endpoints.Map")] [RequiresDynamicCode("Calls Endpoints.Map")] public static async Task Main(string[] args) { var app = Configure(args); - var clientFileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "client")); + app.Logger.LogInformation("Running in {}", app.Environment.ContentRootPath); - app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = clientFileProvider }); - app.UseStaticFiles(new StaticFileOptions { FileProvider = clientFileProvider }); + AddStaticClientHost(app); app.Services.GetRequiredService().Map(app); await app.RunAsync(); } + private static void AddStaticClientHost(WebApplication app) + { + var clientDir = Environment.GetEnvironmentVariable("TANKSSERVER_CLIENT"); + bool required = clientDir != null; + + clientDir ??= Path.Combine(app.Environment.ContentRootPath, "client"); + bool available = Directory.Exists(clientDir); + + if (!available) + { + if (required) + throw new InvalidOperationException($"The environment variable TANKSSERVER_CLIENT is set, but the specified directory {clientDir} does not exist."); + app.Logger.LogError("Not providing static file host for client because {} does not exist", clientDir); + return; + } + + var clientFileProvider = new PhysicalFileProvider(clientDir); + app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = clientFileProvider }); + app.UseStaticFiles(new StaticFileOptions { FileProvider = clientFileProvider }); + } + [RequiresUnreferencedCode("Calls Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure(IConfiguration)")] [RequiresDynamicCode("Calls Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure(IConfiguration)")] private static WebApplication Configure(string[] args) diff --git a/tanks-backend/TanksServer/TanksServer.csproj b/tanks-backend/TanksServer/TanksServer.csproj index 7ba8f60..d1d0acf 100644 --- a/tanks-backend/TanksServer/TanksServer.csproj +++ b/tanks-backend/TanksServer/TanksServer.csproj @@ -26,7 +26,7 @@ - +