Skip to content

Commit

Permalink
add: configurable teleport to cryo for disconnected players (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
Legendaxe committed Jun 18, 2023
1 parent 14ff0d4 commit 0727584
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 8 deletions.
4 changes: 4 additions & 0 deletions Content.Client/Preferences/UI/HumanoidProfileEditor.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@
<humanoid:MarkingPicker Name="CMarkings" IgnoreCategories="Hair,FacialHair" />
</ScrollContainer>
</BoxContainer>
<BoxContainer Name="CAfkPreferences" Orientation="Vertical" Margin="10">
<!-- Afk preferences -->
<CheckBox Name="CTeleportAfkToCryoStorage" Text="{Loc 'humanoid-profile-edtior-afkPreferences-teleport-to-cryo-storage'}"/>
</BoxContainer>
</TabContainer>
</BoxContainer>
<!-- Right side -->
Expand Down
16 changes: 16 additions & 0 deletions Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public sealed partial class HumanoidProfileEditor : Control
private SingleMarkingPicker _facialHairPicker => CFacialHairPicker;
private EyeColorPicker _eyesPicker => CEyeColorPicker;

private CheckBox _teleportAfkToCryoStorage => CTeleportAfkToCryoStorage;

private TabContainer _tabContainer => CTabContainer;
private BoxContainer _jobList => CJobList;
private BoxContainer _antagList => CAntagList;
Expand Down Expand Up @@ -513,6 +515,14 @@ public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IProt
};
_previewSpriteSideControl.AddChild(_previewSpriteSide);
#endregion Dummy

#region TeleportAfkToCryoStorage

_tabContainer.SetTabTitle(5, Loc.GetString("humanoid-profile-edtior-afkPreferences-tab"));
_teleportAfkToCryoStorage.Pressed = Profile?.TeleportAfkToCryoStorage ?? true;
_teleportAfkToCryoStorage.OnToggled += args => SetTeleportAfkToCryoStorage(args.Pressed);

#endregion TeleportAfkToCryoStorage

#endregion Left

Expand Down Expand Up @@ -845,6 +855,12 @@ private void SetBackpack(BackpackPreference newBackpack)
IsDirty = true;
}

private void SetTeleportAfkToCryoStorage(bool newTeleportAfkToCryoStorage)
{
Profile = Profile?.WithTeleportAfkToCryoStorage(newTeleportAfkToCryoStorage);
IsDirty = true;
}

public void Save()
{
IsDirty = false;
Expand Down
101 changes: 101 additions & 0 deletions Content.Server/SS220/CryopodSSD/AfkSSDSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt

using System.Linq;
using Content.Server.Afk;
using Content.Server.GameTicking;
using Content.Server.Mind.Components;
using Content.Server.Preferences.Managers;
using Content.Shared.Body.Components;
using Content.Shared.CCVar;
using Content.Shared.Preferences;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Network;
using Robust.Shared.Timing;
using Robust.Shared.Utility;

namespace Content.Server.SS220.CryopodSSD;

public sealed class AfkSSDSystem : EntitySystem
{
[Dependency] private readonly CryopodSSDSystem _cryopodSSDSystem = default!;
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;

private float _afkTeleportTocryo;

private readonly Dictionary<(EntityUid, NetUserId), (TimeSpan, bool)> _entityEnteredSSDTimes = new();

public override void Initialize()
{
base.Initialize();

_cfg.OnValueChanged(CCVars.AfkTeleportToCryo, SetAfkTeleportToCryo, true);
_playerManager.PlayerStatusChanged += OnPlayerChange;
}

private void SetAfkTeleportToCryo(float value)
=> _afkTeleportTocryo = value;

public override void Shutdown()
{
base.Shutdown();

_cfg.UnsubValueChanged(CCVars.AfkTeleportToCryo, SetAfkTeleportToCryo);
_playerManager.PlayerStatusChanged -= OnPlayerChange;
}

public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var pair in _entityEnteredSSDTimes.Where(uid => HasComp<MindComponent>(uid.Key.Item1)))
{
if (pair.Value.Item2 && IsTeleportAfkToCryoTime(pair.Value.Item1)
&& _cryopodSSDSystem.TeleportEntityToCryoStorageWithDelay(pair.Key.Item1))
{
_entityEnteredSSDTimes.Remove(pair.Key);
}
}
}

