diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..75a838a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "tanks-backend/servicepoint"]
+ path = tanks-backend/servicepoint
+ url = https://github.com/kaesaecracker/servicepoint.git
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index fa1abcd..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "cccb-tanks-cs",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {}
-}
diff --git a/tank-frontend/src/PlayerInfo.tsx b/tank-frontend/src/PlayerInfo.tsx
index 4ab04bc..37c479e 100644
--- a/tank-frontend/src/PlayerInfo.tsx
+++ b/tank-frontend/src/PlayerInfo.tsx
@@ -48,27 +48,31 @@ export default function PlayerInfo({player}: { player: string }) {
return
- Playing as {lastJsonMessage.name}
+ Playing as {lastJsonMessage.player.name}
-
+ {lastJsonMessage.tank && <>
+
+
+
+
+
+
+
+
+ >}
+
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
;
diff --git a/tank-frontend/src/serverCalls.tsx b/tank-frontend/src/serverCalls.tsx
index 83e4f2d..36d2e74 100644
--- a/tank-frontend/src/serverCalls.tsx
+++ b/tank-frontend/src/serverCalls.tsx
@@ -14,24 +14,28 @@ export type Scores = {
readonly pixelsMoved: number;
};
+type Tank = {
+ readonly pixelPosition: { x: number; y: number };
+ readonly orientation: number;
+ readonly moving: boolean;
+ readonly bulletStats: BulletStats;
+ readonly reloadingUntil: string;
+ readonly nextShotAfter: string;
+ readonly usedBullets: number;
+ readonly maxBullets: number;
+}
+
export type Player = {
readonly name: string;
readonly scores: Scores;
-};
-
-type TankInfo = {
- readonly magazine: string;
- readonly position: { x: number; y: number };
- readonly orientation: number;
- readonly moving: boolean;
+ readonly openConnections: number;
+ readonly lastInput: string;
}
export type PlayerInfoMessage = {
- readonly name: string;
- readonly scores: Scores;
+ readonly player: Player;
readonly controls: string;
- readonly tank?: TankInfo;
- readonly openConnections: number;
+ readonly tank?: Tank;
}
export type MapInfo = {
@@ -40,6 +44,13 @@ export type MapInfo = {
readonly preview: string;
}
+export type BulletStats = {
+ speed: number;
+ acceleration: number,
+ explosive: boolean,
+ smart: boolean
+};
+
export function useMyWebSocket(url: string, options: Options = {}) {
return useWebSocket(url, {
shouldReconnect: () => true,
diff --git a/tanks-backend/.dockerignore b/tanks-backend/.dockerignore
index d041927..5aba7dc 100644
--- a/tanks-backend/.dockerignore
+++ b/tanks-backend/.dockerignore
@@ -11,3 +11,5 @@
**/bin
**/Dockerfile*
**/obj
+**/target
+**/examples
diff --git a/tanks-backend/DisplayCommands/ByteGrid.cs b/tanks-backend/DisplayCommands/ByteGrid.cs
deleted file mode 100644
index f7e990f..0000000
--- a/tanks-backend/DisplayCommands/ByteGrid.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System.Diagnostics;
-
-namespace DisplayCommands;
-
-public sealed class ByteGrid(ushort width, ushort height) : IEquatable
-{
- public ushort Height { get; } = height;
-
- public ushort Width { get; } = width;
-
- internal Memory Data { get; } = new byte[width * height].AsMemory();
-
- public byte this[ushort x, ushort y]
- {
- get => Data.Span[GetIndex(x, y)];
- set => Data.Span[GetIndex(x, y)] = value;
- }
-
- public bool Equals(ByteGrid? other)
- {
- if (ReferenceEquals(null, other)) return false;
- if (ReferenceEquals(this, other)) return true;
- return Height == other.Height && Width == other.Width && Data.Span.SequenceEqual(other.Data.Span);
- }
-
- private int GetIndex(ushort x, ushort y)
- {
- Debug.Assert(x < Width);
- Debug.Assert(y < Height);
- return x + y * Width;
- }
-
- public void Clear() => Data.Span.Clear();
-
- public override bool Equals(object? obj) => ReferenceEquals(this, obj) || (obj is ByteGrid other && Equals(other));
-
- public override int GetHashCode() => HashCode.Combine(Height, Width, Data);
-
- public static bool operator ==(ByteGrid? left, ByteGrid? right) => Equals(left, right);
-
- public static bool operator !=(ByteGrid? left, ByteGrid? right) => !Equals(left, right);
-}
diff --git a/tanks-backend/DisplayCommands/Cp437Grid.cs b/tanks-backend/DisplayCommands/Cp437Grid.cs
deleted file mode 100644
index 19afe2f..0000000
--- a/tanks-backend/DisplayCommands/Cp437Grid.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System.Diagnostics;
-using System.Text;
-
-namespace DisplayCommands;
-
-public sealed class Cp437Grid(ushort width, ushort height) : IEquatable
-{
- private readonly ByteGrid _byteGrid = new(width, height);
- private readonly Encoding _encoding = Encoding.GetEncoding(437);
-
- public ushort Height { get; } = height;
-
- public ushort Width { get; } = width;
-
- internal Memory Data => _byteGrid.Data;
-
- public char this[ushort x, ushort y]
- {
- get => ByteToChar(_byteGrid[x, y]);
- set => _byteGrid[x, y] = CharToByte(value);
- }
-
- public string this[ushort row]
- {
- get
- {
- var rowStart = row * Width;
- return _encoding.GetString(_byteGrid.Data[rowStart..(rowStart + Width)].Span);
- }
- set
- {
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(row, Height, nameof(row));
- ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Length, Width, nameof(value));
- ushort x = 0;
- for (; x < value.Length; x++)
- _byteGrid[x, row] = CharToByte(value[x]);
- for (; x < Width; x++)
- _byteGrid[x, row] = CharToByte(' ');
- }
- }
-
- private byte CharToByte(char c)
- {
- ReadOnlySpan valuesStr = stackalloc char[] { c };
- Span convertedStr = stackalloc byte[1];
- var consumed = _encoding.GetBytes(valuesStr, convertedStr);
- Debug.Assert(consumed == 1);
- return convertedStr[0];
- }
-
- private char ByteToChar(byte b)
- {
- ReadOnlySpan valueBytes = stackalloc byte[] { b };
- Span resultStr = stackalloc char[1];
- var consumed = _encoding.GetChars(valueBytes, resultStr);
- Debug.Assert(consumed == 1);
- return resultStr[0];
- }
-
- public bool Equals(Cp437Grid? other)
- {
- if (ReferenceEquals(null, other)) return false;
- if (ReferenceEquals(this, other)) return true;
- return Height == other.Height && Width == other.Width && _byteGrid.Equals(other._byteGrid);
- }
-
- public override bool Equals(object? obj) => ReferenceEquals(this, obj) || (obj is Cp437Grid other && Equals(other));
- public override int GetHashCode() => HashCode.Combine(_byteGrid, Height, Width);
- public static bool operator ==(Cp437Grid? left, Cp437Grid? right) => Equals(left, right);
- public static bool operator !=(Cp437Grid? left, Cp437Grid? right) => !Equals(left, right);
-}
diff --git a/tanks-backend/DisplayCommands/DisplayCommands.csproj b/tanks-backend/DisplayCommands/DisplayCommands.csproj
deleted file mode 100644
index ca146b4..0000000
--- a/tanks-backend/DisplayCommands/DisplayCommands.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
- true
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tanks-backend/DisplayCommands/DisplayExtensions.cs b/tanks-backend/DisplayCommands/DisplayExtensions.cs
deleted file mode 100644
index 5c94d8d..0000000
--- a/tanks-backend/DisplayCommands/DisplayExtensions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Text;
-using DisplayCommands.Internals;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace DisplayCommands;
-
-public static class DisplayExtensions
-{
- static DisplayExtensions()
- {
- Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
- }
-
- public static IServiceCollection AddDisplay(
- this IServiceCollection services,
- IConfigurationSection? configurationSection = null
- )
- {
- services.AddSingleton();
- if (configurationSection != null)
- services.Configure(configurationSection);
- return services;
- }
-}
\ No newline at end of file
diff --git a/tanks-backend/DisplayCommands/GlobalUsings.cs b/tanks-backend/DisplayCommands/GlobalUsings.cs
deleted file mode 100644
index b124d17..0000000
--- a/tanks-backend/DisplayCommands/GlobalUsings.cs
+++ /dev/null
@@ -1,2 +0,0 @@
-global using System;
-global using System.Threading.Tasks;
diff --git a/tanks-backend/DisplayCommands/IDisplayConnection.cs b/tanks-backend/DisplayCommands/IDisplayConnection.cs
deleted file mode 100644
index 950e75b..0000000
--- a/tanks-backend/DisplayCommands/IDisplayConnection.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace DisplayCommands;
-
-public interface IDisplayConnection
-{
- ValueTask SendClearAsync();
-
- ValueTask SendCp437DataAsync(ushort x, ushort y, Cp437Grid grid);
-
- ValueTask SendBrightnessAsync(byte brightness);
-
- ValueTask SendCharBrightnessAsync(ushort x, ushort y, ByteGrid luma);
-
- ValueTask SendHardResetAsync();
-
- ValueTask SendFadeOutAsync(byte loops);
-
- public ValueTask SendBitmapLinearWindowAsync(ushort x, ushort y, PixelGrid pixels);
-
- ///
- /// Returns the IPv4 address that is associated with the interface with which the display is reachable.
- ///
- /// IPv4 as text
- public string GetLocalIPv4();
-}
diff --git a/tanks-backend/DisplayCommands/Internals/DisplayCommand.cs b/tanks-backend/DisplayCommands/Internals/DisplayCommand.cs
deleted file mode 100644
index abe94e1..0000000
--- a/tanks-backend/DisplayCommands/Internals/DisplayCommand.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace DisplayCommands.Internals;
-
-internal enum DisplayCommand : ushort
-{
- Clear = 0x0002,
- Cp437Data = 0x0003,
- CharBrightness = 0x0005,
- Brightness = 0x0007,
- HardReset = 0x000b,
- FadeOut = 0x000d,
- [Obsolete("ignored by display code")] BitmapLegacy = 0x0010,
- BitmapLinear = 0x0012,
- BitmapLinearWin = 0x0013,
- BitmapLinearAnd = 0x0014,
- BitmapLinearOr = 0x0015,
- BitmapLinearXor = 0x0016
-}
diff --git a/tanks-backend/DisplayCommands/Internals/DisplayConnection.cs b/tanks-backend/DisplayCommands/Internals/DisplayConnection.cs
deleted file mode 100644
index 972a5db..0000000
--- a/tanks-backend/DisplayCommands/Internals/DisplayConnection.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-using System.Buffers;
-using System.Diagnostics;
-using System.Net;
-using System.Net.Sockets;
-using System.Runtime.InteropServices;
-using Microsoft.Extensions.Options;
-
-namespace DisplayCommands.Internals;
-
-internal sealed class DisplayConnection(IOptions options) : IDisplayConnection, IDisposable
-{
- private readonly ArrayPool _arrayPool = ArrayPool.Shared;
- private readonly UdpClient _udpClient = new(options.Value.Hostname, options.Value.Port);
-
- public ValueTask SendClearAsync()
- {
- var header = new HeaderWindow { Command = (ushort)DisplayCommand.Clear };
-
- return SendAsync(header, Memory.Empty);
- }
-
- public ValueTask SendCp437DataAsync(ushort x, ushort y, Cp437Grid grid)
- {
- var header = new HeaderWindow
- {
- Command = (ushort)DisplayCommand.Cp437Data,
- Height = grid.Height,
- Width = grid.Width,
- PosX = x,
- PosY = y
- };
-
- return SendAsync(header, grid.Data);
- }
-
- public ValueTask SendCharBrightnessAsync(ushort x, ushort y, ByteGrid luma)
- {
- var header = new HeaderWindow
- {
- Command = (ushort)DisplayCommand.CharBrightness,
- PosX = x,
- PosY = y,
- Height = luma.Height,
- Width = luma.Width
- };
-
- return SendAsync(header, luma.Data);
- }
-
- public async ValueTask SendBrightnessAsync(byte brightness)
- {
- var header = new HeaderWindow { Command = (ushort)DisplayCommand.Brightness };
-
- var payloadBuffer = _arrayPool.Rent(1);
- var payload = payloadBuffer.AsMemory(0, 1);
- payload.Span[0] = brightness;
-
- await SendAsync(header, payload);
- _arrayPool.Return(payloadBuffer);
- }
-
- public ValueTask SendHardResetAsync()
- {
- var header = new HeaderWindow { Command = (ushort)DisplayCommand.HardReset };
- return SendAsync(header, Memory.Empty);
- }
-
- public async ValueTask SendFadeOutAsync(byte loops)
- {
- var header = new HeaderWindow { Command = (ushort)DisplayCommand.FadeOut };
-
- var payloadBuffer = _arrayPool.Rent(1);
- var payload = payloadBuffer.AsMemory(0, 1);
- payload.Span[0] = loops;
-
- await SendAsync(header, payload);
- _arrayPool.Return(payloadBuffer);
- }
-
- public ValueTask SendBitmapLinearWindowAsync(ushort x, ushort y, PixelGrid pixels)
- {
- var header = new HeaderWindow
- {
- Command = (ushort)DisplayCommand.BitmapLinearWin,
- PosX = x,
- PosY = y,
- Width = (ushort)(pixels.Width / 8),
- Height = pixels.Height
- };
-
- return SendAsync(header, pixels.Data);
- }
-
- public string GetLocalIPv4()
- {
- using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0);
- socket.Connect(options.Value.Hostname, options.Value.Port);
- var endPoint = socket.LocalEndPoint as IPEndPoint ?? throw new NotSupportedException();
- return endPoint.Address.ToString();
- }
-
- private async ValueTask SendAsync(HeaderWindow header, Memory payload)
- {
- int headerSize;
- unsafe
- {
- // because we specified the struct layout, no platform-specific padding will be added and this is be safe.
- headerSize = sizeof(HeaderWindow);
- }
-
- Debug.Assert(headerSize == 10);
- var messageSize = headerSize + payload.Length;
-
- var buffer = _arrayPool.Rent(messageSize);
- var message = buffer.AsMemory(0, messageSize);
-
- MemoryMarshal.Write(message.Span, header);
- payload.CopyTo(message[headerSize..]);
-
- await _udpClient.SendAsync(message);
-
- _arrayPool.Return(buffer);
- }
-
- public void Dispose() => _udpClient.Dispose();
-}
diff --git a/tanks-backend/DisplayCommands/Internals/DisplaySubCommand.cs b/tanks-backend/DisplayCommands/Internals/DisplaySubCommand.cs
deleted file mode 100644
index bf5427f..0000000
--- a/tanks-backend/DisplayCommands/Internals/DisplaySubCommand.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace DisplayCommands.Internals;
-
-internal enum DisplaySubCommand : ushort
-{
- BitmapNormal = 0x0,
- BitmapCompressZ = 0x677a,
- BitmapCompressBz = 0x627a,
- BitmapCompressLz = 0x6c7a,
- BitmapCompressZs = 0x7a73
-}
diff --git a/tanks-backend/DisplayCommands/Internals/HeaderBitmap.cs b/tanks-backend/DisplayCommands/Internals/HeaderBitmap.cs
deleted file mode 100644
index b733995..0000000
--- a/tanks-backend/DisplayCommands/Internals/HeaderBitmap.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Runtime.InteropServices;
-using EndiannessSourceGenerator;
-
-namespace DisplayCommands.Internals;
-
-[StructEndianness(IsLittleEndian = false)]
-[StructLayout(LayoutKind.Sequential, Pack = 16, Size = 10)]
-internal partial struct HeaderBitmap
-{
- private ushort _command;
-
- private ushort _offset;
-
- private ushort _length;
-
- private ushort _subCommand;
-
- private ushort _reserved;
-}
diff --git a/tanks-backend/DisplayCommands/Internals/HeaderWindow.cs b/tanks-backend/DisplayCommands/Internals/HeaderWindow.cs
deleted file mode 100644
index 2be4916..0000000
--- a/tanks-backend/DisplayCommands/Internals/HeaderWindow.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Runtime.InteropServices;
-using EndiannessSourceGenerator;
-
-namespace DisplayCommands.Internals;
-
-[StructEndianness(IsLittleEndian = false)]
-[StructLayout(LayoutKind.Sequential, Pack = 16, Size = 10)]
-internal partial struct HeaderWindow
-{
- private ushort _command;
-
- private ushort _posX;
-
- private ushort _posY;
-
- private ushort _width;
-
- private ushort _height;
-}
diff --git a/tanks-backend/DisplayCommands/PixelGrid.cs b/tanks-backend/DisplayCommands/PixelGrid.cs
deleted file mode 100644
index 5006ddb..0000000
--- a/tanks-backend/DisplayCommands/PixelGrid.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System.Diagnostics;
-
-namespace DisplayCommands;
-
-public sealed class PixelGrid(ushort width, ushort height) : IEquatable
-{
- private readonly ByteGrid _byteGrid = new((ushort)(width / 8u), height);
-
- public ushort Width { get; } = width;
-
- public ushort Height { get; } = height;
-
- public Memory Data => _byteGrid.Data;
-
- public bool this[ushort x, ushort y]
- {
- get
- {
- Debug.Assert(y < Height);
- var (byteIndex, bitInByteMask) = GetIndexes(x);
- var byteVal = _byteGrid[byteIndex, y];
- return (byteVal & bitInByteMask) != 0;
- }
- set
- {
- Debug.Assert(y < Height);
- var (byteIndex, bitInByteMask) = GetIndexes(x);
- if (value)
- _byteGrid[byteIndex, y] |= bitInByteMask;
- else
- _byteGrid[byteIndex, y] &= (byte)(ushort.MaxValue ^ bitInByteMask);
- }
- }
-
- public void Clear() => _byteGrid.Clear();
-
- public bool Equals(PixelGrid? other)
- {
- if (ReferenceEquals(null, other)) return false;
- if (ReferenceEquals(this, other)) return true;
- return Width == other.Width && Height == other.Height && _byteGrid.Equals(other._byteGrid);
- }
-
- public override bool Equals(object? obj) => ReferenceEquals(this, obj) || (obj is PixelGrid other && Equals(other));
- public override int GetHashCode() => HashCode.Combine(_byteGrid, Width, Height);
- public static bool operator ==(PixelGrid? left, PixelGrid? right) => Equals(left, right);
- public static bool operator !=(PixelGrid? left, PixelGrid? right) => !Equals(left, right);
-
- private (ushort byteIndex, byte bitInByteMask) GetIndexes(int x)
- {
- Debug.Assert(x < Width);
- var byteIndex = (ushort)(x / 8);
- Debug.Assert(byteIndex < Width);
- var bitInByteIndex = (byte)(7 - x % 8);
- Debug.Assert(bitInByteIndex < 8);
- var bitInByteMask = (byte)(1 << bitInByteIndex);
- return (byteIndex, bitInByteMask);
- }
-}
diff --git a/tanks-backend/Dockerfile b/tanks-backend/Dockerfile
index 4fcda7d..af7acd4 100644
--- a/tanks-backend/Dockerfile
+++ b/tanks-backend/Dockerfile
@@ -1,18 +1,10 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build-server
RUN apk add clang binutils musl-dev build-base zlib-static cmake openssl-dev openssl-libs-static openssl
+RUN apk add rust cargo
WORKDIR /src/tanks-server
-# dependencies
-COPY ./shared.props .
-COPY ./TanksServer.sln .
-COPY ./EndiannessSourceGenerator/EndiannessSourceGenerator.csproj EndiannessSourceGenerator/EndiannessSourceGenerator.csproj
-COPY ./DisplayCommands/DisplayCommands.csproj DisplayCommands/DisplayCommands.csproj
-COPY ./TanksServer/TanksServer.csproj TanksServer/TanksServer.csproj
-RUN dotnet restore --runtime linux-musl-x64 TanksServer.sln
-
-#build
COPY . .
RUN dotnet build TanksServer/TanksServer.csproj -c Release -r linux-musl-x64 -o /build
RUN dotnet publish TanksServer/TanksServer.csproj -c Release -r linux-musl-x64 -o /app
diff --git a/tanks-backend/EndiannessSourceGenerator/EndiannessGenerator.cs b/tanks-backend/EndiannessSourceGenerator/EndiannessGenerator.cs
deleted file mode 100644
index eb772e5..0000000
--- a/tanks-backend/EndiannessSourceGenerator/EndiannessGenerator.cs
+++ /dev/null
@@ -1,260 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Text;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-
-namespace EndiannessSourceGenerator;
-
-internal class DebugMeException(string message) : Exception(message);
-
-internal class InvalidUsageException(string message) : Exception(message);
-
-[Generator]
-public class StructEndiannessSourceGenerator : ISourceGenerator
-{
- private const string Namespace = "EndiannessSourceGenerator";
- private const string AttributeName = "StructEndiannessAttribute";
- private const string IsLittleEndianProperty = "IsLittleEndian";
-
- private const string AttributeSourceCode =
- $$"""
- //
- namespace {{Namespace}}
- {
- [System.AttributeUsage(System.AttributeTargets.Struct)]
- public class {{AttributeName}}: System.Attribute
- {
- public required bool {{IsLittleEndianProperty}} { get; init; }
- }
- }
- """;
-
- public void Initialize(GeneratorInitializationContext context)
- {
- // Register the attribute source
- context.RegisterForPostInitialization(i => i.AddSource($"{AttributeName}.g.cs", AttributeSourceCode));
- }
-
- public void Execute(GeneratorExecutionContext context)
- {
- var treesWithStructsWithAttributes = context.Compilation.SyntaxTrees
- .Where(st => st.GetRoot().DescendantNodes()
- .OfType()
- .Any(p => p.DescendantNodes()
- .OfType()
- .Any()))
- .ToList();
-
- foreach (var tree in treesWithStructsWithAttributes)
- {
- var semanticModel = context.Compilation.GetSemanticModel(tree);
-
- var structsWithAttributes = tree.GetRoot().DescendantNodes()
- .OfType()
- .Where(cd => cd.DescendantNodes()
- .OfType()
- .Any());
-
- foreach (var structDeclaration in structsWithAttributes)
- {
- var foundAttribute = GetEndiannessAttribute(structDeclaration, semanticModel);
- if (foundAttribute == null)
- continue; // not my type
-
- var structIsLittleEndian = GetStructIsLittleEndian(foundAttribute);
- HandleStruct(context, structDeclaration, semanticModel, structIsLittleEndian);
- }
- }
- }
-
- private static void HandleStruct(GeneratorExecutionContext context, TypeDeclarationSyntax structDeclaration,
- SemanticModel semanticModel, bool structIsLittleEndian)
- {
- var isPartial = structDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
- if (!isPartial)
- throw new InvalidUsageException("struct is not marked partial");
-
- var accessibilityModifier = structDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.InternalKeyword))
- ? Token(SyntaxKind.InternalKeyword)
- : Token(SyntaxKind.PublicKeyword);
-
- var structType = semanticModel.GetDeclaredSymbol(structDeclaration);
- if (structType == null)
- throw new DebugMeException("struct type info is null");
-
- var structNamespace = structType.ContainingNamespace?.ToDisplayString();
- if (structNamespace == null)
- throw new InvalidUsageException("struct has to be contained in a namespace");
-
- if (structDeclaration.Members.Any(m => m.IsKind(SyntaxKind.PropertyDeclaration)))
- throw new InvalidUsageException("struct cannot have properties");
-
- var fieldDeclarations = structDeclaration.Members
- .Where(m => m.IsKind(SyntaxKind.FieldDeclaration)).OfType();
-
- var generatedCode = CompilationUnit()
- .WithUsings(List([
- UsingDirective(IdentifierName("System")),
- UsingDirective(IdentifierName("System.Buffers.Binary"))
- ]))
- .WithMembers(List([
- FileScopedNamespaceDeclaration(IdentifierName(structNamespace)),
- StructDeclaration(structType.Name)
- .WithModifiers(TokenList([accessibilityModifier, Token(SyntaxKind.PartialKeyword)]))
- .WithMembers(GenerateStructProperties(fieldDeclarations, semanticModel, structIsLittleEndian))
- ]))
- .NormalizeWhitespace()
- .ToFullString();
-
- context.AddSource(
- $"{structNamespace}.{structType.Name}.g.cs",
- SourceText.From(generatedCode, Encoding.UTF8)
- );
- }
-
- private static SyntaxList GenerateStructProperties(
- IEnumerable fieldDeclarations, SemanticModel semanticModel, bool structIsLittleEndian)
- {
- var result = new List();
- foreach (var field in fieldDeclarations)
- {
- if (!field.Modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword)))
- throw new InvalidUsageException("fields have to be private");
-
- var variableDeclaration = field.DescendantNodes()
- .OfType()
- .FirstOrDefault();
- if (variableDeclaration == null)
- throw new DebugMeException("variable declaration of field declaration null");
-
- var variableTypeInfo = semanticModel.GetTypeInfo(variableDeclaration.Type).Type;
- if (variableTypeInfo == null)
- throw new DebugMeException("variable type info of field declaration null");
-
- var typeName = variableTypeInfo.ToDisplayString();
- var fieldName = variableDeclaration.Variables.First().Identifier.ToString();
-
- result.Add(GenerateProperty(typeName, structIsLittleEndian, fieldName));
- }
-
- return new SyntaxList(result);
- }
-
- private static PropertyDeclarationSyntax GenerateProperty(string typeName,
- bool structIsLittleEndian, string fieldName)
- {
- var propertyName = GeneratePropertyName(fieldName);
- var fieldIdentifier = IdentifierName(fieldName);
-
- ExpressionSyntax condition = MemberAccessExpression(
- kind: SyntaxKind.SimpleMemberAccessExpression,
- expression: IdentifierName("BitConverter"),
- name: IdentifierName("IsLittleEndian")
- );
-
- if (!structIsLittleEndian)
- condition = PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, condition);
-
- var reverseEndiannessMethod = MemberAccessExpression(
- kind: SyntaxKind.SimpleMemberAccessExpression,
- expression: IdentifierName("BinaryPrimitives"),
- name: IdentifierName("ReverseEndianness")
- );
-
- var valueIdentifier = IdentifierName("value");
-
- return PropertyDeclaration(ParseTypeName(typeName), propertyName)
- .WithModifiers(TokenList([Token(SyntaxKind.PublicKeyword)]))
- .WithAccessorList(AccessorList(List([
- AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
- .WithExpressionBody(ArrowExpressionClause(ConditionalExpression(
- condition: condition,
- whenTrue: fieldIdentifier,
- whenFalse: InvocationExpression(
- expression: reverseEndiannessMethod,
- argumentList: ArgumentList(SingletonSeparatedList(
- Argument(fieldIdentifier)
- ))
- )
- )))
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
- AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
- .WithExpressionBody(ArrowExpressionClause(AssignmentExpression(
- kind: SyntaxKind.SimpleAssignmentExpression,
- left: fieldIdentifier,
- right: ConditionalExpression(
- condition: condition,
- whenTrue: valueIdentifier,
- whenFalse: InvocationExpression(
- expression: reverseEndiannessMethod,
- argumentList: ArgumentList(SingletonSeparatedList(
- Argument(valueIdentifier)
- ))
- )
- )
- )))
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
- ]))
- );
- }
-
- private static SyntaxToken GeneratePropertyName(string fieldName)
- {
- var propertyName = fieldName;
- if (propertyName.StartsWith("_"))
- propertyName = propertyName.Substring(1);
- if (!char.IsLetter(propertyName, 0) || char.IsUpper(propertyName, 0))
- throw new InvalidUsageException("field names have to start with a lower case letter");
- propertyName = propertyName.Substring(0, 1).ToUpperInvariant()
- + propertyName.Substring(1);
- return Identifier(propertyName);
- }
-
- private static AttributeSyntax? GetEndiannessAttribute(SyntaxNode structDeclaration, SemanticModel semanticModel)
- {
- AttributeSyntax? foundAttribute = null;
- foreach (var attributeSyntax in structDeclaration.DescendantNodes().OfType())
- {
- var attributeTypeInfo = semanticModel.GetTypeInfo(attributeSyntax).Type;
- if (attributeTypeInfo == null)
- throw new DebugMeException("attribute type info is null");
-
- if (attributeTypeInfo.ContainingNamespace?.Name != Namespace)
- continue;
- if (attributeTypeInfo.Name != AttributeName)
- continue;
-
- foundAttribute = attributeSyntax;
- break;
- }
-
- return foundAttribute;
- }
-
- private static bool GetStructIsLittleEndian(AttributeSyntax foundAttribute)
- {
- var endiannessArguments = foundAttribute.ArgumentList;
- if (endiannessArguments == null)
- throw new InvalidUsageException("endianness attribute has no arguments");
-
- var isLittleEndianArgumentSyntax = endiannessArguments.Arguments
- .FirstOrDefault(argumentSyntax =>
- argumentSyntax.NameEquals?.Name.Identifier.ToString() == IsLittleEndianProperty);
- if (isLittleEndianArgumentSyntax == null)
- throw new InvalidUsageException("endianness attribute argument not found");
-
- bool? structIsLittleEndian = isLittleEndianArgumentSyntax.Expression.Kind() switch
- {
- SyntaxKind.FalseLiteralExpression => false,
- SyntaxKind.TrueLiteralExpression => true,
- SyntaxKind.DefaultLiteralExpression => false,
- _ => throw new InvalidUsageException($"{IsLittleEndianProperty} has to be set with a literal")
- };
- return structIsLittleEndian.Value;
- }
-}
diff --git a/tanks-backend/EndiannessSourceGenerator/EndiannessSourceGenerator.csproj b/tanks-backend/EndiannessSourceGenerator/EndiannessSourceGenerator.csproj
deleted file mode 100644
index ae25c62..0000000
--- a/tanks-backend/EndiannessSourceGenerator/EndiannessSourceGenerator.csproj
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
- netstandard2.0
- false
- enable
- latest
-
- true
- true
- EndiannessSourceGenerator
- false
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
diff --git a/tanks-backend/EndiannessSourceGenerator/Properties/launchSettings.json b/tanks-backend/EndiannessSourceGenerator/Properties/launchSettings.json
deleted file mode 100644
index b8b1379..0000000
--- a/tanks-backend/EndiannessSourceGenerator/Properties/launchSettings.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "$schema": "http://json.schemastore.org/launchsettings.json",
- "profiles": {
- "Generators": {
- "commandName": "DebugRoslynComponent",
- "targetProject": "../DisplayCommands/DisplayCommands.csproj"
- }
- }
-}
diff --git a/tanks-backend/EndiannessSourceGenerator/Readme.md b/tanks-backend/EndiannessSourceGenerator/Readme.md
deleted file mode 100644
index a69efd1..0000000
--- a/tanks-backend/EndiannessSourceGenerator/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Endianness Source Generator
-
-When annotating a struct with the `StructEndianness` attribute, this code generator will generate properties for the declared fields.
-
-Each time a property is read or written, the endianness is converted from runtime endianness to struct endianness or vice-versa.
diff --git a/tanks-backend/TanksServer.sln b/tanks-backend/TanksServer.sln
index fc3a074..f26467a 100644
--- a/tanks-backend/TanksServer.sln
+++ b/tanks-backend/TanksServer.sln
@@ -2,17 +2,13 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TanksServer", "TanksServer\TanksServer.csproj", "{D88BF376-47A4-4C72-ADD1-983F9285C351}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisplayCommands", "DisplayCommands\DisplayCommands.csproj", "{B4B43561-7A2C-486B-99F7-E58A67BC370A}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EndiannessSourceGenerator", "EndiannessSourceGenerator\EndiannessSourceGenerator.csproj", "{D77FE880-F2B8-43B6-8B33-B6FA089CC337}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{12DB7D48-1BB2-488B-B4D9-4126087D2F8C}"
ProjectSection(SolutionItems) = preProject
- global.json = global.json
- shared.props = shared.props
Dockerfile = Dockerfile
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint2", "servicepoint\servicepoint2-binding-cs\src\ServicePoint2.csproj", "{DFCC69ED-E02B-4631-8A23-5D394BA01E03}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -23,13 +19,9 @@ Global
{D88BF376-47A4-4C72-ADD1-983F9285C351}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D88BF376-47A4-4C72-ADD1-983F9285C351}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D88BF376-47A4-4C72-ADD1-983F9285C351}.Release|Any CPU.Build.0 = Release|Any CPU
- {B4B43561-7A2C-486B-99F7-E58A67BC370A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B4B43561-7A2C-486B-99F7-E58A67BC370A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B4B43561-7A2C-486B-99F7-E58A67BC370A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B4B43561-7A2C-486B-99F7-E58A67BC370A}.Release|Any CPU.Build.0 = Release|Any CPU
- {D77FE880-F2B8-43B6-8B33-B6FA089CC337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D77FE880-F2B8-43B6-8B33-B6FA089CC337}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D77FE880-F2B8-43B6-8B33-B6FA089CC337}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D77FE880-F2B8-43B6-8B33-B6FA089CC337}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DFCC69ED-E02B-4631-8A23-5D394BA01E03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DFCC69ED-E02B-4631-8A23-5D394BA01E03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DFCC69ED-E02B-4631-8A23-5D394BA01E03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DFCC69ED-E02B-4631-8A23-5D394BA01E03}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/tanks-backend/TanksServer/Endpoints.cs b/tanks-backend/TanksServer/Endpoints.cs
index e0e0252..baf476b 100644
--- a/tanks-backend/TanksServer/Endpoints.cs
+++ b/tanks-backend/TanksServer/Endpoints.cs
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Diagnostics.HealthChecks;
+using ServicePoint2;
using TanksServer.GameLogic;
using TanksServer.Interactivity;
@@ -17,7 +18,8 @@ internal sealed class Endpoints(
PlayerServer playerService,
ControlsServer controlsServer,
MapService mapService,
- ChangeToRequestedMap changeToRequestedMap
+ ChangeToRequestedMap changeToRequestedMap,
+ Connection displayConnection
)
{
public void Map(WebApplication app)
@@ -29,6 +31,7 @@ internal sealed class Endpoints(
app.Map("/controls", ConnectControlsAsync);
app.MapGet("/map", () => mapService.MapNames);
app.MapPost("/map", PostMap);
+ app.MapPost("/resetDisplay", () => displayConnection.Send(Command.HardReset().IntoPacket()));
app.MapGet("/map/{name}", GetMapByName);
app.MapHealthChecks("/health", new HealthCheckOptions
@@ -114,7 +117,7 @@ internal sealed class Endpoints(
if (!mapService.TryGetPreview(name, out var preview))
return TypedResults.NotFound();
- var mapInfo = new MapInfo(prototype.Name, prototype.GetType().Name, preview.Data);
+ var mapInfo = new MapInfo(prototype.Name, prototype.GetType().Name, preview.Data.ToArray());
return TypedResults.Ok(mapInfo);
}
diff --git a/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs b/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs
index 3b3571f..938f2f5 100644
--- a/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs
+++ b/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs
@@ -1,20 +1,26 @@
-using System.Diagnostics;
-
namespace TanksServer.GameLogic;
-internal sealed class CollectPowerUp(
- MapEntityManager entityManager
-) : ITickStep
+internal sealed class CollectPowerUp : ITickStep
{
- private readonly Predicate _collectPredicate = b => TryCollect(b, entityManager.Tanks);
+ private readonly Predicate _collectPredicate;
+ private readonly GameRules _rules;
+ private readonly MapEntityManager _entityManager;
+
+ public CollectPowerUp(MapEntityManager entityManager,
+ IOptions options)
+ {
+ _entityManager = entityManager;
+ _rules = options.Value;
+ _collectPredicate = b => TryCollect(b, entityManager.Tanks);
+ }
public ValueTask TickAsync(TimeSpan delta)
{
- entityManager.RemoveWhere(_collectPredicate);
+ _entityManager.RemoveWhere(_collectPredicate);
return ValueTask.CompletedTask;
}
- private static bool TryCollect(PowerUp powerUp, IEnumerable tanks)
+ private bool TryCollect(PowerUp powerUp, IEnumerable tanks)
{
var position = powerUp.Position;
foreach (var tank in tanks)
@@ -34,32 +40,38 @@ internal sealed class CollectPowerUp(
return false;
}
- private static void ApplyPowerUpEffect(PowerUp powerUp, Tank tank)
+ private void ApplyPowerUpEffect(PowerUp powerUp, Tank tank)
{
switch (powerUp.Type)
{
- case PowerUpType.MagazineType:
- if (powerUp.MagazineType == null)
- throw new UnreachableException();
-
- tank.Magazine = tank.Magazine with
- {
- Type = tank.Magazine.Type | powerUp.MagazineType.Value,
- UsedBullets = 0
- };
-
- if (tank.ReloadingUntil >= DateTime.Now)
- tank.ReloadingUntil = DateTime.Now;
-
- break;
case PowerUpType.MagazineSize:
- tank.Magazine = tank.Magazine with
+ tank.MaxBullets = int.Clamp(tank.MaxBullets + 1, 1, 32);
+ break;
+
+ case PowerUpType.BulletAcceleration:
+ tank.BulletStats = tank.BulletStats with
{
- MaxBullets = (byte)int.Clamp(tank.Magazine.MaxBullets + 1, 1, 32)
+ Acceleration = tank.BulletStats.Acceleration + _rules.BulletAccelerationUpgradeStrength
};
break;
+
+ case PowerUpType.ExplosiveBullets:
+ tank.BulletStats = tank.BulletStats with { Explosive = true };
+ break;
+
+ case PowerUpType.SmartBullets:
+ tank.BulletStats = tank.BulletStats with { Smart = true };
+ break;
+
+ case PowerUpType.BulletSpeed:
+ tank.BulletStats = tank.BulletStats with
+ {
+ Speed = tank.BulletStats.Speed + _rules.BulletSpeedUpgradeStrength
+ };
+ break;
+
default:
- throw new UnreachableException();
+ throw new NotImplementedException($"unknown type {powerUp.Type}");
}
}
}
diff --git a/tanks-backend/TanksServer/GameLogic/CollideBullets.cs b/tanks-backend/TanksServer/GameLogic/CollideBullets.cs
index 4be0dca..c75fe6a 100644
--- a/tanks-backend/TanksServer/GameLogic/CollideBullets.cs
+++ b/tanks-backend/TanksServer/GameLogic/CollideBullets.cs
@@ -35,7 +35,7 @@ internal sealed class CollideBullets : ITickStep
if (bullet.Timeout > DateTime.Now)
return false;
- ExplodeAt(bullet.Position.ToPixelPosition(), bullet.IsExplosive, bullet.Owner);
+ ExplodeAt(bullet.Position.ToPixelPosition(), bullet.Stats.Explosive, bullet.Owner);
return true;
}
@@ -45,7 +45,7 @@ internal sealed class CollideBullets : ITickStep
if (!_map.Current.IsWall(pixel))
return false;
- ExplodeAt(pixel, bullet.IsExplosive, bullet.Owner);
+ ExplodeAt(pixel, bullet.Stats.Explosive, bullet.Owner);
return true;
}
@@ -55,7 +55,7 @@ internal sealed class CollideBullets : ITickStep
if (hitTank == null)
return false;
- ExplodeAt(bullet.Position.ToPixelPosition(), bullet.IsExplosive, bullet.Owner);
+ ExplodeAt(bullet.Position.ToPixelPosition(), bullet.Stats.Explosive, bullet.Owner);
return true;
}
diff --git a/tanks-backend/TanksServer/GameLogic/GameRules.cs b/tanks-backend/TanksServer/GameLogic/GameRules.cs
index 2284464..1ebf548 100644
--- a/tanks-backend/TanksServer/GameLogic/GameRules.cs
+++ b/tanks-backend/TanksServer/GameLogic/GameRules.cs
@@ -16,7 +16,7 @@ internal sealed class GameRules
public double ShootDelayMs { get; set; }
- public double BulletSpeed { get; set; }
+ public double BulletSpeed { get; set; } = 75;
public int SpawnDelayMs { get; set; }
@@ -27,4 +27,8 @@ internal sealed class GameRules
public int ReloadDelayMs { get; set; } = 3000;
public double SmartBulletInertia { get; set; } = 1;
+
+ public double BulletAccelerationUpgradeStrength { get; set; } = 15;
+
+ public double BulletSpeedUpgradeStrength { get; set; } = 5;
}
diff --git a/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs b/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs
index e7b376d..dd674a1 100644
--- a/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs
+++ b/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs
@@ -15,19 +15,17 @@ internal sealed class MapEntityManager(
public IEnumerable Tanks => _playerTanks.Values;
public IEnumerable PowerUps => _powerUps;
- public void SpawnBullet(Player tankOwner, FloatPosition position, double rotation, MagazineType type)
+ public void SpawnBullet(Player tankOwner, FloatPosition position, double rotation, BulletStats stats)
{
- var speed = _rules.BulletSpeed * (type.HasFlag(MagazineType.Fast) ? 2 : 1);
_bullets.Add(new Bullet
{
Owner = tankOwner,
Position = position,
Rotation = rotation,
- IsExplosive = type.HasFlag(MagazineType.Explosive),
Timeout = DateTime.Now + _bulletTimeout,
OwnerCollisionAfter = DateTime.Now + TimeSpan.FromSeconds(1),
- Speed = speed,
- IsSmart = type.HasFlag(MagazineType.Smart)
+ Speed = _rules.BulletSpeed,
+ Stats = stats
});
}
@@ -35,24 +33,22 @@ internal sealed class MapEntityManager(
public void SpawnTank(Player player, FloatPosition position)
{
- var tank = new Tank
+ var tank = new Tank(player, position)
{
- Owner = player,
- Position = position,
Rotation = Random.Shared.NextDouble(),
- Magazine = new Magazine(MagazineType.Basic, 0, _rules.MagazineSize)
+ MaxBullets = _rules.MagazineSize,
+ BulletStats =new BulletStats(_rules.BulletSpeed, 0, false, false)
};
_playerTanks[player] = tank;
logger.LogInformation("Tank added for player {}", player.Name);
}
- public void SpawnPowerUp(FloatPosition position, PowerUpType type, MagazineType? magazineType)
+ public void SpawnPowerUp(FloatPosition position, PowerUpType type)
{
var powerUp = new PowerUp
{
Position = position,
- Type = type,
- MagazineType = magazineType
+ Type = type
};
_powerUps.Add(powerUp);
}
diff --git a/tanks-backend/TanksServer/GameLogic/MapService.cs b/tanks-backend/TanksServer/GameLogic/MapService.cs
index 28a5aef..753d007 100644
--- a/tanks-backend/TanksServer/GameLogic/MapService.cs
+++ b/tanks-backend/TanksServer/GameLogic/MapService.cs
@@ -1,7 +1,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
-using DisplayCommands;
+using ServicePoint2;
using TanksServer.Graphics;
namespace TanksServer.GameLogic;
@@ -42,7 +42,7 @@ internal sealed class MapService
if (!_mapPrototypes.TryGetValue(name, out var prototype))
return false; // name not found
- pixelGrid = new PixelGrid(PixelsPerRow, PixelsPerColumn);
+ pixelGrid = PixelGrid.New(PixelsPerRow, PixelsPerColumn);
DrawMapStep.Draw(pixelGrid, prototype.CreateInstance());
_mapPreviews.TryAdd(name, pixelGrid); // another thread may have added the map already
diff --git a/tanks-backend/TanksServer/GameLogic/MoveBullets.cs b/tanks-backend/TanksServer/GameLogic/MoveBullets.cs
index 96bde60..60bd5b8 100644
--- a/tanks-backend/TanksServer/GameLogic/MoveBullets.cs
+++ b/tanks-backend/TanksServer/GameLogic/MoveBullets.cs
@@ -17,13 +17,15 @@ internal sealed class MoveBullets(
private void MoveBullet(Bullet bullet, TimeSpan delta)
{
- if (bullet.IsSmart && TryGetSmartRotation(bullet.Position, bullet.Owner, out var wantedRotation))
+ if (bullet.Stats.Smart && TryGetSmartRotation(bullet.Position, bullet.Owner, out var wantedRotation))
{
var inertiaFactor = _smartBulletInertia * delta.TotalSeconds;
var difference = wantedRotation - bullet.Rotation;
bullet.Rotation += difference * inertiaFactor;
}
+ bullet.Speed += (bullet.Stats.Acceleration * delta.TotalSeconds);
+
var speed = bullet.Speed * delta.TotalSeconds;
var angle = bullet.Rotation * 2 * Math.PI;
bullet.Position = new FloatPosition(
diff --git a/tanks-backend/TanksServer/GameLogic/ShootFromTanks.cs b/tanks-backend/TanksServer/GameLogic/ShootFromTanks.cs
index 9df8a64..3014c45 100644
--- a/tanks-backend/TanksServer/GameLogic/ShootFromTanks.cs
+++ b/tanks-backend/TanksServer/GameLogic/ShootFromTanks.cs
@@ -26,24 +26,17 @@ internal sealed class ShootFromTanks(
if (tank.ReloadingUntil >= now)
return;
- if (tank.Magazine.Empty)
+ if (tank.UsedBullets >= tank.MaxBullets)
{
tank.ReloadingUntil = now.AddMilliseconds(_config.ReloadDelayMs);
- tank.Magazine = tank.Magazine with
- {
- UsedBullets = 0,
- Type = MagazineType.Basic
- };
+ tank.UsedBullets = 0;
return;
}
tank.NextShotAfter = now.AddMilliseconds(_config.ShootDelayMs);
- tank.Magazine = tank.Magazine with
- {
- UsedBullets = (byte)(tank.Magazine.UsedBullets + 1)
- };
+ tank.UsedBullets++;
tank.Owner.Scores.ShotsFired++;
- entityManager.SpawnBullet(tank.Owner, tank.Position, tank.Orientation / 16d, tank.Magazine.Type);
+ entityManager.SpawnBullet(tank.Owner, tank.Position, tank.Orientation / 16d, tank.BulletStats);
}
}
diff --git a/tanks-backend/TanksServer/GameLogic/SpawnPowerUp.cs b/tanks-backend/TanksServer/GameLogic/SpawnPowerUp.cs
index 72b0592..b79abb0 100644
--- a/tanks-backend/TanksServer/GameLogic/SpawnPowerUp.cs
+++ b/tanks-backend/TanksServer/GameLogic/SpawnPowerUp.cs
@@ -1,5 +1,3 @@
-using System.Diagnostics;
-
namespace TanksServer.GameLogic;
internal sealed class SpawnPowerUp(
@@ -18,25 +16,9 @@ internal sealed class SpawnPowerUp(
if (Random.Shared.NextDouble() > _spawnChance * delta.TotalSeconds)
return ValueTask.CompletedTask;
-
- var type = Random.Shared.Next(4) == 0
- ? PowerUpType.MagazineSize
- : PowerUpType.MagazineType;
-
- MagazineType? magazineType = type switch
- {
- PowerUpType.MagazineType => Random.Shared.Next(0, 3) switch
- {
- 0 => MagazineType.Fast,
- 1 => MagazineType.Explosive,
- 2 => MagazineType.Smart,
- _ => throw new UnreachableException()
- },
- _ => null
- };
-
+ var type = (PowerUpType)Random.Shared.Next((int)Enum.GetValues().Max());
var position = emptyTileFinder.ChooseEmptyTile().GetCenter().ToFloatPosition();
- entityManager.SpawnPowerUp(position, type, magazineType);
+ entityManager.SpawnPowerUp(position, type);
return ValueTask.CompletedTask;
}
}
diff --git a/tanks-backend/TanksServer/Graphics/DrawMapStep.cs b/tanks-backend/TanksServer/Graphics/DrawMapStep.cs
index 3be6a37..3fc59b5 100644
--- a/tanks-backend/TanksServer/Graphics/DrawMapStep.cs
+++ b/tanks-backend/TanksServer/Graphics/DrawMapStep.cs
@@ -1,4 +1,4 @@
-using DisplayCommands;
+using ServicePoint2;
using TanksServer.GameLogic;
namespace TanksServer.Graphics;
diff --git a/tanks-backend/TanksServer/Graphics/DrawPowerUpsStep.cs b/tanks-backend/TanksServer/Graphics/DrawPowerUpsStep.cs
index 6d47060..c6125ec 100644
--- a/tanks-backend/TanksServer/Graphics/DrawPowerUpsStep.cs
+++ b/tanks-backend/TanksServer/Graphics/DrawPowerUpsStep.cs
@@ -14,12 +14,12 @@ internal sealed class DrawPowerUpsStep(MapEntityManager entityManager) : IDrawSt
{
foreach (var powerUp in entityManager.PowerUps)
{
- var sprite = powerUp switch
+ var sprite = powerUp.Type switch
{
- { Type: PowerUpType.MagazineSize } => _magazineSprite,
- { Type: PowerUpType.MagazineType, MagazineType: MagazineType.Smart } => _smartSprite,
- { Type: PowerUpType.MagazineType, MagazineType: MagazineType.Explosive } => _explosiveSprite,
- { Type: PowerUpType.MagazineType, MagazineType: MagazineType.Fast } => _fastSprite,
+ PowerUpType.MagazineSize => _magazineSprite,
+ PowerUpType.BulletAcceleration or PowerUpType.BulletSpeed => _fastSprite,
+ PowerUpType.SmartBullets => _smartSprite,
+ PowerUpType.ExplosiveBullets => _explosiveSprite,
_ => _genericSprite
};
diff --git a/tanks-backend/TanksServer/Graphics/GeneratePixelsTickStep.cs b/tanks-backend/TanksServer/Graphics/GeneratePixelsTickStep.cs
index 7edf1a0..de83cbb 100644
--- a/tanks-backend/TanksServer/Graphics/GeneratePixelsTickStep.cs
+++ b/tanks-backend/TanksServer/Graphics/GeneratePixelsTickStep.cs
@@ -1,4 +1,4 @@
-using DisplayCommands;
+using ServicePoint2;
using TanksServer.GameLogic;
using TanksServer.Interactivity;
@@ -10,9 +10,9 @@ internal sealed class GeneratePixelsTickStep(
) : ITickStep
{
private GamePixelGrid _lastGamePixelGrid = new(MapService.PixelsPerRow, MapService.PixelsPerColumn);
- private PixelGrid _lastObserverPixelGrid = new(MapService.PixelsPerRow, MapService.PixelsPerColumn);
+ private PixelGrid _lastObserverPixelGrid = PixelGrid.New(MapService.PixelsPerRow, MapService.PixelsPerColumn);
private GamePixelGrid _gamePixelGrid = new(MapService.PixelsPerRow, MapService.PixelsPerColumn);
- private PixelGrid _observerPixelGrid = new(MapService.PixelsPerRow, MapService.PixelsPerColumn);
+ private PixelGrid _observerPixelGrid = PixelGrid.New(MapService.PixelsPerRow, MapService.PixelsPerColumn);
private readonly List _drawSteps = drawSteps.ToList();
private readonly List _consumers = consumers.ToList();
@@ -20,7 +20,7 @@ internal sealed class GeneratePixelsTickStep(
public async ValueTask TickAsync(TimeSpan _)
{
Draw(_gamePixelGrid, _observerPixelGrid);
- if (_observerPixelGrid.Data.Span.SequenceEqual(_lastObserverPixelGrid.Data.Span))
+ if (_observerPixelGrid.Data.SequenceEqual(_lastObserverPixelGrid.Data))
return;
await _consumers.Select(c => c.OnFrameDoneAsync(_gamePixelGrid, _observerPixelGrid))
@@ -36,7 +36,7 @@ internal sealed class GeneratePixelsTickStep(
foreach (var step in _drawSteps)
step.Draw(gamePixelGrid);
- observerPixelGrid.Clear();
+ observerPixelGrid.Fill(false);
for (var y = 0; y < MapService.PixelsPerColumn; y++)
for (var x = 0; x < MapService.PixelsPerRow; x++)
{
diff --git a/tanks-backend/TanksServer/Graphics/IFrameConsumer.cs b/tanks-backend/TanksServer/Graphics/IFrameConsumer.cs
index 5a83a86..9ee8d3e 100644
--- a/tanks-backend/TanksServer/Graphics/IFrameConsumer.cs
+++ b/tanks-backend/TanksServer/Graphics/IFrameConsumer.cs
@@ -1,4 +1,4 @@
-using DisplayCommands;
+using ServicePoint2;
namespace TanksServer.Graphics;
diff --git a/tanks-backend/TanksServer/Interactivity/ClientScreenServer.cs b/tanks-backend/TanksServer/Interactivity/ClientScreenServer.cs
index 6955c4d..2832fba 100644
--- a/tanks-backend/TanksServer/Interactivity/ClientScreenServer.cs
+++ b/tanks-backend/TanksServer/Interactivity/ClientScreenServer.cs
@@ -1,5 +1,5 @@
using System.Net.WebSockets;
-using DisplayCommands;
+using ServicePoint2;
using TanksServer.Graphics;
namespace TanksServer.Interactivity;
diff --git a/tanks-backend/TanksServer/Interactivity/ClientScreenServerConnection.cs b/tanks-backend/TanksServer/Interactivity/ClientScreenServerConnection.cs
index 6208cbf..483176a 100644
--- a/tanks-backend/TanksServer/Interactivity/ClientScreenServerConnection.cs
+++ b/tanks-backend/TanksServer/Interactivity/ClientScreenServerConnection.cs
@@ -1,6 +1,6 @@
using System.Buffers;
using System.Net.WebSockets;
-using DisplayCommands;
+using ServicePoint2;
using TanksServer.Graphics;
namespace TanksServer.Interactivity;
@@ -36,8 +36,9 @@ internal sealed class ClientScreenServerConnection
private Package BuildNextPackage(PixelGrid pixels, GamePixelGrid gamePixelGrid)
{
- var nextPixels = _bufferPool.Rent(pixels.Data.Length);
- pixels.Data.CopyTo(nextPixels.Memory);
+ var pixelsData = pixels.Data;
+ var nextPixels = _bufferPool.Rent(pixelsData.Length);
+ pixelsData.CopyTo(nextPixels.Memory.Span);
if (_playerDataBuilder == null)
return new Package(nextPixels, null);
diff --git a/tanks-backend/TanksServer/Interactivity/DroppablePackageRequestConnection.cs b/tanks-backend/TanksServer/Interactivity/DroppablePackageRequestConnection.cs
index 19956a9..49dccc2 100644
--- a/tanks-backend/TanksServer/Interactivity/DroppablePackageRequestConnection.cs
+++ b/tanks-backend/TanksServer/Interactivity/DroppablePackageRequestConnection.cs
@@ -1,4 +1,3 @@
-using System.Diagnostics;
using DotNext.Threading;
namespace TanksServer.Interactivity;
@@ -10,27 +9,15 @@ internal abstract class DroppablePackageRequestConnection(
where TPackage : class, IDisposable
{
private readonly AsyncAutoResetEvent _nextPackageEvent = new(false, 1);
- private int _runningMessageHandlers = 0;
private TPackage? _next;
- protected override ValueTask HandleMessageAsync(Memory _)
+ protected override async ValueTask HandleMessageAsync(Memory _)
{
- if (Interlocked.Increment(ref _runningMessageHandlers) == 1)
- return Core();
-
- // client has requested multiple frames, ignoring duplicate requests
- Interlocked.Decrement(ref _runningMessageHandlers);
- return ValueTask.CompletedTask;
-
- async ValueTask Core()
- {
- await _nextPackageEvent.WaitAsync();
- var package = Interlocked.Exchange(ref _next, null);
- if (package == null)
- throw new UnreachableException("package should be set here");
- await SendPackageAsync(package);
- Interlocked.Decrement(ref _runningMessageHandlers);
- }
+ await _nextPackageEvent.WaitAsync();
+ var package = Interlocked.Exchange(ref _next, null);
+ if (package == null)
+ return;
+ await SendPackageAsync(package);
}
protected void SetNextPackage(TPackage next)
diff --git a/tanks-backend/TanksServer/Interactivity/PlayerInfoConnection.cs b/tanks-backend/TanksServer/Interactivity/PlayerInfoConnection.cs
index 04661c3..6340fbc 100644
--- a/tanks-backend/TanksServer/Interactivity/PlayerInfoConnection.cs
+++ b/tanks-backend/TanksServer/Interactivity/PlayerInfoConnection.cs
@@ -13,7 +13,7 @@ internal sealed class PlayerInfoConnection
private readonly MapEntityManager _entityManager;
private readonly BufferPool _bufferPool;
private readonly MemoryStream _tempStream = new();
- private IMemoryOwner? _lastMessage = null;
+ private IMemoryOwner? _lastMessage;
public PlayerInfoConnection(
Player player,
@@ -47,20 +47,7 @@ internal sealed class PlayerInfoConnection
private async ValueTask?> GenerateMessageAsync()
{
var tank = _entityManager.GetCurrentTankOfPlayer(_player);
-
- TankInfo? tankInfo = null;
- if (tank != null)
- {
- var magazine = tank.ReloadingUntil > DateTime.Now ? "[ RELOADING ]" : tank.Magazine.ToDisplayString();
- tankInfo = new TankInfo(tank.Orientation, magazine, tank.Position.ToPixelPosition(), tank.Moving);
- }
-
- var info = new PlayerInfo(
- _player.Name,
- _player.Scores,
- _player.Controls.ToDisplayString(),
- tankInfo,
- _player.OpenConnections);
+ var info = new PlayerInfo(_player, _player.Controls.ToDisplayString(), tank);
_tempStream.Position = 0;
await JsonSerializer.SerializeAsync(_tempStream, info, AppSerializerContext.Default.PlayerInfo);
@@ -85,3 +72,9 @@ internal sealed class PlayerInfoConnection
Interlocked.Exchange(ref _lastMessage, data)?.Dispose();
}
}
+
+internal record struct PlayerInfo(
+ Player Player,
+ string Controls,
+ Tank? Tank
+);
diff --git a/tanks-backend/TanksServer/Interactivity/SendToServicePointDisplay.cs b/tanks-backend/TanksServer/Interactivity/SendToServicePointDisplay.cs
index 5625566..3effe4d 100644
--- a/tanks-backend/TanksServer/Interactivity/SendToServicePointDisplay.cs
+++ b/tanks-backend/TanksServer/Interactivity/SendToServicePointDisplay.cs
@@ -1,8 +1,10 @@
using System.Diagnostics;
+using System.Net;
using System.Net.Sockets;
-using DisplayCommands;
+using ServicePoint2;
using TanksServer.GameLogic;
using TanksServer.Graphics;
+using CompressionCode = ServicePoint2.BindGen.CompressionCode;
namespace TanksServer.Interactivity;
@@ -12,12 +14,13 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer
private const int ScoresHeight = 20;
private const int ScoresPlayerRows = ScoresHeight - 6;
- private readonly IDisplayConnection _displayConnection;
+ private readonly Connection _displayConnection;
private readonly MapService _mapService;
private readonly ILogger _logger;
private readonly PlayerServer _players;
- private readonly Cp437Grid _scoresBuffer;
+ private readonly ByteGrid _scoresBuffer;
private readonly TimeSpan _minFrameTime;
+ private readonly IOptionsMonitor _options;
private DateTime _nextFailLogAfter = DateTime.Now;
private DateTime _nextFrameAfter = DateTime.Now;
@@ -25,31 +28,35 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer
public SendToServicePointDisplay(
PlayerServer players,
ILogger logger,
- IDisplayConnection displayConnection,
+ Connection displayConnection,
IOptions hostOptions,
- MapService mapService
- )
+ MapService mapService,
+ IOptionsMonitor options,
+ IOptions displayConfig)
{
_players = players;
_logger = logger;
_displayConnection = displayConnection;
_mapService = mapService;
_minFrameTime = TimeSpan.FromMilliseconds(hostOptions.Value.ServicePointDisplayMinFrameTimeMs);
+ _options = options;
- var localIp = _displayConnection.GetLocalIPv4().Split('.');
+ var localIp = GetLocalIPv4(displayConfig.Value).Split('.');
Debug.Assert(localIp.Length == 4);
- _scoresBuffer = new Cp437Grid(12, 20)
- {
- [00] = "== TANKS! ==",
- [01] = "-- scores --",
- [17] = "-- join --",
- [18] = string.Join('.', localIp[..2]),
- [19] = string.Join('.', localIp[2..])
- };
+ _scoresBuffer = ByteGrid.New(12, 20);
+
+ _scoresBuffer[00] = "== TANKS! ==";
+ _scoresBuffer[01] = "-- scores --";
+ _scoresBuffer[17] = "-- join --";
+ _scoresBuffer[18] = string.Join('.', localIp[..2]);
+ _scoresBuffer[19] = string.Join('.', localIp[2..]);
}
public async Task OnFrameDoneAsync(GamePixelGrid gamePixelGrid, PixelGrid observerPixels)
{
+ if (!_options.CurrentValue.EnableServicePointDisplay)
+ return;
+
if (DateTime.Now < _nextFrameAfter)
return;
@@ -60,8 +67,9 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer
try
{
- await _displayConnection.SendBitmapLinearWindowAsync(0, 0, observerPixels);
- await _displayConnection.SendCp437DataAsync(MapService.TilesPerRow, 0, _scoresBuffer);
+ _displayConnection.Send(Command.BitmapLinearWin(0, 0, observerPixels.Clone(), CompressionCode.Lzma)
+ .IntoPacket());
+ _displayConnection.Send(Command.Cp437Data(MapService.TilesPerRow, 0, _scoresBuffer.Clone()).IntoPacket());
}
catch (SocketException ex)
{
@@ -97,4 +105,12 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer
_scoresBuffer[16] = _mapService.Current.Name[..(Math.Min(ScoresWidth, _mapService.Current.Name.Length) - 1)];
}
+
+ private static string GetLocalIPv4(DisplayConfiguration configuration)
+ {
+ using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0);
+ socket.Connect(configuration.Hostname, configuration.Port);
+ var endPoint = socket.LocalEndPoint as IPEndPoint ?? throw new NotSupportedException();
+ return endPoint.Address.ToString();
+ }
}
diff --git a/tanks-backend/TanksServer/Models/Bullet.cs b/tanks-backend/TanksServer/Models/Bullet.cs
index 9e17066..aed3ddf 100644
--- a/tanks-backend/TanksServer/Models/Bullet.cs
+++ b/tanks-backend/TanksServer/Models/Bullet.cs
@@ -8,15 +8,13 @@ internal sealed class Bullet : IMapEntity
public required FloatPosition Position { get; set; }
- public required bool IsExplosive { get; init; }
-
public required DateTime Timeout { get; init; }
public PixelBounds Bounds => new(Position.ToPixelPosition(), Position.ToPixelPosition());
internal required DateTime OwnerCollisionAfter { get; init; }
- public required double Speed { get; init; }
+ public required double Speed { get; set; }
- public required bool IsSmart { get; init; }
+ public required BulletStats Stats { get; init; }
}
diff --git a/tanks-backend/DisplayCommands/DisplayConfiguration.cs b/tanks-backend/TanksServer/Models/DisplayConfiguration.cs
similarity index 81%
rename from tanks-backend/DisplayCommands/DisplayConfiguration.cs
rename to tanks-backend/TanksServer/Models/DisplayConfiguration.cs
index ff3cf05..fc9e941 100644
--- a/tanks-backend/DisplayCommands/DisplayConfiguration.cs
+++ b/tanks-backend/TanksServer/Models/DisplayConfiguration.cs
@@ -1,8 +1,8 @@
-namespace DisplayCommands;
+namespace TanksServer.Models;
public class DisplayConfiguration
{
public string Hostname { get; set; } = "172.23.42.29";
public int Port { get; set; } = 2342;
-}
\ No newline at end of file
+}
diff --git a/tanks-backend/TanksServer/Models/Magazine.cs b/tanks-backend/TanksServer/Models/Magazine.cs
deleted file mode 100644
index febd5b8..0000000
--- a/tanks-backend/TanksServer/Models/Magazine.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System.Text;
-
-namespace TanksServer.Models;
-
-[Flags]
-internal enum MagazineType
-{
- Basic = 0,
- Fast = 1 << 0,
- Explosive = 1 << 1,
- Smart = 1 << 2,
-}
-
-internal readonly record struct Magazine(MagazineType Type, byte UsedBullets, byte MaxBullets)
-{
- public bool Empty => UsedBullets >= MaxBullets;
-
- public string ToDisplayString()
- {
- var sb = new StringBuilder();
-
- if (Type.HasFlag(MagazineType.Fast))
- sb.Append("» ");
- if (Type.HasFlag(MagazineType.Explosive))
- sb.Append("* ");
- if (Type.HasFlag(MagazineType.Smart))
- sb.Append("@ ");
-
- sb.Append("[ ");
- for (var i = 0; i < UsedBullets; i++)
- sb.Append("\u25cb ");
- for (var i = UsedBullets; i < MaxBullets; i++)
- sb.Append("• ");
- sb.Append(']');
-
- return sb.ToString();
- }
-}
diff --git a/tanks-backend/TanksServer/Models/PlayerInfo.cs b/tanks-backend/TanksServer/Models/PlayerInfo.cs
deleted file mode 100644
index a8545f3..0000000
--- a/tanks-backend/TanksServer/Models/PlayerInfo.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace TanksServer.Models;
-
-internal record struct TankInfo(
- int Orientation,
- string Magazine,
- PixelPosition Position,
- bool Moving
-);
-
-internal record struct PlayerInfo(
- string Name,
- Scores Scores,
- string Controls,
- TankInfo? Tank,
- int OpenConnections
-);
diff --git a/tanks-backend/TanksServer/Models/PowerUp.cs b/tanks-backend/TanksServer/Models/PowerUp.cs
index a8adb25..318a3a4 100644
--- a/tanks-backend/TanksServer/Models/PowerUp.cs
+++ b/tanks-backend/TanksServer/Models/PowerUp.cs
@@ -4,8 +4,11 @@ namespace TanksServer.Models;
internal enum PowerUpType
{
- MagazineType,
- MagazineSize
+ MagazineSize,
+ BulletSpeed,
+ BulletAcceleration,
+ ExplosiveBullets,
+ SmartBullets,
}
internal sealed class PowerUp: IMapEntity
@@ -15,6 +18,4 @@ internal sealed class PowerUp: IMapEntity
public PixelBounds Bounds => Position.GetBoundsForCenter(MapService.TileSize);
public required PowerUpType Type { get; init; }
-
- public MagazineType? MagazineType { get; init; }
}
diff --git a/tanks-backend/TanksServer/Models/Tank.cs b/tanks-backend/TanksServer/Models/Tank.cs
index e9de961..7147349 100644
--- a/tanks-backend/TanksServer/Models/Tank.cs
+++ b/tanks-backend/TanksServer/Models/Tank.cs
@@ -1,15 +1,16 @@
using System.Diagnostics;
+using System.Text.Json.Serialization;
using TanksServer.GameLogic;
namespace TanksServer.Models;
-internal sealed class Tank : IMapEntity
+internal sealed class Tank(Player owner, FloatPosition position) : IMapEntity
{
private double _rotation;
- public required Player Owner { get; init; }
+ [JsonIgnore] public Player Owner { get; } = owner;
- public double Rotation
+ [JsonIgnore] public double Rotation
{
get => _rotation;
set
@@ -24,13 +25,21 @@ internal sealed class Tank : IMapEntity
public bool Moving { get; set; }
- public required FloatPosition Position { get; set; }
+ [JsonIgnore] public FloatPosition Position { get; set; } = position;
- public PixelBounds Bounds => Position.GetBoundsForCenter(MapService.TileSize);
+ public PixelPosition PixelPosition => Position.ToPixelPosition();
+
+ [JsonIgnore] public PixelBounds Bounds => Position.GetBoundsForCenter(MapService.TileSize);
public int Orientation => (int)Math.Round(Rotation * 16) % 16;
- public required Magazine Magazine { get; set; }
+ public int UsedBullets { get; set; }
+
+ public int MaxBullets { get; set; }
public DateTime ReloadingUntil { get; set; }
+
+ public required BulletStats BulletStats { get; set; }
}
+
+internal sealed record class BulletStats(double Speed, double Acceleration, bool Explosive, bool Smart);
diff --git a/tanks-backend/TanksServer/Program.cs b/tanks-backend/TanksServer/Program.cs
index 91c14fe..89c71ae 100644
--- a/tanks-backend/TanksServer/Program.cs
+++ b/tanks-backend/TanksServer/Program.cs
@@ -1,9 +1,9 @@
using System.IO;
-using DisplayCommands;
using Microsoft.AspNetCore.Builder;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
+using ServicePoint2;
+using SixLabors.ImageSharp;
using TanksServer.GameLogic;
using TanksServer.Graphics;
using TanksServer.Interactivity;
@@ -54,11 +54,6 @@ public static class Program
var healthCheckBuilder = builder.Services.AddHealthChecks();
healthCheckBuilder.AddCheck("updates check");
- builder.Services.Configure(builder.Configuration.GetSection("Host"));
- var hostConfiguration = builder.Configuration.GetSection("Host").Get();
- if (hostConfiguration == null)
- throw new InvalidOperationException("'Host' configuration missing");
-
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
@@ -99,12 +94,15 @@ public static class Program
sp.GetRequiredService());
builder.Services.Configure(builder.Configuration.GetSection("GameRules"));
+ builder.Services.Configure(builder.Configuration.GetSection("Host"));
+ builder.Services.Configure(builder.Configuration.GetSection("ServicePointDisplay"));
- if (hostConfiguration.EnableServicePointDisplay)
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton(sp =>
{
- builder.Services.AddSingleton();
- builder.Services.AddDisplay(builder.Configuration.GetSection("ServicePointDisplay"));
- }
+ var config = sp.GetRequiredService>().Value;
+ return Connection.Open($"{config.Hostname}:{config.Port}");
+ });
var app = builder.Build();
diff --git a/tanks-backend/TanksServer/TanksServer.csproj b/tanks-backend/TanksServer/TanksServer.csproj
index 4cce028..aefb03e 100644
--- a/tanks-backend/TanksServer/TanksServer.csproj
+++ b/tanks-backend/TanksServer/TanksServer.csproj
@@ -1,20 +1,35 @@
-
+
+ net8.0
+ disable
+ enable
+
true
+ true
+ true
+
+
+
+ Recommended
+ true
+ CA1805,CA1848
-
+
+
+
+
diff --git a/tanks-backend/TanksServer/appsettings.json b/tanks-backend/TanksServer/appsettings.json
index 37f8c7b..4555c28 100644
--- a/tanks-backend/TanksServer/appsettings.json
+++ b/tanks-backend/TanksServer/appsettings.json
@@ -16,7 +16,8 @@
}
},
"ServicePointDisplay": {
- "Hostname": "172.23.42.29",
+ //"Hostname": "172.23.42.29",
+ "Hostname": "127.0.0.1",
"Port": 2342
},
"GameRules": {
@@ -33,7 +34,7 @@
"SmartBulletHomingSpeed": 1.5
},
"Host": {
- "EnableServicePointDisplay": false,
+ "EnableServicePointDisplay": true,
"ServicePointDisplayMinFrameTimeMs": 28,
"ClientScreenMinFrameTime": 5
}
diff --git a/tanks-backend/global.json b/tanks-backend/global.json
deleted file mode 100644
index 8eb62be..0000000
--- a/tanks-backend/global.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "sdk": {
- "version": "8.0.0",
- "rollForward": "latestMajor"
- }
-}
diff --git a/tanks-backend/servicepoint b/tanks-backend/servicepoint
new file mode 160000
index 0000000..eab2d58
--- /dev/null
+++ b/tanks-backend/servicepoint
@@ -0,0 +1 @@
+Subproject commit eab2d58945ebf68a4a6e8cf69cf113875fe6168d
diff --git a/tanks-backend/shared.props b/tanks-backend/shared.props
deleted file mode 100644
index 2417333..0000000
--- a/tanks-backend/shared.props
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- net8.0
- disable
- enable
-
-
-
- true
- true
-
-
-
- Recommended
- true
- CA1805,CA1848
-
-
-