GameLoop/GameManager.Stage.cs
using System.Diagnostics;
public partial class GameManager
{
public enum GameStage
{
// Show warmup
Warmup,
// play game
Game,
// Show scoreboard
Results,
// game finished, next map
Finished
}
/// <summary>
/// How long has this game been going on for?
/// </summary>
[Sync] public TimeSince TimeElapsed { get; set; }
public GameStage CurrentGameStage
{
get
{
if ( !GameSettings.GameLoop )
return GameStage.Game;
if ( Connection.All.Count < 1 )
return GameStage.Warmup;
if ( TimeElapsed < GameSettings.WarmupTime ) return GameStage.Warmup;
if ( TimeElapsed < GameSettings.WarmupTime + GameSettings.RoundTime ) return GameStage.Game;
if ( TimeElapsed < GameSettings.WarmupTime + GameSettings.RoundTime + GameSettings.ResultTime ) return GameStage.Results;
return GameStage.Finished;
}
}
public float GameTimeRemaining
{
get
{
return (GameSettings.WarmupTime + GameSettings.RoundTime) - TimeElapsed;
}
}
GameStage _previousStage = GameStage.Finished;
/// <summary>
/// Rewards the winners of the game.
/// I've decided for now that if there's multiple people who share the same kills, they also win.
/// </summary>
void RewardWinners()
{
Assert.True( Networking.IsHost, "GameManager.RewardWinners should always run on the host!" );
// This can happen on dedicated servers - but maybe we shouldn't be doing anything at all..
if ( !PlayerData.All.Any() ) return;
var winningGroup = PlayerData.All.GroupBy( x => x.Kills )
.OrderByDescending( g => g.Key )
.First();
// No kills? No stats
if ( winningGroup.Any( x => x.Kills < 1 ) ) return;
foreach ( var winner in winningGroup )
{
winner.AddStat( $"wins" );
}
}
/// <summary>
/// What maps can we switch to? Exclude the current map
/// </summary>
public IEnumerable<string> Maps => GameSettings.MapList.Split( ";" ).Where( x => x != Networking.MapName );
[ConCmd( "sbdm.restart" )]
public static void RestartGame()
{
Log.Info( $"Restarting the game" );
Current.TimeElapsed = 0;
}
void StageUpdate()
{
var stage = CurrentGameStage;
if ( _previousStage != stage )
{
_previousStage = stage;
if ( stage == GameStage.Game )
{
StartGame();
}
if ( stage == GameStage.Results )
{
Results();
}
if ( stage == GameStage.Finished )
{
FinishGame();
}
}
UpdateCurrentStage();
}
Sandbox.Services.BenchmarkSystem _benchmarkSystem;
void StartGame()
{
if ( !Networking.IsHost ) return;
Stopwatch sw = null;
if ( GameSettings.IsBenchmark )
{
GameSettings.BotCount = 9; // 9 Bots + 1 player = 10, seems like a decent amount for datacore
GameSettings.RoundTime = 150; // 2:30 minutes should be enough for a benchmark
_benchmarkSystem = new Sandbox.Services.BenchmarkSystem();
_benchmarkSystem.Start( "deathmatch_botmatch" );
// Capture time it takes to spawn players
sw = Stopwatch.StartNew();
}
// Spawn all players
foreach ( var connection in PlayerData.All )
{
SpawnPlayer( connection );
}
// Spawn bots
SpawnBot( GameSettings.BotCount );
if ( GameSettings.IsBenchmark && sw != null )
{
_benchmarkSystem.SetMetric( "StartDuration", sw.Elapsed.TotalSeconds );
}
}
async void Results()
{
if ( !Networking.IsHost ) return;
if ( GameSettings.IsBenchmark )
{
// 0 because there is not really a shutdown window we can capture?
_benchmarkSystem.SetMetric( "EndDuration", 0 );
_benchmarkSystem.Finish();
await _benchmarkSystem.SendAsync();
_benchmarkSystem = default;
Game.Close();
return;
}
RewardWinners();
}
void FinishGame()
{
if ( !Networking.IsHost ) return;
// TODO - maps in order (once we have more than one map in the cycle)
LaunchArguments.Map = Random.Shared.FromArray( Maps.ToArray() );
// Next Round
Game.Load( Game.Ident, true );
}
void UpdateCurrentStage()
{
if ( GameSettings.IsBenchmark )
{
_benchmarkSystem?.Sample();
}
if ( CurrentGameStage != GameStage.Game )
{
CycleSpawnCameras();
}
}
GameObject[] spawnCams;
void CycleSpawnCameras()
{
spawnCams ??= Scene.GetAllObjects( true ).Where( x => x.Tags.Contains( "spawncam" ) ).ToArray();
if ( spawnCams.Length == 0 )
return;
var secondsPerCamera = 3.0f;
var x = (Time.Now / secondsPerCamera).FloorToInt();
var cam = spawnCams[x % (spawnCams.Length - 1)];
Scene.Camera.WorldTransform = cam.WorldTransform;
}
}