private bool IsTeleportAfkToCryoTime(TimeSpan time)
{
var timeOut = TimeSpan.FromSeconds(_afkTeleportTocryo);
return _gameTiming.CurTime - time > timeOut;
}

private void OnPlayerChange(object? sender, SessionStatusEventArgs e)
{

switch (e.NewStatus)
{
case SessionStatus.Disconnected:
if (e.Session.AttachedEntity is null
|| !HasComp<MindComponent>(e.Session.AttachedEntity)
|| !HasComp<BodyComponent>(e.Session.AttachedEntity))
{
break;
}

if (!_preferencesManager.TryGetCachedPreferences(e.Session.UserId, out var preferences)
|| preferences.SelectedCharacter is not HumanoidCharacterProfile humanoidPreferences)
{
break;
}
_entityEnteredSSDTimes[(e.Session.AttachedEntity.Value, e.Session.UserId)]
= (_gameTiming.CurTime, humanoidPreferences.TeleportAfkToCryoStorage);
break;
case SessionStatus.Connected:
if (_entityEnteredSSDTimes
.TryFirstOrNull(item => item.Key.Item2 == e.Session.UserId, out var item))
{
_entityEnteredSSDTimes.Remove(item.Value.Key);
}

break;
}
}
}
91 changes: 89 additions & 2 deletions Content.Server/SS220/CryopodSSD/CryopodSSDSystem.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt
using Content.Server.Mind.Components;
using Content.Server.Station.Systems;
using Content.Shared.Audio;
using Content.Shared.CCVar;
using Content.Shared.DoAfter;
using Content.Shared.DragDrop;
using Content.Shared.Mobs.Systems;
using Content.Shared.SS220.CryopodSSD;
using Content.Shared.Verbs;
using Robust.Shared.Configuration;
Expand All @@ -20,6 +23,9 @@ public sealed class CryopodSSDSystem : SharedCryopodSSDSystem
{
[Dependency] private readonly SSDStorageConsoleSystem _SSDStorageConsoleSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
private ISawmill _sawmill = default!;
Expand All @@ -35,10 +41,11 @@ public override void Initialize()
_cfg.OnValueChanged(CCVars.AutoTransferToCryoDelay, SetAutoTransferDelay, true);

SubscribeLocalEvent<CryopodSSDComponent, ComponentInit>(OnComponentInit);

SubscribeLocalEvent<CryopodSSDComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
SubscribeLocalEvent<CryopodSSDComponent, CryopodSSDLeaveActionEvent>(OnCryopodSSDLeaveAction);

SubscribeLocalEvent<CryopodSSDComponent, TeleportToCryoFinished>(OnTeleportFinished);
SubscribeLocalEvent<CryopodSSDComponent, CryopodSSDDragFinished>(OnDragFinished);
SubscribeLocalEvent<CryopodSSDComponent, DragDropTargetEvent>(HandleDragDropOn);
}
Expand All @@ -62,6 +69,13 @@ public override void Update(float frameTime)
TransferToCryoStorage(uid, cryopodSSDComp);
}
}

public override void Shutdown()
{
base.Shutdown();

_cfg.UnsubValueChanged(CCVars.AutoTransferToCryoDelay, SetAutoTransferDelay);
}

/// <summary>
/// Ejects body from cryopod
Expand All @@ -84,6 +98,73 @@ public override void Update(float frameTime)
base.EjectBody(uid, cryopodSsdComponent);
return contained;
}


