using System.Buffers.Binary;
using System.Runtime.CompilerServices;
-namespace Impostor.Api.Extensions
+namespace Impostor.Api
{
/// <summary>
/// Priovides a StreamReader-like api throught extensions
GameStates GameState { get; }
+ GameNet GameNet { get; }
+
IEnumerable<IClientPlayer> Players { get; }
IPEndPoint PublicIp { get; }
--- /dev/null
+using Impostor.Api.Innersloth.Net;
+using Impostor.Api.Innersloth.Net.Objects;
+
+namespace Impostor.Api.Innersloth.Data
+{
+ /// <summary>
+ /// Holds all data that is serialized over the network through GameData packets.
+ /// </summary>
+ public class GameNet
+ {
+ public InnerGameData GameData { get; internal set; }
+
+ internal void OnSpawn(InnerNetObject netObj)
+ {
+ if (netObj is InnerGameData data)
+ {
+ GameData = data;
+ }
+ }
+
+ internal void OnDestroy(InnerNetObject netObj)
+ {
+ if (netObj is InnerGameData)
+ {
+ GameData = null;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Api.Innersloth.Data
+{
+ public static class MapId
+ {
+ // TODO: Not sure if correct, check
+ public const int Skeld = 0;
+ public const int MiraHQ = 1;
+ public const int Polus = 2;
+ }
+}
\ No newline at end of file
-using Impostor.Api.Extensions;
-using Impostor.Api.Innersloth.Data;
+using Impostor.Api.Innersloth.Data;
using System;
using System.IO;
--- /dev/null
+using System.Collections.Generic;
+
+namespace Impostor.Api.Innersloth.Net
+{
+ public class GameObject
+ {
+ public GameObject()
+ {
+ Components = new List<object>();
+ }
+
+ protected List<object> Components { get; }
+
+ public List<T> GetComponentsInChildren<T>()
+ {
+ var result = new List<T>();
+
+ foreach (var component in Components)
+ {
+ if (component is T c)
+ {
+ result.Add(c);
+ }
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net
+{
+ public abstract class InnerNetObject : GameObject
+ {
+ public uint NetId { get; internal set; }
+
+ public int OwnerId { get; internal set; }
+
+ public SpawnFlags SpawnFlags { get; internal set; }
+
+ public abstract void HandleRpc(byte callId, IMessageReader reader);
+
+ public abstract bool Serialize(IMessageWriter writer, bool initialState);
+
+ public abstract void Deserialize(IMessageReader reader, bool initialState);
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Numerics;
+using Impostor.Api.Net.Messages;
+using Microsoft.Extensions.Logging;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Components
+{
+ public class InnerCustomNetworkTransform : InnerNetObject
+ {
+ private static readonly FloatRange XRange = new FloatRange(-40f, 40f);
+ private static readonly FloatRange YRange = new FloatRange(-40f, 40f);
+
+ private readonly ILogger<InnerCustomNetworkTransform> _logger;
+
+ private ushort _lastSequenceId;
+ private Vector2 _targetSyncPosition;
+ private Vector2 _targetSyncVelocity;
+
+ public InnerCustomNetworkTransform(ILogger<InnerCustomNetworkTransform> logger)
+ {
+ _logger = logger;
+ }
+
+ 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 static void WriteVector2(IMessageWriter writer, Vector2 vec)
+ {
+ writer.Write((ushort)(XRange.ReverseLerp(vec.X) * (double) ushort.MaxValue));
+ writer.Write((ushort)(YRange.ReverseLerp(vec.Y) * (double) ushort.MaxValue));
+ }
+
+ private static Vector2 ReadVector2(IMessageReader reader)
+ {
+ var v1 = reader.ReadUInt16() / (float) ushort.MaxValue;
+ var v2 = reader.ReadUInt16() / (float) ushort.MaxValue;
+
+ return new Vector2(XRange.Lerp(v1), YRange.Lerp(v2));
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ if (callId == 0)
+ {
+ SnapTo(ReadVector2(reader), reader.ReadUInt16());
+ }
+ else
+ {
+ _logger.LogWarning("InnerCustomNetworkTransform: Unknown rpc call {0}", callId);
+ }
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ if (initialState)
+ {
+ writer.Write(_lastSequenceId);
+ WriteVector2(writer, _targetSyncPosition);
+ WriteVector2(writer, _targetSyncVelocity);
+ return true;
+ }
+
+ // TODO: DirtyBits == 0 return false.
+ _lastSequenceId++;
+
+ writer.Write(_lastSequenceId);
+ WriteVector2(writer, _targetSyncPosition);
+ WriteVector2(writer, _targetSyncVelocity);
+ return true;
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ var sequenceId = reader.ReadUInt16();
+
+ if (initialState)
+ {
+ _lastSequenceId = sequenceId;
+ _targetSyncPosition = ReadVector2(reader);
+ _targetSyncVelocity = ReadVector2(reader);
+ }
+ else
+ {
+ if (!SidGreaterThan(sequenceId, _lastSequenceId))
+ {
+ return;
+ }
+
+ _lastSequenceId = sequenceId;
+ _targetSyncPosition = ReadVector2(reader);
+ _targetSyncVelocity = ReadVector2(reader);
+ }
+ }
+
+ private void SnapTo(Vector2 position, ushort minSid)
+ {
+ if (SidGreaterThan(minSid, _lastSequenceId))
+ {
+ return;
+ }
+
+ _lastSequenceId = minSid;
+ _targetSyncPosition = position;
+ _targetSyncVelocity = Vector2.Zero;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Components
+{
+ public class InnerPlayerPhysics : InnerNetObject
+ {
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Components
+{
+ public class InnerVoteBanSystem : InnerNetObject
+ {
+ private readonly Dictionary<int, int[]> _votes;
+
+ public InnerVoteBanSystem()
+ {
+ _votes = new Dictionary<int, int[]>();
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ var votes = _votes;
+ var unknown = reader.ReadByte();
+ if (unknown != 0)
+ {
+ for (var i = 0; i < unknown; i++)
+ {
+ var v4 = reader.ReadInt32();
+ if (v4 == 0)
+ {
+ break;
+ }
+
+ if (!votes.TryGetValue(v4, out var v12))
+ {
+ v12 = new int[3];
+ votes[v4] = v12;
+ }
+
+ for (var j = 0; j < 3; j++)
+ {
+ v12[j] = reader.ReadPackedInt32();
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects
+{
+ public partial class InnerGameData
+ {
+ public class PlayerInfo
+ {
+ public PlayerInfo(byte playerId)
+ {
+ PlayerId = playerId;
+ }
+
+ public byte PlayerId { get; }
+
+ public string PlayerName { get; private set; }
+
+ public byte ColorId { get; private set; }
+
+ public uint HatId { get; private set; }
+
+ public uint PetId { get; private set; }
+
+ public uint SkinId { get; private set; }
+
+ public bool Disconnected { get; private set; }
+
+ public bool IsImpostor { get; private set; }
+
+ public bool IsDead { get; private set; }
+
+ public List<TaskInfo> Tasks { get; private set; }
+
+ public void Serialize(IMessageWriter writer)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader)
+ {
+ PlayerName = reader.ReadString();
+ ColorId = reader.ReadByte();
+ HatId = reader.ReadPackedUInt32();
+ PetId = reader.ReadPackedUInt32();
+ SkinId = reader.ReadPackedUInt32();
+ var flag = reader.ReadByte();
+ Disconnected = (flag & 1) > 0;
+ IsImpostor = (flag & 2) > 0;
+ IsDead = (flag & 4) > 0;
+ var taskCount = reader.ReadByte();
+ Tasks = new List<TaskInfo>();
+ for (var i = 0; i < taskCount; i++)
+ {
+ Tasks.Add(new TaskInfo());
+ Tasks[i].Deserialize(reader);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects
+{
+ public partial class InnerGameData
+ {
+ public class TaskInfo
+ {
+ public uint Id { get; private set; }
+
+ public bool Complete { get; private set; }
+
+ public void Serialize(IMessageWriter writer)
+ {
+ writer.WritePacked(Id);
+ writer.Write(Complete);
+ }
+
+ public void Deserialize(IMessageReader reader)
+ {
+ this.Id = reader.ReadPackedUInt32();
+ this.Complete = reader.ReadBoolean();
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using Impostor.Api.Games;
+using Impostor.Api.Innersloth.Net.Objects.Components;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects
+{
+ public partial class InnerGameData : InnerNetObject
+ {
+ private readonly IGame _game;
+ private readonly List<PlayerInfo> _allPlayers;
+
+ public InnerGameData(IGame game)
+ {
+ _game = game;
+ _allPlayers = new List<PlayerInfo>();
+
+ Components.Add(this);
+ Components.Add(new InnerVoteBanSystem());
+ }
+
+ public int PlayerCount => _allPlayers.Count;
+
+ public IReadOnlyList<PlayerInfo> Players => _allPlayers;
+
+ public PlayerInfo GetPlayerById(byte id)
+ {
+ foreach (var player in _allPlayers)
+ {
+ if (player.PlayerId == id)
+ {
+ return player;
+ }
+ }
+
+ return null;
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ if (initialState)
+ {
+ var num = reader.ReadPackedInt32();
+
+ for (var i = 0; i < num; i++)
+ {
+ var playerInfo = new PlayerInfo(reader.ReadByte());
+ playerInfo.Deserialize(reader);
+ _allPlayers.Add(playerInfo);
+ }
+ }
+ else
+ {
+ throw new NotImplementedException("This shouldn't happen, according to Among Us disassembly..");
+
+ // var num = reader.ReadByte();
+ //
+ // for (var i = 0; i < num; i++)
+ // {
+ // var id = reader.ReadByte();
+ // var player = GetPlayerById(id);
+ // if (player != null)
+ // {
+ // player.Deserialize(reader);
+ // }
+ // else
+ // {
+ // var playerInfo = new PlayerInfo(id);
+ // playerInfo.Deserialize(reader);
+ // _allPlayers.Add(playerInfo);
+ // }
+ // }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Games;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects
+{
+ public class InnerLobbyBehaviour : InnerNetObject
+ {
+ private readonly IGame _game;
+
+ public InnerLobbyBehaviour(IGame game)
+ {
+ _game = game;
+
+ Components.Add(this);
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects
+{
+ public partial class InnerMeetingHud
+ {
+ public class PlayerVoteArea
+ {
+ private const byte VoteMask = 15;
+ private const byte ReportedBit = 32;
+ private const byte VotedBit = 64;
+ private const byte DeadBit = 128;
+
+ public PlayerVoteArea(InnerMeetingHud parent, byte targetPlayerId)
+ {
+ Parent = parent;
+ TargetPlayerId = targetPlayerId;
+ }
+
+ public InnerMeetingHud Parent { get; }
+
+ public byte TargetPlayerId { get; }
+
+ public bool IsDead { get; private set; }
+
+ public bool DidVote { get; private set; }
+
+ public bool DidReport { get; private set; }
+
+ public sbyte VotedFor { get; private set; }
+
+ internal void SetDead(bool didReport, bool isDead)
+ {
+ DidReport = didReport;
+ IsDead = isDead;
+ }
+
+ public void Deserialize(IMessageReader reader)
+ {
+ var num = reader.ReadByte();
+
+ VotedFor = (sbyte)((num & VoteMask) - 1);
+ IsDead = (num & DeadBit) > 0;
+ DidVote = (num & VotedBit) > 0;
+ DidReport = (num & ReportedBit) > 0;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using Impostor.Api.Games;
+using Impostor.Api.Innersloth.Data;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects
+{
+ public partial class InnerMeetingHud : InnerNetObject
+ {
+ private readonly IGame _game;
+ private readonly GameNet _gameNet;
+ private PlayerVoteArea[] _playerStates;
+
+ public InnerMeetingHud(IGame game)
+ {
+ _game = game;
+ _gameNet = game.GameNet;
+ _playerStates = null;
+
+ Components.Add(this);
+ }
+
+ public byte ReporterId { get; private set; }
+
+ private void PopulateButtons(byte reporter)
+ {
+ _playerStates = new PlayerVoteArea[_gameNet.GameData.PlayerCount];
+
+ for (var i = 0; i < _playerStates.Length; i++)
+ {
+ var player = _gameNet.GameData.Players[i];
+ var playerVoteArea = _playerStates[i] = new PlayerVoteArea(this, player.PlayerId);
+
+ playerVoteArea.SetDead(player.PlayerId == reporter, player.Disconnected || player.IsDead);
+ }
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ if (initialState)
+ {
+ PopulateButtons(0);
+
+ foreach (var playerState in _playerStates)
+ {
+ playerState.Deserialize(reader);
+
+ if (playerState.DidReport)
+ {
+ ReporterId = playerState.TargetPlayerId;
+ }
+ }
+ }
+ else
+ {
+ var num = reader.ReadPackedUInt32();
+
+ for (var i = 0; i < _playerStates.Length; i++)
+ {
+ if ((num & 1 << i) != 0)
+ {
+ _playerStates[i].Deserialize(reader);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using Impostor.Api.Games;
+using Impostor.Api.Innersloth.Net.Objects.Components;
+using Impostor.Api.Net.Messages;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Impostor.Api.Innersloth.Net.Objects
+{
+ public class InnerPlayerControl : InnerNetObject
+ {
+ private readonly IGame _game;
+
+ public InnerPlayerControl(IGame game, IServiceProvider serviceProvider)
+ {
+ _game = game;
+
+ Components.Add(this);
+ Components.Add(ActivatorUtilities.CreateInstance<InnerPlayerPhysics>(serviceProvider));
+ Components.Add(ActivatorUtilities.CreateInstance<InnerCustomNetworkTransform>(serviceProvider));
+
+ PlayerId = byte.MaxValue;
+ }
+
+ public bool IsNew { get; private set; }
+
+ public byte PlayerId { get; private set; }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ if (initialState)
+ {
+ IsNew = reader.ReadBoolean();
+ }
+
+ PlayerId = reader.ReadByte();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Generic;
+using Impostor.Api.Games;
+using Impostor.Api.Innersloth.Net.Objects.Systems;
+using Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects
+{
+ public class InnerShipStatus : InnerNetObject
+ {
+ private readonly IGame _game;
+ private readonly Dictionary<SystemTypes, ISystemType> _systems;
+
+ public InnerShipStatus(IGame game)
+ {
+ _game = game;
+
+ _systems = new Dictionary<SystemTypes, ISystemType>
+ {
+ [SystemTypes.Electrical] = new SwitchSystem(),
+ [SystemTypes.MedBay] = new MedScanSystem(),
+ [SystemTypes.Reactor] = new ReactorSystemType(),
+ [SystemTypes.LifeSupp] = new LifeSuppSystemType(),
+ [SystemTypes.Security] = new SecurityCameraSystemType(),
+ [SystemTypes.Comms] = new HudOverrideSystemType(),
+ [SystemTypes.Doors] = new DoorsSystemType(_game),
+ };
+
+ _systems.Add(SystemTypes.Sabotage, new SabotageSystemType(new[]
+ {
+ (IActivatable) _systems[SystemTypes.Comms],
+ (IActivatable) _systems[SystemTypes.Reactor],
+ (IActivatable) _systems[SystemTypes.LifeSupp],
+ (IActivatable) _systems[SystemTypes.Electrical],
+ }));
+
+ Components.Add(this);
+ }
+
+ public override void HandleRpc(byte callId, IMessageReader reader)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override bool Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void Deserialize(IMessageReader reader, bool initialState)
+ {
+ if (initialState)
+ {
+ // TODO: (_systems[SystemTypes.Doors] as DoorsSystemType).SetDoors();
+ foreach (var systemType in SystemTypeHelpers.AllTypes)
+ {
+ if (_systems.TryGetValue(systemType, out var system))
+ {
+ system.Deserialize(reader, true);
+ }
+ }
+ }
+ else
+ {
+ var count = reader.ReadPackedUInt32();
+
+ foreach (var systemType in SystemTypeHelpers.AllTypes)
+ {
+ // TODO: Not sure what is going on here, check.
+ if ((count & 1 << (int) (systemType & (SystemTypes.ShipTasks | SystemTypes.Doors))) != 0L)
+ {
+ if (_systems.TryGetValue(systemType, out var system))
+ {
+ system.Deserialize(reader, false);
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Api.Innersloth.Net.Objects.Systems
+{
+ public interface IActivatable
+ {
+ bool IsActive { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems
+{
+ public interface ISystemType
+ {
+ void Serialize(IMessageWriter writer, bool initialState);
+
+ void Deserialize(IMessageReader reader, bool initialState);
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using Impostor.Api.Games;
+using Impostor.Api.Innersloth.Data;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus
+{
+ public class DoorsSystemType : ISystemType
+ {
+ // TODO: AutoDoors
+ private readonly Dictionary<int, bool> _doors;
+
+ public DoorsSystemType(IGame game)
+ {
+ // TODO: Check
+ var doorCount = game.Options.MapId switch
+ {
+ MapId.Skeld => 13,
+ MapId.MiraHQ => 2,
+ MapId.Polus => 12,
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ _doors = new Dictionary<int, bool>(doorCount);
+
+ for (var i = 0; i < doorCount; i++)
+ {
+ _doors.Add(i, false);
+ }
+ }
+
+ public void Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader, bool initialState)
+ {
+ if (initialState)
+ {
+ for (var i = 0; i < _doors.Count; i++)
+ {
+ _doors[i] = reader.ReadBoolean();
+ }
+ }
+ else
+ {
+ var num = reader.ReadPackedUInt32();
+
+ for (var i = 0; i < _doors.Count; i++)
+ {
+ if ((num & 1 << i) != 0)
+ {
+ _doors[i] = reader.ReadBoolean();
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus
+{
+ public class HudOverrideSystemType : ISystemType, IActivatable
+ {
+ public bool IsActive { get; private set; }
+
+ public void Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader, bool initialState)
+ {
+ IsActive = reader.ReadBoolean();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Generic;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus
+{
+ public class LifeSuppSystemType : ISystemType, IActivatable
+ {
+ public LifeSuppSystemType()
+ {
+ Countdown = 10000f;
+ CompletedConsoles = new HashSet<int>();
+ }
+
+ public float Countdown { get; private set; }
+
+ public HashSet<int> CompletedConsoles { get; }
+
+ public bool IsActive => Countdown < 10000.0;
+
+ public void Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader, bool initialState)
+ {
+ Countdown = reader.ReadSingle();
+
+ if (reader.Position >= reader.Length)
+ {
+ return;
+ }
+
+ CompletedConsoles.Clear(); // TODO: Thread safety
+
+ var num = reader.ReadPackedInt32();
+
+ for (var i = 0; i < num; i++)
+ {
+ CompletedConsoles.Add(reader.ReadPackedInt32());
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Generic;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus
+{
+ public class MedScanSystem : ISystemType
+ {
+ public MedScanSystem()
+ {
+ UsersList = new List<byte>();
+ }
+
+ public List<byte> UsersList { get; }
+
+ public void Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader, bool initialState)
+ {
+ UsersList.Clear();
+
+ var num = reader.ReadPackedInt32();
+
+ for (var i = 0; i < num; i++)
+ {
+ UsersList.Add(reader.ReadByte());
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus
+{
+ public class ReactorSystemType : ISystemType, IActivatable
+ {
+ public ReactorSystemType()
+ {
+ Countdown = 10000f;
+ UserConsolePairs = new HashSet<Tuple<byte, byte>>();
+ }
+
+ public float Countdown { get; private set; }
+
+ public HashSet<Tuple<byte, byte>> UserConsolePairs { get; }
+
+ public bool IsActive => Countdown < 10000.0;
+
+ public void Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader, bool initialState)
+ {
+ Countdown = reader.ReadSingle();
+ UserConsolePairs.Clear(); // TODO: Thread safety
+
+ var count = reader.ReadPackedInt32();
+
+ for (var i = 0; i < count; i++)
+ {
+ UserConsolePairs.Add(new Tuple<byte, byte>(reader.ReadByte(), reader.ReadByte()));
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus
+{
+ public class SabotageSystemType : ISystemType
+ {
+ private readonly IActivatable[] _specials;
+
+ public SabotageSystemType(IActivatable[] specials)
+ {
+ _specials = specials;
+ }
+
+ public float Timer { get; set; }
+
+ public void Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader, bool initialState)
+ {
+ Timer = reader.ReadSingle();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus
+{
+ public class SecurityCameraSystemType : ISystemType
+ {
+ public byte InUse { get; internal set; }
+
+ public void Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader, bool initialState)
+ {
+ InUse = reader.ReadByte();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems.ShipStatus
+{
+ public class SwitchSystem : ISystemType, IActivatable
+ {
+ public byte ExpectedSwitches { get; set; }
+
+ public byte ActualSwitches { get; set; }
+
+ public byte Value { get; set; } = byte.MaxValue;
+
+ public bool IsActive { get; }
+
+ public void Serialize(IMessageWriter writer, bool initialState)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Deserialize(IMessageReader reader, bool initialState)
+ {
+ ExpectedSwitches = reader.ReadByte();
+ ActualSwitches = reader.ReadByte();
+ Value = reader.ReadByte();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Linq;
+
+namespace Impostor.Api.Innersloth.Net.Objects.Systems
+{
+ internal class SystemTypeHelpers
+ {
+ public static readonly SystemTypes[] AllTypes;
+ public static readonly string[] Names;
+
+ static SystemTypeHelpers()
+ {
+ AllTypes = Enum.GetValues(typeof(SystemTypes)).Cast<SystemTypes>().ToArray();
+ Names = AllTypes.Select(x =>
+ {
+ return x switch
+ {
+ SystemTypes.UpperEngine => "Upper Engine",
+ SystemTypes.Nav => "Navigations",
+ SystemTypes.LifeSupp => "O2",
+ SystemTypes.LowerEngine => "Lower Engine",
+ SystemTypes.LockerRoom => "Locker Room",
+ _ => x.ToString()
+ };
+ }).ToArray();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Api.Innersloth.Net.Objects.Systems
+{
+ public enum SystemTypes : byte
+ {
+ Hallway,
+ Storage,
+ Cafeteria,
+ Reactor,
+ UpperEngine,
+ Nav,
+ Admin,
+ Electrical,
+ LifeSupp,
+ Shields,
+ MedBay,
+ Security,
+ Weapons,
+ LowerEngine,
+ Comms,
+ ShipTasks,
+ Doors,
+ Sabotage,
+ Decontamination,
+ Launchpad,
+ LockerRoom,
+ Laboratory,
+ Balcony,
+ Office,
+ Greenhouse,
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Impostor.Api.Innersloth.Net
+{
+ [Flags]
+ public enum SpawnFlags : byte
+ {
+ None = 0,
+ IsClientCharacter = 1,
+ }
+}
\ No newline at end of file
using System;
-using Impostor.Api.Extensions;
namespace Impostor.Api.Net.Messages.C2S
{
-using Impostor.Api.Extensions;
-using Impostor.Api.Innersloth.Data;
+using Impostor.Api.Innersloth.Data;
namespace Impostor.Api.Net.Messages.C2S
{
--- /dev/null
+using System.Runtime.CompilerServices;
+
+[assembly:InternalsVisibleTo("Impostor.Server")]
\ No newline at end of file
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Impostor.Server.GameData
-{
- public class GameObject
- {
- public GameObject()
- {
- Components = new List<object>();
- }
-
- protected List<object> Components { get; }
-
- public List<T> GetComponentsInChildren<T>()
- {
- var result = new List<T>();
-
- foreach (var component in Components)
- {
- if (component.GetType().IsAssignableTo(typeof(T)))
- {
- result.Add((T) component);
- }
- }
-
- return result;
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Api.Net.Messages;
-
-namespace Impostor.Server.GameData
-{
- public abstract class InnerNetObject : GameObject
- {
- public uint NetId { get; set; }
-
- public int OwnerId { get; set; }
-
- public SpawnFlags SpawnFlags { get; internal set; }
-
- public abstract void HandleRpc(byte callId, IMessageReader reader);
-
- public abstract bool Serialize(IMessageWriter writer, bool initialState);
-
- public abstract void Deserialize(IMessageReader reader, bool initialState);
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Numerics;
-using Impostor.Api.Innersloth;
-using Impostor.Api.Net.Messages;
-using Microsoft.Extensions.Logging;
-
-namespace Impostor.Server.GameData.Objects.Components
-{
- public class InnerCustomNetworkTransform : InnerNetObject
- {
- private static readonly FloatRange XRange = new FloatRange(-40f, 40f);
- private static readonly FloatRange YRange = new FloatRange(-40f, 40f);
-
- private readonly ILogger<InnerCustomNetworkTransform> _logger;
-
- private ushort _lastSequenceId;
- private Vector2 _targetSyncPosition;
- private Vector2 _targetSyncVelocity;
-
- public InnerCustomNetworkTransform(ILogger<InnerCustomNetworkTransform> logger)
- {
- _logger = logger;
- }
-
- 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 static void WriteVector2(IMessageWriter writer, Vector2 vec)
- {
- writer.Write((ushort)(XRange.ReverseLerp(vec.X) * (double) ushort.MaxValue));
- writer.Write((ushort)(YRange.ReverseLerp(vec.Y) * (double) ushort.MaxValue));
- }
-
- private static Vector2 ReadVector2(IMessageReader reader)
- {
- var v1 = reader.ReadUInt16() / (float) ushort.MaxValue;
- var v2 = reader.ReadUInt16() / (float) ushort.MaxValue;
-
- return new Vector2(XRange.Lerp(v1), YRange.Lerp(v2));
- }
-
- public override void HandleRpc(byte callId, IMessageReader reader)
- {
- if (callId == 0)
- {
- SnapTo(ReadVector2(reader), reader.ReadUInt16());
- }
- else
- {
- _logger.LogWarning("InnerCustomNetworkTransform: Unknown rpc call {0}", callId);
- }
- }
-
- public override bool Serialize(IMessageWriter writer, bool initialState)
- {
- if (initialState)
- {
- writer.Write(_lastSequenceId);
- WriteVector2(writer, _targetSyncPosition);
- WriteVector2(writer, _targetSyncVelocity);
- return true;
- }
-
- // TODO: DirtyBits == 0 return false.
- _lastSequenceId++;
-
- writer.Write(_lastSequenceId);
- WriteVector2(writer, _targetSyncPosition);
- WriteVector2(writer, _targetSyncVelocity);
- return true;
- }
-
- public override void Deserialize(IMessageReader reader, bool initialState)
- {
- var sequenceId = reader.ReadUInt16();
-
- if (initialState)
- {
- _lastSequenceId = sequenceId;
- _targetSyncPosition = ReadVector2(reader);
- _targetSyncVelocity = ReadVector2(reader);
- }
- else
- {
- if (!SidGreaterThan(sequenceId, _lastSequenceId))
- {
- return;
- }
-
- _lastSequenceId = sequenceId;
- _targetSyncPosition = ReadVector2(reader);
- _targetSyncVelocity = ReadVector2(reader);
- }
- }
-
- private void SnapTo(Vector2 position, ushort minSid)
- {
- if (SidGreaterThan(minSid, _lastSequenceId))
- {
- return;
- }
-
- _lastSequenceId = minSid;
- _targetSyncPosition = position;
- _targetSyncVelocity = Vector2.Zero;
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Api.Net.Messages;
-
-namespace Impostor.Server.GameData.Objects.Components
-{
- public class InnerPlayerPhysics : InnerNetObject
- {
- public override void HandleRpc(byte callId, IMessageReader reader)
- {
- throw new System.NotImplementedException();
- }
-
- public override bool Serialize(IMessageWriter writer, bool initialState)
- {
- throw new System.NotImplementedException();
- }
-
- public override void Deserialize(IMessageReader reader, bool initialState)
- {
- throw new System.NotImplementedException();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using Impostor.Api.Net.Messages;
-
-namespace Impostor.Server.GameData.Objects.Components
-{
- public class InnerVoteBanSystem : InnerNetObject
- {
- private readonly Dictionary<int, int[]> _votes;
-
- public InnerVoteBanSystem()
- {
- _votes = new Dictionary<int, int[]>();
- }
-
- public override void HandleRpc(byte callId, IMessageReader reader)
- {
- throw new NotImplementedException();
- }
-
- public override bool Serialize(IMessageWriter writer, bool initialState)
- {
- throw new NotImplementedException();
- }
-
- public override void Deserialize(IMessageReader reader, bool initialState)
- {
- var votes = _votes;
- var unknown = reader.ReadBoolean();
- if (unknown)
- {
- while (true)
- {
- var v4 = reader.ReadInt32();
-
- if (!votes.TryGetValue(v4, out var v12))
- {
- v12 = new int[3];
- votes[v4] = v12;
- }
-
- for (var i = 0; i < 3; i++)
- {
- v12[i] = reader.ReadPackedInt32();
- }
- }
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using Impostor.Api.Net.Messages;
-
-namespace Impostor.Server.GameData.Objects
-{
- public partial class InnerGameData
- {
- public class PlayerInfo
- {
- public PlayerInfo(byte playerId)
- {
- PlayerId = playerId;
- }
-
- public byte PlayerId { get; }
-
- public string PlayerName { get; private set; }
-
- public byte ColorId { get; private set; }
-
- public uint HatId { get; private set; }
-
- public uint PetId { get; private set; }
-
- public uint SkinId { get; private set; }
-
- public bool Disconnected { get; private set; }
-
- public bool IsImpostor { get; private set; }
-
- public bool IsDead { get; private set; }
-
- public List<TaskInfo> Tasks { get; private set; }
-
- public void Serialize(IMessageWriter writer)
- {
- throw new NotImplementedException();
- }
-
- public void Deserialize(IMessageReader reader)
- {
- PlayerName = reader.ReadString();
- ColorId = reader.ReadByte();
- HatId = reader.ReadPackedUInt32();
- PetId = reader.ReadPackedUInt32();
- SkinId = reader.ReadPackedUInt32();
- var flag = reader.ReadByte();
- Disconnected = (flag & 1) > 0;
- IsImpostor = (flag & 2) > 0;
- IsDead = (flag & 4) > 0;
- var taskCount = reader.ReadByte();
- Tasks = new List<TaskInfo>();
- for (var i = 0; i < taskCount; i++)
- {
- Tasks.Add(new TaskInfo());
- Tasks[i].Deserialize(reader);
- }
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Api.Net.Messages;
-
-namespace Impostor.Server.GameData.Objects
-{
- public partial class InnerGameData
- {
- public class TaskInfo
- {
- public uint Id { get; private set; }
-
- public bool Complete { get; private set; }
-
- public void Serialize(IMessageWriter writer)
- {
- writer.WritePacked(Id);
- writer.Write(Complete);
- }
-
- public void Deserialize(IMessageReader reader)
- {
- this.Id = reader.ReadPackedUInt32();
- this.Complete = reader.ReadBoolean();
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using Impostor.Api.Games;
-using Impostor.Api.Net.Messages;
-using Impostor.Server.GameData.Objects.Components;
-
-namespace Impostor.Server.GameData.Objects
-{
- public partial class InnerGameData : InnerNetObject
- {
- private readonly IGame _game;
- private readonly List<PlayerInfo> _allPlayers;
-
- public InnerGameData(IGame game)
- {
- _game = game;
- _allPlayers = new List<PlayerInfo>();
-
- Components.Add(this);
- Components.Add(new InnerVoteBanSystem());
- }
-
- public PlayerInfo GetPlayerById(byte id)
- {
- foreach (var player in _allPlayers)
- {
- if (player.PlayerId == id)
- {
- return player;
- }
- }
-
- return null;
- }
-
- public override void HandleRpc(byte callId, IMessageReader reader)
- {
- throw new NotImplementedException();
- }
-
- public override bool Serialize(IMessageWriter writer, bool initialState)
- {
- throw new NotImplementedException();
- }
-
- public override void Deserialize(IMessageReader reader, bool initialState)
- {
- if (initialState)
- {
- var num = reader.ReadPackedInt32();
-
- for (var i = 0; i < num; i++)
- {
- var playerInfo = new PlayerInfo(reader.ReadByte());
- playerInfo.Deserialize(reader);
- _allPlayers.Add(playerInfo);
- }
- }
- else
- {
- throw new NotImplementedException("This shouldn't happen, according to Among Us assembly..");
-
- // var num = reader.ReadByte();
- //
- // for (var i = 0; i < num; i++)
- // {
- // var id = reader.ReadByte();
- // var player = GetPlayerById(id);
- // if (player != null)
- // {
- // player.Deserialize(reader);
- // }
- // else
- // {
- // var playerInfo = new PlayerInfo(id);
- // playerInfo.Deserialize(reader);
- // _allPlayers.Add(playerInfo);
- // }
- // }
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Api.Games;
-using Impostor.Api.Net.Messages;
-
-namespace Impostor.Server.GameData.Objects
-{
- public class InnerLobbyBehaviour : InnerNetObject
- {
- private readonly IGame _game;
-
- public InnerLobbyBehaviour(IGame game)
- {
- _game = game;
-
- Components.Add(this);
- }
-
- public override void HandleRpc(byte callId, IMessageReader reader)
- {
- throw new System.NotImplementedException();
- }
-
- public override bool Serialize(IMessageWriter writer, bool initialState)
- {
- throw new System.NotImplementedException();
- }
-
- public override void Deserialize(IMessageReader reader, bool initialState)
- {
- throw new System.NotImplementedException();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Api.Games;
-using Impostor.Api.Net.Messages;
-
-namespace Impostor.Server.GameData.Objects
-{
- public class InnerMeetingHud : InnerNetObject
- {
- private readonly IGame _game;
-
- public InnerMeetingHud(IGame game)
- {
- _game = game;
-
- Components.Add(this);
- }
-
- public override void HandleRpc(byte callId, IMessageReader reader)
- {
- throw new System.NotImplementedException();
- }
-
- public override bool Serialize(IMessageWriter writer, bool initialState)
- {
- throw new System.NotImplementedException();
- }
-
- public override void Deserialize(IMessageReader reader, bool initialState)
- {
- throw new System.NotImplementedException();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using Impostor.Api.Games;
-using Impostor.Api.Net.Messages;
-using Impostor.Server.GameData.Objects.Components;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Impostor.Server.GameData.Objects
-{
- public class InnerPlayerControl : InnerNetObject
- {
- private readonly IGame _game;
-
- public InnerPlayerControl(IGame game, IServiceProvider serviceProvider)
- {
- _game = game;
-
- Components.Add(this);
- Components.Add(ActivatorUtilities.CreateInstance<InnerPlayerPhysics>(serviceProvider));
- Components.Add(ActivatorUtilities.CreateInstance<InnerCustomNetworkTransform>(serviceProvider));
-
- PlayerId = byte.MaxValue;
- }
-
- public bool IsNew { get; private set; }
-
- public byte PlayerId { get; private set; }
-
- public override void HandleRpc(byte callId, IMessageReader reader)
- {
- throw new NotImplementedException();
- }
-
- public override bool Serialize(IMessageWriter writer, bool initialState)
- {
- throw new NotImplementedException();
- }
-
- public override void Deserialize(IMessageReader reader, bool initialState)
- {
- if (initialState)
- {
- IsNew = reader.ReadBoolean();
- }
-
- PlayerId = reader.ReadByte();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Api.Games;
-using Impostor.Api.Net.Messages;
-
-namespace Impostor.Server.GameData.Objects
-{
- public class InnerShipStatus : InnerNetObject
- {
- private readonly IGame _game;
-
- public InnerShipStatus(IGame game)
- {
- _game = game;
-
- Components.Add(this);
- }
-
- public override void HandleRpc(byte callId, IMessageReader reader)
- {
- throw new System.NotImplementedException();
- }
-
- public override bool Serialize(IMessageWriter writer, bool initialState)
- {
- throw new System.NotImplementedException();
- }
-
- public override void Deserialize(IMessageReader reader, bool initialState)
- {
- throw new System.NotImplementedException();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-
-namespace Impostor.Server.GameData
-{
- [Flags]
- public enum SpawnFlags : byte
- {
- None = 0,
- IsClientCharacter = 1,
- }
-}
\ No newline at end of file
using Impostor.Api;
using Impostor.Api.Innersloth.Data;
using Impostor.Api.Innersloth.GameData;
+using Impostor.Api.Innersloth.Net;
+using Impostor.Api.Innersloth.Net.Objects;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.C2S;
using Impostor.Api.Net.Messages.S2C;
using Impostor.Hazel;
-using Impostor.Server.GameData;
-using Impostor.Server.GameData.Objects;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
continue;
}
- _logger.LogTrace(
+ _logger.LogDebug(
"Spawning {0} components, SpawnFlags {1}",
innerNetObject.GetType().Name,
innerNetObject.SpawnFlags);
obj.NetId = reader.ReadPackedUInt32();
obj.OwnerId = ownerClientId;
- _logger.LogTrace(
+ _logger.LogDebug(
"- {0}, NetId {1}, OwnerId {2}",
obj.GetType().Name,
obj.NetId,
{
obj.Deserialize(readerSub, true);
}
+
+ GameNet.OnSpawn(obj);
}
if ((innerNetObject.SpawnFlags & SpawnFlags.IsClientCharacter) != SpawnFlags.None)
case GameDataTag.DespawnFlag:
{
// Only the host is allowed to despawn objects.
- if (!sender.IsHost)
- {
- _logger.LogWarning("Player {0} ({1}) tried to send DespawnFlag as non-host.", sender.Client.Name, sender.Client.Id);
- return false;
- }
-
var netId = reader.ReadPackedUInt32();
if (_allObjectsFast.TryGetValue(netId, out var obj))
{
+ if (sender.Client.Id != obj.OwnerId && !sender.IsHost)
+ {
+ _logger.LogWarning(
+ "Player {0} ({1}) tried to send DespawnFlag for {2} but was denied.",
+ sender.Client.Name,
+ sender.Client.Id,
+ netId);
+ return false;
+ }
+
RemoveNetObject(obj);
- _logger.LogTrace("Destroyed InnerNetObject {0} ({1})", obj.GetType().Name, netId);
+ GameNet.OnSpawn(obj);
+ _logger.LogDebug("Destroyed InnerNetObject {0} ({1}), OwnerId {2}", obj.GetType().Name, netId, obj.OwnerId);
}
else
{
- _logger.LogWarning("Received DespawnFlag for unregistered NetId {0}.", netId);
+ _logger.LogWarning(
+ "Player {0} ({1}) sent DespawnFlag for unregistered NetId {2}.",
+ sender.Client.Name,
+ sender.Client.Id,
+ netId);
}
break;
Code = code;
HostId = -1;
GameState = GameStates.NotStarted;
+ GameNet = new GameNet();
Options = options;
_clientManager = clientManager;
_eventManager = eventManager;
public GameStates GameState { get; private set; }
+ public GameNet GameNet { get; }
+
public GameOptionsData Options { get; }
public IDictionary<object, object> Items { get; }
private static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
- .MinimumLevel.Verbose()
+ // .MinimumLevel.Verbose()
+ .MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();