Files

487 lines
16 KiB
C#

using System.Linq;
using Content.Server._Miracle.Components;
using Content.Server.Administration.Managers;
using Content.Server.Administration.Systems;
using Content.Server.Cargo.Components;
using Content.Server.Cargo.Systems;
using Content.Server.Chat.Managers;
using Content.Server.Database;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Events;
using Content.Server.KillTracking;
using Content.Server.Materials;
using Content.Server.Mind;
using Content.Server.Parallax;
using Content.Server.Preferences.Managers;
using Content.Server.Spawners.Components;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Server.Storage.EntitySystems;
using Content.Shared._Miracle.Cvars;
using Content.Shared._Miracle.GulagSystem;
using Content.Shared.GameTicking;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Materials;
using Content.Shared.Mind;
using Content.Shared.Parallax.Biomes;
using Content.Shared.Popups;
using Content.Shared.Preferences;
using Content.Shared.Throwing;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server._Miracle.GulagSystem;
public sealed partial class GulagSystem : SharedGulagSystem
{
//1 second = 0.01 points
[Dependency] private readonly AdminSystem _adminSystem = default!;
[Dependency] private readonly IBanManager _banManager = default!;
[Dependency] private readonly BiomeSystem _biome = default!;
[Dependency] private readonly IServerDbManager _db = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StationSpawningSystem _spawningSystem = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly CargoSystem _cargoSystem = default!;
[Dependency] private readonly MaterialStorageSystem _materialStorageSystem = default!;
[Dependency] private readonly EntityStorageSystem _entityStorageSystem = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
private readonly List<ProtoId<BiomeTemplatePrototype>> _gulagBiomes = new()
{
"GulagBiome"
};
private readonly List<string> _gulagMaps = new()
{
"/Maps/Gulags/gulag.yml"
};
private double _timeToPointsRatio;
private MapId? _activeMap;
private EntityUid? _mapEntity;
private readonly TimeSpan _safeguardUpdateRate = TimeSpan.FromSeconds(10);
private DateTime _nextSafeguardUpdate = DateTime.MinValue;
private readonly TimeSpan _shuttleFillUpdateRate = TimeSpan.FromMinutes(10);
private DateTime _nextShuttleFillUpdate = DateTime.MinValue;
private List<EntityCoordinates> _spawnCoords = new();
private readonly Dictionary<NetUserId, double> _pointsPerPlayer = new();
private readonly Dictionary<ProtoId<MaterialPrototype>, int> _gulagMaterialStorage = new();
public override void Initialize()
{
base.Initialize();
_cfg.OnValueChanged(MiracleCvars.GulagPointsToTimeRatio, newValue => _timeToPointsRatio = newValue, true);
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStarting);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
SubscribeLocalEvent<PlayerBeforeSpawnEvent>(BeforeSpawn);
SubscribeLocalEvent<GulagOreProcessorComponent, MaterialEntityInsertedEvent>(OnOreInserted);
SubscribeLocalEvent<GulagOreProcessorComponent, InteractUsingEvent>(OnInteract, before: new[] {typeof(MaterialStorageSystem)});
SubscribeLocalEvent<GulagFillContainerComponent, MapInitEvent>(OnGulagContainerSpawned);
SubscribeLocalEvent<KillReportedEvent>(OnKillReported);
SubscribeLocalEvent<PlayerJoinedLobbyEvent>(OnJoinedLobby);
//safeguard
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
}
private void OnJoinedLobby(PlayerJoinedLobbyEvent ev)
{
if(IsUserGulagged(ev.PlayerSession.UserId, out _))
{
_chatManager.DispatchServerMessage(ev.PlayerSession, Loc.GetString("gulag-chat-join-message"));
}
}
private void OnKillReported(ref KillReportedEvent ev)
{
if (!HasComp<GulagBoundComponent>(ev.Entity))
{
return;
}
if (ev.Primary is not KillPlayerSource source)
{
return;
}
var player = source.PlayerId;
if (!IsUserGulagged(player, out var ban))
{
return;
}
var banDef = ban.First();
var newExpirationTime = banDef.ExpirationTime!.Value + TimeSpan.FromDays(1);
EditServerBan(banDef, newExpirationTime);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
Safeguard();
TryFillCargoShuttle();
}
private void OnGulagContainerSpawned(EntityUid uid, GulagFillContainerComponent component, MapInitEvent args)
{
var coords = Transform(uid).Coordinates;
foreach (var (materialId, value) in _gulagMaterialStorage)
{
var materialEntities = _materialStorageSystem.SpawnMultipleFromMaterial(value, materialId, coords);
foreach (var material in materialEntities)
{
_entityStorageSystem.Insert(material, uid);
}
}
_gulagMaterialStorage.Clear();
}
private void TryFillCargoShuttle()
{
if (_nextShuttleFillUpdate > DateTime.Now)
{
return;
}
if (_gulagMaterialStorage.Count == 0)
{
return;
}
var station = GetMainStation();
if (!station.HasValue)
{
return;
}
if (!TryComp<StationCargoOrderDatabaseComponent>(station.Value, out var comp) ||
!TryComp(station.Value, out StationDataComponent? dataComp))
{
return;
}
_cargoSystem.AddAndApproveOrder(station.Value, "CrateGulag", "Автоматическая система доставки", 0, 1, Loc.GetString("gulag-sender"),
Loc.GetString("gulag-order-description"), Loc.GetString("gulag-order-destination"), comp, (station.Value, dataComp));
_nextShuttleFillUpdate = DateTime.Now + _shuttleFillUpdateRate;
}
// Just check if we need to bring back somehow escaped players
private void Safeguard()
{
if (_nextSafeguardUpdate > DateTime.Now)
{
return;
}
var querry = EntityQueryEnumerator<GulagBoundComponent, TransformComponent>();
while (querry.MoveNext(out var uid, out var gulagbound, out var xform))
{
if (xform.MapID != _activeMap)
{
SendToGulag(uid);
}
}
_nextSafeguardUpdate = DateTime.Now + _safeguardUpdateRate;
}
public void SendToGulag(ICommonSession session)
{
var playerEntity = session.AttachedEntity;
if (_mapEntity == null)
{
_adminSystem.Erase(session);
return;
}
if (playerEntity.HasValue)
{
SendToGulag(playerEntity.Value);
}
else
{
SpawnPlayer(session, (HumanoidCharacterProfile)_preferencesManager.GetPreferences(session.UserId).SelectedCharacter);
}
var banDef = _banManager.GetServerBans(session.UserId).First();
var message = Loc.GetString("gulag-greetings-message", ("BanTime", $"{(banDef.ExpirationTime! - DateTime.Now).Value.TotalHours}"));
_chatManager.DispatchServerMessage(session, message);
}
private void OnInteract(EntityUid uid, GulagOreProcessorComponent component, InteractUsingEvent args)
{
//It wasn't player who interacted with the entity
if (!_playerManager.TryGetSessionByEntity(args.User, out var session))
{
return;
}
component.LastInteractedUser = session.UserId;
Log.Info("OnInteract raised");
}
private void OnOreInserted(EntityUid uid, GulagOreProcessorComponent component, MaterialEntityInsertedEvent args)
{
var storageComponent = Comp<MaterialStorageComponent>(uid);
var userId = component.LastInteractedUser!.Value;
foreach (var (materialId, currentVolume ) in storageComponent.Storage)
{
var materialPrototype = _prototypeManager.Index<MaterialPrototype>(materialId.Id);
var stackVolume = _materialStorageSystem.GetSheetVolume(materialPrototype);
var actualOreCount = currentVolume / stackVolume;
var points = materialPrototype.Price * actualOreCount;
_pointsPerPlayer[userId] = points + _pointsPerPlayer.GetValueOrDefault(userId);
_gulagMaterialStorage[materialId] = currentVolume + _gulagMaterialStorage.GetValueOrDefault(materialId);
_materialStorageSystem.TrySetMaterialAmount(uid, materialId, 0);
}
var time = ConvertPointsToTime(_pointsPerPlayer[userId]);
_popupSystem.PopupEntity(Loc.GetString("gulag-ban-time-changed", ("Time", $"{time.TotalSeconds}")), uid, PopupType.Medium);
}
public bool IsUserGulagged(NetUserId playerId, out HashSet<ServerBanDef> bans)
{
bans = _banManager.GetServerBans(playerId);
return bans.Count != 0;
}
public bool IsMindGulagged(EntityUid mindId)
{
if (!TryComp(mindId, out MindComponent? mind) || mind.UserId == null)
return false;
return IsUserGulagged(mind.UserId.Value, out _);
}
private void SendToGulag(EntityUid playerEntity)
{
if (_inventorySystem.TryGetContainerSlotEnumerator(playerEntity, out var enumerator))
{
while (enumerator.NextItem(out var item, out var slot))
{
if (_inventorySystem.TryUnequip(playerEntity, playerEntity, slot.Name, true, true))
_physicsSystem.ApplyAngularImpulse(item, ThrowingSystem.ThrowAngularImpulse);
}
}
if (TryComp(playerEntity, out HandsComponent? hands))
{
foreach (var hand in _handsSystem.EnumerateHands(playerEntity, hands))
{
_handsSystem.TryDrop(playerEntity, hand, checkActionBlocker: false, doDropInteraction: false,
handsComp: hands);
}
}
var newPosition = GetSpawnPosition();
_transformSystem.SetCoordinates(playerEntity, newPosition);
_transformSystem.AttachToGridOrMap(playerEntity);
EnsureComp<GulagBoundComponent>(playerEntity);
EnsureComp<KillTrackerComponent>(playerEntity);
}
private void SpawnPlayer(ICommonSession session, HumanoidCharacterProfile profile)
{
var newMind = _mind.CreateMind(session.UserId, profile.Name);
_mind.SetUserId(newMind, session.UserId);
var coords = GetSpawnPosition();
var mob = _spawningSystem.SpawnPlayerMob(coords, null, profile, null);
_mind.TransferTo(newMind, mob);
EnsureComp<GulagBoundComponent>(mob);
EnsureComp<KillTrackerComponent>(mob);
}
private void OnPlayerAttached(PlayerAttachedEvent ev)
{
var bans = _banManager.GetServerBans(ev.Player.UserId);
if (bans.Count == 0)
{
return;
}
var xform = Transform(ev.Entity);
if (xform.MapID != _activeMap)
{
SendToGulag(ev.Player);
}
}
private void BeforeSpawn(PlayerBeforeSpawnEvent ev)
{
var bans = _banManager.GetServerBans(ev.Player.UserId);
if (bans.Count == 0)
{
return;
}
ev.Handled = true;
SendToGulag(ev.Player);
}
private void OnRoundRestart(RoundRestartCleanupEvent ev)
{
foreach (var (player, points) in _pointsPerPlayer)
{
var banDef = _banManager.GetServerBans(player).FirstOrDefault();
if (banDef == null)
{
continue;
}
var newExpirationTime = banDef.ExpirationTime!.Value - ConvertPointsToTime(points);
EditServerBan(banDef, newExpirationTime);
}
_pointsPerPlayer.Clear();
_activeMap = null!;
_mapEntity = null!;
}
private void EditServerBan(ServerBanDef banDef, DateTimeOffset newExpirationTime)
{
_db.EditServerBan(banDef.Id!.Value, banDef.Reason, banDef.Severity, newExpirationTime.UtcDateTime, banDef.UserId!.Value,
DateTime.UtcNow);
_banManager.RemoveCachedServerBan(banDef.UserId.Value, banDef.Id.Value);
_banManager.AddCachedServerBan(new ServerBanDef(
banDef.Id, banDef.UserId, banDef.Address, banDef.HWId, banDef.BanTime, newExpirationTime,
banDef.RoundId, banDef.PlaytimeAtNote, banDef.Reason, banDef.Severity, banDef.BanningAdmin,
banDef.Unban, banDef.ServerName));
}
private void OnRoundStarting(RoundStartingEvent ev)
{
//Spawn Gulag
var mapId = _mapManager.CreateMap();
var mapUid = _mapManager.GetMapEntityId(mapId);
_metaData.SetEntityName(mapUid, "Gulag Map");
_mapManager.AddUninitializedMap(mapId);
var pickedMap = _random.Pick(_gulagMaps);
if (!_mapLoader.TryLoad(mapId, pickedMap, out var uids))
{
_mapManager.DeleteMap(mapId);
Log.Error("Can't spawn map with path {0}", pickedMap);
return;
}
foreach (var uid in uids)
{
_metaData.SetEntityName(uid, $"Gulag grid {uid}");
}
var pickerBiome = _random.Pick(_gulagBiomes);
_biome.EnsurePlanet(mapUid, _prototypeManager.Index<BiomeTemplatePrototype>(pickerBiome));
_mapManager.DoMapInitialize(mapId);
_activeMap = mapId;
_mapEntity = mapUid;
//Item2 = TransformComponent
_spawnCoords = EntityQuery<SpawnPointComponent, TransformComponent>()
.Where(x => x.Item2.MapID == mapId)
.Select(x => x.Item2.Coordinates)
.ToList();
}
private TimeSpan ConvertPointsToTime(double points)
{
return TimeSpan.FromSeconds(points / _timeToPointsRatio);
}
private EntityCoordinates GetSpawnPosition()
{
return _spawnCoords.Count != 0 ? _random.Pick(_spawnCoords) : Transform(_mapEntity!.Value).Coordinates;
}
private EntityUid? GetMainStation()
{
var stations = _stationSystem.GetStations();
foreach (var station in stations)
{
var stationData = Comp<StationDataComponent>(station);
if (!HasComp<StationCargoOrderDatabaseComponent>(station))
{
continue;
}
foreach (var grid in stationData.Grids)
{
if (Transform(grid).MapID == _gameTicker.DefaultMap)
{
return station;
}
}
}
return null!;
}
}