wip client "secret"

This commit is contained in:
Vinzenz Schroeter 2024-04-13 18:35:36 +02:00
parent 698271ae9f
commit b192cd7da0
15 changed files with 117 additions and 71 deletions

View file

@ -22,8 +22,8 @@ internal sealed class CollideBulletsWithTanks(
continue; continue;
if (bullet.Owner != tank.Owner) if (bullet.Owner != tank.Owner)
bullet.Owner.Kills++; bullet.Owner.Scores.Kills++;
tank.Owner.Deaths++; tank.Owner.Scores.Deaths++;
tanks.Remove(tank); tanks.Remove(tank);
spawnQueue.EnqueueForDelayedSpawn(tank.Owner); spawnQueue.EnqueueForDelayedSpawn(tank.Owner);

View file

@ -36,7 +36,7 @@ internal sealed class MoveTanks(
return false; 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 newX = tank.Position.X + Math.Sin(angle) * speed;
var newY = tank.Position.Y - Math.Cos(angle) * speed; var newY = tank.Position.Y - Math.Cos(angle) * speed;

View file

@ -1,6 +1,10 @@
namespace TanksServer.GameLogic; namespace TanksServer.GameLogic;
internal sealed class RotateTanks(TankManager tanks, IOptions<TanksConfiguration> options) : ITickStep internal sealed class RotateTanks(
TankManager tanks,
IOptions<TanksConfiguration> options,
ILogger<RotateTanks> logger
) : ITickStep
{ {
private readonly TanksConfiguration _config = options.Value; private readonly TanksConfiguration _config = options.Value;
@ -10,10 +14,20 @@ internal sealed class RotateTanks(TankManager tanks, IOptions<TanksConfiguration
{ {
var player = tank.Owner; var player = tank.Owner;
if (player.Controls.TurnLeft) switch (player.Controls)
tank.Rotation -= _config.TurnSpeed / 16d; {
if (player.Controls.TurnRight) case { TurnRight: true, TurnLeft: true }:
tank.Rotation += _config.TurnSpeed / 16d; case { TurnRight: false, TurnLeft: false }:
continue;
case { TurnLeft: true }:
tank.Rotation -= _config.TurnSpeed;
break;
case { TurnRight: true }:
tank.Rotation += _config.TurnSpeed;
break;
}
logger.LogTrace("rotated tank to {}", tank.Rotation);
} }
return Task.CompletedTask; return Task.CompletedTask;

View file

@ -25,12 +25,13 @@ internal sealed class ShootFromTanks(
tank.NextShotAfter = DateTime.Now.AddMilliseconds(_config.ShootDelayMs); tank.NextShotAfter = DateTime.Now.AddMilliseconds(_config.ShootDelayMs);
var angle = tank.Rotation * 2 * Math.PI; var rotation = tank.Orientation / 16d;
var angle = rotation * 2d * Math.PI;
var position = new FloatPosition( var position = new FloatPosition(
tank.Position.X + Math.Sin(angle) * _config.BulletSpeed, tank.Position.X + Math.Sin(angle) * _config.BulletSpeed,
tank.Position.Y - Math.Cos(angle) * _config.BulletSpeed tank.Position.Y - Math.Cos(angle) * _config.BulletSpeed
); );
bulletManager.Spawn(new Bullet(tank.Owner, position, tank.Rotation)); bulletManager.Spawn(new Bullet(tank.Owner, position, rotation));
} }
} }

View file

@ -32,12 +32,11 @@ internal sealed class DrawTanksStep : IDrawStep
foreach (var tank in _tanks) foreach (var tank in _tanks)
{ {
var tankPosition = tank.Bounds.TopLeft; var tankPosition = tank.Bounds.TopLeft;
var orientation = (int)Math.Round(tank.Rotation * 16d) % 16;
for (byte dy = 0; dy < MapService.TileSize; dy++) for (byte dy = 0; dy < MapService.TileSize; dy++)
for (byte dx = 0; dx < MapService.TileSize; dx++) for (byte dx = 0; dx < MapService.TileSize; dx++)
{ {
if (!TankSpriteAt(dx, dy, orientation)) if (!TankSpriteAt(dx, dy, tank.Orientation))
continue; continue;
var (x, y) = tankPosition.GetPixelRelative(dx, dy); var (x, y) = tankPosition.GetPixelRelative(dx, dy);

View file

@ -3,4 +3,6 @@ using System.Text.Json.Serialization;
namespace TanksServer.Interactivity; namespace TanksServer.Interactivity;
[JsonSerializable(typeof(Player))] [JsonSerializable(typeof(Player))]
[JsonSerializable(typeof(IEnumerable<Player>))]
[JsonSerializable(typeof(Guid))]
internal sealed partial class AppSerializerContext : JsonSerializerContext; internal sealed partial class AppSerializerContext : JsonSerializerContext;

View file

@ -7,9 +7,19 @@ internal sealed class PlayerServer(ILogger<PlayerServer> logger, SpawnQueue spaw
{ {
private readonly ConcurrentDictionary<string, Player> _players = new(); private readonly ConcurrentDictionary<string, Player> _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); logger.LogInformation("player {} (re)joined", player.Id);
return player; return player;
} }
@ -29,11 +39,4 @@ internal sealed class PlayerServer(ILogger<PlayerServer> logger, SpawnQueue spaw
} }
public IEnumerable<Player> GetAll() => _players.Values; public IEnumerable<Player> GetAll() => _players.Values;
private Player AddAndSpawn(string name)
{
var player = new Player(name);
spawnQueue.EnqueueForImmediateSpawn(player);
return player;
}
} }

View file

@ -71,13 +71,13 @@ internal sealed class SendToServicePointDisplay : ITickStep
private void RefreshScores() private void RefreshScores()
{ {
var playersToDisplay = _players.GetAll() var playersToDisplay = _players.GetAll()
.OrderByDescending(p => p.Kills) .OrderByDescending(p => p.Scores.Kills)
.Take(ScoresPlayerRows); .Take(ScoresPlayerRows);
ushort row = 2; ushort row = 2;
foreach (var p in playersToDisplay) 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 nameLength = Math.Min(p.Name.Length, ScoresWidth - score.Length - 1);
var name = p.Name[..nameLength]; var name = p.Name[..nameLength];

View file

@ -2,17 +2,15 @@ using System.Text.Json.Serialization;
namespace TanksServer.Models; namespace TanksServer.Models;
internal sealed class Player(string name) internal sealed class Player(string name, Guid id)
{ {
public string Name => name; public string Name => name;
public Guid Id { get; } = Guid.NewGuid(); [JsonIgnore] public Guid Id => id;
[JsonIgnore] public PlayerControls Controls { get; } = new(); [JsonIgnore] public PlayerControls Controls { get; } = new();
public int Kills { get; set; } public Scores Scores { get; } = new();
public int Deaths { get; set; }
public DateTime LastInput { get; set; } = DateTime.Now; public DateTime LastInput { get; set; } = DateTime.Now;
} }

View file

@ -23,8 +23,8 @@ internal static class PositionHelpers
public static FloatPosition ToFloatPosition(this PixelPosition position) => new(position.X, position.Y); public static FloatPosition ToFloatPosition(this PixelPosition position) => new(position.X, position.Y);
public static double Distance(this FloatPosition p1, FloatPosition p2) => public static double Distance(this FloatPosition p1, FloatPosition p2)
Math.Sqrt( => Math.Sqrt(
Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.X - p2.X, 2) +
Math.Pow(p1.Y - p2.Y, 2) Math.Pow(p1.Y - p2.Y, 2)
); );

View file

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

View file

@ -1,5 +1,4 @@
using System.Diagnostics; using System.Diagnostics;
using TanksServer.GameLogic;
namespace TanksServer.Models; namespace TanksServer.Models;
@ -28,12 +27,14 @@ internal sealed class Tank(Player player, FloatPosition spawnPosition) : IMapEnt
public PixelBounds Bounds => GetBoundsForCenter(Position); public PixelBounds Bounds => GetBoundsForCenter(Position);
public int Orientation => (int)Math.Round(Rotation * 16) % 16;
public static PixelBounds GetBoundsForCenter(FloatPosition position) public static PixelBounds GetBoundsForCenter(FloatPosition position)
{ {
var pixelPosition = position.ToPixelPosition(); var pixelPosition = position.ToPixelPosition();
return new PixelBounds( return new PixelBounds(
pixelPosition.GetPixelRelative(-3, -3), pixelPosition.GetPixelRelative(-4, -4),
pixelPosition.GetPixelRelative(4, 4) pixelPosition.GetPixelRelative(3, 3)
); );
} }
} }

View file

@ -25,18 +25,29 @@ public static class Program
app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = clientFileProvider }); app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = clientFileProvider });
app.UseStaticFiles(new StaticFileOptions { 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) if (!context.WebSockets.IsWebSocketRequest)
{ return Results.BadRequest();
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
using var ws = await context.WebSockets.AcceptWebSocketAsync(); using var ws = await context.WebSockets.AcceptWebSocketAsync();
await clientScreenServer.HandleClient(ws); await clientScreenServer.HandleClient(ws);
return null;
}); });
app.Map("/controls", async (HttpContext context, [FromQuery] Guid playerId) => app.Map("/controls", async (HttpContext context, [FromQuery] Guid playerId) =>
@ -49,7 +60,7 @@ public static class Program
using var ws = await context.WebSockets.AcceptWebSocketAsync(); using var ws = await context.WebSockets.AcceptWebSocketAsync();
await controlsServer.HandleClient(ws, player); await controlsServer.HandleClient(ws, player);
return Results.Ok(); return null;
}); });
app.Run(); app.Run();

View file

@ -1,33 +1,34 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Information", "Default": "Information",
"Microsoft.AspNetCore": "Warning", "Microsoft.AspNetCore": "Warning",
"TanksServer": "Debug", "TanksServer": "Debug",
"Microsoft.AspNetCore.HttpLogging": "Information" "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
}
} }

View file

@ -0,0 +1,8 @@
POST localhost/player?name=test&id=a31d35c2-1d9e-4a34-93e5-680195c6a9d2
###
GET localhost/scores
###