linmeideli committed 2026-10-07 09:23:18 +08:00
1 parent 4e4f87a0a7
commit 3888bfa7ee
32 files changed
+1059 -126

No files matched your search

@@ -5,5 +5,7 @@
"Reactor": "ReactorSystemType",
"LifeSupp": "LifeSuppSystemType",
"Ventilation": "VentilationSystem",
"Decontamination": "DeconSystem",
"Decontamination2": "DeconSystem",
"Sabotage": "SabotageSystemType"
}
@@ -6,5 +6,7 @@
"Security": "SecurityCameraSystemType",
"Ventilation": "VentilationSystem",
"Laboratory": "ReactorSystemType",
"Decontamination": "DeconSystem",
"Decontamination2": "DeconSystem",
"Sabotage": "SabotageSystemType"
}
@@ -0,0 +1,16 @@
using System.Threading.Tasks;
using Empostor.Api.Net.Inner.Objects;
using Empostor.Api.Net.Inner.Objects.ShipStatus;
namespace Empostor.Api.Net.Custom
{
/// <summary>
/// Handles custom <see cref="IInnerShipStatus" /> system state and update payloads.
/// </summary>
public interface ICustomSystemType : ICustomMessage
{
ValueTask DeserializeAsync(IInnerShipStatus shipStatus, IClientPlayer sender, IClientPlayer? target, IMessageReader reader, bool initialState);
ValueTask<bool> HandleUpdateSystemAsync(IInnerShipStatus shipStatus, IInnerPlayerControl? playerControl, IClientPlayer sender, IClientPlayer? target, IMessageReader reader);
}
}
@@ -30,6 +30,11 @@ public static class MessageWriterExtensions
}
}
public static void Write(this IMessageWriter writer, IMessageWriter message, bool includeHeader)
{
writer.Write(message.ToByteArray(includeHeader));
}
public static void Write(this IMessageWriter writer, Vector2 vector)
{
writer.Write((ushort)(Mathf.ReverseLerp(vector.X) * (double)ushort.MaxValue));
@@ -6,22 +6,27 @@ namespace Empostor.Api.Net.Messages.Rpcs
{
public static class Rpc35UpdateSystem
{
public static void Serialize(IMessageWriter writer, SystemTypes systemType, IInnerPlayerControl playerControl, ushort sequenceId, byte state, byte ventId)
public static void Serialize(IMessageWriter writer, SystemTypes systemType, IInnerPlayerControl playerControl, IMessageWriter payload)
{
writer.Write((byte)systemType);
writer.Write(playerControl);
writer.Write(sequenceId);
writer.Write(state);
writer.Write(ventId);
writer.Write(payload, false);
}
public static void Deserialize(IMessageReader reader, IGame game, out SystemTypes systemType, out IInnerPlayerControl? playerControl, out ushort sequenceId, out byte state, out byte ventId)
public static void Serialize(IMessageWriter writer, SystemTypes systemType, IInnerPlayerControl playerControl, byte amount)
{
writer.Write((byte)systemType);
writer.Write(playerControl);
writer.Write(amount);
}
public static void Deserialize(IMessageReader reader, IGame game, out SystemTypes systemType, out IInnerPlayerControl? playerControl, out IMessageReader payload)
{
systemType = (SystemTypes)reader.ReadByte();
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
sequenceId = reader.ReadUInt16();
state = reader.ReadByte();
ventId = reader.ReadByte();
// The system payload is appended directly with Write(payload, false); it is not a nested message.
payload = reader;
}
}
}
@@ -10,8 +10,9 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
internal class InnerAirshipStatus : InnerShipStatus, IInnerAirshipStatus
{
public InnerAirshipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game, MapTypes.Airship)
public InnerAirshipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, ICustomMessageManager<ICustomSystemType> customSystemManager, Game game) : base(customMessageManager, customSystemManager, game, MapTypes.Airship)
{
InitializeSystems();
}
public Vector2 PreSpawnLocation { get; } = new Vector2(-25f, 40f);
@@ -35,13 +36,14 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
base.AddSystems(systems);
systems.Add(SystemTypes.Doors, new DoorsSystemType(Doors));
systems.Add(SystemTypes.Doors, new DoorsSystemType(Doors, Data.Doors));
systems.Add(SystemTypes.Comms, new HudOverrideSystemType());
systems.Add(SystemTypes.GapRoom, new MovingPlatformBehaviour());
systems.Add(SystemTypes.Reactor, new HeliSabotageSystemType());
systems.Add(SystemTypes.Decontamination, new ElectricalDoors(Doors));
systems.Add(SystemTypes.Decontamination2, new AutoDoorsSystemType(Doors));
systems.Add(SystemTypes.HeliSabotage, new HeliSabotageSystemType());
systems.Add(SystemTypes.Decontamination, new ElectricalDoors());
systems.Add(SystemTypes.Decontamination2, new AutoDoorsSystemType(Doors, Data.Doors));
systems.Add(SystemTypes.Security, new SecurityCameraSystemType());
systems.Add(SystemTypes.Ventilation, new VentilationSystemType());
}
}
}
@@ -9,19 +9,21 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
internal class InnerDleksShipStatus : InnerShipStatus, IInnerDleksShipStatus
{
public InnerDleksShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game, MapTypes.Dleks)
public InnerDleksShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, ICustomMessageManager<ICustomSystemType> customSystemManager, Game game) : base(customMessageManager, customSystemManager, game, MapTypes.Dleks)
{
InitializeSystems();
}
protected override void AddSystems(Dictionary<SystemTypes, ISystemType> systems)
{
base.AddSystems(systems);
systems.Add(SystemTypes.Doors, new AutoDoorsSystemType(Doors));
systems.Add(SystemTypes.Doors, new AutoDoorsSystemType(Doors, Data.Doors));
systems.Add(SystemTypes.Comms, new HudOverrideSystemType());
systems.Add(SystemTypes.Security, new SecurityCameraSystemType());
systems.Add(SystemTypes.Reactor, new ReactorSystemType());
systems.Add(SystemTypes.LifeSupp, new LifeSuppSystemType());
systems.Add(SystemTypes.Reactor, new ReactorSystemType(30f, SystemTypes.Reactor));
systems.Add(SystemTypes.LifeSupp, new LifeSuppSystemType(30f));
systems.Add(SystemTypes.Ventilation, new VentilationSystemType());
}
}
}
@@ -8,17 +8,17 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
internal class InnerFungleShipStatus : InnerShipStatus
{
public InnerFungleShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game, MapTypes.Fungle)
public InnerFungleShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, ICustomMessageManager<ICustomSystemType> customSystemManager, Game game) : base(customMessageManager, customSystemManager, game, MapTypes.Fungle)
{
InitializeSystems();
}
protected override void AddSystems(Dictionary<SystemTypes, ISystemType> systems)
{
base.AddSystems(systems);
systems.Add(SystemTypes.Comms, new HudOverrideSystemType());
systems.Add(SystemTypes.Reactor, new ReactorSystemType());
systems.Add(SystemTypes.Doors, new DoorsSystemType(Doors));
systems.Add(SystemTypes.Ventilation, new VentilationSystemType());
systems.Add(SystemTypes.Comms, new HqHudSystemType());
systems.Add(SystemTypes.Reactor, new ReactorSystemType(60f, SystemTypes.Reactor));
systems.Add(SystemTypes.Doors, new DoorsSystemType(Doors, Data.Doors));
systems.Add(SystemTypes.MushroomMixupSabotage, new MushroomMixupSabotageSystemType());
}
}
@@ -9,17 +9,21 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
internal class InnerMiraShipStatus : InnerShipStatus, IInnerMiraShipStatus
{
public InnerMiraShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game, MapTypes.MiraHQ)
public InnerMiraShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, ICustomMessageManager<ICustomSystemType> customSystemManager, Game game) : base(customMessageManager, customSystemManager, game, MapTypes.MiraHQ)
{
InitializeSystems();
}
protected override void AddSystems(Dictionary<SystemTypes, ISystemType> systems)
{
base.AddSystems(systems);
systems.Add(SystemTypes.Comms, new HudOverrideSystemType());
systems.Add(SystemTypes.Reactor, new ReactorSystemType());
systems.Add(SystemTypes.LifeSupp, new LifeSuppSystemType());
systems.Add(SystemTypes.Comms, new HqHudSystemType());
systems.Add(SystemTypes.Reactor, new ReactorSystemType(45f, SystemTypes.Reactor));
systems.Add(SystemTypes.LifeSupp, new LifeSuppSystemType(45f));
systems.Add(SystemTypes.Ventilation, new VentilationSystemType());
systems.Add(SystemTypes.Decontamination, new DeconSystemType());
systems.Add(SystemTypes.Decontamination2, new DeconSystemType());
}
}
}
@@ -10,8 +10,9 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
internal class InnerPolusShipStatus : InnerShipStatus, IInnerPolusShipStatus
{
public InnerPolusShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game, MapTypes.Polus)
public InnerPolusShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, ICustomMessageManager<ICustomSystemType> customSystemManager, Game game) : base(customMessageManager, customSystemManager, game, MapTypes.Polus)
{
InitializeSystems();
}
public override Vector2 GetSpawnLocation(InnerPlayerControl player, int numPlayers, bool initialSpawn)
@@ -37,10 +38,13 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
base.AddSystems(systems);
systems.Add(SystemTypes.Doors, new DoorsSystemType(Doors));
systems.Add(SystemTypes.Doors, new DoorsSystemType(Doors, Data.Doors));
systems.Add(SystemTypes.Comms, new HudOverrideSystemType());
systems.Add(SystemTypes.Security, new SecurityCameraSystemType());
systems.Add(SystemTypes.Laboratory, new ReactorSystemType());
systems.Add(SystemTypes.Ventilation, new VentilationSystemType());
systems.Add(SystemTypes.Laboratory, new ReactorSystemType(60f, SystemTypes.Laboratory));
systems.Add(SystemTypes.Decontamination, new DeconSystemType());
systems.Add(SystemTypes.Decontamination2, new DeconSystemType());
}
}
}
@@ -8,6 +8,7 @@ using Empostor.Api.Innersloth.Maps;
using Empostor.Api.Net;
using Empostor.Api.Net.Custom;
using Empostor.Api.Net.Inner;
using Empostor.Api.Net.Inner.Objects;
using Empostor.Api.Net.Inner.Objects.ShipStatus;
using Empostor.Api.Net.Messages.Rpcs;
using Empostor.Server.Net.Inner.Objects.Systems;
@@ -18,10 +19,13 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
internal abstract class InnerShipStatus : InnerNetObject, IInnerShipStatus
{
private readonly ICustomMessageManager<ICustomSystemType> _customSystemManager;
private readonly Dictionary<SystemTypes, ISystemType> _systems = new Dictionary<SystemTypes, ISystemType>();
protected InnerShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, MapTypes mapType) : base(customMessageManager, game)
protected InnerShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, ICustomMessageManager<ICustomSystemType> customSystemManager, Game game, MapTypes mapType) : base(customMessageManager, game)
{
_customSystemManager = customSystemManager;
Components.Add(this);
MapType = mapType;
@@ -35,22 +39,21 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
public Dictionary<int, bool> Doors { get; }
internal override ValueTask OnSpawnAsync()
{
for (var i = 0; i < Doors.Count; i++)
{
Doors.Add(i, false);
}
AddSystems(_systems);
_systems.Add(SystemTypes.Sabotage, new SabotageSystemType(_systems.Values.OfType<IActivatable>().ToArray()));
return base.OnSpawnAsync();
}
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
var result = false;
foreach (var systemType in SystemTypeHelpers.AllTypes)
{
if (_systems.TryGetValue(systemType, out var value))
{
result = true;
writer.StartMessage((byte)systemType);
value.Serialize(writer, initialState);
writer.EndMessage();
}
}
return new ValueTask<bool>(result);
}
public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer? target, IMessageReader reader, bool initialState)
@@ -62,12 +65,16 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
while (reader.Position < reader.Length)
{
var messageReader = reader.ReadMessage();
using var messageReader = reader.ReadMessage();
var type = (SystemTypes)messageReader.Tag;
if (_systems.TryGetValue(type, out var value))
{
value.Deserialize(messageReader, initialState);
}
else if (_customSystemManager.TryGet(messageReader.Tag, out var customSystem))
{
await customSystem.DeserializeAsync(this, sender, target, messageReader, initialState);
}
}
}
@@ -84,6 +91,7 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
}
Rpc27CloseDoorsOfType.Deserialize(reader, out var systemType);
CloseDoorsOfType(systemType);
break;
}
@@ -94,8 +102,20 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
return false;
}
// TODO: properly deserialize this RPC
// Rpc35UpdateSystem.Deserialize(reader, Game, out var systemType, out var playerControl, out var sequenceId, out var state, out var ventId);
Rpc35UpdateSystem.Deserialize(reader, Game, out var systemType, out var playerControl, out var payload);
if (_systems.TryGetValue(systemType, out var system))
{
system.UpdateSystem(playerControl, payload);
}
else if (_customSystemManager.TryGet((byte)systemType, out var customSystem))
{
return await customSystem.HandleUpdateSystemAsync(this, playerControl, sender, target, payload);
}
else if (await sender.Client.ReportCheatAsync(call, CheatCategory.ProtocolExtension, $"Client sent unregistered system type {(byte)systemType}"))
{
return false;
}
break;
}
@@ -143,6 +163,58 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
systems.Add(SystemTypes.MedBay, new MedScanSystem());
}
protected void InitializeSystems()
{
for (var i = 0; i < Data.Doors.Count; i++)
{
Doors[i] = true;
}
AddSystems(_systems);
_systems.Add(SystemTypes.Sabotage, new SabotageSystemType(_systems.Values.OfType<IActivatable>().ToArray(), UpdateSystem));
}
protected void UpdateSystem(IInnerPlayerControl? playerControl, SystemTypes systemType, byte amount)
{
if (!_systems.TryGetValue(systemType, out var system))
{
return;
}
switch (system)
{
case SwitchSystem switchSystem:
switchSystem.UpdateSystem(playerControl, amount);
break;
case ReactorSystemType reactorSystem:
reactorSystem.UpdateSystem(playerControl, amount);
break;
case LifeSuppSystemType lifeSuppSystem:
lifeSuppSystem.UpdateSystem(playerControl, amount);
break;
case HudOverrideSystemType hudOverrideSystem:
hudOverrideSystem.UpdateSystem(playerControl, amount);
break;
case HqHudSystemType hqHudSystem:
hqHudSystem.UpdateSystem(playerControl, amount);
break;
case HeliSabotageSystemType heliSabotageSystem:
heliSabotageSystem.UpdateSystem(playerControl, amount);
break;
case MushroomMixupSabotageSystemType mushroomMixupSabotageSystem:
mushroomMixupSabotageSystem.UpdateSystem(playerControl, amount);
break;
}
}
protected void CloseDoorsOfType(SystemTypes room)
{
if (_systems.TryGetValue(SystemTypes.Doors, out var system) && system is IDoorSystem doorSystem)
{
doorSystem.CloseDoorsOfType(room);
}
}
private static Vector2 Rotate(Vector2 self, float degrees)
{
var f = 0.017453292f * degrees;
@@ -9,19 +9,21 @@ namespace Empostor.Server.Net.Inner.Objects.ShipStatus
{
internal class InnerSkeldShipStatus : InnerShipStatus, IInnerSkeldShipStatus
{
public InnerSkeldShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game, MapTypes.Skeld)
public InnerSkeldShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, ICustomMessageManager<ICustomSystemType> customSystemManager, Game game) : base(customMessageManager, customSystemManager, game, MapTypes.Skeld)
{
InitializeSystems();
}
protected override void AddSystems(Dictionary<SystemTypes, ISystemType> systems)
{
base.AddSystems(systems);
systems.Add(SystemTypes.Doors, new AutoDoorsSystemType(Doors));
systems.Add(SystemTypes.Doors, new AutoDoorsSystemType(Doors, Data.Doors));
systems.Add(SystemTypes.Comms, new HudOverrideSystemType());
systems.Add(SystemTypes.Security, new SecurityCameraSystemType());
systems.Add(SystemTypes.Reactor, new ReactorSystemType());
systems.Add(SystemTypes.LifeSupp, new LifeSuppSystemType());
systems.Add(SystemTypes.Reactor, new ReactorSystemType(30f, SystemTypes.Reactor));
systems.Add(SystemTypes.LifeSupp, new LifeSuppSystemType(30f));
systems.Add(SystemTypes.Ventilation, new VentilationSystemType());
}
}
}
@@ -1,9 +1,13 @@
namespace Empostor.Server.Net.Inner.Objects.Systems
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems
{
public interface ISystemType
{
void Serialize(IMessageWriter writer, bool initialState);
void Deserialize(IMessageReader reader, bool initialState);
void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader);
}
}
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
namespace Empostor.Server.Net.Inner.Objects.Systems
{
internal sealed class SequenceBuffer<T>
{
private readonly List<SequencedData> _buffer = new();
public SequenceBuffer(ushort sidStart = 0)
{
LastSid = sidStart;
}
public ushort LastSid { get; set; }
public void Add(ushort sid, T info)
{
_buffer.Add(new SequencedData(sid, info));
}
public void BumpSid()
{
LastSid++;
}
public bool IsInvalidSid(ushort sid)
{
return !SidGreaterThan(sid, LastSid);
}
public bool IsNextSid(ushort sid)
{
return sid == (ushort)(LastSid + 1);
}
public IEnumerable<T> SubsequentObjs()
{
_buffer.Sort();
while (_buffer.Count > 0 && IsNextSid(_buffer[0].Sid))
{
var result = _buffer[0].Data;
_buffer.RemoveAt(0);
yield return result;
}
}
private static bool SidGreaterThan(ushort newSid, ushort prevSid)
{
var num = (ushort)(prevSid + (uint)short.MaxValue);
return (int)prevSid < (int)num
? newSid > prevSid && newSid <= num
: newSid > prevSid || newSid <= num;
}
private readonly struct SequencedData : IComparable<SequencedData>
{
public SequencedData(ushort sid, T data)
{
Sid = sid;
Data = data;
}
public ushort Sid { get; }
public T Data { get; }
public int CompareTo(SequencedData other)
{
return Sid.CompareTo(other.Sid);
}
}
}
}
@@ -1,20 +1,44 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Empostor.Api.Innersloth.Maps;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class AutoDoorsSystemType : ISystemType
public class AutoDoorsSystemType : ISystemType, IDoorSystem
{
private readonly Dictionary<int, bool> _doors;
private readonly IReadOnlyDictionary<int, DoorData> _doorData;
private uint _dirtyBits;
public AutoDoorsSystemType(Dictionary<int, bool> doors)
public AutoDoorsSystemType(Dictionary<int, bool> doors, IReadOnlyDictionary<int, DoorData> doorData)
{
_doors = doors;
_doorData = doorData;
}
public bool IsActive => _doors.Values.Any(open => !open);
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
if (initialState)
{
for (var i = 0; i < _doors.Count; i++)
{
writer.Write(_doors[i]);
}
return;
}
writer.WritePacked(_dirtyBits);
for (var i = 0; i < _doors.Count; i++)
{
if ((_dirtyBits & (uint)(1 << i)) != 0)
{
writer.Write(_doors[i]);
}
}
}
public void Deserialize(IMessageReader reader, bool initialState)
@@ -32,12 +56,28 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
for (var i = 0; i < _doors.Count; i++)
{
if ((num & 1 << i) != 0)
if ((num & (uint)(1 << i)) != 0)
{
_doors[i] = reader.ReadBoolean();
}
}
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
}
public void CloseDoorsOfType(SystemTypes room)
{
for (var i = 0; i < _doors.Count; i++)
{
if (_doorData.TryGetValue(i, out var door) && door.Room == room)
{
_doors[i] = false;
_dirtyBits |= (uint)(1 << i);
}
}
}
}
}
@@ -0,0 +1,65 @@
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class DeconSystemType : ISystemType
{
private const byte HeadUpCommand = 1;
private const byte HeadDownCommand = 2;
private const byte HeadUpInsideCommand = 3;
private const byte HeadDownInsideCommand = 4;
public enum States : byte
{
Idle = 0,
Enter = 1,
Closed = 2,
Exit = 4,
HeadingUp = 8,
}
public byte Timer { get; private set; }
public States CurState { get; private set; }
public void Serialize(IMessageWriter writer, bool initialState)
{
writer.Write(Timer);
writer.Write((byte)CurState);
}
public void Deserialize(IMessageReader reader, bool initialState)
{
Timer = reader.ReadByte();
CurState = (States)reader.ReadByte();
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
if (CurState != States.Idle)
{
return;
}
switch (reader.ReadByte())
{
case HeadUpCommand:
CurState = States.Enter | States.HeadingUp;
Timer = 3;
break;
case HeadDownCommand:
CurState = States.Enter;
Timer = 3;
break;
case HeadUpInsideCommand:
CurState = States.Exit | States.HeadingUp;
Timer = 3;
break;
case HeadDownInsideCommand:
CurState = States.Exit;
Timer = 3;
break;
}
}
}
}
@@ -1,21 +1,36 @@
using System;
using System.Collections.Generic;
using Empostor.Api.Innersloth.Maps;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class DoorsSystemType : ISystemType
public class DoorsSystemType : ISystemType, IDoorSystem
{
private readonly Dictionary<SystemTypes, float> _timers = new Dictionary<SystemTypes, float>();
private readonly Dictionary<SystemTypes, float> _timers = new();
private readonly Dictionary<int, bool> _doors;
private readonly IReadOnlyDictionary<int, DoorData> _doorData;
public DoorsSystemType(Dictionary<int, bool> doors)
public DoorsSystemType(Dictionary<int, bool> doors, IReadOnlyDictionary<int, DoorData> doorData)
{
_doors = doors;
_doorData = doorData;
}
public bool IsActive => false;
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.Write((byte)_timers.Count);
foreach (var timer in _timers)
{
writer.Write((byte)timer.Key);
writer.Write(timer.Value);
}
for (var i = 0; i < _doors.Count; i++)
{
writer.Write(_doors[i]);
}
}
public void Deserialize(IMessageReader reader, bool initialState)
@@ -34,5 +49,28 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
_doors[j] = reader.ReadBoolean();
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
var value = reader.ReadByte();
var doorId = value & 0x1f;
if ((value & 0xc0) == 0x40 && _doors.ContainsKey(doorId))
{
_doors[doorId] = true;
}
}
public void CloseDoorsOfType(SystemTypes room)
{
_timers[room] = 30f;
for (var i = 0; i < _doors.Count; i++)
{
if (_doorData.TryGetValue(i, out var door) && door.Room == room)
{
_doors[i] = false;
}
}
}
}
}
@@ -1,29 +1,23 @@
using System;
using System.Collections.Generic;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class ElectricalDoors : ISystemType
{
private readonly Dictionary<int, bool> _doors;
public ElectricalDoors(Dictionary<int, bool> doors)
{
_doors = doors;
}
private uint _state;
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.Write(_state);
}
public void Deserialize(IMessageReader reader, bool initialState)
{
var num = reader.ReadUInt32();
for (var i = 0; i < _doors.Count; i++)
{
_doors[i] = (num & (ulong)(1L << (i & 31))) > 0UL;
}
_state = reader.ReadUInt32();
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
}
}
}
@@ -1,52 +1,98 @@
using System;
using System.Collections.Generic;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class HeliSabotageSystemType : ISystemType, IActivatable
{
private readonly HashSet<ActiveConsoleData> _activeConsoles = new();
private readonly HashSet<byte> _completedConsoles = new();
public HeliSabotageSystemType()
{
Countdown = 10000f;
ActiveConsoles = new HashSet<Tuple<byte, byte>>();
CompletedConsoles = new HashSet<byte>();
_completedConsoles.Add(0);
_completedConsoles.Add(1);
}
public float Countdown { get; private set; }
public float Timer { get; private set; }
public HashSet<Tuple<byte, byte>> ActiveConsoles { get; }
public HashSet<byte> CompletedConsoles { get; }
public bool IsActive => Countdown < 10000.0;
public bool IsActive => _completedConsoles.Count < 2;
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.Write(Countdown);
writer.Write(Timer);
writer.WritePacked(_activeConsoles.Count);
foreach (var activeConsole in _activeConsoles)
{
writer.Write(activeConsole.PlayerId);
writer.Write(activeConsole.ConsoleId);
}
writer.WritePacked(_completedConsoles.Count);
foreach (var completedConsole in _completedConsoles)
{
writer.Write(completedConsole);
}
}
public void Deserialize(IMessageReader reader, bool initialState)
{
Countdown = reader.ReadSingle();
Timer = reader.ReadSingle();
ActiveConsoles.Clear(); // TODO: Thread safety
CompletedConsoles.Clear(); // TODO: Thread safety
_activeConsoles.Clear();
_completedConsoles.Clear();
var activeCount = reader.ReadPackedUInt32();
for (var i = 0; i < activeCount; i++)
{
ActiveConsoles.Add(new Tuple<byte, byte>(reader.ReadByte(), reader.ReadByte()));
_activeConsoles.Add(new ActiveConsoleData(reader.ReadByte(), reader.ReadByte()));
}
var completedCount = reader.ReadPackedUInt32();
for (var i = 0; i < completedCount; i++)
{
CompletedConsoles.Add(reader.ReadByte());
_completedConsoles.Add(reader.ReadByte());
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
UpdateSystem(playerControl, reader.ReadByte());
}
internal void UpdateSystem(IInnerPlayerControl? playerControl, byte amount)
{
var value = amount;
var consoleId = (byte)(value & 0x0f);
switch (value & 0xf0)
{
case 0x80:
Timer = -1f;
Countdown = 90f;
_completedConsoles.Clear();
_activeConsoles.Clear();
break;
case 0x40 when playerControl != null:
_activeConsoles.Add(new ActiveConsoleData(playerControl.PlayerId, consoleId));
break;
case 0x20 when playerControl != null:
_activeConsoles.Remove(new ActiveConsoleData(playerControl.PlayerId, consoleId));
break;
case 0x10:
Timer = 10f;
_completedConsoles.Add(consoleId);
break;
}
}
private readonly record struct ActiveConsoleData(byte PlayerId, byte ConsoleId);
}
}
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class HqHudSystemType : ISystemType, IActivatable
{
private readonly HashSet<Tuple<byte, byte>> _activeConsoles = new();
private readonly HashSet<byte> _completedConsoles = new();
public HqHudSystemType()
{
_completedConsoles.Add(0);
_completedConsoles.Add(1);
}
public float Timer { get; private set; }
public bool IsActive => _completedConsoles.Count < 2;
public float NumComplete => _completedConsoles.Count;
public float PercentActive => Timer / 10f;
public void Serialize(IMessageWriter writer, bool initialState)
{
writer.WritePacked(_activeConsoles.Count);
foreach (var activeConsole in _activeConsoles)
{
writer.Write(activeConsole.Item1);
writer.Write(activeConsole.Item2);
}
writer.WritePacked(_completedConsoles.Count);
foreach (var completedConsole in _completedConsoles)
{
writer.Write(completedConsole);
}
}
public void Deserialize(IMessageReader reader, bool initialState)
{
_activeConsoles.Clear();
var activeCount = reader.ReadPackedInt32();
for (var i = 0; i < activeCount; i++)
{
_activeConsoles.Add(new Tuple<byte, byte>(reader.ReadByte(), reader.ReadByte()));
}
_completedConsoles.Clear();
var completedCount = reader.ReadPackedInt32();
for (var i = 0; i < completedCount; i++)
{
_completedConsoles.Add(reader.ReadByte());
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
UpdateSystem(playerControl, reader.ReadByte());
}
internal void UpdateSystem(IInnerPlayerControl? playerControl, byte amount)
{
var value = amount;
var consoleId = (byte)(value & 0x0f);
switch (value & 0xf0)
{
case 0x80:
Timer = -1f;
_completedConsoles.Clear();
_activeConsoles.Clear();
break;
case 0x40 when playerControl != null:
_activeConsoles.Add(new Tuple<byte, byte>(playerControl.PlayerId, consoleId));
break;
case 0x20 when playerControl != null:
_activeConsoles.Remove(new Tuple<byte, byte>(playerControl.PlayerId, consoleId));
break;
case 0x10:
Timer = 10f;
_completedConsoles.Add(consoleId);
break;
}
}
internal bool IsConsoleActive(int consoleId)
{
return _activeConsoles.Any(x => x.Item2 == (byte)consoleId);
}
internal bool IsConsoleOkay(int consoleId)
{
return _completedConsoles.Contains((byte)consoleId);
}
}
}
@@ -1,4 +1,4 @@
using System;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
@@ -8,12 +8,22 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.Write(IsActive);
}
public void Deserialize(IMessageReader reader, bool initialState)
{
IsActive = reader.ReadBoolean();
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
UpdateSystem(playerControl, reader.ReadByte());
}
internal void UpdateSystem(IInnerPlayerControl? playerControl, byte amount)
{
IsActive = (amount & 0x80) != 0;
}
}
}
@@ -0,0 +1,7 @@
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public interface IDoorSystem : IActivatable
{
void CloseDoorsOfType(SystemTypes room);
}
}
@@ -1,12 +1,15 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class LifeSuppSystemType : ISystemType, IActivatable
{
public LifeSuppSystemType()
private readonly float _lifeSuppDuration;
public LifeSuppSystemType(float lifeSuppDuration = 30f)
{
_lifeSuppDuration = lifeSuppDuration;
Countdown = 10000f;
CompletedConsoles = new HashSet<int>();
}
@@ -15,11 +18,16 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
public HashSet<int> CompletedConsoles { get; }
public bool IsActive => Countdown < 10000.0;
public bool IsActive => Countdown < 10000.0f;
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.Write(Countdown);
writer.WritePacked(CompletedConsoles.Count);
foreach (var consoleId in CompletedConsoles)
{
writer.WritePacked(consoleId);
}
}
public void Deserialize(IMessageReader reader, bool initialState)
@@ -31,7 +39,7 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
return;
}
CompletedConsoles.Clear(); // TODO: Thread safety
CompletedConsoles.Clear();
var num = reader.ReadPackedInt32();
@@ -40,5 +48,34 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
CompletedConsoles.Add(reader.ReadPackedInt32());
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
UpdateSystem(playerControl, reader.ReadByte());
}
internal void UpdateSystem(IInnerPlayerControl? playerControl, byte amount)
{
var value = amount;
var consoleId = value & 3;
if (value == 0x80 && !IsActive)
{
Countdown = _lifeSuppDuration;
CompletedConsoles.Clear();
}
else if (value == 0x10)
{
Countdown = 10000f;
}
else if ((value & 0x40) != 0)
{
CompletedConsoles.Add(consoleId);
if (CompletedConsoles.Count >= 2)
{
Countdown = 10000f;
}
}
}
}
}
@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
@@ -12,9 +12,15 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
public List<byte> UsersList { get; }
public byte CurrentUser { get; private set; } = byte.MaxValue;
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.WritePacked(UsersList.Count);
foreach (var user in UsersList)
{
writer.Write(user);
}
}
public void Deserialize(IMessageReader reader, bool initialState)
@@ -28,5 +34,26 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
UsersList.Add(reader.ReadByte());
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
var value = reader.ReadByte();
var playerId = (byte)(value & 0x1f);
if ((value & 0x80) != 0)
{
if (!UsersList.Contains(playerId))
{
UsersList.Add(playerId);
}
}
else if ((value & 0x40) != 0)
{
UsersList.RemoveAll(x => x == playerId);
if (CurrentUser == playerId)
{
CurrentUser = byte.MaxValue;
}
}
}
}
}
@@ -1,21 +1,55 @@
using System;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class MovingPlatformBehaviour : ISystemType, IActivatable
public class MovingPlatformBehaviour : ISystemType
{
public bool IsActive { get; private set; }
private byte _useId;
public uint TargetNetId { get; private set; } = uint.MaxValue;
public bool IsLeft { get; private set; } = true;
public bool InUse => TargetNetId != uint.MaxValue;
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
_useId++;
writer.Write(_useId);
writer.Write(TargetNetId);
writer.Write(IsLeft);
}
public void Deserialize(IMessageReader reader, bool initialState)
{
var sid = reader.ReadByte();
var targetId = reader.ReadUInt32();
var isLeft = reader.ReadBoolean();
if (initialState)
{
_useId = reader.ReadByte();
TargetNetId = reader.ReadUInt32();
IsLeft = reader.ReadBoolean();
return;
}
var newSid = reader.ReadByte();
if (SidGreaterThan(newSid, _useId))
{
_useId = newSid;
TargetNetId = reader.ReadUInt32();
IsLeft = reader.ReadBoolean();
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
}
private static bool SidGreaterThan(byte newSid, byte prevSid)
{
var num = (byte)(prevSid + sbyte.MaxValue);
return prevSid < num
? newSid > prevSid && newSid <= num
: newSid > prevSid || newSid <= num;
}
}
}
@@ -1,8 +1,9 @@
using System.Collections.Generic;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus;
public class MushroomMixupSabotageSystemType : ISystemType
public class MushroomMixupSabotageSystemType : ISystemType, IActivatable
{
private readonly Dictionary<byte, CondensedOutfit> _currentMixups = new();
private State _currentState;
@@ -15,6 +16,8 @@ public class MushroomMixupSabotageSystemType : ISystemType
IdleButMixedUp,
}
public bool IsActive => _currentSecondsUntilHeal > 0f;
public void Serialize(IMessageWriter writer, bool initialState)
{
writer.Write((byte)_currentState);
@@ -26,6 +29,11 @@ public class MushroomMixupSabotageSystemType : ISystemType
writer.Write(playerId);
outfit.Serialize(writer);
}
if (_currentState == State.JustTriggered)
{
_currentState = State.IdleButMixedUp;
}
}
public void Deserialize(IMessageReader reader, bool initialState)
@@ -40,7 +48,22 @@ public class MushroomMixupSabotageSystemType : ISystemType
var playerId = reader.ReadByte();
var outfit = CondensedOutfit.Deserialize(reader);
_currentMixups.Add(playerId, outfit);
_currentMixups[playerId] = outfit;
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
UpdateSystem(playerControl, reader.ReadByte());
}
internal void UpdateSystem(IInnerPlayerControl? playerControl, byte amount)
{
var operation = amount;
if (operation == 1)
{
_currentMixups.Clear();
_currentState = State.JustTriggered;
}
}
@@ -1,31 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class ReactorSystemType : ISystemType, IActivatable
{
public ReactorSystemType()
private readonly float _reactorDuration;
public ReactorSystemType(float reactorDuration = 30f, SystemTypes system = SystemTypes.Reactor)
{
_reactorDuration = reactorDuration;
System = system;
Countdown = 10000f;
UserConsolePairs = new HashSet<Tuple<byte, byte>>();
}
public SystemTypes System { get; }
public float Countdown { get; private set; }
public HashSet<Tuple<byte, byte>> UserConsolePairs { get; }
public bool IsActive => Countdown < 10000.0;
public bool IsActive => Countdown < 10000.0f;
public int UserCount
{
get
{
var result = 0;
var consoles = 0;
foreach (var pair in UserConsolePairs)
{
var bit = 1 << pair.Item2;
if ((bit & consoles) == 0)
{
result++;
consoles |= bit;
}
}
return result;
}
}
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.Write(Countdown);
writer.WritePacked(UserConsolePairs.Count);
foreach (var pair in UserConsolePairs)
{
writer.Write(pair.Item1);
writer.Write(pair.Item2);
}
}
public void Deserialize(IMessageReader reader, bool initialState)
{
Countdown = reader.ReadSingle();
UserConsolePairs.Clear(); // TODO: Thread safety
UserConsolePairs.Clear();
var count = reader.ReadPackedInt32();
@@ -34,5 +68,43 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
UserConsolePairs.Add(new Tuple<byte, byte>(reader.ReadByte(), reader.ReadByte()));
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
UpdateSystem(playerControl, reader.ReadByte());
}
internal void UpdateSystem(IInnerPlayerControl? playerControl, byte amount)
{
var value = amount;
var consoleId = (byte)(value & 3);
if (value == 0x80 && !IsActive)
{
Countdown = _reactorDuration;
UserConsolePairs.Clear();
}
else if (value == 0x10)
{
Countdown = 10000f;
}
else if ((value & 0x40) != 0 && playerControl != null)
{
UserConsolePairs.Add(new Tuple<byte, byte>(playerControl.PlayerId, consoleId));
if (UserCount >= 2)
{
Countdown = 10000f;
}
}
else if ((value & 0x20) != 0 && playerControl != null)
{
UserConsolePairs.Remove(new Tuple<byte, byte>(playerControl.PlayerId, consoleId));
}
}
public bool GetConsoleComplete(int consoleId)
{
return UserConsolePairs.Any(x => x.Item2 == consoleId);
}
}
}
@@ -1,26 +1,69 @@
using System;
using System.Linq;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class SabotageSystemType : ISystemType
{
private readonly IActivatable[] _specials;
private readonly Action<IInnerPlayerControl?, SystemTypes, byte> _updateSystem;
public SabotageSystemType(IActivatable[] specials)
public SabotageSystemType(IActivatable[] specials, Action<IInnerPlayerControl?, SystemTypes, byte> updateSystem)
{
_specials = specials;
_specials = specials.Where(x => x is not IDoorSystem).ToArray();
_updateSystem = updateSystem;
}
public float Timer { get; set; }
public float Timer { get; private set; }
public bool AnyActive => _specials.Any(x => x.IsActive);
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.Write(Timer);
}
public void Deserialize(IMessageReader reader, bool initialState)
{
Timer = reader.ReadSingle();
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
if (Timer > 0f)
{
return;
}
var systemType = (SystemTypes)reader.ReadByte();
switch (systemType)
{
case SystemTypes.Laboratory:
case SystemTypes.Reactor:
case SystemTypes.HeliSabotage:
case SystemTypes.LifeSupp:
case SystemTypes.Comms:
_updateSystem(playerControl, systemType, 0x80);
break;
case SystemTypes.Electrical:
var value = (byte)4;
for (var i = 0; i < 5; i++)
{
if (Random.Shared.Next(2) == 0)
{
value |= (byte)(1 << i);
}
}
_updateSystem(playerControl, systemType, (byte)(value | 0x80));
break;
case SystemTypes.MushroomMixupSabotage:
_updateSystem(playerControl, systemType, 1);
break;
}
Timer = 30f;
}
}
}
@@ -1,19 +1,48 @@
using System;
using System.Collections.Generic;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class SecurityCameraSystemType : ISystemType
{
public byte InUse { get; internal set; }
private readonly HashSet<byte> _playersUsing = new();
public bool InUse => _playersUsing.Count > 0;
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.WritePacked(_playersUsing.Count);
foreach (var playerId in _playersUsing)
{
writer.Write(playerId);
}
}
public void Deserialize(IMessageReader reader, bool initialState)
{
InUse = reader.ReadByte();
_playersUsing.Clear();
var num = reader.ReadPackedInt32();
for (var i = 0; i < num; i++)
{
_playersUsing.Add(reader.ReadByte());
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
if (playerControl == null)
{
return;
}
if (reader.ReadByte() == 1)
{
_playersUsing.Add(playerControl.PlayerId);
}
else
{
_playersUsing.Remove(playerControl.PlayerId);
}
}
}
}
@@ -1,4 +1,4 @@
using System;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
@@ -10,11 +10,13 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
public byte Value { get; set; } = byte.MaxValue;
public bool IsActive { get; }
public bool IsActive => ExpectedSwitches != ActualSwitches;
public void Serialize(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
writer.Write(ExpectedSwitches);
writer.Write(ActualSwitches);
writer.Write(Value);
}
public void Deserialize(IMessageReader reader, bool initialState)
@@ -23,5 +25,23 @@ namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
ActualSwitches = reader.ReadByte();
Value = reader.ReadByte();
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
UpdateSystem(playerControl, reader.ReadByte());
}
internal void UpdateSystem(IInnerPlayerControl? playerControl, byte amount)
{
var value = amount;
if ((value & 0x80) != 0)
{
ActualSwitches ^= (byte)(value & 0x1f);
}
else
{
ActualSwitches ^= (byte)(1 << value);
}
}
}
}
@@ -0,0 +1,152 @@
using System.Collections.Generic;
using System.Linq;
using Empostor.Api.Net.Inner.Objects;
namespace Empostor.Server.Net.Inner.Objects.Systems.ShipStatus
{
public class VentilationSystemType : ISystemType
{
private readonly Dictionary<byte, SequenceBuffer<VentMoveInfo>> _sequenceBuffers = new();
private readonly Dictionary<byte, byte> _playersCleaningVents = new();
private readonly Dictionary<byte, byte> _playersInsideVents = new();
public enum Operation : byte
{
StartCleaning,
StopCleaning,
Enter,
Exit,
Move,
BootImpostors,
}
public void Serialize(IMessageWriter writer, bool initialState)
{
writer.WritePacked(_playersCleaningVents.Count);
foreach (var pair in _playersCleaningVents)
{
writer.Write(pair.Key);
writer.Write(pair.Value);
}
writer.WritePacked(_playersInsideVents.Count);
foreach (var pair in _playersInsideVents)
{
writer.Write(pair.Key);
writer.Write(pair.Value);
}
}
public void Deserialize(IMessageReader reader, bool initialState)
{
_playersCleaningVents.Clear();
var cleaningCount = reader.ReadPackedInt32();
for (var i = 0; i < cleaningCount; i++)
{
_playersCleaningVents[reader.ReadByte()] = reader.ReadByte();
}
_playersInsideVents.Clear();
var insideCount = reader.ReadPackedInt32();
for (var i = 0; i < insideCount; i++)
{
_playersInsideVents[reader.ReadByte()] = reader.ReadByte();
}
}
public void UpdateSystem(IInnerPlayerControl? playerControl, IMessageReader reader)
{
if (playerControl == null)
{
return;
}
var opId = reader.ReadUInt16();
var operation = (Operation)reader.ReadByte();
var ventId = reader.ReadByte();
var playerId = playerControl.PlayerId;
if (!_sequenceBuffers.TryGetValue(playerId, out var sequenceBuffer))
{
sequenceBuffer = new SequenceBuffer<VentMoveInfo>((ushort)(opId - 1));
_sequenceBuffers[playerId] = sequenceBuffer;
}
if (sequenceBuffer.IsInvalidSid(opId))
{
return;
}
if (sequenceBuffer.IsNextSid(opId))
{
PerformVentOp(playerId, operation, ventId, sequenceBuffer);
}
else
{
sequenceBuffer.Add(opId, new VentMoveInfo(operation, ventId, playerId));
}
foreach (var info in sequenceBuffer.SubsequentObjs())
{
PerformVentOp(info.PlayerId, info.Op, info.VentId, sequenceBuffer);
}
}
public bool IsVentCurrentlyBeingCleaned(int ventId)
{
return _playersCleaningVents.Any(x => x.Value == ventId);
}
public bool IsImpostorInsideVent(int ventId)
{
return _playersInsideVents.Any(x => x.Value == ventId);
}
private void PerformVentOp(byte playerId, Operation operation, byte ventId, SequenceBuffer<VentMoveInfo> sequenceBuffer)
{
sequenceBuffer.BumpSid();
switch (operation)
{
case Operation.StartCleaning:
BootImpostorsFromVent(ventId);
_playersCleaningVents[playerId] = ventId;
break;
case Operation.StopCleaning:
foreach (var pair in _playersCleaningVents.ToArray())
{
if (pair.Value == ventId)
{
_playersCleaningVents.Remove(pair.Key);
}
}
break;
case Operation.Enter:
_playersInsideVents[playerId] = ventId;
break;
case Operation.Exit:
_playersInsideVents.Remove(playerId);
break;
case Operation.Move:
_playersInsideVents[playerId] = ventId;
break;
case Operation.BootImpostors:
BootImpostorsFromVent(ventId);
break;
}
}
private void BootImpostorsFromVent(byte ventId)
{
foreach (var pair in _playersInsideVents.ToArray())
{
if (pair.Value == ventId)
{
_playersInsideVents.Remove(pair.Key);
}
}
}
private readonly record struct VentMoveInfo(Operation Op, byte VentId, byte PlayerId);
}
}
+1
View File
@@ -200,6 +200,7 @@ namespace Empostor.Server
services.AddHazel();
services.AddSingleton<ICustomMessageManager<ICustomRootMessage>, CustomMessageManager<ICustomRootMessage>>();
services.AddSingleton<ICustomMessageManager<ICustomRpc>, CustomMessageManager<ICustomRpc>>();
services.AddSingleton<ICustomMessageManager<ICustomSystemType>, CustomMessageManager<ICustomSystemType>>();
services.AddSingleton<IMessageWriterProvider, MessageWriterProvider>();
services.AddSingleton<IGameCodeFactory, GameCodeFactory>();
services.AddSingleton<IEventManager, EventManager>();