/// <summary>
/// Tries to teleport target inside cryopod, if any available
/// </summary>
/// <param name="target"> Target to teleport in first matching cryopod</param>
/// <returns> true if player successfully transferred to cryo storage, otherwise returns false</returns>
public bool TeleportEntityToCryoStorageWithDelay(EntityUid target)
{
var station = _stationSystem.GetOwningStation(target);

if (station is null)
{
return false;
}

foreach (var comp in EntityQuery<CryopodSSDComponent>())
{
if (comp.BodyContainer.ContainedEntity == target)
{
return true;
}
}

var cryopodSSDComponents = EntityQueryEnumerator<CryopodSSDComponent>();

while (cryopodSSDComponents
.MoveNext(out var cryopodSSDUid, out var cryopodSSDComp))
{
if (cryopodSSDComp.BodyContainer.ContainedEntity is null
&& _stationSystem.GetOwningStation(cryopodSSDUid) == station)
{
var portal = Spawn("CryoStoragePortal", Transform(target).Coordinates);

if (TryComp<AmbientSoundComponent>(portal, out var ambientSoundComponent))
{
_audioSystem.PlayPvs(ambientSoundComponent.Sound, portal);
}

var doAfterArgs = new DoAfterArgs(target, cryopodSSDComp.EntryDelay, new TeleportToCryoFinished(portal), cryopodSSDUid)
{
BreakOnDamage = false,
BreakOnTargetMove = false,
BreakOnUserMove = true,
NeedHand = false,
};

_doAfterSystem.TryStartDoAfter(doAfterArgs);
return true;
}
}

return false;
}

private void OnTeleportFinished(EntityUid uid, CryopodSSDComponent component, TeleportToCryoFinished args)
{
InsertBody(uid, args.User, component);
TransferToCryoStorage(uid, component);

if (TryComp<AmbientSoundComponent>(args.PortalId, out var ambientSoundComponent))
{
_audioSystem.PlayPvs(ambientSoundComponent.Sound, args.PortalId);
}

EntityManager.DeleteEntity(args.PortalId);
}

private void SetAutoTransferDelay(float value) => _autoTransferDelay = value;

Expand All @@ -96,7 +177,13 @@ private void HandleDragDropOn(EntityUid uid, CryopodSSDComponent cryopodSsdCompo

if (!TryComp(args.Dragged, out MindComponent? mind) || !mind.HasMind)
{
_sawmill.Error($"{ToPrettyString(args.User)} tries to put non-playable entity into SSD cryopod {ToPrettyString(args.Dragged)}");
_sawmill.Info($"{ToPrettyString(args.User)} tries to put non-playable entity into SSD cryopod {ToPrettyString(args.Dragged)}");
return;
}

if (_mobStateSystem.IsDead(args.Dragged))
{
_sawmill.Log(LogLevel.Warning,$"{ToPrettyString(args.User)} tries to put dead entity(passing client check) {ToPrettyString(args.Dragged)} into SSD cryopod, potentially client exploit");
return;
}

Expand Down
3 changes: 3 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1341,6 +1341,9 @@ public static readonly CVarDef<int>
public static readonly CVarDef<float> AfkTimeKick =
CVarDef.Create("afk.time_kick", 600f, CVar.SERVERONLY);

public static readonly CVarDef<float> AfkTeleportToCryo =
CVarDef.Create("afk.teleport_to_cryo", 1800f, CVar.SERVERONLY);

/*
* IC
*/
Expand Down
10 changes: 8 additions & 2 deletions Content.Shared/Preferences/HumanoidCharacterProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ private HumanoidCharacterProfile(
Dictionary<string, JobPriority> jobPriorities,
PreferenceUnavailableMode preferenceUnavailable,
List<string> antagPreferences,
List<string> traitPreferences)
List<string> traitPreferences,
bool teleportAfkToCryoStorage = true)
{
Name = name;
FlavorText = flavortext;
Expand All @@ -62,6 +63,7 @@ private HumanoidCharacterProfile(
PreferenceUnavailable = preferenceUnavailable;
_antagPreferences = antagPreferences;
_traitPreferences = traitPreferences;
TeleportAfkToCryoStorage = teleportAfkToCryoStorage;
}

/// <summary>Copy constructor but with overridable references (to prevent useless copies)</summary>
Expand All @@ -71,7 +73,7 @@ private HumanoidCharacterProfile(
List<string> antagPreferences,
List<string> traitPreferences)
: this(other.Name, other.FlavorText, other.Species, other.Voice, other.Age, other.Sex, other.Gender, other.Appearance, other.Clothing, other.Backpack,
jobPriorities, other.PreferenceUnavailable, antagPreferences, traitPreferences)
jobPriorities, other.PreferenceUnavailable, antagPreferences, traitPreferences, other.TeleportAfkToCryoStorage)
{
}

