diff --git a/TanksServer/GameLogic/CollideBulletsWithTanks.cs b/TanksServer/GameLogic/CollideBulletsWithTanks.cs index b62ac95..c32d4d0 100644 --- a/TanksServer/GameLogic/CollideBulletsWithTanks.cs +++ b/TanksServer/GameLogic/CollideBulletsWithTanks.cs @@ -22,8 +22,8 @@ internal sealed class CollideBulletsWithTanks( continue; if (bullet.Owner != tank.Owner) - bullet.Owner.Kills++; - tank.Owner.Deaths++; + bullet.Owner.Scores.Kills++; + tank.Owner.Scores.Deaths++; tanks.Remove(tank); spawnQueue.EnqueueForDelayedSpawn(tank.Owner); diff --git a/TanksServer/GameLogic/MoveTanks.cs b/TanksServer/GameLogic/MoveTanks.cs index c77c0d1..0bd0656 100644 --- a/TanksServer/GameLogic/MoveTanks.cs +++ b/TanksServer/GameLogic/MoveTanks.cs @@ -36,7 +36,7 @@ internal sealed class MoveTanks( return false; } - var angle = tank.Rotation * 2d * Math.PI; + var angle = tank.Orientation / 16d * 2d * Math.PI; var newX = tank.Position.X + Math.Sin(angle) * speed; var newY = tank.Position.Y - Math.Cos(angle) * speed; diff --git a/TanksServer/GameLogic/RotateTanks.cs b/TanksServer/GameLogic/RotateTanks.cs index 168f86c..316700f 100644 --- a/TanksServer/GameLogic/RotateTanks.cs +++ b/TanksServer/GameLogic/RotateTanks.cs @@ -1,6 +1,10 @@ namespace TanksServer.GameLogic; -internal sealed class RotateTanks(TankManager tanks, IOptions options) : ITickStep +internal sealed class RotateTanks( + TankManager tanks, + IOptions options, + ILogger logger +) : ITickStep { private readonly TanksConfiguration _config = options.Value; @@ -10,10 +14,20 @@ internal sealed class RotateTanks(TankManager tanks, IOptions))] +[JsonSerializable(typeof(Guid))] internal sealed partial class AppSerializerContext : JsonSerializerContext; diff --git a/TanksServer/Interactivity/PlayerServer.cs b/TanksServer/Interactivity/PlayerServer.cs index 3a3c8a1..26f33be 100644 --- a/TanksServer/Interactivity/PlayerServer.cs +++ b/TanksServer/Interactivity/PlayerServer.cs @@ -7,9 +7,19 @@ internal sealed class PlayerServer(ILogger logger, SpawnQueue spaw { private readonly ConcurrentDictionary _players = new(); - public Player GetOrAdd(string name) + public Player? GetOrAdd(string name, Guid id) { - var player = _players.GetOrAdd(name, AddAndSpawn); + Player AddAndSpawn() + { + var player = new Player(name, id); + spawnQueue.EnqueueForImmediateSpawn(player); + return player; + } + + var player = _players.GetOrAdd(name, _ => AddAndSpawn()); + if (player.Id != id) + return null; + logger.LogInformation("player {} (re)joined", player.Id); return player; } @@ -29,11 +39,4 @@ internal sealed class PlayerServer(ILogger logger, SpawnQueue spaw } public IEnumerable GetAll() => _players.Values; - - private Player AddAndSpawn(string name) - { - var player = new Player(name); - spawnQueue.EnqueueForImmediateSpawn(player); - return player; - } } diff --git a/TanksServer/Interactivity/SendToServicePointDisplay.cs b/TanksServer/Interactivity/SendToServicePointDisplay.cs index 7ba5c1b..5466c44 100644 --- a/TanksServer/Interactivity/SendToServicePointDisplay.cs +++ b/TanksServer/Interactivity/SendToServicePointDisplay.cs @@ -71,13 +71,13 @@ internal sealed class SendToServicePointDisplay : ITickStep private void RefreshScores() { var playersToDisplay = _players.GetAll() - .OrderByDescending(p => p.Kills) + .OrderByDescending(p => p.Scores.Kills) .Take(ScoresPlayerRows); ushort row = 2; foreach (var p in playersToDisplay) { - var score = p.Kills.ToString(); + var score = p.Scores.Kills.ToString(); var nameLength = Math.Min(p.Name.Length, ScoresWidth - score.Length - 1); var name = p.Name[..nameLength]; diff --git a/TanksServer/Models/Player.cs b/TanksServer/Models/Player.cs index cf8cb88..b6a96e5 100644 --- a/TanksServer/Models/Player.cs +++ b/TanksServer/Models/Player.cs @@ -2,17 +2,15 @@ using System.Text.Json.Serialization; namespace TanksServer.Models; -internal sealed class Player(string name) +internal sealed class Player(string name, Guid id) { public string Name => name; - public Guid Id { get; } = Guid.NewGuid(); + [JsonIgnore] public Guid Id => id; [JsonIgnore] public PlayerControls Controls { get; } = new(); - public int Kills { get; set; } - - public int Deaths { get; set; } + public Scores Scores { get; } = new(); public DateTime LastInput { get; set; } = DateTime.Now; } diff --git a/TanksServer/Models/PositionHelpers.cs b/TanksServer/Models/PositionHelpers.cs index baa819f..c5ef45d 100644 --- a/TanksServer/Models/PositionHelpers.cs +++ b/TanksServer/Models/PositionHelpers.cs @@ -23,8 +23,8 @@ internal static class PositionHelpers public static FloatPosition ToFloatPosition(this PixelPosition position) => new(position.X, position.Y); - public static double Distance(this FloatPosition p1, FloatPosition p2) => - Math.Sqrt( + public static double Distance(this FloatPosition p1, FloatPosition p2) + => Math.Sqrt( Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2) ); diff --git a/TanksServer/Models/Scores.cs b/TanksServer/Models/Scores.cs new file mode 100644 index 0000000..65c29c1 --- /dev/null +++ b/TanksServer/Models/Scores.cs @@ -0,0 +1,8 @@ +namespace TanksServer.Models; + +internal sealed record class Scores(int Kills = 0, int Deaths = 0) +{ + public int Kills { get; set; } = Kills; + + public int Deaths { get; set; } = Deaths; +} diff --git a/TanksServer/Models/Tank.cs b/TanksServer/Models/Tank.cs index 6072659..2e2b220 100644 --- a/TanksServer/Models/Tank.cs +++ b/TanksServer/Models/Tank.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using TanksServer.GameLogic; namespace TanksServer.Models; @@ -28,12 +27,14 @@ internal sealed class Tank(Player player, FloatPosition spawnPosition) : IMapEnt public PixelBounds Bounds => GetBoundsForCenter(Position); + public int Orientation => (int)Math.Round(Rotation * 16) % 16; + public static PixelBounds GetBoundsForCenter(FloatPosition position) { var pixelPosition = position.ToPixelPosition(); return new PixelBounds( - pixelPosition.GetPixelRelative(-3, -3), - pixelPosition.GetPixelRelative(4, 4) + pixelPosition.GetPixelRelative(-4, -4), + pixelPosition.GetPixelRelative(3, 3) ); } } diff --git a/TanksServer/Program.cs b/TanksServer/Program.cs index d0a846d..ee0ebf6 100644 --- a/TanksServer/Program.cs +++ b/TanksServer/Program.cs @@ -25,18 +25,29 @@ public static class Program app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = clientFileProvider }); app.UseStaticFiles(new StaticFileOptions { FileProvider = clientFileProvider }); - app.MapGet("/player", playerService.GetOrAdd); + app.MapPost("/player", (string name, Guid id) => + { + var player = playerService.GetOrAdd(name, id); + return player != null + ? Results.Ok(player.Id) + : Results.Unauthorized(); + }); + app.MapGet("/player", ([FromQuery] Guid id) => + playerService.TryGet(id, out var foundPlayer) + ? Results.Ok((object?)foundPlayer) + : Results.NotFound() + ); - app.Map("/screen", async context => + app.MapGet("/scores", () => playerService.GetAll()); + + app.Map("/screen", async (HttpContext context) => { if (!context.WebSockets.IsWebSocketRequest) - { - context.Response.StatusCode = StatusCodes.Status400BadRequest; - return; - } + return Results.BadRequest(); using var ws = await context.WebSockets.AcceptWebSocketAsync(); await clientScreenServer.HandleClient(ws); + return null; }); app.Map("/controls", async (HttpContext context, [FromQuery] Guid playerId) => @@ -49,7 +60,7 @@ public static class Program using var ws = await context.WebSockets.AcceptWebSocketAsync(); await controlsServer.HandleClient(ws, player); - return Results.Ok(); + return null; }); app.Run(); @@ -122,4 +133,4 @@ public static class Program return app; } -} \ No newline at end of file +} diff --git a/TanksServer/appsettings.json b/TanksServer/appsettings.json index f346790..2d31e8b 100644 --- a/TanksServer/appsettings.json +++ b/TanksServer/appsettings.json @@ -1,33 +1,34 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "TanksServer": "Debug", - "Microsoft.AspNetCore.HttpLogging": "Information" + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "TanksServer": "Debug", + "Microsoft.AspNetCore.HttpLogging": "Information", + "TanksServer.GameLogic.RotateTanks": "Trace" + } + }, + "AllowedHosts": "*", + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://localhost:3000" + } + } + }, + "ServicePointDisplay": { + "Enable": true, + "Hostname": "172.23.42.29", + "Port": 2342 + }, + "Tanks": { + "MoveSpeed": 1.5, + "TurnSpeed": 0.02, + "ShootDelayMs": 450, + "BulletSpeed":4 + }, + "Players": { + "SpawnDelayMs": 3000, + "IdleTimeoutMs": 30000 } - }, - "AllowedHosts": "*", - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://localhost:3000" - } - } - }, - "ServicePointDisplay": { - "Enable": true, - "Hostname": "172.23.42.29", - "Port": 2342 - }, - "Tanks": { - "MoveSpeed": 1.4, - "TurnSpeed": 0.4, - "ShootDelayMs": 400, - "BulletSpeed": 3 - }, - "Players": { - "SpawnDelayMs": 3000, - "IdleTimeoutMs": 30000 - } } diff --git a/TanksServer/userRequests.http b/TanksServer/userRequests.http new file mode 100644 index 0000000..7a21d3e --- /dev/null +++ b/TanksServer/userRequests.http @@ -0,0 +1,8 @@ +POST localhost/player?name=test&id=a31d35c2-1d9e-4a34-93e5-680195c6a9d2 + +### + +GET localhost/scores + +### +