@@ -15,11 +15,12 @@ namespace OpenPolytopia.Server;
1515/// </remarks>
1616/// <param name="port">the port to listen on</param>
1717/// <param name="bindAddress">the ip address to bind to; null to listen on every interface</param>
18- public partial class GameServer ( int port , string ? bindAddress = null , string ? databasePath = null ) : IDisposable {
18+ public partial class GameServer ( int port , string ? bindAddress = null , string ? databasePath = null ,
19+ TimeProvider ? timeProvider = null ) : IDisposable {
1920 /// <summary>
2021 /// How often the server checks for lobbies to start
2122 /// </summary>
22- private static readonly TimeSpan START_LOBBY_INTERVAL = TimeSpan . FromSeconds ( 5 ) ;
23+ private static readonly TimeSpan START_LOBBY_INTERVAL = TimeSpan . FromSeconds ( 1 ) ;
2324
2425 /// <summary>
2526 /// Max lobbies the server accepts before refusing new ones
@@ -50,6 +51,7 @@ public async Task RunAsync() {
5051 _server = new ServerConnection ( port , bindAddress , _certificate ) ;
5152 RestoreState ( _store . LoadState ( ) ) ;
5253 RegisterHandlers ( ) ;
54+ RegisterTimerHandlers ( ) ;
5355
5456 _server . OnPacketReceived += ManagePacketAsync ;
5557 _server . OnClientDisconnected += connection => _ = ClientDisconnectedAsync ( connection ) ;
@@ -344,13 +346,14 @@ private async Task StartLobbyAsync(LobbyData lobby) {
344346 . Select ( player => online [ player . PlayerId ] ) . ToList ( ) ;
345347
346348 try {
347- var session = await _gameManager . CreateGameAsync ( lobby , _gameData , onlineConnections : online ) ;
349+ var session = await _gameManager . CreateGameAsync ( lobby , _gameData , onlineConnections : online , startedAt : _timeProvider . GetUtcNow ( ) ) ;
348350
349351 Console . WriteLine ( $ "Starting game for lobby { lobby . Id } with { lobby . PlayersCount } players") ;
350352
351353 // notify the players that their game started and send them its full state
352354 BroadcastTo ( connectionIds , new GameStartedPacket { LobbyId = lobby . Id , Players = lobby . Players } ) ;
353355 BroadcastTo ( connectionIds , session . BuildState ( ) ) ;
356+ SendClock ( session , connectionIds ) ;
354357 }
355358 catch ( Exception e ) {
356359 Console . Error . WriteLine ( $ "Couldn't start the game for lobby { lobby . Id } : { e } ") ;
@@ -378,7 +381,7 @@ private async Task StartLobbyAsync(LobbyData lobby) {
378381 /// </param>
379382 /// <returns>true if a session was found and the connection is a player in it</returns>
380383 private bool TryResolveSession ( NetworkConnection connection , ulong gameId ,
381- [ NotNullWhen ( true ) ] out GameSession ? session , out int playerId , out GameActionResult result ) {
384+ [ NotNullWhen ( true ) ] out GameSession ? session , out int playerId , out GameActionResult result , uint ? expectedTurn = null ) {
382385 session = _gameManager [ gameId ] ;
383386 if ( session == null ) {
384387 playerId = 0 ;
@@ -392,6 +395,12 @@ private bool TryResolveSession(NetworkConnection connection, ulong gameId,
392395 return false ;
393396 }
394397
398+ if ( expectedTurn . HasValue && expectedTurn . Value != session . Game . Turn &&
399+ ( session . Clock ? . Mode == TurnTimerMode . Live || expectedTurn . Value != 0 ) ) {
400+ result = GameActionResult . InvalidParameters ;
401+ return false ;
402+ }
403+
395404 result = GameActionResult . Ok ;
396405 return true ;
397406 }
@@ -408,7 +417,7 @@ private async Task ManageGetGameStateAsync(NetworkConnection connection, GetGame
408417
409418 private async Task ManageMoveTroopAsync ( NetworkConnection connection , MoveTroopPacket packet ) {
410419 {
411- if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check ) ) {
420+ if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check , packet . ExpectedTurn ) ) {
412421 SendTo ( connection . Id , new MoveTroopResponsePacket { Result = check } ) ;
413422 return ;
414423 }
@@ -429,7 +438,7 @@ private async Task ManageMoveTroopAsync(NetworkConnection connection, MoveTroopP
429438
430439 private async Task ManageAttackAsync ( NetworkConnection connection , AttackPacket packet ) {
431440 {
432- if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check ) ) {
441+ if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check , packet . ExpectedTurn ) ) {
433442 SendTo ( connection . Id , new AttackResponsePacket { Result = check } ) ;
434443 return ;
435444 }
@@ -452,7 +461,7 @@ private async Task ManageAttackAsync(NetworkConnection connection, AttackPacket
452461
453462 private async Task ManageTrainTroopAsync ( NetworkConnection connection , TrainTroopPacket packet ) {
454463 {
455- if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check ) ) {
464+ if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check , packet . ExpectedTurn ) ) {
456465 SendTo ( connection . Id , new TrainTroopResponsePacket { Result = check } ) ;
457466 return ;
458467 }
@@ -473,7 +482,7 @@ private async Task ManageTrainTroopAsync(NetworkConnection connection, TrainTroo
473482
474483 private async Task ManageResearchTechAsync ( NetworkConnection connection , ResearchTechPacket packet ) {
475484 {
476- if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check ) ) {
485+ if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check , packet . ExpectedTurn ) ) {
477486 SendTo ( connection . Id , new ResearchTechResponsePacket { Result = check } ) ;
478487 return ;
479488 }
@@ -493,7 +502,7 @@ private async Task ManageResearchTechAsync(NetworkConnection connection, Researc
493502
494503 private async Task ManageBuildAsync ( NetworkConnection connection , BuildPacket packet ) {
495504 {
496- if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check ) ) {
505+ if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check , packet . ExpectedTurn ) ) {
497506 SendTo ( connection . Id , new BuildResponsePacket { Result = check } ) ;
498507 return ;
499508 }
@@ -514,7 +523,7 @@ private async Task ManageBuildAsync(NetworkConnection connection, BuildPacket pa
514523
515524 private async Task ManageCaptureAsync ( NetworkConnection connection , CapturePacket packet ) {
516525 {
517- if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check ) ) {
526+ if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check , packet . ExpectedTurn ) ) {
518527 SendTo ( connection . Id , new CaptureResponsePacket { Result = check } ) ;
519528 return ;
520529 }
@@ -541,7 +550,7 @@ private async Task ManageCaptureAsync(NetworkConnection connection, CapturePacke
541550
542551 private async Task ManageEndTurnAsync ( NetworkConnection connection , EndTurnPacket packet ) {
543552 {
544- if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check ) ) {
553+ if ( ! TryResolveSession ( connection , packet . GameId , out var session , out var playerId , out var check , packet . ExpectedTurn ) ) {
545554 SendTo ( connection . Id , new EndTurnResponsePacket { Result = check } ) ;
546555 return ;
547556 }
0 commit comments