wip move to new uniffi language binding
This commit is contained in:
		
							parent
							
								
									f7a5d8f823
								
							
						
					
					
						commit
						53cbdd8440
					
				
					 30 changed files with 211 additions and 187 deletions
				
			
		|  | @ -1,5 +1,6 @@ | |||
|  | ||||
| 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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{12DB7D48-1BB2-488B-B4D9-4126087D2F8C}" | ||||
|  | @ -9,7 +10,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{12DB7D | |||
| 		..\.envrc = ..\.envrc | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "servicepoint/crates/servicepoint_binding_cs/ServicePoint/ServicePoint.csproj", "{DFCC69ED-E02B-4631-8A23-5D394BA01E03}" | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "servicepoint", "servicepoint", "{A10FB29A-9078-4E90-9CE1-E6C2B5209E19}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "crates", "crates", "{656A7CBA-9445-41CC-B1AF-A6897AAC9F17}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "servicepoint_binding_uniffi", "servicepoint_binding_uniffi", "{5FD9FAD7-07BA-4DF9-8C84-15A9558373F1}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libraries", "libraries", "{6082A0DC-5345-48C8-BA2E-667754A2F0E9}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "csharp", "csharp", "{D8A3290B-5DFB-43C6-99EE-56AB5F53F468}" | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "servicepoint\crates\servicepoint_binding_uniffi\libraries\csharp\ServicePoint\ServicePoint.csproj", "{D1DDCD0D-6152-45E6-B673-DD78C466BDC3}" | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint.Tests", "servicepoint\crates\servicepoint_binding_uniffi\libraries\csharp\ServicePoint.Tests\ServicePoint.Tests.csproj", "{EF42D6B7-70B1-490B-BB5F-5A44D1309A7C}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
|  | @ -21,9 +34,21 @@ 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 | ||||
| 		{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 | ||||
| 		{D1DDCD0D-6152-45E6-B673-DD78C466BDC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{D1DDCD0D-6152-45E6-B673-DD78C466BDC3}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{D1DDCD0D-6152-45E6-B673-DD78C466BDC3}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{D1DDCD0D-6152-45E6-B673-DD78C466BDC3}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{EF42D6B7-70B1-490B-BB5F-5A44D1309A7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{EF42D6B7-70B1-490B-BB5F-5A44D1309A7C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{EF42D6B7-70B1-490B-BB5F-5A44D1309A7C}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{EF42D6B7-70B1-490B-BB5F-5A44D1309A7C}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(NestedProjects) = preSolution | ||||
| 		{656A7CBA-9445-41CC-B1AF-A6897AAC9F17} = {A10FB29A-9078-4E90-9CE1-E6C2B5209E19} | ||||
| 		{5FD9FAD7-07BA-4DF9-8C84-15A9558373F1} = {656A7CBA-9445-41CC-B1AF-A6897AAC9F17} | ||||
| 		{6082A0DC-5345-48C8-BA2E-667754A2F0E9} = {5FD9FAD7-07BA-4DF9-8C84-15A9558373F1} | ||||
| 		{D8A3290B-5DFB-43C6-99EE-56AB5F53F468} = {6082A0DC-5345-48C8-BA2E-667754A2F0E9} | ||||
| 		{D1DDCD0D-6152-45E6-B673-DD78C466BDC3} = {D8A3290B-5DFB-43C6-99EE-56AB5F53F468} | ||||
| 		{EF42D6B7-70B1-490B-BB5F-5A44D1309A7C} = {D8A3290B-5DFB-43C6-99EE-56AB5F53F468} | ||||
| 	EndGlobalSection | ||||
| EndGlobal | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.IO; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | @ -7,7 +8,6 @@ using Microsoft.AspNetCore.Http; | |||
| using Microsoft.AspNetCore.Http.HttpResults; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.Extensions.Diagnostics.HealthChecks; | ||||
| using ServicePoint; | ||||
| using TanksServer.GameLogic; | ||||
| using TanksServer.Interactivity; | ||||
| 
 | ||||
|  | @ -22,6 +22,8 @@ internal sealed class Endpoints( | |||
|     Connection displayConnection | ||||
| ) | ||||
| { | ||||
|     [RequiresUnreferencedCode("Calls Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapPost(String, Delegate)")] | ||||
|     [RequiresDynamicCode("Calls Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapPost(String, Delegate)")] | ||||
|     public void Map(WebApplication app) | ||||
|     { | ||||
|         app.MapPost("/player", PostPlayer); | ||||
|  | @ -31,7 +33,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.MapPost("/resetDisplay", () => displayConnection.Send(Command.HardReset())); | ||||
|         app.MapGet("/map/{name}", GetMapByName); | ||||
| 
 | ||||
|         app.MapHealthChecks("/health", new HealthCheckOptions | ||||
|  | @ -117,7 +119,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.ToArray()); | ||||
|         var mapInfo = new MapInfo(prototype.Name, prototype.GetType().Name, preview.CopyRaw()); | ||||
|         return TypedResults.Ok(mapInfo); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -88,13 +88,13 @@ internal sealed class CollideBullets : ITickStep | |||
| 
 | ||||
|         pixel = pixel.GetPixelRelative(-4, -4); | ||||
|         for (short dx = 0; dx < _explosiveSprite.Width; dx++) | ||||
|         for (short dy = 0; dy < _explosiveSprite.Height; dy++) | ||||
|         { | ||||
|             if (!_explosiveSprite[dx, dy].HasValue) | ||||
|                 continue; | ||||
|             for (short dy = 0; dy < _explosiveSprite.Height; dy++) | ||||
|             { | ||||
|                 if (!_explosiveSprite[dx, dy].HasValue) | ||||
|                     continue; | ||||
| 
 | ||||
|             Core(pixel.GetPixelRelative(dx, dy)); | ||||
|         } | ||||
|                 Core(pixel.GetPixelRelative(dx, dy)); | ||||
|             } | ||||
| 
 | ||||
|         return; | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,22 +10,22 @@ internal sealed class EmptyTileFinder( | |||
|         var maxMinDistance = 0d; | ||||
|         TilePosition spawnTile = default; | ||||
|         for (ushort x = 1; x < MapService.TilesPerRow - 1; x++) | ||||
|         for (ushort y = 1; y < MapService.TilesPerColumn - 1; y++) | ||||
|         { | ||||
|             var tile = new TilePosition(x, y); | ||||
|             if (mapService.Current.IsWall(tile)) | ||||
|                 continue; | ||||
|             for (ushort y = 1; y < MapService.TilesPerColumn - 1; y++) | ||||
|             { | ||||
|                 var tile = new TilePosition(x, y); | ||||
|                 if (mapService.Current.IsWall(tile)) | ||||
|                     continue; | ||||
| 
 | ||||
|             var tilePixelCenter = tile.GetCenter().ToFloatPosition(); | ||||
|             var minDistance = entityManager.AllEntities | ||||
|                 .Select(entity => entity.Position.Distance(tilePixelCenter)) | ||||
|                 .Aggregate(double.MaxValue, Math.Min); | ||||
|             if (minDistance <= maxMinDistance) | ||||
|                 continue; | ||||
|                 var tilePixelCenter = tile.GetCenter().ToFloatPosition(); | ||||
|                 var minDistance = entityManager.AllEntities | ||||
|                     .Select(entity => entity.Position.Distance(tilePixelCenter)) | ||||
|                     .Aggregate(double.MaxValue, Math.Min); | ||||
|                 if (minDistance <= maxMinDistance) | ||||
|                     continue; | ||||
| 
 | ||||
|             maxMinDistance = minDistance; | ||||
|             spawnTile = tile; | ||||
|         } | ||||
|                 maxMinDistance = minDistance; | ||||
|                 spawnTile = tile; | ||||
|             } | ||||
| 
 | ||||
|         return spawnTile; | ||||
|     } | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ internal sealed class Map(string name, bool[,] walls) | |||
| { | ||||
|     public string Name => name; | ||||
| 
 | ||||
|     public bool IsWall(int x, int y) => walls[x, y]; | ||||
|     public bool IsWall(ulong x, ulong y) => walls[x, y]; | ||||
| 
 | ||||
|     public bool IsWall(PixelPosition position) => walls[position.X, position.Y]; | ||||
| 
 | ||||
|  | @ -12,12 +12,12 @@ internal sealed class Map(string name, bool[,] walls) | |||
|     { | ||||
|         var pixel = position.ToPixelPosition(); | ||||
| 
 | ||||
|         for (short dx = 0; dx < MapService.TileSize; dx++) | ||||
|         for (short dy = 0; dy < MapService.TileSize; dy++) | ||||
|         { | ||||
|             if (IsWall(pixel.GetPixelRelative(dx, dy))) | ||||
|                 return true; | ||||
|         } | ||||
|         for (long dx = 0; dx < (long)MapService.TileSize; dx++) | ||||
|             for (long dy = 0; dy < (long)MapService.TileSize; dy++) | ||||
|             { | ||||
|                 if (IsWall(pixel.GetPixelRelative(dx, dy))) | ||||
|                     return true; | ||||
|             } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ internal sealed class MapEntityManager( | |||
|         { | ||||
|             Rotation = Random.Shared.NextDouble(), | ||||
|             MaxBullets = _rules.MagazineSize, | ||||
|             BulletStats =new BulletStats(_rules.BulletSpeed, 0, false, false) | ||||
|             BulletStats = new BulletStats(_rules.BulletSpeed, 0, false, false) | ||||
|         }; | ||||
|         _playerTanks[player] = tank; | ||||
|         logger.LogInformation("Tank added for player {}", player.Name); | ||||
|  |  | |||
|  | @ -1,18 +1,17 @@ | |||
| using System.Diagnostics; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.IO; | ||||
| using ServicePoint; | ||||
| using TanksServer.Graphics; | ||||
| 
 | ||||
| namespace TanksServer.GameLogic; | ||||
| 
 | ||||
| internal sealed class MapService | ||||
| { | ||||
|     public const ushort TilesPerRow = 44; | ||||
|     public const ushort TilesPerColumn = 20; | ||||
|     public const ushort TileSize = 8; | ||||
|     public const ushort PixelsPerRow = TilesPerRow * TileSize; | ||||
|     public const ushort PixelsPerColumn = TilesPerColumn * TileSize; | ||||
|     public const ulong TilesPerRow = 44; | ||||
|     public const ulong TilesPerColumn = 20; | ||||
|     public const ulong TileSize = 8; | ||||
|     public const ulong PixelsPerRow = TilesPerRow * TileSize; | ||||
|     public const ulong PixelsPerColumn = TilesPerColumn * TileSize; | ||||
| 
 | ||||
|     private readonly ConcurrentDictionary<string, MapPrototype> _mapPrototypes = new(); | ||||
|     private readonly ConcurrentDictionary<string, Bitmap> _mapPreviews = new(); | ||||
|  |  | |||
|  | @ -68,13 +68,13 @@ internal sealed class MoveTanks( | |||
|     { | ||||
|         var (topLeft, _) = newPosition.GetBoundsForCenter(MapService.TileSize); | ||||
| 
 | ||||
|         for (short y = 0; y < MapService.TileSize; y++) | ||||
|         for (short x = 0; x < MapService.TileSize; x++) | ||||
|         { | ||||
|             var pixelToCheck = topLeft.GetPixelRelative(x, y); | ||||
|             if (map.Current.IsWall(pixelToCheck)) | ||||
|                 return true; | ||||
|         } | ||||
|         for (ulong y = 0; y < MapService.TileSize; y++) | ||||
|             for (ulong x = 0; x < MapService.TileSize; x++) | ||||
|             { | ||||
|                 var pixelToCheck = topLeft.GetPixelRelative((long)x, (long)y); | ||||
|                 if (map.Current.IsWall(pixelToCheck)) | ||||
|                     return true; | ||||
|             } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ internal sealed class SpriteMapPrototype : MapPrototype | |||
| 
 | ||||
|     public SpriteMapPrototype(string name, Sprite sprite) | ||||
|     { | ||||
|         if (sprite.Width != MapService.PixelsPerRow || sprite.Height != MapService.PixelsPerColumn) | ||||
|         if ((ulong)sprite.Width != MapService.PixelsPerRow || (ulong)sprite.Height != MapService.PixelsPerColumn) | ||||
|             throw new FileLoadException($"invalid image size in file {Name}"); | ||||
| 
 | ||||
|         Name = name; | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ internal sealed class TextMapPrototype : MapPrototype | |||
| 
 | ||||
|     public TextMapPrototype(string name, string text) | ||||
|     { | ||||
|         if (text.Length != MapService.TilesPerColumn * MapService.TilesPerRow) | ||||
|         if ((ulong)text.Length != MapService.TilesPerColumn * MapService.TilesPerRow) | ||||
|             throw new ArgumentException($"cannot load map {name}: invalid length"); | ||||
|         Name = name; | ||||
|         Text = text; | ||||
|  | @ -19,20 +19,20 @@ internal sealed class TextMapPrototype : MapPrototype | |||
|     { | ||||
|         var walls = new bool[MapService.PixelsPerRow, MapService.PixelsPerColumn]; | ||||
| 
 | ||||
|         for (ushort tileX = 0; tileX < MapService.TilesPerRow; tileX++) | ||||
|         for (ushort tileY = 0; tileY < MapService.TilesPerColumn; tileY++) | ||||
|         { | ||||
|             var tile = new TilePosition(tileX, tileY); | ||||
|             if (Text[tileX + tileY * MapService.TilesPerRow] != '#') | ||||
|                 continue; | ||||
| 
 | ||||
|             for (byte pixelInTileX = 0; pixelInTileX < MapService.TileSize; pixelInTileX++) | ||||
|             for (byte pixelInTileY = 0; pixelInTileY < MapService.TileSize; pixelInTileY++) | ||||
|         for (ulong tileX = 0; tileX < MapService.TilesPerRow; tileX++) | ||||
|             for (ulong tileY = 0; tileY < MapService.TilesPerColumn; tileY++) | ||||
|             { | ||||
|                 var (x, y) = tile.ToPixelPosition().GetPixelRelative(pixelInTileX, pixelInTileY); | ||||
|                 walls[x, y] = true; | ||||
|                 var tile = new TilePosition(tileX, tileY); | ||||
|                 if (Text[(int)(tileX + tileY * MapService.TilesPerRow)] != '#') | ||||
|                     continue; | ||||
| 
 | ||||
|                 for (byte pixelInTileX = 0; pixelInTileX < MapService.TileSize; pixelInTileX++) | ||||
|                     for (byte pixelInTileY = 0; pixelInTileY < MapService.TileSize; pixelInTileY++) | ||||
|                     { | ||||
|                         var (x, y) = tile.ToPixelPosition().GetPixelRelative(pixelInTileX, pixelInTileY); | ||||
|                         walls[x, y] = true; | ||||
|                     } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return new Map(Name, walls); | ||||
|     } | ||||
|  |  | |||
|  | @ -7,3 +7,4 @@ global using System.Threading.Tasks; | |||
| global using Microsoft.Extensions.Logging; | ||||
| global using Microsoft.Extensions.Options; | ||||
| global using TanksServer.Models; | ||||
| global using ServicePoint; | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| using ServicePoint; | ||||
| using TanksServer.GameLogic; | ||||
| 
 | ||||
| namespace TanksServer.Graphics; | ||||
|  | @ -9,24 +8,24 @@ internal sealed class DrawMapStep(MapService map) : IDrawStep | |||
| 
 | ||||
|     private static void Draw(GamePixelGrid pixels, Map map) | ||||
|     { | ||||
|         for (ushort y = 0; y < MapService.PixelsPerColumn; y++) | ||||
|         for (ushort x = 0; x < MapService.PixelsPerRow; x++) | ||||
|         { | ||||
|             if (!map.IsWall(x, y)) | ||||
|                 continue; | ||||
|         for (ulong y = 0; y < MapService.PixelsPerColumn; y++) | ||||
|             for (ulong x = 0; x < MapService.PixelsPerRow; x++) | ||||
|             { | ||||
|                 if (!map.IsWall(x, y)) | ||||
|                     continue; | ||||
| 
 | ||||
|             pixels[x, y].EntityType = GamePixelEntityType.Wall; | ||||
|         } | ||||
|                 pixels[x, y].EntityType = GamePixelEntityType.Wall; | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     public static void Draw(Bitmap pixels, Map map) | ||||
|     { | ||||
|         for (ushort y = 0; y < MapService.PixelsPerColumn; y++) | ||||
|         for (ushort x = 0; x < MapService.PixelsPerRow; x++) | ||||
|         { | ||||
|             if (!map.IsWall(x, y)) | ||||
|                 continue; | ||||
|             pixels[x, y] = true; | ||||
|         } | ||||
|         for (ulong y = 0; y < MapService.PixelsPerColumn; y++) | ||||
|             for (ulong x = 0; x < MapService.PixelsPerRow; x++) | ||||
|             { | ||||
|                 if (!map.IsWall(x, y)) | ||||
|                     continue; | ||||
|                 pixels.Set(x, y, true); | ||||
|             } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -30,16 +30,16 @@ internal sealed class DrawPowerUpsStep(MapEntityManager entityManager) : IDrawSt | |||
|     private static void DrawPowerUp(GamePixelGrid pixels, Sprite sprite, PixelPosition position) | ||||
|     { | ||||
|         for (byte dy = 0; dy < MapService.TileSize; dy++) | ||||
|         for (byte dx = 0; dx < MapService.TileSize; dx++) | ||||
|         { | ||||
|             var pixelState = sprite[dx, dy]; | ||||
|             if (!pixelState.HasValue) | ||||
|                 continue; | ||||
|             for (byte dx = 0; dx < MapService.TileSize; dx++) | ||||
|             { | ||||
|                 var pixelState = sprite[dx, dy]; | ||||
|                 if (!pixelState.HasValue) | ||||
|                     continue; | ||||
| 
 | ||||
|             var (x, y) = position.GetPixelRelative(dx, dy); | ||||
|             pixels[x, y].EntityType = pixelState.Value | ||||
|                 ? GamePixelEntityType.PowerUp | ||||
|                 : null; | ||||
|         } | ||||
|                 var (x, y) = position.GetPixelRelative(dx, dy); | ||||
|                 pixels[x, y].EntityType = pixelState.Value | ||||
|                     ? GamePixelEntityType.PowerUp | ||||
|                     : null; | ||||
|             } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ namespace TanksServer.Graphics; | |||
| internal sealed class DrawTanksStep(MapEntityManager entityManager) : IDrawStep | ||||
| { | ||||
|     private readonly SpriteSheet _tankSprites = | ||||
|         SpriteSheet.FromImageFile("assets/tank.png", MapService.TileSize, MapService.TileSize); | ||||
|         SpriteSheet.FromImageFile("assets/tank.png", (int)MapService.TileSize, (int)MapService.TileSize); | ||||
| 
 | ||||
|     public void Draw(GamePixelGrid pixels) | ||||
|     { | ||||
|  | @ -14,16 +14,16 @@ internal sealed class DrawTanksStep(MapEntityManager entityManager) : IDrawStep | |||
|             var tankPosition = tank.Bounds.TopLeft; | ||||
| 
 | ||||
|             for (byte dy = 0; dy < MapService.TileSize; dy++) | ||||
|             for (byte dx = 0; dx < MapService.TileSize; dx++) | ||||
|             { | ||||
|                 var pixel = _tankSprites[tank.Orientation][dx, dy]; | ||||
|                 if (!pixel.HasValue || !pixel.Value) | ||||
|                     continue; | ||||
|                 for (byte dx = 0; dx < MapService.TileSize; dx++) | ||||
|                 { | ||||
|                     var pixel = _tankSprites[tank.Orientation][dx, dy]; | ||||
|                     if (!pixel.HasValue || !pixel.Value) | ||||
|                         continue; | ||||
| 
 | ||||
|                 var (x, y) = tankPosition.GetPixelRelative(dx, dy); | ||||
|                 pixels[x, y].EntityType = GamePixelEntityType.Tank; | ||||
|                 pixels[x, y].BelongsTo = tank.Owner; | ||||
|             } | ||||
|                     var (x, y) = tankPosition.GetPixelRelative(dx, dy); | ||||
|                     pixels[x, y].EntityType = GamePixelEntityType.Tank; | ||||
|                     pixels[x, y].BelongsTo = tank.Owner; | ||||
|                 } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,27 +5,27 @@ namespace TanksServer.Graphics; | |||
| 
 | ||||
| internal sealed class GamePixelGrid : IEnumerable<GamePixel> | ||||
| { | ||||
|     public int Width { get; } | ||||
|     public int Height { get; } | ||||
|     public ulong Width { get; } | ||||
|     public ulong Height { get; } | ||||
| 
 | ||||
|     private readonly GamePixel[,] _pixels; | ||||
| 
 | ||||
|     public GamePixelGrid(int width, int height) | ||||
|     public GamePixelGrid(ulong width, ulong height) | ||||
|     { | ||||
|         Width = width; | ||||
|         Height = height; | ||||
| 
 | ||||
|         _pixels = new GamePixel[width, height]; | ||||
|         for (var y = 0; y < height; y++) | ||||
|         for (var x = 0; x < width; x++) | ||||
|             this[x, y] = new GamePixel(); | ||||
|         for (ulong y = 0; y < height; y++) | ||||
|             for (ulong x = 0; x < width; x++) | ||||
|                 this[x, y] = new GamePixel(); | ||||
|     } | ||||
| 
 | ||||
|     public GamePixel this[int x, int y] | ||||
|     public GamePixel this[ulong x, ulong y] | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             Debug.Assert(y * Width + x < _pixels.Length); | ||||
|             Debug.Assert(y * Width + x < (ulong)_pixels.Length); | ||||
|             return _pixels[x, y]; | ||||
|         } | ||||
|         set => _pixels[x, y] = value; | ||||
|  | @ -41,8 +41,8 @@ internal sealed class GamePixelGrid : IEnumerable<GamePixel> | |||
| 
 | ||||
|     public IEnumerator<GamePixel> GetEnumerator() | ||||
|     { | ||||
|         for (var y = 0; y < Height; y++) | ||||
|         for (var x = 0; x < Width; x++) | ||||
|             yield return this[x, y]; | ||||
|         for (ulong y = 0; y < Height; y++) | ||||
|             for (ulong x = 0; x < Width; x++) | ||||
|                 yield return this[x, y]; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ internal sealed class GeneratePixelsTickStep( | |||
|     public async ValueTask TickAsync(TimeSpan _) | ||||
|     { | ||||
|         Draw(_gamePixelGrid, _observerPixelGrid); | ||||
|         if (_observerPixelGrid.Data.SequenceEqual(_lastObserverPixelGrid.Data)) | ||||
|         if (_observerPixelGrid.Equals(_lastObserverPixelGrid)) | ||||
|             return; | ||||
| 
 | ||||
|         await _consumers.Select(c => c.OnFrameDoneAsync(_gamePixelGrid, _observerPixelGrid)) | ||||
|  | @ -37,12 +37,12 @@ internal sealed class GeneratePixelsTickStep( | |||
|             step.Draw(gamePixelGrid); | ||||
| 
 | ||||
|         observerPixelGrid.Fill(false); | ||||
|         for (var y = 0; y < MapService.PixelsPerColumn; y++) | ||||
|         for (var x = 0; x < MapService.PixelsPerRow; x++) | ||||
|         { | ||||
|             if (gamePixelGrid[x, y].EntityType.HasValue) | ||||
|                 observerPixelGrid[(ushort)x, (ushort)y] = true; | ||||
|         } | ||||
|         for (ulong y = 0; y < MapService.PixelsPerColumn; y++) | ||||
|             for (ulong x = 0; x < MapService.PixelsPerRow; x++) | ||||
|             { | ||||
|                 if (gamePixelGrid[x, y].EntityType.HasValue) | ||||
|                     observerPixelGrid.Set(x, y, true); | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     public void Dispose() | ||||
|  |  | |||
|  | @ -12,13 +12,13 @@ internal sealed class Sprite(bool?[,] data) | |||
| 
 | ||||
|         var whitePixel = new Rgba32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); | ||||
|         for (var y = 0; y < image.Height; y++) | ||||
|         for (var x = 0; x < image.Width; x++) | ||||
|         { | ||||
|             var pixelValue = image[x, y]; | ||||
|             data[x, y] = pixelValue.A == 0 | ||||
|                 ? null | ||||
|                 : pixelValue == whitePixel; | ||||
|         } | ||||
|             for (var x = 0; x < image.Width; x++) | ||||
|             { | ||||
|                 var pixelValue = image[x, y]; | ||||
|                 data[x, y] = pixelValue.A == 0 | ||||
|                     ? null | ||||
|                     : pixelValue == whitePixel; | ||||
|             } | ||||
| 
 | ||||
|         return new Sprite(data); | ||||
|     } | ||||
|  | @ -34,8 +34,8 @@ internal sealed class Sprite(bool?[,] data) | |||
|         var result = new bool[Width, Height]; | ||||
| 
 | ||||
|         for (var y = 0; y < Height; y++) | ||||
|         for (var x = 0; x < Width; x++) | ||||
|             result[x, y] = this[x, y] ?? false; | ||||
|             for (var x = 0; x < Width; x++) | ||||
|                 result[x, y] = this[x, y] ?? false; | ||||
| 
 | ||||
|         return result; | ||||
|     } | ||||
|  |  | |||
|  | @ -21,24 +21,24 @@ internal sealed class SpriteSheet(Sprite[,] sprites) | |||
| 
 | ||||
|         var whitePixel = new Rgba32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); | ||||
|         for (var spriteY = 0; spriteY < spritesPerColumn; spriteY++) | ||||
|         for (var spriteX = 0; spriteX < spritesPerRow; spriteX++) | ||||
|         { | ||||
|             var data = new bool?[spriteWidth, spriteHeight]; | ||||
|             for (var dy = 0; dy < spriteHeight; dy++) | ||||
|             for (var dx = 0; dx < spriteWidth; dx++) | ||||
|             for (var spriteX = 0; spriteX < spritesPerRow; spriteX++) | ||||
|             { | ||||
|                 var x = spriteX * spriteWidth + dx; | ||||
|                 var y = spriteY * spriteHeight + dy; | ||||
|                 var data = new bool?[spriteWidth, spriteHeight]; | ||||
|                 for (var dy = 0; dy < spriteHeight; dy++) | ||||
|                     for (var dx = 0; dx < spriteWidth; dx++) | ||||
|                     { | ||||
|                         var x = spriteX * spriteWidth + dx; | ||||
|                         var y = spriteY * spriteHeight + dy; | ||||
| 
 | ||||
|                 var pixelValue = image[x, y]; | ||||
|                 data[dx, dy] = pixelValue.A == 0 | ||||
|                     ? null | ||||
|                     : pixelValue == whitePixel; | ||||
|                         var pixelValue = image[x, y]; | ||||
|                         data[dx, dy] = pixelValue.A == 0 | ||||
|                             ? null | ||||
|                             : pixelValue == whitePixel; | ||||
|                     } | ||||
| 
 | ||||
|                 sprites[spriteX, spriteY] = new Sprite(data); | ||||
|             } | ||||
| 
 | ||||
|             sprites[spriteX, spriteY] = new Sprite(data); | ||||
|         } | ||||
| 
 | ||||
|         return new SpriteSheet(sprites); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,13 +2,13 @@ using System.Buffers; | |||
| 
 | ||||
| namespace TanksServer.Interactivity; | ||||
| 
 | ||||
| internal sealed class BufferPool: MemoryPool<byte> | ||||
| internal sealed class BufferPool : MemoryPool<byte> | ||||
| { | ||||
|     private readonly MemoryPool<byte> _actualPool = Shared; | ||||
| 
 | ||||
|     public override int MaxBufferSize => int.MaxValue; | ||||
| 
 | ||||
|     protected override void Dispose(bool disposing) {} | ||||
|     protected override void Dispose(bool disposing) { } | ||||
| 
 | ||||
|     public override IMemoryOwner<byte> Rent(int minBufferSize = -1) | ||||
|     { | ||||
|  | @ -16,7 +16,7 @@ internal sealed class BufferPool: MemoryPool<byte> | |||
|         return new BufferPoolMemoryOwner(_actualPool.Rent(minBufferSize), minBufferSize); | ||||
|     } | ||||
| 
 | ||||
|     private sealed class BufferPoolMemoryOwner(IMemoryOwner<byte> actualOwner, int wantedSize): IMemoryOwner<byte> | ||||
|     private sealed class BufferPoolMemoryOwner(IMemoryOwner<byte> actualOwner, int wantedSize) : IMemoryOwner<byte> | ||||
|     { | ||||
|         public Memory<byte> Memory { get; } = actualOwner.Memory[..wantedSize]; | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ internal sealed class ClientScreenServerConnection | |||
| 
 | ||||
|     private Package BuildNextPackage(Bitmap pixels, GamePixelGrid gamePixelGrid) | ||||
|     { | ||||
|         var pixelsData = pixels.Data; | ||||
|         var pixelsData = pixels.CopyRaw(); | ||||
|         var nextPixels = _bufferPool.Rent(pixelsData.Length); | ||||
|         pixelsData.CopyTo(nextPixels.Memory.Span); | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer, IDisposable | |||
|     private readonly MapService _mapService; | ||||
|     private readonly ILogger<SendToServicePointDisplay> _logger; | ||||
|     private readonly PlayerServer _players; | ||||
|     private readonly Cp437Grid _scoresBuffer; | ||||
|     private readonly CharGrid _scoresBuffer; | ||||
|     private readonly TimeSpan _minFrameTime; | ||||
|     private readonly IOptionsMonitor<HostConfiguration> _options; | ||||
| 
 | ||||
|  | @ -42,13 +42,13 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer, IDisposable | |||
| 
 | ||||
|         var localIp = GetLocalIPv4(displayConfig.Value).Split('.'); | ||||
|         Debug.Assert(localIp.Length == 4); | ||||
|         _scoresBuffer = new Cp437Grid(12, 20); | ||||
|         _scoresBuffer = new CharGrid(12, 20); | ||||
| 
 | ||||
|         _scoresBuffer[00] = "== TANKS! =="; | ||||
|         _scoresBuffer[01] = "-- scores --"; | ||||
|         _scoresBuffer[17] = "--  join  --"; | ||||
|         _scoresBuffer[18] = string.Join('.', localIp[..2]); | ||||
|         _scoresBuffer[19] = string.Join('.', localIp[2..]); | ||||
|         _scoresBuffer.SetRow(00, "== TANKS! =="); | ||||
|         _scoresBuffer.SetRow(01, "-- scores --"); | ||||
|         _scoresBuffer.SetRow(17, "--  join  --"); | ||||
|         _scoresBuffer.SetRow(18, string.Join('.', localIp[..2])); | ||||
|         _scoresBuffer.SetRow(19, string.Join('.', localIp[2..])); | ||||
|     } | ||||
| 
 | ||||
|     public async Task OnFrameDoneAsync(GamePixelGrid gamePixelGrid, Bitmap observerPixels) | ||||
|  | @ -66,9 +66,8 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer, IDisposable | |||
| 
 | ||||
|         try | ||||
|         { | ||||
|             _displayConnection.Send(Command.BitmapLinearWin(0, 0, observerPixels.Clone(), CompressionCode.Lzma) | ||||
|                 .IntoPacket()); | ||||
|             _displayConnection.Send(Command.Cp437Data(MapService.TilesPerRow, 0, _scoresBuffer.Clone()).IntoPacket()); | ||||
|             _displayConnection.Send(Command.BitmapLinearWin(0, 0, observerPixels, CompressionCode.Lzma)); | ||||
|             _displayConnection.Send(Command.Cp437Data(MapService.TilesPerRow, 0, _scoresBuffer.ToCp437())); | ||||
|         } | ||||
|         catch (SocketException ex) | ||||
|         { | ||||
|  | @ -95,14 +94,14 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer, IDisposable | |||
|             var name = p.Name[..nameLength]; | ||||
|             var spaces = new string(' ', ScoresWidth - score.Length - nameLength); | ||||
| 
 | ||||
|             _scoresBuffer[row] = name + spaces + score; | ||||
|             _scoresBuffer.SetRow(row, name + spaces + score); | ||||
|             row++; | ||||
|         } | ||||
| 
 | ||||
|         for (; row < 16; row++) | ||||
|             _scoresBuffer[row] = string.Empty; | ||||
|             _scoresBuffer.SetRow(row, new string(' ', ScoresWidth)); | ||||
| 
 | ||||
|         _scoresBuffer[16] = _mapService.Current.Name[..(Math.Min(ScoresWidth, _mapService.Current.Name.Length) - 1)]; | ||||
|         _scoresBuffer.SetRow(16, _mapService.Current.Name[..(Math.Min(ScoresWidth, _mapService.Current.Name.Length) - 1)]); | ||||
|     } | ||||
| 
 | ||||
|     private static string GetLocalIPv4(DisplayConfiguration configuration) | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ namespace TanksServer.Interactivity; | |||
| internal abstract class WebsocketServerConnection( | ||||
|     ILogger logger, | ||||
|     ByteChannelWebSocket socket | ||||
| ): IDisposable | ||||
| ) : IDisposable | ||||
| { | ||||
|     protected readonly ByteChannelWebSocket Socket = socket; | ||||
|     protected readonly ILogger Logger = logger; | ||||
|  |  | |||
|  | @ -4,12 +4,12 @@ using TanksServer.GameLogic; | |||
| namespace TanksServer.Models; | ||||
| 
 | ||||
| [DebuggerDisplay("({X} | {Y})")] | ||||
| internal readonly struct PixelPosition(int x, int y) | ||||
| internal readonly struct PixelPosition(ulong x, ulong y) | ||||
| { | ||||
|     public int X { get; } = (x + MapService.PixelsPerRow) % MapService.PixelsPerRow; | ||||
|     public int Y { get; } = (y + MapService.PixelsPerColumn) % MapService.PixelsPerColumn; | ||||
|     public ulong X { get; } = (x + MapService.PixelsPerRow) % MapService.PixelsPerRow; | ||||
|     public ulong Y { get; } = (y + MapService.PixelsPerColumn) % MapService.PixelsPerColumn; | ||||
| 
 | ||||
|     public void Deconstruct(out int x, out int y) | ||||
|     public void Deconstruct(out ulong x, out ulong y) | ||||
|     { | ||||
|         x = X; | ||||
|         y = Y; | ||||
|  |  | |||
|  | @ -4,20 +4,20 @@ namespace TanksServer.Models; | |||
| 
 | ||||
| internal static class PositionHelpers | ||||
| { | ||||
|     public static PixelPosition GetPixelRelative(this PixelPosition position, short subX, short subY) | ||||
|         => new(position.X + subX, position.Y + subY); | ||||
|     public static PixelPosition GetPixelRelative(this PixelPosition position, long subX, long subY) | ||||
|         => new((ulong)((long)position.X + subX), (ulong)((long)position.Y + subY)); | ||||
| 
 | ||||
|     public static PixelPosition ToPixelPosition(this FloatPosition position) | ||||
|         => new((int)Math.Round(position.X), (int)Math.Round(position.Y)); | ||||
|         => new((ulong)Math.Round(position.X), (ulong)Math.Round(position.Y)); | ||||
| 
 | ||||
|     public static PixelPosition ToPixelPosition(this TilePosition position) => new( | ||||
|         (ushort)(position.X * MapService.TileSize), | ||||
|         (ushort)(position.Y * MapService.TileSize) | ||||
|         (ulong)(position.X * MapService.TileSize), | ||||
|         (ulong)(position.Y * MapService.TileSize) | ||||
|     ); | ||||
| 
 | ||||
|     public static TilePosition ToTilePosition(this PixelPosition position) => new( | ||||
|         (ushort)(position.X / MapService.TileSize), | ||||
|         (ushort)(position.Y / MapService.TileSize) | ||||
|         (ulong)(position.X / MapService.TileSize), | ||||
|         (ulong)(position.Y / MapService.TileSize) | ||||
|     ); | ||||
| 
 | ||||
|     public static FloatPosition ToFloatPosition(this PixelPosition position) => new(position.X, position.Y); | ||||
|  | @ -28,10 +28,10 @@ internal static class PositionHelpers | |||
|             Math.Pow(p1.Y - p2.Y, 2) | ||||
|         ); | ||||
| 
 | ||||
|     public static PixelBounds GetBoundsForCenter(this FloatPosition position, ushort size) | ||||
|     public static PixelBounds GetBoundsForCenter(this FloatPosition position, ulong size) | ||||
|     { | ||||
|         var sub = (short)(-size / 2d); | ||||
|         var add = (short)(size / 2d - 1); | ||||
|         var sub = (long)(-(long)size / 2d); | ||||
|         var add = (long)(size / 2d - 1); | ||||
|         var pixelPosition = position.ToPixelPosition(); | ||||
|         return new PixelBounds( | ||||
|             pixelPosition.GetPixelRelative(sub, sub), | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ internal enum PowerUpType | |||
|     SmartBullets, | ||||
| } | ||||
| 
 | ||||
| internal sealed class PowerUp: IMapEntity | ||||
| internal sealed class PowerUp : IMapEntity | ||||
| { | ||||
|     public required FloatPosition Position { get; init; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,7 +10,8 @@ internal sealed class Tank(Player owner, FloatPosition position) : IMapEntity | |||
| 
 | ||||
|     [JsonIgnore] public Player Owner { get; } = owner; | ||||
| 
 | ||||
|     [JsonIgnore] public double Rotation | ||||
|     [JsonIgnore] | ||||
|     public double Rotation | ||||
|     { | ||||
|         get => _rotation; | ||||
|         set | ||||
|  |  | |||
|  | @ -4,8 +4,8 @@ using TanksServer.GameLogic; | |||
| namespace TanksServer.Models; | ||||
| 
 | ||||
| [DebuggerDisplay("({X} | {Y})")] | ||||
| internal readonly struct TilePosition(ushort x, ushort y) | ||||
| internal readonly struct TilePosition(ulong x, ulong y) | ||||
| { | ||||
|     public ushort X { get; } = (ushort)((x + MapService.TilesPerRow) % MapService.TilesPerRow); | ||||
|     public ushort Y { get; } = (ushort)((y + MapService.TilesPerColumn) % MapService.TilesPerColumn); | ||||
|     public ulong X { get; } = (ulong)((x + MapService.TilesPerRow) % MapService.TilesPerRow); | ||||
|     public ulong Y { get; } = (ulong)((y + MapService.TilesPerColumn) % MapService.TilesPerColumn); | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.IO; | ||||
| using Microsoft.AspNetCore.Builder; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.FileProviders; | ||||
| using ServicePoint; | ||||
| using TanksServer.GameLogic; | ||||
| using TanksServer.Graphics; | ||||
| using TanksServer.Interactivity; | ||||
|  | @ -11,6 +11,8 @@ namespace TanksServer; | |||
| 
 | ||||
| public static class Program | ||||
| { | ||||
|     [RequiresUnreferencedCode("Calls Endpoints.Map")] | ||||
|     [RequiresDynamicCode("Calls Endpoints.Map")] | ||||
|     public static async Task Main(string[] args) | ||||
|     { | ||||
|         var app = Configure(args); | ||||
|  | @ -25,6 +27,8 @@ public static class Program | |||
|         await app.RunAsync(); | ||||
|     } | ||||
| 
 | ||||
|     [RequiresUnreferencedCode("Calls Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure<TOptions>(IConfiguration)")] | ||||
|     [RequiresDynamicCode("Calls Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure<TOptions>(IConfiguration)")] | ||||
|     private static WebApplication Configure(string[] args) | ||||
|     { | ||||
|         var builder = WebApplication.CreateSlimBuilder(args); | ||||
|  | @ -43,10 +47,7 @@ public static class Program | |||
|                 .AllowAnyOrigin()) | ||||
|         ); | ||||
| 
 | ||||
|         builder.Services.ConfigureHttpJsonOptions(options => | ||||
|         { | ||||
|             options.SerializerOptions.TypeInfoResolverChain.Insert(0, new AppSerializerContext()); | ||||
|         }); | ||||
|         builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.TypeInfoResolverChain.Insert(0, new AppSerializerContext())); | ||||
| 
 | ||||
|         builder.Services.AddHttpLogging(_ => { }); | ||||
| 
 | ||||
|  | @ -100,10 +101,7 @@ public static class Program | |||
|         builder.Services.AddSingleton<Connection>(sp => | ||||
|         { | ||||
|             var config = sp.GetRequiredService<IOptions<DisplayConfiguration>>().Value; | ||||
|             var connection = Connection.Open($"{config.Hostname}:{config.Port}"); | ||||
|             if (connection == null) | ||||
|                 throw new IOException($"Could not open connection to {config.Hostname}:{config.Port}"); | ||||
| 
 | ||||
|             var connection = new Connection($"{config.Hostname}:{config.Port}"); | ||||
|             return connection; | ||||
|         }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ | |||
|     </ItemGroup> | ||||
| 
 | ||||
|     <ItemGroup> | ||||
|       <ProjectReference Include="..\servicepoint\crates\servicepoint_binding_cs\ServicePoint\ServicePoint.csproj" /> | ||||
|       <ProjectReference Include="..\servicepoint\crates\servicepoint_binding_uniffi\libraries\csharp\ServicePoint\ServicePoint.csproj" /> | ||||
|     </ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| Subproject commit f968f929173c6646acb7d9a3ed19112e626732b3 | ||||
| Subproject commit 234510842d6676b8e8a7cbabbe8d3373aa8d3e6e | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter