Adds support for a map rotation system. (#7162)
* Adds support for a map rotation system. It is now the default way of selecting a map, map votes have been disabled. * whoops * Randomize the map it starts off with, too. (it'd pick bagel every time otherwise) * Address review * remove knight from rotation due to it being an unmaintained map. * minor cleanup
This commit is contained in:
@@ -71,7 +71,7 @@ namespace Content.Server.GameTicking
|
|||||||
DefaultMap = _mapManager.CreateMap();
|
DefaultMap = _mapManager.CreateMap();
|
||||||
_mapManager.AddUninitializedMap(DefaultMap);
|
_mapManager.AddUninitializedMap(DefaultMap);
|
||||||
var startTime = _gameTiming.RealTime;
|
var startTime = _gameTiming.RealTime;
|
||||||
var maps = new List<GameMapPrototype>() { _gameMapManager.GetSelectedMapChecked(true) };
|
var maps = new List<GameMapPrototype>() { _gameMapManager.GetSelectedMapChecked(true, true) };
|
||||||
|
|
||||||
// Let game rules dictate what maps we should load.
|
// Let game rules dictate what maps we should load.
|
||||||
RaiseLocalEvent(new LoadingMapsEvent(maps));
|
RaiseLocalEvent(new LoadingMapsEvent(maps));
|
||||||
|
|||||||
@@ -22,8 +22,11 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
|
|
||||||
|
private readonly Queue<string> _previousMaps = new Queue<string>();
|
||||||
private GameMapPrototype _currentMap = default!;
|
private GameMapPrototype _currentMap = default!;
|
||||||
private bool _currentMapForced;
|
private bool _currentMapForced;
|
||||||
|
private bool _mapRotationEnabled;
|
||||||
|
private int _mapQueueDepth = 1;
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
@@ -35,6 +38,25 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
throw new ArgumentException($"Unknown map prototype {value} was selected!");
|
throw new ArgumentException($"Unknown map prototype {value} was selected!");
|
||||||
}, true);
|
}, true);
|
||||||
_configurationManager.OnValueChanged(CCVars.GameMapForced, value => _currentMapForced = value, true);
|
_configurationManager.OnValueChanged(CCVars.GameMapForced, value => _currentMapForced = value, true);
|
||||||
|
_configurationManager.OnValueChanged(CCVars.GameMapRotation, value => _mapRotationEnabled = value, true);
|
||||||
|
_configurationManager.OnValueChanged(CCVars.GameMapMemoryDepth, value =>
|
||||||
|
{
|
||||||
|
_mapQueueDepth = value;
|
||||||
|
// Drain excess.
|
||||||
|
while (_previousMaps.Count > _mapQueueDepth)
|
||||||
|
{
|
||||||
|
_previousMaps.Dequeue();
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
var maps = AllVotableMaps().ToArray();
|
||||||
|
_random.Shuffle(maps);
|
||||||
|
foreach (var map in maps)
|
||||||
|
{
|
||||||
|
if (_previousMaps.Count >= _mapQueueDepth)
|
||||||
|
break;
|
||||||
|
_previousMaps.Enqueue(map.ID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<GameMapPrototype> CurrentlyEligibleMaps()
|
public IEnumerable<GameMapPrototype> CurrentlyEligibleMaps()
|
||||||
@@ -75,17 +97,18 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
public void SelectRandomMap()
|
public void SelectRandomMap()
|
||||||
{
|
{
|
||||||
var maps = CurrentlyEligibleMaps().ToList();
|
var maps = CurrentlyEligibleMaps().ToList();
|
||||||
_random.Shuffle(maps);
|
_currentMap = _random.Pick(maps);
|
||||||
_currentMap = maps[0];
|
|
||||||
_currentMapForced = false;
|
_currentMapForced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameMapPrototype GetSelectedMap()
|
public GameMapPrototype GetSelectedMap()
|
||||||
{
|
{
|
||||||
return _currentMap;
|
if (!_mapRotationEnabled || _currentMapForced)
|
||||||
|
return _currentMap;
|
||||||
|
return SelectMapInQueue() ?? CurrentlyEligibleMaps().First();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameMapPrototype GetSelectedMapChecked(bool loud = false)
|
public GameMapPrototype GetSelectedMapChecked(bool loud = false, bool markAsPlayed = false)
|
||||||
{
|
{
|
||||||
if (!_currentMapForced && !IsMapEligible(GetSelectedMap()))
|
if (!_currentMapForced && !IsMapEligible(GetSelectedMap()))
|
||||||
{
|
{
|
||||||
@@ -100,7 +123,11 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetSelectedMap();
|
var map = GetSelectedMap();
|
||||||
|
|
||||||
|
if (markAsPlayed)
|
||||||
|
_previousMaps.Enqueue(map.ID);
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CheckMapExists(string gameMap)
|
public bool CheckMapExists(string gameMap)
|
||||||
@@ -127,4 +154,39 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
else
|
else
|
||||||
return gameMap.MapName;
|
return gameMap.MapName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetMapQueuePriority(string gameMapProtoName)
|
||||||
|
{
|
||||||
|
var i = 0;
|
||||||
|
foreach (var map in _previousMaps.Reverse())
|
||||||
|
{
|
||||||
|
if (map == gameMapProtoName)
|
||||||
|
return i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mapQueueDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameMapPrototype? SelectMapInQueue()
|
||||||
|
{
|
||||||
|
var eligible = CurrentlyEligibleMaps()
|
||||||
|
.Where(x => x.Votable)
|
||||||
|
.Select(x => (proto: x, weight: GetMapQueuePriority(x.ID)))
|
||||||
|
.OrderByDescending(x => x.weight).ToArray();
|
||||||
|
if (eligible.Length is 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var weight = eligible[0].weight;
|
||||||
|
return eligible.Where(x => x.Item2 == weight).OrderBy(x => x.proto.ID).First().proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnqueueMap(string mapProtoName)
|
||||||
|
{
|
||||||
|
_previousMaps.Enqueue(mapProtoName);
|
||||||
|
while (_previousMaps.Count > _mapQueueDepth)
|
||||||
|
{
|
||||||
|
_previousMaps.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public interface IGameMapManager
|
|||||||
/// Gets the currently selected map, double-checking if it can be used.
|
/// Gets the currently selected map, double-checking if it can be used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>selected map</returns>
|
/// <returns>selected map</returns>
|
||||||
GameMapPrototype GetSelectedMapChecked(bool loud = false);
|
GameMapPrototype GetSelectedMapChecked(bool loud = false, bool markAsPlayed = false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the given map exists
|
/// Checks if the given map exists
|
||||||
|
|||||||
@@ -126,6 +126,19 @@ namespace Content.Shared.CCVar
|
|||||||
public static readonly CVarDef<bool>
|
public static readonly CVarDef<bool>
|
||||||
GameMapForced = CVarDef.Create("game.mapforced", false, CVar.SERVERONLY);
|
GameMapForced = CVarDef.Create("game.mapforced", false, CVar.SERVERONLY);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The depth of the queue used to calculate which map is next in rotation.
|
||||||
|
/// This is how long the game "remembers" that some map was put in play. Default is 16 rounds.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<int>
|
||||||
|
GameMapMemoryDepth = CVarDef.Create("game.map_memory_depth", 16, CVar.SERVERONLY);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is map rotation enabled?
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<bool>
|
||||||
|
GameMapRotation = CVarDef.Create<bool>("game.map_rotation", true, CVar.SERVERONLY);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a random position offset will be applied to the station on roundstart.
|
/// Whether a random position offset will be applied to the station on roundstart.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -562,7 +575,7 @@ namespace Content.Shared.CCVar
|
|||||||
/// See vote.enabled, but specific to map votes
|
/// See vote.enabled, but specific to map votes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly CVarDef<bool> VoteMapEnabled =
|
public static readonly CVarDef<bool> VoteMapEnabled =
|
||||||
CVarDef.Create("vote.map_enabled", true, CVar.SERVERONLY);
|
CVarDef.Create("vote.map_enabled", false, CVar.SERVERONLY);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The required ratio of the server that must agree for a restart round vote to go through.
|
/// The required ratio of the server that must agree for a restart round vote to go through.
|
||||||
|
|||||||
@@ -88,6 +88,7 @@
|
|||||||
!type:NanotrasenNameGenerator
|
!type:NanotrasenNameGenerator
|
||||||
prefixCreator: '14'
|
prefixCreator: '14'
|
||||||
mapPath: /Maps/knightship.yml
|
mapPath: /Maps/knightship.yml
|
||||||
|
votable: false
|
||||||
minPlayers: 0
|
minPlayers: 0
|
||||||
maxPlayers: 8
|
maxPlayers: 8
|
||||||
overflowJobs: []
|
overflowJobs: []
|
||||||
|
|||||||
Reference in New Issue
Block a user