@@ -167,6 +167,21 @@ public string GenerateClientScriptPassword(string name)
167167 return Hash . HashString ( battleInstanceGuid + name ) . ToString ( ) ;
168168 }
169169
170+ /// <summary>
171+ /// Tries to claim a preallocated team slot for a mid-game joiner on the given ally team.
172+ /// Returns the team number if a slot is available, null otherwise.
173+ /// </summary>
174+ private int ? TryClaimMidGameTeamSlot ( int allyTeamId )
175+ {
176+ if ( spring ? . Context ? . MidGameTeamSlots != null &&
177+ spring . Context . MidGameTeamSlots . TryGetValue ( allyTeamId , out var queue ) &&
178+ queue . TryDequeue ( out var teamNum ) )
179+ {
180+ return teamNum ;
181+ }
182+ return null ;
183+ }
184+
170185 public void Dispose ( )
171186 {
172187 spring . UnsubscribeEvents ( this ) ;
@@ -349,11 +364,28 @@ public virtual async Task ProcessPlayerJoin(ConnectedUser user, string joinPassw
349364
350365 if ( spring . IsRunning )
351366 {
352- spring . AddUser ( ubs . Name , ubs . ScriptPassword , ubs . LobbyUser ) ;
353- var started = DateTime . UtcNow . Subtract ( spring . IngameStartTime ?? RunningSince ?? DateTime . UtcNow ) ;
354- started = new TimeSpan ( ( int ) started . TotalHours , started . Minutes , started . Seconds ) ;
355- await SayBattle ( $ "THIS GAME IS CURRENTLY IN PROGRESS, PLEASE WAIT UNTIL IT ENDS! Running for { started } ", ubs . Name ) ;
356- await SayBattle ( "If you say !notify, I will message you when the current game ends." , ubs . Name ) ;
367+ // Try to assign a preallocated mid-game team slot if available
368+ int ? midGameTeam = null ;
369+ if ( ! ubs . IsSpectator )
370+ {
371+ midGameTeam = TryClaimMidGameTeamSlot ( ubs . AllyNumber ) ;
372+ if ( midGameTeam == null )
373+ ubs . IsSpectator = true ; // no slot available, force spectator
374+ }
375+
376+ spring . AddUser ( ubs . Name , ubs . ScriptPassword , ubs . LobbyUser , ubs . IsSpectator , midGameTeam ) ;
377+
378+ if ( midGameTeam . HasValue )
379+ {
380+ await SayBattle ( $ "Game is in progress — { ubs . Name } is joining as a player on team { midGameTeam . Value } .") ;
381+ }
382+ else
383+ {
384+ var started = DateTime . UtcNow . Subtract ( spring . IngameStartTime ?? RunningSince ?? DateTime . UtcNow ) ;
385+ started = new TimeSpan ( ( int ) started . TotalHours , started . Minutes , started . Seconds ) ;
386+ await SayBattle ( $ "THIS GAME IS CURRENTLY IN PROGRESS, PLEASE WAIT UNTIL IT ENDS! Running for { started } ", ubs . Name ) ;
387+ await SayBattle ( "If you say !notify, I will message you when the current game ends." , ubs . Name ) ;
388+ }
357389 }
358390
359391 try
@@ -406,22 +438,34 @@ public async Task RequestConnectSpring(ConnectedUser conus, string joinPassword)
406438 UserBattleStatus ubs ;
407439
408440 startGameStatus = spring . LobbyStartContext . Players . FirstOrDefault ( x => x . Name == conus . Name ) ;
409-
441+
410442 if ( ! Users . TryGetValue ( conus . Name , out ubs ) && ! ( IsInGame && startGameStatus != null ) )
411443 if ( IsPassworded && ( Password != joinPassword ) )
412444 {
413445 await conus . Respond ( "Invalid password" ) ;
414446 return ;
415447 }
448+
449+ var isSpectator = startGameStatus ? . IsSpectator != false ;
450+
451+ // For mid-game joins of users not in the original player list, try to assign a team slot
452+ int ? midGameTeam = null ;
453+ if ( isSpectator && startGameStatus == null && Users . TryGetValue ( conus . Name , out ubs ) && ! ubs . IsSpectator )
454+ {
455+ midGameTeam = TryClaimMidGameTeamSlot ( ubs . AllyNumber ) ;
456+ if ( midGameTeam . HasValue )
457+ isSpectator = false ;
458+ }
459+
416460 var pwd = GenerateClientScriptPassword ( conus . Name ) ;
417- spring . AddUser ( conus . Name , pwd , conus . User ) ;
461+ spring . AddUser ( conus . Name , pwd , conus . User , isSpectator , midGameTeam ) ;
418462
419463 if ( spring . Context . LobbyStartContext . Players . Any ( x => x . Name == conus . Name ) && conus . MyBattle != this )
420464 {
421465 await ProcessPlayerJoin ( conus , joinPassword ) ;
422466 }
423467
424- await conus . SendCommand ( GetConnectSpringStructure ( pwd , startGameStatus ? . IsSpectator != false ) ) ;
468+ await conus . SendCommand ( GetConnectSpringStructure ( pwd , isSpectator ) ) ;
425469 }
426470
427471
0 commit comments