make big display toggleable
This commit is contained in:
		
							parent
							
								
									a9aaf899a2
								
							
						
					
					
						commit
						dc9ad21a3d
					
				
					 29 changed files with 119 additions and 46 deletions
				
			
		|  | @ -1,4 +1,5 @@ | |||
| using TanksServer.Helpers; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.DrawSteps; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| using TanksServer.Helpers; | ||||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.DrawSteps; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| using SixLabors.ImageSharp; | ||||
| using SixLabors.ImageSharp.PixelFormats; | ||||
| using TanksServer.Helpers; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.DrawSteps; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,7 @@ | |||
| global using System; | ||||
| global using System.Collections.Concurrent; | ||||
| global using System.Collections.Generic; | ||||
| global using System.Linq; | ||||
| global using System.Threading; | ||||
| global using System.Threading.Tasks; | ||||
| global using Microsoft.Extensions.Options; | ||||
| global using TanksServer.Models; | ||||
| global using TanksServer.Services; | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| using System.Text.Json.Serialization; | ||||
| using TanksServer.Models; | ||||
| 
 | ||||
| namespace TanksServer.Helpers; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| using System.Diagnostics; | ||||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.Helpers; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| namespace TanksServer.Services; | ||||
| namespace TanksServer.Models; | ||||
| 
 | ||||
| internal sealed class ServicePointDisplayConfiguration | ||||
| { | ||||
|     public bool Enable { get; set; } = true; | ||||
|     public string Hostname { get; set; } = string.Empty; | ||||
|     public int Port { get; set; } | ||||
| } | ||||
|  |  | |||
|  | @ -6,7 +6,9 @@ using Microsoft.Extensions.DependencyInjection; | |||
| using Microsoft.Extensions.FileProviders; | ||||
| using TanksServer.DrawSteps; | ||||
| using TanksServer.Helpers; | ||||
| using TanksServer.Models; | ||||
| using TanksServer.Servers; | ||||
| using TanksServer.Services; | ||||
| using TanksServer.TickSteps; | ||||
| 
 | ||||
| namespace TanksServer; | ||||
|  | @ -74,14 +76,17 @@ internal static class Program | |||
|             options.SerializerOptions.TypeInfoResolverChain.Insert(0, new AppSerializerContext()); | ||||
|         }); | ||||
| 
 | ||||
|         builder.Services.AddOptions(); | ||||
| 
 | ||||
|         builder.Services.AddSingleton<MapService>(); | ||||
|         builder.Services.AddSingleton<BulletManager>(); | ||||
|         builder.Services.AddSingleton<TankManager>(); | ||||
|         builder.Services.AddSingleton<SpawnNewTanks>(); | ||||
|         builder.Services.AddSingleton<PixelDrawer>(); | ||||
|         builder.Services.AddSingleton<ControlsServer>(); | ||||
|         builder.Services.AddSingleton<PlayerServer>(); | ||||
|         builder.Services.AddSingleton<ClientScreenServer>(); | ||||
|         builder.Services.AddSingleton<LastFinishedFrameProvider>(); | ||||
|         builder.Services.AddSingleton<SpawnQueueProvider>(); | ||||
| 
 | ||||
|         builder.Services.AddHostedService<GameTickService>(); | ||||
|         builder.Services.AddHostedService(sp => sp.GetRequiredService<ControlsServer>()); | ||||
|  | @ -94,7 +99,7 @@ internal static class Program | |||
|         builder.Services.AddSingleton<ITickStep, MoveTanks>(); | ||||
|         builder.Services.AddSingleton<ITickStep, ShootFromTanks>(); | ||||
|         builder.Services.AddSingleton<ITickStep>(sp => sp.GetRequiredService<SpawnNewTanks>()); | ||||
|         builder.Services.AddSingleton<ITickStep>(sp => sp.GetRequiredService<PixelDrawer>()); | ||||
|         builder.Services.AddSingleton<ITickStep, DrawStateToFrame>(); | ||||
|         builder.Services.AddSingleton<ITickStep, SendToServicePointDisplay>(); | ||||
|         builder.Services.AddSingleton<ITickStep, SendToClientScreen>(); | ||||
| 
 | ||||
