mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
* Scope creep my ass * why was this shit not pushed???? * gone... * fix that * review plus buildd * asfsafsafsafsafas * grug * burber * I'll fix this later * shrimply die * also this * it's snerf or snuthin * assaads * round snend * aaaa * fix this shit for real * actually, don't start GameRules until MapInit now that I think aboot it. * forgot u * don't actually need this part nullable * Deffer comp removal * one liner * do that too * chungus * Honestly makes more sense here, plus WHY are we doing all that shit on "GameRuleAdded" rather than GameRuleStarted(?) * deferred removal :V * aaaaaaaaaaaaaaa * whhop * Whoop * better * landmine that I placed for myself then forgot about lmao * cleanup * FUCKING `!` * gotovo * amogus * please don't check by metadata I beg * waawsasfdfsasfa * sfafsasfafsas * fdsaasffsasfa * code * fix * fasasffasfasfsad * chungaas * I may be stupid * gra * graaaaaaaaaaaaaaaaa * comment * gra * clearing is not predicted and would require major refactors, so don't cache on client. * lame shit :P * last holes * nukeopsfsafsasafsfafsa * why was that shit public bruh * bubrbgwahrghfaka * dynamic * revert change that did nothing lmao
243 lines
7.4 KiB
C#
243 lines
7.4 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Content.Shared.CCVar;
|
|
using Content.Shared.GameTicking.Prototypes;
|
|
using Content.Shared.Maps;
|
|
using Robust.Shared.Player;
|
|
|
|
namespace Content.Server.GameTicking;
|
|
|
|
public sealed partial class ServerGameTicker
|
|
{
|
|
public const float PresetFailedCooldownIncrease = 30f;
|
|
|
|
/// <summary>
|
|
/// The selected preset that will be used at the start of the next round.
|
|
/// </summary>
|
|
public GamePresetPrototype? Preset { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The selected preset that will be shown at the lobby screen to fool players.
|
|
/// </summary>
|
|
public GamePresetPrototype? Decoy { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The preset that's currently active.
|
|
/// </summary>
|
|
public GamePresetPrototype? CurrentPreset { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Countdown to the preset being reset to the server default.
|
|
/// </summary>
|
|
public int? ResetCountdown;
|
|
|
|
private bool StartPreset(ICommonSession[] origReadyPlayers, bool force)
|
|
{
|
|
Log.Info($"Attempting to start preset '{CurrentPreset?.ID}'");
|
|
var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force);
|
|
RaiseLocalEvent(startAttempt);
|
|
|
|
if (!startAttempt.Cancelled)
|
|
return true;
|
|
|
|
var presetTitle = CurrentPreset != null ? Loc.GetString(CurrentPreset.ModeTitle) : string.Empty;
|
|
|
|
void FailedPresetRestart()
|
|
{
|
|
SendServerMessage(Loc.GetString("game-ticker-start-round-cannot-start-game-mode-restart",
|
|
("failedGameMode", presetTitle)));
|
|
RestartRound();
|
|
DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease));
|
|
}
|
|
|
|
if (Cfg.GetCVar(CCVars.GameLobbyFallbackEnabled))
|
|
{
|
|
var fallbackPresets = Cfg.GetCVar(CCVars.GameLobbyFallbackPreset).Split(",");
|
|
var startFailed = true;
|
|
|
|
Log.Info($"Fallback - Failed to start round, attempting to start fallback presets.");
|
|
foreach (var preset in fallbackPresets)
|
|
{
|
|
Log.Info($"Fallback - Clearing up gamerules");
|
|
ClearGameRules();
|
|
Log.Info($"Fallback - Attempting to start '{preset}'");
|
|
SetGamePreset(preset, resetDelay: 1);
|
|
AddGamePresetRules();
|
|
StartGamePresetRules();
|
|
|
|
startAttempt.Uncancel();
|
|
RaiseLocalEvent(startAttempt);
|
|
|
|
if (!startAttempt.Cancelled)
|
|
{
|
|
_chatManager.SendAdminAnnouncement(
|
|
Loc.GetString("game-ticker-start-round-cannot-start-game-mode-fallback",
|
|
("failedGameMode", presetTitle),
|
|
("fallbackMode", Loc.GetString(preset))));
|
|
RefreshLateJoinAllowed();
|
|
startFailed = false;
|
|
break;
|
|
}
|
|
Log.Info($"Fallback - '{preset}' failed to start.");
|
|
}
|
|
|
|
if (startFailed)
|
|
{
|
|
FailedPresetRestart();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
Log.Info($"Fallback - Failed to start preset but fallbacks are disabled. Returning to Lobby.");
|
|
FailedPresetRestart();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void InitializeGamePreset()
|
|
{
|
|
SetGamePreset(LobbyEnabled ? Cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox");
|
|
}
|
|
|
|
public void SetGamePreset(GamePresetPrototype? preset, bool force = false, GamePresetPrototype? decoy = null, int? resetDelay = null)
|
|
{
|
|
// Do nothing if this game ticker is a dummy!
|
|
if (DummyTicker)
|
|
return;
|
|
|
|
if (resetDelay is not null)
|
|
{
|
|
ResetCountdown = resetDelay.Value;
|
|
// Reset counter is checked and changed at the end of each round
|
|
// So if the game is in the lobby, the first requested round will happen before the check, and we need one less check
|
|
if (CurrentPreset is null)
|
|
ResetCountdown = resetDelay.Value - 1;
|
|
}
|
|
else
|
|
{
|
|
ResetCountdown = null;
|
|
}
|
|
|
|
Preset = preset;
|
|
Decoy = decoy;
|
|
ValidateMap();
|
|
UpdateInfoText();
|
|
|
|
if (force)
|
|
{
|
|
StartRound(true);
|
|
}
|
|
}
|
|
|
|
public void SetGamePreset(string preset, bool force = false, int? resetDelay = null)
|
|
{
|
|
var proto = FindGamePreset(preset);
|
|
if (proto != null)
|
|
SetGamePreset(proto, force, null, resetDelay);
|
|
}
|
|
|
|
public GamePresetPrototype? FindGamePreset(string preset)
|
|
{
|
|
if (ProtoMan.TryIndex(preset, out GamePresetPrototype? presetProto))
|
|
return presetProto;
|
|
|
|
foreach (var proto in ProtoMan.EnumeratePrototypes<GamePresetPrototype>())
|
|
{
|
|
foreach (var alias in proto.Alias)
|
|
{
|
|
if (preset.Equals(alias, StringComparison.InvariantCultureIgnoreCase))
|
|
return proto;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public bool TryFindGamePreset(string preset, [NotNullWhen(true)] out GamePresetPrototype? prototype)
|
|
{
|
|
prototype = FindGamePreset(preset);
|
|
|
|
return prototype != null;
|
|
}
|
|
|
|
public bool IsMapEligible(GameMapPrototype map)
|
|
{
|
|
if (Preset == null)
|
|
return true;
|
|
|
|
if (Preset.MapPool == null || !ProtoMan.TryIndex<GameMapPoolPrototype>(Preset.MapPool, out var pool))
|
|
return true;
|
|
|
|
return pool.Maps.Contains(map.ID);
|
|
}
|
|
|
|
private void ValidateMap()
|
|
{
|
|
if (Preset == null || _gameMapManager.GetSelectedMap() is not { } map)
|
|
return;
|
|
|
|
if (Preset.MapPool == null ||
|
|
!ProtoMan.TryIndex<GameMapPoolPrototype>(Preset.MapPool, out var pool))
|
|
return;
|
|
|
|
if (pool.Maps.Contains(map.ID))
|
|
return;
|
|
|
|
_gameMapManager.SelectMapRandom();
|
|
}
|
|
|
|
private bool AddGamePresetRules()
|
|
{
|
|
if (DummyTicker || Preset == null)
|
|
return false;
|
|
|
|
CurrentPreset = Preset;
|
|
foreach (var rule in Preset.Rules)
|
|
{
|
|
AddGameRule(rule);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void TryResetPreset()
|
|
{
|
|
if (ResetCountdown is null || ResetCountdown-- > 0)
|
|
return;
|
|
|
|
InitializeGamePreset();
|
|
ResetCountdown = null;
|
|
}
|
|
|
|
public void StartGamePresetRules()
|
|
{
|
|
// May be touched by the preset during init.
|
|
var rules = new List<EntityUid>(GetAddedGameRules());
|
|
foreach (var rule in rules)
|
|
{
|
|
StartGameRule(rule);
|
|
}
|
|
}
|
|
|
|
private void IncrementRoundNumber()
|
|
{
|
|
var playerIds = _playerGameStatuses.Keys.Select(player => player.UserId).ToArray();
|
|
|
|
// TODO FIXME AAAAAAAAAAAAAAAAAAAH THIS IS BROKEN
|
|
// Task.Run as a terrible dirty workaround to avoid synchronization context deadlock from .Result here.
|
|
// This whole setup logic should be made asynchronous so we can properly wait on the DB AAAAAAAAAAAAAH
|
|
var task = Task.Run(async () =>
|
|
{
|
|
var server = await _dbEntryManager.ServerEntity;
|
|
return await _db.AddNewRound(server, playerIds);
|
|
});
|
|
|
|
_taskManager.BlockWaitOnTask(task);
|
|
RoundId = task.GetAwaiter().GetResult();
|
|
}
|
|
}
|