Expand Down Expand Up @@ -224,6 +226,7 @@ public static HumanoidCharacterProfile RandomWithSpecies(string species = Shared
public IReadOnlyList<string> AntagPreferences => _antagPreferences;
public IReadOnlyList<string> TraitPreferences => _traitPreferences;
public PreferenceUnavailableMode PreferenceUnavailable { get; private set; }
public bool TeleportAfkToCryoStorage { get; private set; } = true;

public HumanoidCharacterProfile WithName(string name)
{
Expand Down Expand Up @@ -346,6 +349,9 @@ public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref)
return new(this, _jobPriorities, _antagPreferences, list);
}

public HumanoidCharacterProfile WithTeleportAfkToCryoStorage(bool teleportAfkToCryoStorage)
=> new(this) { TeleportAfkToCryoStorage = teleportAfkToCryoStorage };

public string Summary =>
Loc.GetString(
"humanoid-character-profile-summary",
Expand Down
15 changes: 13 additions & 2 deletions Content.Shared/SS220/CryopodSSD/SharedCryopodSSDSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public bool InsertBody(EntityUid uid, EntityUid target, CryopodSSDComponent cryo
return false;

var xform = Transform(target);
cryopodSsdComponent.BodyContainer.Insert(target, transform: xform);
cryopodSsdComponent.BodyContainer.Insert(target, transform: xform, force: true);

if (_prototypeManager.TryIndex<InstantActionPrototype>("CryopodSSDLeave", out var leaveAction))
{
Expand Down Expand Up @@ -118,7 +118,7 @@ private void OnCryopodSSDCanDropTarget(EntityUid uid, CryopodSSDComponent compon
if (args.Handled)
return;

args.CanDrop = HasComp<BodyComponent>(args.Dragged);
args.CanDrop = HasComp<BodyComponent>(args.Dragged) && _mobStateSystem.IsAlive(args.Dragged);
args.Handled = true;
}

Expand Down Expand Up @@ -168,6 +168,17 @@ protected void AddAlternativeVerbs(EntityUid uid, CryopodSSDComponent cryopodSSD
public sealed class CryopodSSDDragFinished : SimpleDoAfterEvent
{
}

[Serializable, NetSerializable]
public sealed class TeleportToCryoFinished : SimpleDoAfterEvent
{
public EntityUid PortalId { get; private set; }

public TeleportToCryoFinished(EntityUid portalId)
{
PortalId = portalId;
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ humanoid-profile-editor-job-priority-never-button = Никогда
humanoid-profile-editor-naming-rules-warning = Внимание: Оскорбительные или странные имена и описания могут повлечь за собой беседу с администрацией. Прочитайте \[Правила\].
humanoid-profile-editor-markings-tab = Черты внешности
humanoid-profile-editor-flavortext-tab = Описание
humanoid-profile-edtior-afkPreferences-tab = ССД
humanoid-profile-edtior-afkPreferences-teleport-to-cryo-storage = Телепортация в крио хранилище при длительном ССД
Loading

0 comments on commit 0727584

Please sign in to comment.