|  | @ -102,6 +107,9 @@ internal static class Program | |||
|         builder.Services.AddSingleton<IDrawStep, TankDrawer>(); | ||||
|         builder.Services.AddSingleton<IDrawStep, BulletDrawer>(); | ||||
| 
 | ||||
|         builder.Services.Configure<ServicePointDisplayConfiguration>( | ||||
|             builder.Configuration.GetSection("ServicePointDisplay")); | ||||
| 
 | ||||
|         return builder.Build(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| using System.Collections.Concurrent; | ||||
| using System.Diagnostics; | ||||
| using System.Net.WebSockets; | ||||
| using System.Threading.Channels; | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ using System.Net.WebSockets; | |||
| using Microsoft.Extensions.Hosting; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using TanksServer.Helpers; | ||||
| using TanksServer.Models; | ||||
| 
 | ||||
| namespace TanksServer.Servers; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,11 +1,11 @@ | |||
| using System.Collections.Concurrent; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using TanksServer.TickSteps; | ||||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.Servers; | ||||
| 
 | ||||
| internal sealed class PlayerServer(ILogger<PlayerServer> logger, SpawnNewTanks spawnNewTanks) | ||||
| internal sealed class PlayerServer(ILogger<PlayerServer> logger, SpawnQueueProvider spawnQueueProvider) | ||||
| { | ||||
|     private readonly ConcurrentDictionary<string, Player> _players = new(); | ||||
| 
 | ||||
|  | @ -33,7 +33,7 @@ internal sealed class PlayerServer(ILogger<PlayerServer> logger, SpawnNewTanks s | |||
|     private Player AddAndSpawn(string name) | ||||
|     { | ||||
|         var player = new Player(name); | ||||
|         spawnNewTanks.SpawnTankForPlayer(player); | ||||
|         spawnQueueProvider.Queue.Enqueue(player); | ||||
|         return player; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| using TanksServer.Models; | ||||
| 
 | ||||
| namespace TanksServer.Services; | ||||
| 
 | ||||
| internal sealed class BulletManager | ||||
|  |  | |||
|  | @ -1,9 +1,12 @@ | |||
| using Microsoft.Extensions.Hosting; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using TanksServer.TickSteps; | ||||
| 
 | ||||
| namespace TanksServer.Services; | ||||
| 
 | ||||
| internal sealed class GameTickService(IEnumerable<ITickStep> steps) : IHostedService, IDisposable | ||||
| internal sealed class GameTickService( | ||||
|     IEnumerable<ITickStep> steps, IHostApplicationLifetime lifetime, ILogger<GameTickService> logger | ||||
| ) : IHostedService, IDisposable | ||||
| { | ||||
|     private readonly CancellationTokenSource _cancellation = new(); | ||||
|     private readonly List<ITickStep> _steps = steps.ToList(); | ||||
|  | @ -17,11 +20,19 @@ internal sealed class GameTickService(IEnumerable<ITickStep> steps) : IHostedSer | |||
| 
 | ||||
|     private async Task RunAsync() | ||||
|     { | ||||
|         while (!_cancellation.IsCancellationRequested) | ||||
|         try | ||||
|         { | ||||
|             foreach (var step in _steps) | ||||
|                 await step.TickAsync(); | ||||
|             await Task.Delay(1000/25); | ||||
|             while (!_cancellation.IsCancellationRequested) | ||||
|             { | ||||
|                 foreach (var step in _steps) | ||||
|                     await step.TickAsync(); | ||||
|                 await Task.Delay(1000 / 25); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             logger.LogError(ex, "game tick service crashed"); | ||||
|             lifetime.StopApplication(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										14
									
								
								TanksServer/Services/LastFinishedFrameProvider.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								TanksServer/Services/LastFinishedFrameProvider.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| using TanksServer.Helpers; | ||||
| 
 | ||||
| namespace TanksServer.Services; | ||||
| 
 | ||||
| internal sealed class LastFinishedFrameProvider | ||||
| { | ||||
|     private DisplayPixelBuffer? _lastFrame; | ||||
|      | ||||
|     public DisplayPixelBuffer LastFrame | ||||
|     { | ||||
|         get => _lastFrame ?? throw new InvalidOperationException("first frame not yet drawn"); | ||||
|         set => _lastFrame = value; | ||||
|     } | ||||
| } | ||||
|  | @ -1,3 +1,5 @@ | |||
| using TanksServer.Models; | ||||
| 
 | ||||
| namespace TanksServer.Services; | ||||
| 
 | ||||
| internal sealed class MapService | ||||
|  |  | |||
							
								
								
									
										8
									
								
								TanksServer/Services/SpawnQueueProvider.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								TanksServer/Services/SpawnQueueProvider.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| using TanksServer.Models; | ||||
| 
 | ||||
| namespace TanksServer.Services; | ||||
| 
 | ||||
| internal sealed class SpawnQueueProvider | ||||
| { | ||||
|     public ConcurrentQueue<Player> Queue { get; } = new(); | ||||
| } | ||||
|  | @ -1,6 +1,6 @@ | |||
| using System.Collections; | ||||
| using System.Collections.Concurrent; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using TanksServer.Models; | ||||
| 
 | ||||
| namespace TanksServer.Services; | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
|     </ItemGroup> | ||||
| 
 | ||||
|     <ItemGroup> | ||||
|       <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" /> | ||||
|       <PackageReference Include="SixLabors.ImageSharp" Version="3.1.3" /> | ||||
|     </ItemGroup> | ||||
|      | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| using TanksServer.Helpers; | ||||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class CollideBulletsWithTanks(BulletManager bullets) : ITickStep | ||||
|  |  | |||
|  | @ -1,30 +1,25 @@ | |||
| using TanksServer.DrawSteps; | ||||
| using TanksServer.Helpers; | ||||
| using TanksServer.TickSteps; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.Services; | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class PixelDrawer(IEnumerable<IDrawStep> drawSteps) : ITickStep | ||||
| internal sealed class DrawStateToFrame( | ||||
|     IEnumerable<IDrawStep> drawSteps, LastFinishedFrameProvider lastFrameProvider | ||||
| ) : ITickStep | ||||
| { | ||||
|     private const uint GameFieldPixelCount = MapService.PixelsPerRow * MapService.PixelsPerColumn; | ||||
|     private DisplayPixelBuffer? _lastFrame; | ||||
|     private readonly List<IDrawStep> _drawSteps = drawSteps.ToList(); | ||||
| 
 | ||||
|     public DisplayPixelBuffer LastFrame | ||||
|     { | ||||
|         get => _lastFrame ?? throw new InvalidOperationException("first frame not yet drawn"); | ||||
|         private set => _lastFrame = value; | ||||
|     } | ||||
| 
 | ||||
|     public Task TickAsync() | ||||
|     { | ||||
|         var buffer = CreateGameFieldPixelBuffer(); | ||||
|         foreach (var step in _drawSteps)  | ||||
|         foreach (var step in _drawSteps) | ||||
|             step.Draw(buffer); | ||||
|         LastFrame = buffer; | ||||
|         lastFrameProvider.LastFrame = buffer; | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     private static DisplayPixelBuffer CreateGameFieldPixelBuffer() | ||||
|     { | ||||
|         var data = new byte[10 + GameFieldPixelCount / 8]; | ||||
|  | @ -1,3 +1,6 @@ | |||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class MoveBullets(BulletManager bullets) : ITickStep | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class MoveTanks( | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class RotateTanks(TankManager tanks, IOptions<TanksConfiguration> options) : ITickStep | ||||
|  |  | |||
|  | @ -1,13 +1,18 @@ | |||
| using TanksServer.Servers; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class SendToClientScreen( | ||||
|     ClientScreenServer clientScreenServer, PixelDrawer drawer | ||||
|     ClientScreenServer clientScreenServer, | ||||
|     LastFinishedFrameProvider lastFinishedFrameProvider | ||||
| ) : ITickStep | ||||
| { | ||||
|     public Task TickAsync() | ||||
|     { | ||||
|         return Task.WhenAll(clientScreenServer.GetConnections().Select(c => c.SendAsync(drawer.LastFrame))); | ||||
|         var tasks = clientScreenServer | ||||
|             .GetConnections() | ||||
|             .Select(c => c.SendAsync(lastFinishedFrameProvider.LastFrame)); | ||||
|         return Task.WhenAll(tasks); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,21 +1,25 @@ | |||
| using System.Net.Sockets; | ||||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class SendToServicePointDisplay( | ||||
|     IOptions<ServicePointDisplayConfiguration> options,  | ||||
|     PixelDrawer drawer | ||||
|     IOptions<ServicePointDisplayConfiguration> options, | ||||
|     LastFinishedFrameProvider lastFinishedFrameProvider | ||||
| ) : ITickStep, IDisposable | ||||
| { | ||||
|     private readonly UdpClient _udpClient = new(options.Value.Hostname, options.Value.Port); | ||||
|     private readonly UdpClient? _udpClient = options.Value.Enable | ||||
|         ? new(options.Value.Hostname, options.Value.Port) | ||||
|         : null; | ||||
| 
 | ||||
|     public Task TickAsync() | ||||
|     { | ||||
|         return _udpClient.SendAsync(drawer.LastFrame.Data).AsTask(); | ||||
|         return _udpClient?.SendAsync(lastFinishedFrameProvider.LastFrame.Data).AsTask() ?? Task.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     public void Dispose() | ||||
|     { | ||||
|         _udpClient.Dispose(); | ||||
|         _udpClient?.Dispose(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class ShootFromTanks( | ||||
|  |  | |||
|  | @ -1,14 +1,13 @@ | |||
| using System.Collections.Concurrent; | ||||
| using TanksServer.Models; | ||||
| using TanksServer.Services; | ||||
| 
 | ||||
| namespace TanksServer.TickSteps; | ||||
| 
 | ||||
| internal sealed class SpawnNewTanks(TankManager tanks, MapService map) : ITickStep | ||||
| internal sealed class SpawnNewTanks(TankManager tanks, MapService map, SpawnQueueProvider queueProvider) : ITickStep | ||||
| { | ||||
|     private readonly ConcurrentQueue<Player> _playersToSpawn = new(); | ||||
| 
 | ||||
|     public Task TickAsync() | ||||
|     { | ||||
|         while (_playersToSpawn.TryDequeue(out var player)) | ||||
|         while (queueProvider.Queue.TryDequeue(out var player)) | ||||
|         { | ||||
|             var tank = new Tank(player, ChooseSpawnPosition()) | ||||
|             { | ||||
|  | @ -42,9 +41,4 @@ internal sealed class SpawnNewTanks(TankManager tanks, MapService map) : ITickSt | |||
|             chosenTile.Y * MapService.TileSize | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public void SpawnTankForPlayer(Player player) | ||||
|     { | ||||
|         _playersToSpawn.Enqueue(player); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -13,5 +13,10 @@ | |||
|                 "Url": "http://localhost:3000" | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     "ServicePointDisplay": { | ||||
|         "Enable": false, | ||||
|         "Hostname": "172.23.42.29", | ||||
|         "Port": 2342 | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter