diff options
Diffstat (limited to 'Impostor-dev/src/Impostor.Api')
117 files changed, 3177 insertions, 0 deletions
diff --git a/Impostor-dev/src/Impostor.Api/Events/Attributes/EventListenerAttribute.cs b/Impostor-dev/src/Impostor.Api/Events/Attributes/EventListenerAttribute.cs new file mode 100644 index 0000000..b31d2d1 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Attributes/EventListenerAttribute.cs @@ -0,0 +1,34 @@ +using System; + +namespace Impostor.Api.Events +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class EventListenerAttribute : Attribute + { + public EventListenerAttribute(EventPriority priority = EventPriority.Normal) + { + Priority = priority; + } + + public EventListenerAttribute(Type @event, EventPriority priority = EventPriority.Normal) + { + Priority = priority; + Event = @event; + } + + /// <summary> + /// The priority of the event listener. + /// </summary> + public EventPriority Priority { get; set; } + + /// <summary> + /// The events that the listener is listening to. + /// </summary> + public Type? Event { get; set; } + + /// <summary> + /// If set to true, the listener will be called regardless of the <see cref="IEventCancelable.IsCancelled"/>. + /// </summary> + public bool IgnoreCancelled { get; set; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Events/EventPriority.cs b/Impostor-dev/src/Impostor.Api/Events/EventPriority.cs new file mode 100644 index 0000000..7acdbeb --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/EventPriority.cs @@ -0,0 +1,12 @@ +namespace Impostor.Api.Events +{ + public enum EventPriority + { + Lowest = 0, + Low = 1, + Normal = 2, + High = 3, + Highest = 4, + Monitor = 5, + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGameAlterEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGameAlterEvent.cs new file mode 100644 index 0000000..18956ea --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGameAlterEvent.cs @@ -0,0 +1,7 @@ +namespace Impostor.Api.Events +{ + public interface IGameAlterEvent : IGameEvent + { + bool IsPublic { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGameCreatedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGameCreatedEvent.cs new file mode 100644 index 0000000..ef45f1d --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGameCreatedEvent.cs @@ -0,0 +1,11 @@ +using Impostor.Api.Games; + +namespace Impostor.Api.Events +{ + /// <summary> + /// Called whenever a new <see cref="IGame"/> is created. + /// </summary> + public interface IGameCreatedEvent : IGameEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGameDestroyedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGameDestroyedEvent.cs new file mode 100644 index 0000000..7ff7b46 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGameDestroyedEvent.cs @@ -0,0 +1,11 @@ +using Impostor.Api.Games; + +namespace Impostor.Api.Events +{ + /// <summary> + /// Called whenever a new <see cref="IGame"/> is destroyed. + /// </summary> + public interface IGameDestroyedEvent : IGameEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGameEndedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGameEndedEvent.cs new file mode 100644 index 0000000..d8ae159 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGameEndedEvent.cs @@ -0,0 +1,9 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Events +{ + public interface IGameEndedEvent : IGameEvent + { + public GameOverReason GameOverReason { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGameEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGameEvent.cs new file mode 100644 index 0000000..c9ae579 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGameEvent.cs @@ -0,0 +1,12 @@ +using Impostor.Api.Games; + +namespace Impostor.Api.Events +{ + public interface IGameEvent : IEvent + { + /// <summary> + /// Gets the <see cref="IGame"/> this event belongs to. + /// </summary> + IGame Game { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGamePlayerJoinedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGamePlayerJoinedEvent.cs new file mode 100644 index 0000000..921568e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGamePlayerJoinedEvent.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Events +{ + public interface IGamePlayerJoinedEvent : IGameEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGamePlayerLeftEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGamePlayerLeftEvent.cs new file mode 100644 index 0000000..21d8b7c --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGamePlayerLeftEvent.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Events +{ + public interface IGamePlayerLeftEvent : IGameEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGameStartedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGameStartedEvent.cs new file mode 100644 index 0000000..b6e5111 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGameStartedEvent.cs @@ -0,0 +1,9 @@ +namespace Impostor.Api.Events +{ + /// <summary> + /// The game is started here and players have been initialized. + /// </summary> + public interface IGameStartedEvent : IGameEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/IGameStartingEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/IGameStartingEvent.cs new file mode 100644 index 0000000..5998bf2 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/IGameStartingEvent.cs @@ -0,0 +1,11 @@ +namespace Impostor.Api.Events +{ + /// <summary> + /// Called when the game is going to start. + /// When this is called, not all players are initialized properly yet. + /// If you want to get correct player states, use <see cref="IGameStartedEvent"/>. + /// </summary> + public interface IGameStartingEvent : IGameEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingEndedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingEndedEvent.cs new file mode 100644 index 0000000..a217580 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingEndedEvent.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Events.Meeting +{ + public interface IMeetingEndedEvent : IMeetingEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingEvent.cs new file mode 100644 index 0000000..4461318 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingEvent.cs @@ -0,0 +1,9 @@ +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Api.Events.Meeting +{ + public interface IMeetingEvent : IGameEvent + { + IInnerMeetingHud MeetingHud { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingStartedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingStartedEvent.cs new file mode 100644 index 0000000..a237fff --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingStartedEvent.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Events.Meeting +{ + public interface IMeetingStartedEvent : IMeetingEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerChatEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerChatEvent.cs new file mode 100644 index 0000000..52efe96 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerChatEvent.cs @@ -0,0 +1,10 @@ +namespace Impostor.Api.Events.Player +{ + public interface IPlayerChatEvent : IPlayerEvent + { + /// <summary> + /// Gets the message sent by the player. + /// </summary> + string Message { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerCompletedTaskEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerCompletedTaskEvent.cs new file mode 100644 index 0000000..78ccd2d --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerCompletedTaskEvent.cs @@ -0,0 +1,10 @@ +using Impostor.Api.Innersloth; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Api.Events.Player +{ + public interface IPlayerCompletedTaskEvent : IPlayerEvent + { + ITaskInfo Task { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerDestroyedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerDestroyedEvent.cs new file mode 100644 index 0000000..ac80d64 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerDestroyedEvent.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Events.Player +{ + public interface IPlayerDestroyedEvent : IPlayerEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerEvent.cs new file mode 100644 index 0000000..247fe64 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerEvent.cs @@ -0,0 +1,19 @@ +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Api.Events.Player +{ + public interface IPlayerEvent : IGameEvent + { + /// <summary> + /// Gets the <see cref="IClientPlayer"/> that triggered this <see cref="IPlayerEvent"/>. + /// </summary> + IClientPlayer ClientPlayer { get; } + + /// <summary> + /// Gets the networked <see cref="IInnerPlayerControl"/> that triggered this <see cref="IPlayerEvent"/>. + /// This <see cref="IInnerPlayerControl"/> belongs to the <see cref="IClientPlayer"/>. + /// </summary> + IInnerPlayerControl PlayerControl { get; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerExileEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerExileEvent.cs new file mode 100644 index 0000000..65ade4e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerExileEvent.cs @@ -0,0 +1,9 @@ +namespace Impostor.Api.Events.Player +{ + /// <summary> + /// Called whenever a player gets exiled (voted out). + /// </summary> + public interface IPlayerExileEvent : IPlayerEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerMurderEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerMurderEvent.cs new file mode 100644 index 0000000..c47c00b --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerMurderEvent.cs @@ -0,0 +1,12 @@ +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Api.Events.Player +{ + public interface IPlayerMurderEvent : IPlayerEvent + { + /// <summary> + /// Gets the player who got murdered. + /// </summary> + IInnerPlayerControl Victim { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerSetStartCounterEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerSetStartCounterEvent.cs new file mode 100644 index 0000000..c03d782 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerSetStartCounterEvent.cs @@ -0,0 +1,10 @@ +namespace Impostor.Api.Events.Player +{ + public interface IPlayerSetStartCounterEvent : IPlayerEvent + { + /// <summary> + /// Gets the current time of the start counter. + /// </summary> + byte SecondsLeft { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerSpawnedEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerSpawnedEvent.cs new file mode 100644 index 0000000..a3be654 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerSpawnedEvent.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Events.Player +{ + public interface IPlayerSpawnedEvent : IPlayerEvent + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerStartMeetingEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerStartMeetingEvent.cs new file mode 100644 index 0000000..1a28115 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerStartMeetingEvent.cs @@ -0,0 +1,12 @@ +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Api.Events.Player +{ + public interface IPlayerStartMeetingEvent : IPlayerEvent + { + /// <summary> + /// Gets the player who's body got reported. Is null when the meeting started by Emergency call button + /// </summary> + IInnerPlayerControl? Body { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerVentEvent.cs b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerVentEvent.cs new file mode 100644 index 0000000..81f178b --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerVentEvent.cs @@ -0,0 +1,17 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Events.Player +{ + public interface IPlayerVentEvent : IPlayerEvent + { + /// <summary> + /// Gets get the id of the used vent. + /// </summary> + public VentLocation VentId { get; } + + /// <summary> + /// Gets a value indicating whether the vent was entered or exited. + /// </summary> + public bool VentEnter { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Events/IEvent.cs b/Impostor-dev/src/Impostor.Api/Events/IEvent.cs new file mode 100644 index 0000000..796898e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/IEvent.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Events +{ + public interface IEvent + { + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Events/IEventCancelable.cs b/Impostor-dev/src/Impostor.Api/Events/IEventCancelable.cs new file mode 100644 index 0000000..319f02a --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/IEventCancelable.cs @@ -0,0 +1,10 @@ +namespace Impostor.Api.Events +{ + public interface IEventCancelable : IEvent + { + /// <summary> + /// True if the event was cancelled. + /// </summary> + bool IsCancelled { get; set; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Events/IEventListener.cs b/Impostor-dev/src/Impostor.Api/Events/IEventListener.cs new file mode 100644 index 0000000..76392fc --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/IEventListener.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Events +{ + public interface IEventListener + { + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Events/IManualEventListener.cs b/Impostor-dev/src/Impostor.Api/Events/IManualEventListener.cs new file mode 100644 index 0000000..b5c140e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/IManualEventListener.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace Impostor.Api.Events +{ + public interface IManualEventListener : IEventListener + { + public bool CanExecute<T>(); + + public ValueTask Execute(IEvent @event); + + EventPriority Priority { get; set; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Events/Managers/IEventManager.cs b/Impostor-dev/src/Impostor.Api/Events/Managers/IEventManager.cs new file mode 100644 index 0000000..07a7f7c --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Events/Managers/IEventManager.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; + +namespace Impostor.Api.Events.Managers +{ + public interface IEventManager + { + /// <summary> + /// Register a temporary event listener. + /// </summary> + /// <param name="listener">Event listener.</param> + /// <param name="invoker">Middleware between the events, which can be used to swap to the correct thread dispatcher.</param> + /// <returns>Disposable that unregisters the callback from the event manager.</returns> + /// <typeparam name="TListener">Type of the event listener.</typeparam> + IDisposable RegisterListener<TListener>(TListener listener, Func<Func<Task>, Task>? invoker = null) + where TListener : IEventListener; + + /// <summary> + /// Returns true if an event with the type <see cref="TEvent"/> is registered. + /// </summary> + /// <returns>True if the <see cref="TEvent"/> is registered.</returns> + /// <typeparam name="TEvent">Type of the event.</typeparam> + bool IsRegistered<TEvent>() + where TEvent : IEvent; + + /// <summary> + /// Call all the event listeners for the type <see cref="TEvent"/>. + /// </summary> + /// <param name="event">The event argument.</param> + /// <typeparam name="TEvent">Type of the event.</typeparam> + /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns> + ValueTask CallAsync<TEvent>(TEvent @event) + where TEvent : IEvent; + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorCheatException.cs b/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorCheatException.cs new file mode 100644 index 0000000..8eb72f8 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorCheatException.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.Serialization; + +namespace Impostor.Api +{ + public class ImpostorCheatException : ImpostorException + { + public ImpostorCheatException() + { + } + + protected ImpostorCheatException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public ImpostorCheatException(string? message) : base(message) + { + } + + public ImpostorCheatException(string? message, Exception? innerException) : base(message, innerException) + { + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorConfigException.cs b/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorConfigException.cs new file mode 100644 index 0000000..1e59a9b --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorConfigException.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.Serialization; + +namespace Impostor.Api +{ + public class ImpostorConfigException : ImpostorException + { + public ImpostorConfigException() + { + } + + protected ImpostorConfigException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public ImpostorConfigException(string? message) : base(message) + { + } + + public ImpostorConfigException(string? message, Exception? innerException) : base(message, innerException) + { + } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorException.cs b/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorException.cs new file mode 100644 index 0000000..188c50e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorException.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.Serialization; + +namespace Impostor.Api +{ + public class ImpostorException : Exception + { + public ImpostorException() + { + } + + protected ImpostorException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public ImpostorException(string? message) : base(message) + { + } + + public ImpostorException(string? message, Exception? innerException) : base(message, innerException) + { + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorProtocolException.cs b/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorProtocolException.cs new file mode 100644 index 0000000..864602d --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Exceptions/ImpostorProtocolException.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.Serialization; + +namespace Impostor.Api +{ + public class ImpostorProtocolException : ImpostorException + { + public ImpostorProtocolException() + { + } + + protected ImpostorProtocolException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public ImpostorProtocolException(string? message) : base(message) + { + } + + public ImpostorProtocolException(string? message, Exception? innerException) : base(message, innerException) + { + } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Extensions/SpanReaderExtensions.cs b/Impostor-dev/src/Impostor.Api/Extensions/SpanReaderExtensions.cs new file mode 100644 index 0000000..c103ee7 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Extensions/SpanReaderExtensions.cs @@ -0,0 +1,70 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace Impostor.Api +{ + /// <summary> + /// Priovides a StreamReader-like api throught extensions + /// </summary> + public static class SpanReaderExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte ReadByte(this ref ReadOnlySpan<byte> input) + { + var original = Advance<byte>(ref input); + return original[0]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ReadInt32(this ref ReadOnlySpan<byte> input) + { + var original = Advance<int>(ref input); + return BinaryPrimitives.ReadInt32LittleEndian(original); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint ReadUInt32(this ref ReadOnlySpan<byte> input) + { + var original = Advance<uint>(ref input); + return BinaryPrimitives.ReadUInt32LittleEndian(original); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float ReadSingle(this ref ReadOnlySpan<byte> input) + { + var original = Advance<float>(ref input); + + // BitConverter.Int32BitsToSingle + // Doesn't exist in net 2.0 for some reason + return Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(original)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ReadBoolean(this ref ReadOnlySpan<byte> input) + { + return input.ReadByte() != 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe float Int32BitsToSingle(int value) + { + return *((float*)&value); + } + + /// <summary> + /// Advances the position of <see cref="input"/> by the size of <see cref="T"/>. + /// </summary> + /// <typeparam name="T">Type that will be read.</typeparam> + /// <param name="input">input "stream"/span.</param> + /// <returns>The original input</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ReadOnlySpan<byte> Advance<T>(ref ReadOnlySpan<byte> input) + where T : unmanaged + { + var original = input; + input = input.Slice(sizeof(T)); + return original; + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Extensions/SystemTypesExtensions.cs b/Impostor-dev/src/Impostor.Api/Extensions/SystemTypesExtensions.cs new file mode 100644 index 0000000..68f1efd --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Extensions/SystemTypesExtensions.cs @@ -0,0 +1,12 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api +{ + public static class SystemTypesExtensions + { + public static string GetFriendlyName(this SystemTypes type) + { + return SystemTypeHelpers.Names[(int)type]; + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Games/Extensions/GameExtensions.cs b/Impostor-dev/src/Impostor.Api/Games/Extensions/GameExtensions.cs new file mode 100644 index 0000000..a736978 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Games/Extensions/GameExtensions.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading.Tasks; +using Impostor.Api.Net; +using Impostor.Api.Net.Messages; + +namespace Impostor.Api.Games +{ + public static class GameExtensions + { + public static ValueTask SendToAllExceptAsync(this IGame game, IMessageWriter writer, LimboStates states, int? id) + { + return id.HasValue + ? game.SendToAllExceptAsync(writer, id.Value, states) + : game.SendToAllAsync(writer, states); + } + + public static ValueTask SendToAllExceptAsync(this IGame game, IMessageWriter writer, LimboStates states, IClient client) + { + if (client == null) + { + throw new ArgumentNullException(nameof(client)); + } + + return game.SendToAllExceptAsync(writer, client.Id, states); + } + + public static ValueTask SendToAsync(this IGame game, IMessageWriter writer, IClient client) + { + if (client == null) + { + throw new ArgumentNullException(nameof(client)); + } + + return game.SendToAsync(writer, client.Id); + } + + public static ValueTask SendToAsync(this IGame game, IMessageWriter writer, IClientPlayer player) + { + return game.SendToAsync(writer, player.Client); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Games/Extensions/GameManagerExtensions.cs b/Impostor-dev/src/Impostor.Api/Games/Extensions/GameManagerExtensions.cs new file mode 100644 index 0000000..9a5a2b4 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Games/Extensions/GameManagerExtensions.cs @@ -0,0 +1,14 @@ +using System.Linq; +using Impostor.Api.Games.Managers; +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Games +{ + public static class GameManagerExtensions + { + public static int GetGameCount(this IGameManager manager, MapFlags map) + { + return manager.Games.Count(game => map.HasFlag((MapFlags)(1 << game.Options.MapId))); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Games/GameCode.cs b/Impostor-dev/src/Impostor.Api/Games/GameCode.cs new file mode 100644 index 0000000..2c30e7e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Games/GameCode.cs @@ -0,0 +1,74 @@ +using System; +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Games +{ + public readonly struct GameCode : IEquatable<GameCode> + { + public GameCode(int value) + { + Value = value; + Code = GameCodeParser.IntToGameName(value); + } + + public GameCode(string code) + { + Value = GameCodeParser.GameNameToInt(code); + Code = code; + } + + public string Code { get; } + + public int Value { get; } + + public static implicit operator string(GameCode code) => code.Code; + + public static implicit operator int(GameCode code) => code.Value; + + public static implicit operator GameCode(string code) => From(code); + + public static implicit operator GameCode(int value) => From(value); + + public static bool operator ==(GameCode left, GameCode right) + { + return left.Equals(right); + } + + public static bool operator !=(GameCode left, GameCode right) + { + return !left.Equals(right); + } + + public static GameCode Create() + { + return new GameCode(GameCodeParser.GenerateCode(6)); + } + + public static GameCode From(int value) => new GameCode(value); + + public static GameCode From(string value) => new GameCode(value); + + /// <inheritdoc/> + public bool Equals(GameCode other) + { + return Code == other.Code && Value == other.Value; + } + + /// <inheritdoc/> + public override bool Equals(object? obj) + { + return obj is GameCode other && Equals(other); + } + + /// <inheritdoc/> + public override int GetHashCode() + { + return HashCode.Combine(Code, Value); + } + + public override string ToString() + { + return Code; + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Games/GameJoinError.cs b/Impostor-dev/src/Impostor.Api/Games/GameJoinError.cs new file mode 100644 index 0000000..4889ea9 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Games/GameJoinError.cs @@ -0,0 +1,48 @@ +namespace Impostor.Api.Games +{ + public enum GameJoinError + { + /// <summary> + /// No error occured while joining the game. + /// </summary> + None, + + /// <summary> + /// The client is not registered in the client manager. + /// </summary> + InvalidClient, + + /// <summary> + /// The client has been banned from the game. + /// </summary> + Banned, + + /// <summary> + /// The game is full. + /// </summary> + GameFull, + + /// <summary> + /// The limbo state of the player is incorrect. + /// </summary> + InvalidLimbo, + + /// <summary> + /// The game is already started. + /// </summary> + GameStarted, + + /// <summary> + /// The game has been destroyed. + /// </summary> + GameDestroyed, + + /// <summary> + /// Custom error by a plugin. + /// </summary> + /// <remarks> + /// A custom message can be set in <see cref="GameJoinResult.Message"/>. + /// </remarks> + Custom, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Games/GameJoinResult.cs b/Impostor-dev/src/Impostor.Api/Games/GameJoinResult.cs new file mode 100644 index 0000000..b33a2b6 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Games/GameJoinResult.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Impostor.Api.Net; + +namespace Impostor.Api.Games +{ + public readonly struct GameJoinResult + { + private GameJoinResult(GameJoinError error, string? message = null, IClientPlayer? player = null) + { + Error = error; + Message = message; + Player = player; + } + + public GameJoinError Error { get; } + + public bool IsSuccess => Error == GameJoinError.None; + + public bool IsCustomError => Error == GameJoinError.Custom; + + [MemberNotNullWhen(true, nameof(IsCustomError))] + public string? Message { get; } + + [MemberNotNullWhen(true, nameof(IsSuccess))] + public IClientPlayer? Player { get; } + + public static GameJoinResult CreateCustomError(string message) + { + return new GameJoinResult(GameJoinError.Custom, message); + } + + public static GameJoinResult CreateSuccess(IClientPlayer player) + { + return new GameJoinResult(GameJoinError.None, player: player); + } + + public static GameJoinResult FromError(GameJoinError error) + { + if (error == GameJoinError.Custom) + { + throw new InvalidOperationException($"Custom errors should provide a message, use {nameof(CreateCustomError)} instead."); + } + + return new GameJoinResult(error); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Games/IGame.cs b/Impostor-dev/src/Impostor.Api/Games/IGame.cs new file mode 100644 index 0000000..ad71986 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Games/IGame.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using Impostor.Api.Innersloth; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner; +using Impostor.Api.Net.Inner.Objects; +using Impostor.Api.Net.Messages; + +namespace Impostor.Api.Games +{ + public interface IGame + { + GameOptionsData Options { get; } + + GameCode Code { get; } + + GameStates GameState { get; } + + IGameNet GameNet { get; } + + IEnumerable<IClientPlayer> Players { get; } + + IPEndPoint PublicIp { get; } + + int PlayerCount { get; } + + IClientPlayer Host { get; } + + bool IsPublic { get; } + + IDictionary<object, object> Items { get; } + + int HostId { get; } + + IClientPlayer GetClientPlayer(int clientId); + + /// <summary> + /// Adds an <see cref="IPAddress"/> to the ban list of this game. + /// Prevents all future joins from this <see cref="IPAddress"/>. + /// + /// This does not kick the player with that <see cref="IPAddress"/> from the lobby. + /// </summary> + /// <param name="ipAddress"> + /// The <see cref="IPAddress"/> to ban. + /// </param> + void BanIp(IPAddress ipAddress); + + /// <summary> + /// Syncs the internal <see cref="GameOptionsData"/> to all players. + /// Necessary to do if you modified it, otherwise it won't be used. + /// </summary> + /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns> + ValueTask SyncSettingsAsync(); + + /// <summary> + /// Sets the specified list as Impostor on all connected players. + /// </summary> + /// <param name="players">List of players to be Impostor.</param> + /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns> + ValueTask SetInfectedAsync(IEnumerable<IInnerPlayerControl> players); + + /// <summary> + /// Send the message to all players. + /// </summary> + /// <param name="writer">Message to send.</param> + /// <param name="states">Required limbo state of the player.</param> + /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns> + ValueTask SendToAllAsync(IMessageWriter writer, LimboStates states = LimboStates.NotLimbo); + + /// <summary> + /// Send the message to all players except one. + /// </summary> + /// <param name="writer">Message to send.</param> + /// <param name="senderId">The player to exclude from sending the message.</param> + /// <param name="states">Required limbo state of the player.</param> + /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns> + ValueTask SendToAllExceptAsync(IMessageWriter writer, int senderId, LimboStates states = LimboStates.NotLimbo); + + /// <summary> + /// Send a message to a specific player. + /// </summary> + /// <param name="writer">Message to send.</param> + /// <param name="id">ID of the client.</param> + /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns> + ValueTask SendToAsync(IMessageWriter writer, int id); + } +} diff --git a/Impostor-dev/src/Impostor.Api/Games/IGameCodeFactory.cs b/Impostor-dev/src/Impostor.Api/Games/IGameCodeFactory.cs new file mode 100644 index 0000000..f264fe0 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Games/IGameCodeFactory.cs @@ -0,0 +1,7 @@ +namespace Impostor.Api.Games +{ + public interface IGameCodeFactory + { + GameCode Create(); + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Games/Managers/IGameManager.cs b/Impostor-dev/src/Impostor.Api/Games/Managers/IGameManager.cs new file mode 100644 index 0000000..10a05f0 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Games/Managers/IGameManager.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Impostor.Api.Games.Managers +{ + public interface IGameManager + { + IEnumerable<IGame> Games { get; } + + IGame? Find(GameCode code); + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Impostor.Api.csproj b/Impostor-dev/src/Impostor.Api/Impostor.Api.csproj new file mode 100644 index 0000000..8ad2582 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Impostor.Api.csproj @@ -0,0 +1,38 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <CodeAnalysisRuleSet>ProjectRules.ruleset</CodeAnalysisRuleSet> + <LangVersion>9</LangVersion> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <Nullable>enable</Nullable> + <Version>1.0.0</Version> + <IncludeSymbols>true</IncludeSymbols> + <SymbolPackageFormat>snupkg</SymbolPackageFormat> + <AssemblyName>Impostor.Api</AssemblyName> + <AssemblyTitle>Impostor.Api</AssemblyTitle> + <Authors>AeonLucid</Authors> + <Description>An api library for Impostor, an Among Us private server. You need this package to write plugins for Impostor.</Description> + <PackageId>Impostor.Api</PackageId> + <PackageTags>Among Us;Impostor;Impostor Plugin</PackageTags> + <PackageIconUrl>https://raw.githubusercontent.com/Impostor/Impostor/dev/docs/images/logo_64.png</PackageIconUrl> + <PackageProjectUrl>https://github.com/Impostor/Impostor</PackageProjectUrl> + <RepositoryType>git</RepositoryType> + <RepositoryUrl>https://github.com/Impostor/Impostor</RepositoryUrl> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.0" /> + <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> + <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" /> + <PackageReference Include="Nullable" Version="1.3.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + </ItemGroup> + +</Project>
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Impostor.Api.csproj.DotSettings b/Impostor-dev/src/Impostor.Api/Impostor.Api.csproj.DotSettings new file mode 100644 index 0000000..2df7445 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Impostor.Api.csproj.DotSettings @@ -0,0 +1,8 @@ +<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=attributes/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events_005Cattributes/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events_005Cgame/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=games_005Cextensions/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=net_005Cextensions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/AlterGameTags.cs b/Impostor-dev/src/Impostor.Api/Innersloth/AlterGameTags.cs new file mode 100644 index 0000000..46d1b2e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/AlterGameTags.cs @@ -0,0 +1,7 @@ +namespace Impostor.Api.Innersloth +{ + public enum AlterGameTags : byte + { + ChangePrivacy = 1, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/ChatNoteType.cs b/Impostor-dev/src/Impostor.Api/Innersloth/ChatNoteType.cs new file mode 100644 index 0000000..c163601 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/ChatNoteType.cs @@ -0,0 +1,7 @@ +namespace Impostor.Api.Innersloth +{ + public enum ChatNoteType : byte + { + DidVote = 0, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/Customization/ColorType.cs b/Impostor-dev/src/Impostor.Api/Innersloth/Customization/ColorType.cs new file mode 100644 index 0000000..fc7be4c --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/Customization/ColorType.cs @@ -0,0 +1,18 @@ +namespace Impostor.Api.Innersloth.Customization +{ + public enum ColorType : byte + { + Red = 0, + Blue = 1, + Green = 2, + Pink = 3, + Orange = 4, + Yellow = 5, + Black = 6, + White = 7, + Purple = 8, + Brown = 9, + Cyan = 10, + Lime = 11, + } +} diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/Customization/HatType.cs b/Impostor-dev/src/Impostor.Api/Innersloth/Customization/HatType.cs new file mode 100644 index 0000000..5e0a3ef --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/Customization/HatType.cs @@ -0,0 +1,100 @@ +namespace Impostor.Api.Innersloth.Customization +{ + public enum HatType + { + NoHat = 0, + Astronaut = 1, + BaseballCap = 2, + BrainSlug = 3, + BushHat = 4, + CaptainsHat = 5, + DoubleTopHat = 6, + Flowerpot = 7, + Goggles = 8, + HardHat = 9, + Military = 10, + PaperHat = 11, + PartyHat = 12, + Police = 13, + Stethescope = 14, + TopHat = 15, + TowelWizard = 16, + Ushanka = 17, + Viking = 18, + WallCap = 19, + Snowman = 20, + Reindeer = 21, + Lights = 22, + Santa = 23, + Tree = 24, + Present = 25, + Candycanes = 26, + ElfHat = 27, + NewYears2018 = 28, + WhiteHat = 29, + Crown = 30, + Eyebrows = 31, + HaloHat = 32, + HeroCap = 33, + PipCap = 34, + PlungerHat = 35, + ScubaHat = 36, + StickminHat = 37, + StrawHat = 38, + TenGallonHat = 39, + ThirdEyeHat = 40, + ToiletPaperHat = 41, + Toppat = 42, + Fedora = 43, + Goggles2 = 44, + Headphones = 45, + MaskHat = 46, + PaperMask = 47, + Security = 48, + StrapHat = 49, + Banana = 50, + Beanie = 51, + Bear = 52, + Cheese = 53, + Cherry = 54, + Egg = 55, + Fedora2 = 56, + Flamingo = 57, + FlowerPin = 58, + Helmet = 59, + Plant = 60, + BatEyes = 61, + BatWings = 62, + Horns = 63, + Mohawk = 64, + Pumpkin = 65, + ScaryBag = 66, + Witch = 67, + Wolf = 68, + Pirate = 69, + Plague = 70, + Machete = 71, + Fred = 72, + MinerCap = 73, + WinterHat = 74, + Archae = 75, + Antenna = 76, + Balloon = 77, + BirdNest = 78, + BlackBelt = 79, + Caution = 80, + Chef = 81, + CopHat = 82, + DoRag = 83, + DumSticker = 84, + Fez = 85, + GeneralHat = 86, + GreyThing = 87, + HunterCap = 88, + JungleHat = 89, + MiniCrewmate = 90, + NinjaMask = 91, + RamHorns = 92, + Snowman2 = 93, + } +} diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/Customization/PetType.cs b/Impostor-dev/src/Impostor.Api/Innersloth/Customization/PetType.cs new file mode 100644 index 0000000..456e327 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/Customization/PetType.cs @@ -0,0 +1,18 @@ +namespace Impostor.Api.Innersloth.Customization +{ + public enum PetType + { + NoPet = 0, + Alien = 1, + Crewmate = 2, + Doggy = 3, + Stickmin = 4, + Hamster = 5, + Robot = 6, + Ufo = 7, + Ellie = 8, + Squig = 9, + Bedcrab = 10, + Glitch = 11, + } +} diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/Customization/SkinType.cs b/Impostor-dev/src/Impostor.Api/Innersloth/Customization/SkinType.cs new file mode 100644 index 0000000..35da312 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/Customization/SkinType.cs @@ -0,0 +1,22 @@ +namespace Impostor.Api.Innersloth.Customization +{ + public enum SkinType : byte + { + None = 0, + Astro = 1, + Capt = 2, + Mech = 3, + Military = 4, + Police = 5, + Science = 6, + SuitB = 7, + SuitW = 8, + Wall = 9, + Hazmat = 10, + Security = 11, + Tarmac = 12, + Miner = 13, + Winter = 14, + Archae = 15, + } +} diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/DeathReason.cs b/Impostor-dev/src/Impostor.Api/Innersloth/DeathReason.cs new file mode 100644 index 0000000..a07ac05 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/DeathReason.cs @@ -0,0 +1,9 @@ +namespace Impostor.Api.Innersloth +{ + public enum DeathReason + { + Exile = 0, + Kill = 1, + Disconnect = 2, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/DisconnectReason.cs b/Impostor-dev/src/Impostor.Api/Innersloth/DisconnectReason.cs new file mode 100644 index 0000000..9526f58 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/DisconnectReason.cs @@ -0,0 +1,54 @@ +namespace Impostor.Api.Innersloth +{ + public enum DisconnectReason + { + ExitGame = 0, + // The game you tried to join is full. + // Check with the host to see if you can join next round. + GameFull = 1, + // The game you tried to join already started. + // Check with the host to see if you can join next round. + GameStarted = 2, + // Could not find the game you're looking for. + GameMissing = 3, + IncorrectGame = 18, + // For these a message can be given, specifying an empty message shows + // "An unknown error disconnected you from the server." + CustomMessage1 = 4, + Custom = 8, + // CustomMessage3 = 11, + // CustomMessage4 = 12, + // CustomMessage5 = 13, + // CustomMessage6 = 14, + // CustomMessage7 = 15, + // You are running an older version of the game. + // Please update to play with others. + IncorrectVersion = 5, + // You cannot rejoin that room. + // You were banned + Banned = 6, + // You can rejoin if the room hasn't started + // You were kicked + Kicked = 7, + // You were banned for hacking. + // Please stop. + Hacking = 10, + Destroy = 16, + // You disconnected from the host. + // If this happens often, check your WiFi strength. + // + // You disconnected from the server. + // If this happens often, check your network strength. + // This may also be a server issue. + Error = 17, + // The server stopped this game. Possibly due to inactivity. + ServerRequest = 19, + // The Among Us servers are overloaded. + // Sorry! Please try again later! + ServerFull = 20, + FocusLostBackground = 207, + IntentionalLeaving = 208, + FocusLost = 209, + NewConnection = 210, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/FloatRange.cs b/Impostor-dev/src/Impostor.Api/Innersloth/FloatRange.cs new file mode 100644 index 0000000..c8a0824 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/FloatRange.cs @@ -0,0 +1,22 @@ +using Impostor.Api.Unity; + +namespace Impostor.Api.Innersloth +{ + public class FloatRange + { + private readonly float _min; + private readonly float _max; + + public FloatRange(float min, float max) + { + _min = min; + _max = max; + } + + public float Width => _max - _min; + + public float Lerp(float v) => Mathf.Lerp(_min, _max, v); + + public float ReverseLerp(float t) => Mathf.Clamp((t - _min) / Width, 0.0f, 1f); + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/GameCodeParser.cs b/Impostor-dev/src/Impostor.Api/Innersloth/GameCodeParser.cs new file mode 100644 index 0000000..9717cff --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/GameCodeParser.cs @@ -0,0 +1,142 @@ +using System; +using System.Buffers.Binary; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace Impostor.Api.Innersloth +{ + public static class GameCodeParser + { + private const string V2 = "QWXRTYLPESDFGHUJKZOCVBINMA"; + private static readonly int[] V2Map = { + 25, + 21, + 19, + 10, + 8, + 11, + 12, + 13, + 22, + 15, + 16, + 6, + 24, + 23, + 18, + 7, + 0, + 3, + 9, + 4, + 14, + 20, + 1, + 2, + 5, + 17 + }; + private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider(); + + public static string IntToGameName(int input) + { + // V2. + if (input < -1) + { + return IntToGameNameV2(input); + } + + // V1. + Span<byte> code = stackalloc byte[4]; + BinaryPrimitives.WriteInt32LittleEndian(code, input); +#if NETSTANDARD2_0 + return Encoding.UTF8.GetString(code.Slice(0, 4).ToArray()); +#else + return Encoding.UTF8.GetString(code.Slice(0, 4)); +#endif + } + + private static string IntToGameNameV2(int input) + { + var a = input & 0x3FF; + var b = (input >> 10) & 0xFFFFF; + + return new string(new [] + { + V2[a % 26], + V2[a / 26], + V2[b % 26], + V2[b / 26 % 26], + V2[b / (26 * 26) % 26], + V2[b / (26 * 26 * 26) % 26] + }); + } + + public static int GameNameToInt(string code) + { + var upper = code.ToUpperInvariant(); + if (upper.Any(x => !char.IsLetter(x))) + { + return -1; + } + + var len = code.Length; + if (len == 6) + { + return GameNameToIntV2(upper); + } + + if (len == 4) + { + return code[0] | ((code[1] | ((code[2] | (code[3] << 8)) << 8)) << 8); + } + + return -1; + } + + private static int GameNameToIntV2(string code) + { + var a = V2Map[code[0] - 65]; + var b = V2Map[code[1] - 65]; + var c = V2Map[code[2] - 65]; + var d = V2Map[code[3] - 65]; + var e = V2Map[code[4] - 65]; + var f = V2Map[code[5] - 65]; + + var one = (a + 26 * b) & 0x3FF; + var two = (c + 26 * (d + 26 * (e + 26 * f))); + + return (int) (one | ((two << 10) & 0x3FFFFC00) | 0x80000000); + } + + public static int GenerateCode(int len) + { + if (len != 4 && len != 6) + { + throw new ArgumentException("should be 4 or 6", nameof(len)); + } + + // Generate random bytes. +#if NETSTANDARD2_0 + var data = new byte[len]; +#else + Span<byte> data = stackalloc byte[len]; +#endif + Random.GetBytes(data); + + // Convert to their char representation. + Span<char> dataChar = stackalloc char[len]; + for (var i = 0; i < len; i++) + { + dataChar[i] = V2[V2Map[data[i] % 26]]; + } + +#if NETSTANDARD2_0 + return GameNameToInt(new string(dataChar.ToArray())); +#else + return GameNameToInt(new string(dataChar)); +#endif + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/GameKeywords.cs b/Impostor-dev/src/Impostor.Api/Innersloth/GameKeywords.cs new file mode 100644 index 0000000..bbb463f --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/GameKeywords.cs @@ -0,0 +1,19 @@ +using System; + +namespace Impostor.Api.Innersloth +{ + [Flags] + public enum GameKeywords : uint + { + All = 0, + Other = 1, + Spanish = 2, + Korean = 4, + Russian = 8, + Portuguese = 16, + Arabic = 32, + Filipone = 64, + Polish = 128, + English = 256, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/GameOptionsData.cs b/Impostor-dev/src/Impostor.Api/Innersloth/GameOptionsData.cs new file mode 100644 index 0000000..d18efd8 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/GameOptionsData.cs @@ -0,0 +1,261 @@ +using System; +using System.IO; +using Impostor.Api.Net.Messages; + +namespace Impostor.Api.Innersloth +{ + public class GameOptionsData + { + /// <summary> + /// The latest major version of the game client. + /// </summary> + public const int LatestVersion = 4; + + /// <summary> + /// Gets or sets host's version of the game. + /// </summary> + public byte Version { get; set; } + + /// <summary> + /// Gets or sets the maximum amount of players for this lobby. + /// </summary> + public byte MaxPlayers { get; set; } + + /// <summary> + /// Gets or sets the language of the lobby as per <see cref="GameKeywords"/> enum. + /// </summary> + public GameKeywords Keywords { get; set; } + + /// <summary> + /// Gets or sets the MapId selected for this lobby + /// </summary> + /// <remarks> + /// Skeld = 0, MiraHQ = 1, Polus = 2. + /// </remarks> + internal byte MapId { get; set; } + + /// <summary> + /// Gets or sets the map selected for this lobby + /// </summary> + public MapTypes Map + { + get => (MapTypes)MapId; + set => MapId = (byte)value; + } + + /// <summary> + /// Gets or sets the Player speed modifier. + /// </summary> + public float PlayerSpeedMod { get; set; } + + /// <summary> + /// Gets or sets the Light modifier for the players that are members of the crew as a multiplier value. + /// </summary> + public float CrewLightMod { get; set; } + + /// <summary> + /// Gets or sets the Light modifier for the players that are Impostors as a multiplier value. + /// </summary> + public float ImpostorLightMod { get; set; } + + /// <summary> + /// Gets or sets the Impostor cooldown to kill in seconds. + /// </summary> + public float KillCooldown { get; set; } + + /// <summary> + /// Gets or sets the number of common tasks. + /// </summary> + public int NumCommonTasks { get; set; } + + /// <summary> + /// Gets or sets the number of long tasks. + /// </summary> + public int NumLongTasks { get; set; } + + /// <summary> + /// Gets or sets the number of short tasks. + /// </summary> + public int NumShortTasks { get; set; } + + /// <summary> + /// Gets or sets the maximum amount of emergency meetings each player can call during the game in seconds. + /// </summary> + public int NumEmergencyMeetings { get; set; } + + /// <summary> + /// Gets or sets the cooldown between each time any player can call an emergency meeting in seconds. + /// </summary> + public int EmergencyCooldown { get; set; } + + /// <summary> + /// Gets or sets the number of impostors for this lobby. + /// </summary> + public int NumImpostors { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether ghosts (dead crew members) can do tasks. + /// </summary> + public bool GhostsDoTasks { get; set; } + + /// <summary> + /// Gets or sets the Kill as per values in <see cref="KillDistances"/>. + /// </summary> + /// <remarks> + /// Short = 0, Normal = 1, Long = 2. + /// </remarks> + public KillDistances KillDistance { get; set; } + + /// <summary> + /// Gets or sets the time for discussion before voting time in seconds. + /// </summary> + public int DiscussionTime { get; set; } + + /// <summary> + /// Gets or sets the time for voting in seconds. + /// </summary> + public int VotingTime { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether an ejected player is an impostor or not. + /// </summary> + public bool ConfirmImpostor { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether players are able to see tasks being performed by other players. + /// </summary> + /// <remarks> + /// By being set to true, tasks such as Empty Garbage, Submit Scan, Clear asteroids, Prime shields execution will be visible to other players. + /// </remarks> + public bool VisualTasks { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether the vote is anonymous. + /// </summary> + public bool AnonymousVotes { get; set; } + + /// <summary> + /// Gets or sets the task bar update mode as per values in <see cref="Innersloth.TaskBarUpdate"/>. + /// </summary> + public TaskBarUpdate TaskBarUpdate { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether the GameOptions are the default ones. + /// </summary> + public bool IsDefaults { get; set; } + + /// <summary> + /// Deserialize a packet/message to a new GameOptionsData object. + /// </summary> + /// <param name="reader">Message reader object containing the raw message.</param> + /// <returns>GameOptionsData object.</returns> + public static GameOptionsData DeserializeCreate(IMessageReader reader) + { + var options = new GameOptionsData(); + options.Deserialize(reader.ReadBytesAndSize()); + return options; + } + + /// <summary> + /// Serializes this instance of GameOptionsData object to a specified BinaryWriter. + /// </summary> + /// <param name="writer">The stream to write the message to.</param> + /// <param name="version">The version of the game.</param> + public void Serialize(BinaryWriter writer, byte version) + { + writer.Write((byte)version); + writer.Write((byte)MaxPlayers); + writer.Write((uint)Keywords); + writer.Write((byte)MapId); + writer.Write((float)PlayerSpeedMod); + writer.Write((float)CrewLightMod); + writer.Write((float)ImpostorLightMod); + writer.Write((float)KillCooldown); + writer.Write((byte)NumCommonTasks); + writer.Write((byte)NumLongTasks); + writer.Write((byte)NumShortTasks); + writer.Write((int)NumEmergencyMeetings); + writer.Write((byte)NumImpostors); + writer.Write((byte)KillDistance); + writer.Write((uint)DiscussionTime); + writer.Write((uint)VotingTime); + writer.Write((bool)IsDefaults); + + if (version > 1) + { + writer.Write((byte)EmergencyCooldown); + } + + if (version > 2) + { + writer.Write((bool)ConfirmImpostor); + writer.Write((bool)VisualTasks); + } + + if (version > 3) + { + writer.Write((bool)AnonymousVotes); + writer.Write((byte)TaskBarUpdate); + } + + if (version > 4) + { + throw new ImpostorException($"Unknown GameOptionsData version {Version}."); + } + } + + /// <summary> + /// Deserialize a ReadOnlyMemory object to this instance of the GameOptionsData object. + /// </summary> + /// <param name="memory">Memory containing the message/packet.</param> + public void Deserialize(ReadOnlyMemory<byte> memory) + { + var bytes = memory.Span; + + Version = bytes.ReadByte(); + MaxPlayers = bytes.ReadByte(); + Keywords = (GameKeywords)bytes.ReadUInt32(); + MapId = bytes.ReadByte(); + PlayerSpeedMod = bytes.ReadSingle(); + + CrewLightMod = bytes.ReadSingle(); + ImpostorLightMod = bytes.ReadSingle(); + KillCooldown = bytes.ReadSingle(); + + NumCommonTasks = bytes.ReadByte(); + NumLongTasks = bytes.ReadByte(); + NumShortTasks = bytes.ReadByte(); + + NumEmergencyMeetings = bytes.ReadInt32(); + + NumImpostors = bytes.ReadByte(); + KillDistance = (KillDistances)bytes.ReadByte(); + DiscussionTime = bytes.ReadInt32(); + VotingTime = bytes.ReadInt32(); + + IsDefaults = bytes.ReadBoolean(); + + if (Version > 1) + { + EmergencyCooldown = bytes.ReadByte(); + } + + if (Version > 2) + { + ConfirmImpostor = bytes.ReadBoolean(); + VisualTasks = bytes.ReadBoolean(); + } + + if (Version > 3) + { + AnonymousVotes = bytes.ReadBoolean(); + TaskBarUpdate = (TaskBarUpdate)bytes.ReadByte(); + } + + if (Version > 4) + { + throw new ImpostorException($"Unknown GameOptionsData version {Version}."); + } + } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/GameOverReason.cs b/Impostor-dev/src/Impostor.Api/Innersloth/GameOverReason.cs new file mode 100644 index 0000000..6a95d37 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/GameOverReason.cs @@ -0,0 +1,15 @@ +namespace Impostor.Api.Innersloth +{ + public enum GameOverReason : byte + { + HumansByVote = 0, + HumansByTask = 1, + ImpostorByVote = 2, + ImpostorByKill = 3, + ImpostorBySabotage = 4, + + // Unused (?) + ImpostorDisconnect = 5, + HumansDisconnect = 6, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/GameStates.cs b/Impostor-dev/src/Impostor.Api/Innersloth/GameStates.cs new file mode 100644 index 0000000..f5aaa9c --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/GameStates.cs @@ -0,0 +1,11 @@ +namespace Impostor.Api.Innersloth +{ + public enum GameStates : byte + { + NotStarted = 0, + Starting = 1, + Started = 2, + Ended = 3, + Destroyed = 4, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/GameVersion.cs b/Impostor-dev/src/Impostor.Api/Innersloth/GameVersion.cs new file mode 100644 index 0000000..c11933e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/GameVersion.cs @@ -0,0 +1,10 @@ +namespace Impostor.Api.Innersloth +{ + public class GameVersion + { + public static int GetVersion(int year, int month, int day, int rev = 0) + { + return (year * 25000) + (month * 1800) + (day * 50) + rev; + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/KillDistances.cs b/Impostor-dev/src/Impostor.Api/Innersloth/KillDistances.cs new file mode 100644 index 0000000..b2ee5d9 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/KillDistances.cs @@ -0,0 +1,12 @@ +using System; + +namespace Impostor.Api.Innersloth +{ + [Flags] + public enum KillDistances : byte + { + Short = 0, + Normal = 1, + Long = 2, + } +} diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/MapFlags.cs b/Impostor-dev/src/Impostor.Api/Innersloth/MapFlags.cs new file mode 100644 index 0000000..ad462a2 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/MapFlags.cs @@ -0,0 +1,12 @@ +using System; + +namespace Impostor.Api.Innersloth +{ + [Flags] + public enum MapFlags + { + Skeld = 1, + MiraHQ = 2, + Polus = 4, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/MapTypes.cs b/Impostor-dev/src/Impostor.Api/Innersloth/MapTypes.cs new file mode 100644 index 0000000..8dc07b5 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/MapTypes.cs @@ -0,0 +1,9 @@ +namespace Impostor.Api.Innersloth +{ + public enum MapTypes + { + Skeld = 0, + MiraHQ = 1, + Polus = 2, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/RegionInfo.cs b/Impostor-dev/src/Impostor.Api/Innersloth/RegionInfo.cs new file mode 100644 index 0000000..c78978b --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/RegionInfo.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.IO; + +namespace Impostor.Api.Innersloth +{ + public class RegionInfo + { + public RegionInfo(string name, string ping, IReadOnlyList<ServerInfo> servers) + { + Name = name; + Ping = ping; + Servers = servers; + } + + public string Name { get; } + public string Ping { get; } + public IReadOnlyList<ServerInfo> Servers { get; } + + public void Serialize(BinaryWriter writer) + { + writer.Write(0); + writer.Write(Name); + writer.Write(Ping); + writer.Write(Servers.Count); + + foreach (var server in Servers) + { + server.Serialize(writer); + } + } + + public static RegionInfo Deserialize(BinaryReader reader) + { + var unknown = reader.ReadInt32(); + var name = reader.ReadString(); + var ping = reader.ReadString(); + var servers = new List<ServerInfo>(); + var serverCount = reader.ReadInt32(); + + for (var i = 0; i < serverCount; i++) + { + servers.Add(ServerInfo.Deserialize(reader)); + } + + return new RegionInfo(name, ping, servers); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/ServerInfo.cs b/Impostor-dev/src/Impostor.Api/Innersloth/ServerInfo.cs new file mode 100644 index 0000000..7785823 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/ServerInfo.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Net; + +namespace Impostor.Api.Innersloth +{ + public class ServerInfo + { + public string Name { get; } + public string Ip { get; } + public ushort Port { get; } + + public ServerInfo(string name, string ip, ushort port) + { + Name = name; + Ip = ip; + Port = port; + } + + public void Serialize(BinaryWriter writer) + { + writer.Write(Name); + writer.Write(IPAddress.Parse(Ip).GetAddressBytes()); + writer.Write(Port); + writer.Write(0); + } + + public static ServerInfo Deserialize(BinaryReader reader) + { + var name = reader.ReadString(); + var ip = new IPAddress(reader.ReadBytes(4)).ToString(); + var port = reader.ReadUInt16(); + var unknown = reader.ReadInt32(); + + return new ServerInfo(name, ip, port); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/SystemTypeHelpers.cs b/Impostor-dev/src/Impostor.Api/Innersloth/SystemTypeHelpers.cs new file mode 100644 index 0000000..ad88c28 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/SystemTypeHelpers.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq; + +namespace Impostor.Api.Innersloth +{ + 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 diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/SystemTypes.cs b/Impostor-dev/src/Impostor.Api/Innersloth/SystemTypes.cs new file mode 100644 index 0000000..7f91718 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/SystemTypes.cs @@ -0,0 +1,42 @@ +namespace Impostor.Api.Innersloth +{ + public enum SystemTypes : byte + { + Hallway = 0, + Storage = 1, + Cafeteria = 2, + Reactor = 3, + UpperEngine = 4, + Nav = 5, + Admin = 6, + Electrical = 7, + LifeSupp = 8, + Shields = 9, + MedBay = 10, + Security = 11, + Weapons = 12, + LowerEngine = 13, + Comms = 14, + ShipTasks = 15, + Doors = 16, + Sabotage = 17, + /// <summary> + /// Decontam on Mira and bottom decontam on Polus + /// </summary> + Decontamination = 18, + Launchpad = 19, + LockerRoom = 20, + Laboratory = 21, + Balcony = 22, + Office = 23, + Greenhouse = 24, + Dropship = 25, + /// <summary> + /// Top decontam on Polus + /// </summary> + Decontamination2 = 26, + Outside = 27, + Specimens = 28, + BoilerRoom = 29 + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/TaskBarUpdate.cs b/Impostor-dev/src/Impostor.Api/Innersloth/TaskBarUpdate.cs new file mode 100644 index 0000000..f4d7c1f --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/TaskBarUpdate.cs @@ -0,0 +1,9 @@ +namespace Impostor.Api.Innersloth +{ + public enum TaskBarUpdate : byte + { + Always = 0, + Meetings = 1, + Never = 2 + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/TaskTypes.cs b/Impostor-dev/src/Impostor.Api/Innersloth/TaskTypes.cs new file mode 100644 index 0000000..8b15354 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/TaskTypes.cs @@ -0,0 +1,49 @@ +namespace Impostor.Api.Innersloth +{ + public enum TaskTypes : uint + { + SubmitScan = 0, + PrimeShields = 1, + FuelEngines = 2, + ChartCourse = 3, + StartReactor = 4, + SwipeCard = 5, + ClearAsteroids = 6, + UploadData = 7, + InspectSample = 8, + EmptyChute = 9, + EmptyGarbage = 10, + AlignEngineOutput = 11, + FixWiring = 12, + CalibrateDistributor = 13, + DivertPower = 14, + UnlockManifolds = 15, + ResetReactor = 16, + FixLights = 17, + Filter = 18, + FixComms = 19, + RestoreOxy = 20, + StabilizeSteering = 21, + AssembleArtifact = 22, + SortSamples = 23, + MeasureWeather = 24, + EnterIdCode = 25, + BuyBeverage = 26, + ProcessData = 27, + RunDiagnostics = 28, + WaterPlants = 29, + MonitorOxygen = 30, + StoreArtifact = 31, + FillCanisters = 32, + ActivateWeatherNodes = 33, + InsertKeys = 34, + ResetSeismic = 35, + ScanBoardingPass = 36, + OpenWaterways = 37, + ReplaceWaterJug = 38, + RepairDrill = 39, + AlignTelescope = 40, + RecordTemperature = 41, + RebootWifi = 42, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/TextBox.cs b/Impostor-dev/src/Impostor.Api/Innersloth/TextBox.cs new file mode 100644 index 0000000..9533d83 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/TextBox.cs @@ -0,0 +1,10 @@ +namespace Impostor.Api.Innersloth +{ + public static class TextBox + { + public static bool IsCharAllowed(char i) + { + return i == ' ' || (i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || (i >= '0' && i <= '9') || (i >= 'À' && i <= 'ÿ') || (i >= 'Ѐ' && i <= 'џ') || (i >= 'ㄱ' && i <= 'ㆎ') || (i >= '가' && i <= '힣'); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Innersloth/VentLocation.cs b/Impostor-dev/src/Impostor.Api/Innersloth/VentLocation.cs new file mode 100644 index 0000000..f9b8567 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Innersloth/VentLocation.cs @@ -0,0 +1,48 @@ +namespace Impostor.Api.Innersloth +{ + public enum VentLocation : uint + { + // Skeld + SkeldAdmin = 0, + SkeldRightHallway = 1, + SkeldCafeteria = 2, + SkeldElectrical = 3, + SkeldUpperEngine = 4, + SkeldSecurity = 5, + SkeldMedbay = 6, + SkeldWeapons = 7, + SkeldLowerReactor = 8, + SkeldLowerEngine = 9, + SkeldShields = 10, + SkeldUpperReactor = 11, + SkeldUpperNavigation = 12, + SkeldLowerNavigation = 13, + + // Mira HQ + MiraBalcony = 1, + MiraCafeteria = 2, + MiraReactor = 3, + MiraLaboratory = 4, + MiraOffice = 5, + MiraAdmin = 6, + MiraGreenhouse = 7, + MiraMedbay = 8, + MiraDecontamination = 9, + MiraLockerRoom = 10, + MiraLaunchpad = 11, + + // Polus + PolusSecurity = 0, + PolusElectrical = 1, + PolusO2 = 2, + PolusCommunications = 3, + PolusOffice = 4, + PolusAdmin = 5, + PolusLaboratory = 6, + PolusLava = 7, + PolusStorage = 8, + PolusRightStabilizer = 9, + PolusLeftStabilizer = 10, + PolusOutsideAdmin = 11, + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/IClient.cs b/Impostor-dev/src/Impostor.Api/Net/IClient.cs new file mode 100644 index 0000000..48efeda --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/IClient.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Impostor.Api.Innersloth; +using Impostor.Api.Net.Messages; + +namespace Impostor.Api.Net +{ + /// <summary> + /// Represents a connected game client. + /// </summary> + public interface IClient + { + /// <summary> + /// Gets or sets the unique ID of the client. + /// </summary> + /// <remarks> + /// This ID is generated when the client is registered in the client manager and should not be used + /// to store persisted data. + /// </remarks> + int Id { get; set; } + + /// <summary> + /// Gets the name that was provided by the player in the client. + /// </summary> + /// <remarks> + /// The name is provided by the player and should not be used to store persisted data. + /// </remarks> + string Name { get; } + + /// <summary> + /// Gets the connection of the client. + /// </summary> + /// <remarks> + /// Null when the client was not registered by the matchmaker. + /// </remarks> + IHazelConnection? Connection { get; } + + /// <summary> + /// Gets a key/value collection that can be used to share data between messages. + /// </summary> + /// <remarks> + /// <para> + /// The stored data will not be saved. + /// After the connection has been closed all data will be lost. + /// </para> + /// <para> + /// Note that the values will not be disposed after the connection has been closed. + /// This has to be implemented by the plugin. + /// </para> + /// </remarks> + IDictionary<object, object> Items { get; } + + /// <summary> + /// Gets or sets the current game data of the <see cref="IClient"/>. + /// </summary> + IClientPlayer? Player { get; } + + ValueTask HandleMessageAsync(IMessageReader message, MessageType messageType); + + ValueTask HandleDisconnectAsync(string reason); + + /// <summary> + /// Disconnect the client with a <see cref="DisconnectReason"/>. + /// </summary> + /// <param name="reason"> + /// The message to show to the player. + /// </param> + /// <param name="message"> + /// Only used when <see cref="reason"/> is set to <see cref="DisconnectReason.Custom"/>. + /// </param> + /// <returns> + /// A <see cref="ValueTask"/> representing the asynchronous operation. + /// </returns> + ValueTask DisconnectAsync(DisconnectReason reason, string? message = null); + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/IClientPlayer.cs b/Impostor-dev/src/Impostor.Api/Net/IClientPlayer.cs new file mode 100644 index 0000000..6070210 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/IClientPlayer.cs @@ -0,0 +1,43 @@ +using System.Threading.Tasks; +using Impostor.Api.Games; +using Impostor.Api.Net.Inner; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Api.Net +{ + /// <summary> + /// Represents a player in <see cref="IGame"/>. + /// </summary> + public interface IClientPlayer + { + /// <summary> + /// Gets the client that belongs to the player. + /// </summary> + IClient Client { get; } + + /// <summary> + /// Gets the game where the <see cref="IClientPlayer"/> belongs to. + /// </summary> + IGame Game { get; } + + /// <summary> + /// Gets or sets the current limbo state of the player. + /// </summary> + LimboStates Limbo { get; set; } + + IInnerPlayerControl? Character { get; } + + public bool IsHost { get; } + + /// <summary> + /// Checks if the specified <see cref="IInnerNetObject"/> is owned by <see cref="IClientPlayer"/>. + /// </summary> + /// <param name="netObject">The <see cref="IInnerNetObject"/>.</param> + /// <returns>Returns true if owned by <see cref="IClientPlayer"/>.</returns> + bool IsOwner(IInnerNetObject netObject); + + ValueTask KickAsync(); + + ValueTask BanAsync(); + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/IConnection.cs b/Impostor-dev/src/Impostor.Api/Net/IConnection.cs new file mode 100644 index 0000000..94f9b8b --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/IConnection.cs @@ -0,0 +1,7 @@ +namespace Impostor.Api.Net +{ + public interface IConnection + { + + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/IHazelConnection.cs b/Impostor-dev/src/Impostor.Api/Net/IHazelConnection.cs new file mode 100644 index 0000000..4e6c4b3 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/IHazelConnection.cs @@ -0,0 +1,41 @@ +using System.Net; +using System.Threading.Tasks; +using Impostor.Api.Net.Messages; + +namespace Impostor.Api.Net +{ + /// <summary> + /// Represents the connection of the client. + /// </summary> + public interface IHazelConnection + { + /// <summary> + /// Gets the IP endpoint of the client. + /// </summary> + IPEndPoint EndPoint { get; } + + /// <summary> + /// Gets a value indicating whether the client is connected to the server. + /// </summary> + bool IsConnected { get; } + + /// <summary> + /// Gets the client of the connection. + /// </summary> + IClient? Client { get; set; } + + /// <summary> + /// Sends a message writer to the connection. + /// </summary> + /// <param name="writer">The message.</param> + /// <returns></returns> + ValueTask SendAsync(IMessageWriter writer); + + /// <summary> + /// Disconnects the client and invokes the disconnect handler. + /// </summary> + /// <param name="reason">A reason.</param> + /// <returns></returns> + ValueTask DisconnectAsync(string? reason); + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/IGameNet.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/IGameNet.cs new file mode 100644 index 0000000..933a4de --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/IGameNet.cs @@ -0,0 +1,18 @@ +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Api.Net.Inner +{ + /// <summary> + /// Holds all data that is serialized over the network through GameData packets. + /// </summary> + public interface IGameNet + { + IInnerLobbyBehaviour LobbyBehaviour { get; } + + IInnerGameData GameData { get; } + + IInnerVoteBanSystem VoteBan { get; } + + IInnerShipStatus ShipStatus { get; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/IInnerNetObject.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/IInnerNetObject.cs new file mode 100644 index 0000000..c171377 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/IInnerNetObject.cs @@ -0,0 +1,9 @@ +namespace Impostor.Api.Net.Inner +{ + public interface IInnerNetObject + { + public uint NetId { get; } + + public int OwnerId { get; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/Components/IInnerCustomNetworkTransform.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/Components/IInnerCustomNetworkTransform.cs new file mode 100644 index 0000000..6d867e7 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/Components/IInnerCustomNetworkTransform.cs @@ -0,0 +1,15 @@ +using System.Numerics; +using System.Threading.Tasks; + +namespace Impostor.Api.Net.Inner.Objects.Components +{ + public interface IInnerCustomNetworkTransform : IInnerNetObject + { + /// <summary> + /// Snaps the current to the given position <see cref="IInnerPlayerControl"/>. + /// </summary> + /// <param name="position">The target position.</param> + /// <returns>Task that must be awaited.</returns> + ValueTask SnapToAsync(Vector2 position); + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/Components/IInnerPlayerPhysics.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/Components/IInnerPlayerPhysics.cs new file mode 100644 index 0000000..9378c5b --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/Components/IInnerPlayerPhysics.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Net.Inner.Objects.Components +{ + public interface IInnerPlayerPhysics : IInnerNetObject + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerGameData.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerGameData.cs new file mode 100644 index 0000000..6e41020 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerGameData.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Net.Inner.Objects +{ + public interface IInnerGameData : IInnerNetObject + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerLobbyBehaviour.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerLobbyBehaviour.cs new file mode 100644 index 0000000..f05f4cf --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerLobbyBehaviour.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Net.Inner.Objects +{ + public interface IInnerLobbyBehaviour : IInnerNetObject + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerMeetingHud.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerMeetingHud.cs new file mode 100644 index 0000000..9c89d05 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerMeetingHud.cs @@ -0,0 +1,6 @@ +namespace Impostor.Api.Net.Inner.Objects +{ + public interface IInnerMeetingHud + { + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerPlayerControl.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerPlayerControl.cs new file mode 100644 index 0000000..04558b9 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerPlayerControl.cs @@ -0,0 +1,116 @@ +using System.Threading.Tasks; +using Impostor.Api.Innersloth.Customization; +using Impostor.Api.Net.Inner.Objects.Components; + +namespace Impostor.Api.Net.Inner.Objects +{ + public interface IInnerPlayerControl : IInnerNetObject + { + /// <summary> + /// Gets the <see cref="PlayerId"/> assigned by the client of the host of the game. + /// </summary> + byte PlayerId { get; } + + /// <summary> + /// Gets the <see cref="IInnerPlayerPhysics"/> of the <see cref="IInnerPlayerControl"/>. + /// Contains vent logic. + /// </summary> + IInnerPlayerPhysics Physics { get; } + + /// <summary> + /// Gets the <see cref="IInnerCustomNetworkTransform"/> of the <see cref="IInnerPlayerControl"/>. + /// Contains position data about the player. + /// </summary> + IInnerCustomNetworkTransform NetworkTransform { get; } + + /// <summary> + /// Gets the <see cref="IInnerPlayerInfo"/> of the <see cref="IInnerPlayerControl"/>. + /// Contains metadata about the player. + /// </summary> + IInnerPlayerInfo PlayerInfo { get; } + + /// <summary> + /// Sets the name of the current <see cref="IInnerPlayerControl"/>. + /// Visible to all players. + /// </summary> + /// <param name="name">A name for the player.</param> + /// <returns>Task that must be awaited.</returns> + ValueTask SetNameAsync(string name); + + /// <summary> + /// Sets the color of the current <see cref="IInnerPlayerControl"/>. + /// Visible to all players. + /// </summary> + /// <param name="colorId">A color for the player.</param> + /// <returns>Task that must be awaited.</returns> + ValueTask SetColorAsync(byte colorId); + + /// <param name="colorType">A color for the player.</param> + /// <inheritdoc cref="SetColorAsync(byte)" /> + ValueTask SetColorAsync(ColorType colorType); + + /// <summary> + /// Sets the hat of the current <see cref="IInnerPlayerControl"/>. + /// Visible to all players. + /// </summary> + /// <param name="hatId">An hat for the player.</param> + /// <returns>Task that must be awaited.</returns> + ValueTask SetHatAsync(uint hatId); + + /// <param name="hatType">An hat for the player.</param> + /// <inheritdoc cref="SetHatAsync(uint)" /> + ValueTask SetHatAsync(HatType hatType); + + /// <summary> + /// Sets the pet of the current <see cref="IInnerPlayerControl"/>. + /// Visible to all players. + /// </summary> + /// <param name="petId">A pet for the player.</param> + /// <returns>Task that must be awaited.</returns> + ValueTask SetPetAsync(uint petId); + + /// <param name="petType">A pet for the player.</param> + /// <inheritdoc cref="SetPetAsync(uint)" /> + ValueTask SetPetAsync(PetType petType); + + /// <summary> + /// Sets the skin of the current <see cref="IInnerPlayerControl"/>. + /// Visible to all players. + /// </summary> + /// <param name="skinId">A skin for the player.</param> + /// <returns>Task that must be awaited.</returns> + ValueTask SetSkinAsync(uint skinId); + + /// <param name="skinType">A skin for the player.</param> + /// <inheritdoc cref="SetSkinAsync(uint)" /> + ValueTask SetSkinAsync(SkinType skinType); + + /// <summary> + /// Send a chat message as the current <see cref="IInnerPlayerControl"/>. + /// Visible to all players. + /// </summary> + /// <param name="text">The message to send.</param> + /// <returns>Task that must be awaited.</returns> + ValueTask SendChatAsync(string text); + + /// <summary> + /// Send a chat message as the current <see cref="IInnerPlayerControl"/>. + /// Visible to only the current. + /// </summary> + /// <param name="text">The message to send.</param> + /// <param name="player"> + /// The player that should receive this chat message. + /// When left as null, will send message to self. + /// </param> + /// <returns>Task that must be awaited.</returns> + ValueTask SendChatToPlayerAsync(string text, IInnerPlayerControl? player = null); + + /// <summary> + /// Sets the current to be murdered by an impostor <see cref="IInnerPlayerControl"/>. + /// Visible to all players. + /// </summary> + /// /// <param name="impostor">The Impostor who kill.</param> + /// <returns>Task that must be awaited.</returns> + ValueTask SetMurderedByAsync(IClientPlayer impostor); + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerPlayerInfo.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerPlayerInfo.cs new file mode 100644 index 0000000..6cb3302 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerPlayerInfo.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Inner.Objects +{ + public interface IInnerPlayerInfo + { + /// <summary> + /// Gets the name of the player as decided by the host. + /// </summary> + string PlayerName { get; } + + /// <summary> + /// Gets the color of the player. + /// </summary> + byte ColorId { get; } + + /// <summary> + /// Gets the hat of the player. + /// </summary> + uint HatId { get; } + + /// <summary> + /// Gets the pet of the player. + /// </summary> + uint PetId { get; } + + /// <summary> + /// Gets the skin of the player. + /// </summary> + uint SkinId { get; } + + /// <summary> + /// Gets a value indicating whether the player is an impostor. + /// </summary> + bool IsImpostor { get; } + + /// <summary> + /// Gets a value indicating whether the player is a dead in the current game. + /// </summary> + bool IsDead { get; } + + /// <summary> + /// Gets the reason why the player is dead in the current game. + /// </summary> + DeathReason LastDeathReason { get; } + + IEnumerable<ITaskInfo> Tasks { get; } + + DateTimeOffset LastMurder { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerShipStatus.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerShipStatus.cs new file mode 100644 index 0000000..c0a05ae --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerShipStatus.cs @@ -0,0 +1,7 @@ +namespace Impostor.Api.Net.Inner.Objects +{ + public interface IInnerShipStatus : IInnerNetObject + { + + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerVoteBanSystem.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerVoteBanSystem.cs new file mode 100644 index 0000000..d0a816d --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerVoteBanSystem.cs @@ -0,0 +1,7 @@ +namespace Impostor.Api.Net.Inner.Objects +{ + public interface IInnerVoteBanSystem : IInnerNetObject + { + + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/ITaskInfo.cs b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/ITaskInfo.cs new file mode 100644 index 0000000..2b6dd86 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Inner/Objects/ITaskInfo.cs @@ -0,0 +1,14 @@ +using Impostor.Api.Innersloth; +using Impostor.Api.Net.Messages; + +namespace Impostor.Api.Net.Inner.Objects +{ + public interface ITaskInfo + { + uint Id { get; } + + TaskTypes Type { get; } + + bool Complete { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/LimboStates.cs b/Impostor-dev/src/Impostor.Api/Net/LimboStates.cs new file mode 100644 index 0000000..44c493e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/LimboStates.cs @@ -0,0 +1,13 @@ +using System; + +namespace Impostor.Api.Net +{ + [Flags] + public enum LimboStates + { + PreSpawn = 1, + NotLimbo = 2, + WaitingForHost = 4, + All = PreSpawn | NotLimbo | WaitingForHost, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Manager/IClientManager.cs b/Impostor-dev/src/Impostor.Api/Net/Manager/IClientManager.cs new file mode 100644 index 0000000..92bf89f --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Manager/IClientManager.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Impostor.Api.Net.Manager +{ + public interface IClientManager + { + IEnumerable<IClient> Clients { get; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message00HostGameC2S.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message00HostGameC2S.cs new file mode 100644 index 0000000..4f5b39c --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message00HostGameC2S.cs @@ -0,0 +1,27 @@ +using System.IO; +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Messages.C2S +{ + public static class Message00HostGameC2S + { + public static void Serialize(IMessageWriter writer, GameOptionsData gameOptionsData) + { + writer.StartMessage(MessageFlags.HostGame); + + using (var memory = new MemoryStream()) + using (var writerBin = new BinaryWriter(memory)) + { + gameOptionsData.Serialize(writerBin, GameOptionsData.LatestVersion); + writer.WriteBytesAndSize(memory.ToArray()); + } + + writer.EndMessage(); + } + + public static GameOptionsData Deserialize(IMessageReader reader) + { + return GameOptionsData.DeserializeCreate(reader); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message01JoinGameC2S.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message01JoinGameC2S.cs new file mode 100644 index 0000000..f121b97 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message01JoinGameC2S.cs @@ -0,0 +1,20 @@ +using System; + +namespace Impostor.Api.Net.Messages.C2S +{ + public static class Message01JoinGameC2S + { + public static void Serialize(IMessageWriter writer) + { + throw new System.NotImplementedException(); + } + + public static void Deserialize(IMessageReader reader, out int gameCode, out byte unknown) + { + var slice = reader.ReadBytes(sizeof(Int32) + sizeof(byte)).Span; + + gameCode = slice.ReadInt32(); + unknown = slice.ReadByte(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message04RemovePlayerC2S.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message04RemovePlayerC2S.cs new file mode 100644 index 0000000..99cdcfa --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message04RemovePlayerC2S.cs @@ -0,0 +1,16 @@ +namespace Impostor.Api.Net.Messages.C2S +{ + public class Message04RemovePlayerC2S + { + public static void Serialize(IMessageWriter writer) + { + throw new System.NotImplementedException(); + } + + public static void Deserialize(IMessageReader reader, out int playerId, out byte reason) + { + playerId = reader.ReadPackedInt32(); + reason = reader.ReadByte(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message08EndGameC2S.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message08EndGameC2S.cs new file mode 100644 index 0000000..7ca5e3a --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message08EndGameC2S.cs @@ -0,0 +1,18 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Messages.C2S +{ + public class Message08EndGameC2S + { + public static void Serialize(IMessageWriter writer) + { + throw new System.NotImplementedException(); + } + + public static void Deserialize(IMessageReader reader, out GameOverReason gameOverReason) + { + gameOverReason = (GameOverReason)reader.ReadByte(); + reader.ReadBoolean(); // showAd + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message10AlterGameC2S.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message10AlterGameC2S.cs new file mode 100644 index 0000000..330f3b5 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message10AlterGameC2S.cs @@ -0,0 +1,20 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Messages.C2S +{ + public class Message10AlterGameC2S + { + public static void Serialize(IMessageWriter writer) + { + throw new System.NotImplementedException(); + } + + public static void Deserialize(IMessageReader reader, out AlterGameTags gameTag, out bool isPublic) + { + var slice = reader.ReadBytes(sizeof(byte) + sizeof(byte)).Span; + + gameTag = (AlterGameTags)slice.ReadByte(); + isPublic = slice.ReadBoolean(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message11KickPlayerC2S.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message11KickPlayerC2S.cs new file mode 100644 index 0000000..7c5b8b9 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message11KickPlayerC2S.cs @@ -0,0 +1,16 @@ +namespace Impostor.Api.Net.Messages.C2S +{ + public class Message11KickPlayerC2S + { + public static void Serialize(IMessageWriter writer) + { + throw new System.NotImplementedException(); + } + + public static void Deserialize(IMessageReader reader, out int playerId, out bool isBan) + { + playerId = reader.ReadPackedInt32(); + isBan = reader.ReadBoolean(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message16GetGameListC2S.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message16GetGameListC2S.cs new file mode 100644 index 0000000..2b7e12a --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message16GetGameListC2S.cs @@ -0,0 +1,18 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Messages.C2S +{ + public class Message16GetGameListC2S + { + public static void Serialize(IMessageWriter writer) + { + throw new System.NotImplementedException(); + } + + public static void Deserialize(IMessageReader reader, out GameOptionsData options) + { + reader.ReadPackedInt32(); // Hardcoded 0. + options = GameOptionsData.DeserializeCreate(reader); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageReader.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageReader.cs new file mode 100644 index 0000000..87c06c4 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageReader.cs @@ -0,0 +1,68 @@ +using System; + +namespace Impostor.Api.Net.Messages +{ + public interface IMessageReader : IDisposable + { + /// <summary> + /// Gets the tag of the message. + /// </summary> + byte Tag { get; } + + /// <summary> + /// Gets the buffer of the message. + /// </summary> + byte[] Buffer { get; } + + /// <summary> + /// Gets the offset of our current <see cref="IMessageReader"/> in the entire <see cref="Buffer"/>. + /// </summary> + int Offset { get; } + + /// <summary> + /// Gets the current position of the reader. + /// </summary> + int Position { get; } + + /// <summary> + /// Gets the length of the buffer. + /// </summary> + int Length { get; } + + IMessageReader ReadMessage(); + + bool ReadBoolean(); + + sbyte ReadSByte(); + + byte ReadByte(); + + ushort ReadUInt16(); + + short ReadInt16(); + + uint ReadUInt32(); + + int ReadInt32(); + + float ReadSingle(); + + string ReadString(); + + ReadOnlyMemory<byte> ReadBytesAndSize(); + + ReadOnlyMemory<byte> ReadBytes(int length); + + int ReadPackedInt32(); + + uint ReadPackedUInt32(); + + void CopyTo(IMessageWriter writer); + + void Seek(int position); + + void RemoveMessage(IMessageReader message); + + IMessageReader Copy(int offset = 0); + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageWriter.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageWriter.cs new file mode 100644 index 0000000..4f6765b --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageWriter.cs @@ -0,0 +1,127 @@ +using System; +using System.Net; +using Impostor.Api.Games; + +namespace Impostor.Api.Net.Messages +{ + /// <summary> + /// Base message writer. + /// </summary> + public interface IMessageWriter : IDisposable + { + public byte[] Buffer { get; } + + public int Length { get; set; } + + public int Position { get; set; } + + public MessageType SendOption { get; } + + /// <summary> + /// Writes a boolean to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(bool value); + + /// <summary> + /// Writes a sbyte to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(sbyte value); + + /// <summary> + /// Writes a byte to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(byte value); + + /// <summary> + /// Writes a short to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(short value); + + /// <summary> + /// Writes an ushort to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(ushort value); + + /// <summary> + /// Writes an uint to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(uint value); + + /// <summary> + /// Writes an int to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(int value); + + /// <summary> + /// Writes a float to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(float value); + + /// <summary> + /// Writes a string to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(string value); + + /// <summary> + /// Writes a <see cref="IPAddress"/> to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(IPAddress value); + + /// <summary> + /// Writes an packed int to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void WritePacked(int value); + + /// <summary> + /// Writes an packed uint to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void WritePacked(uint value); + + /// <summary> + /// Writes raw bytes to the message. + /// </summary> + /// <param name="data">Bytes to write.</param> + void Write(ReadOnlyMemory<byte> data); + + /// <summary> + /// Writes a game code to the message. + /// </summary> + /// <param name="value">Value to write.</param> + void Write(GameCode value); + + void WriteBytesAndSize(byte[] bytes); + + void WriteBytesAndSize(byte[] bytes, int length); + + void WriteBytesAndSize(byte[] bytes, int offset, int length); + + /// <summary> + /// Starts a new message. + /// </summary> + /// <param name="typeFlag">Message flag header.</param> + void StartMessage(byte typeFlag); + + /// <summary> + /// Mark the end of the message. + /// </summary> + void EndMessage(); + + /// <summary> + /// Clear the message writer. + /// </summary> + /// <param name="type">New type of the message.</param> + void Clear(MessageType type); + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageWriterProvider.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageWriterProvider.cs new file mode 100644 index 0000000..f398939 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/IMessageWriterProvider.cs @@ -0,0 +1,16 @@ +namespace Impostor.Api.Net.Messages +{ + public interface IMessageWriterProvider + { + /// <summary> + /// Retrieves a <see cref="IMessageWriter"/> from the internal pool. + /// Make sure to call <see cref="IMessageWriter.Dispose"/> when you are done! + /// </summary> + /// <param name="sendOption"> + /// Whether to send the message as <see cref="MessageType.Reliable"/> or <see cref="MessageType.Unreliable"/>. + /// Reliable packets will ensure delivery while unreliable packets may be lost. + /// </param> + /// <returns>A <see cref="IMessageWriter"/> from the pool.</returns> + IMessageWriter Get(MessageType sendOption = MessageType.Unreliable); + } +} diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/MessageFlags.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/MessageFlags.cs new file mode 100644 index 0000000..aea0c60 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/MessageFlags.cs @@ -0,0 +1,22 @@ +namespace Impostor.Api.Net.Messages +{ + public static class MessageFlags + { + public const byte HostGame = 0; + public const byte JoinGame = 1; + public const byte StartGame = 2; + public const byte RemoveGame = 3; + public const byte RemovePlayer = 4; + public const byte GameData = 5; + public const byte GameDataTo = 6; + public const byte JoinedGame = 7; + public const byte EndGame = 8; + public const byte AlterGame = 10; + public const byte KickPlayer = 11; + public const byte WaitForHost = 12; + public const byte Redirect = 13; + public const byte ReselectServer = 14; + public const byte GetGameList = 9; + public const byte GetGameListV2 = 16; + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/MessageType.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/MessageType.cs new file mode 100644 index 0000000..1604358 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/MessageType.cs @@ -0,0 +1,32 @@ +using System; + +namespace Impostor.Api.Net.Messages +{ + /// <summary> + /// Specifies how a message should be sent between connections. + /// </summary> + [Flags] + public enum MessageType : byte + { + /// <summary> + /// Requests unreliable delivery with no fragmentation. + /// </summary> + /// <remarks> + /// Sending data using unreliable delivery means that data is not guaranteed to arrive at it's destination nor is + /// it guaranteed to arrive only once. However, unreliable delivery can be faster than other methods and it + /// typically requires a smaller number of protocol bytes than other methods. There is also typically less + /// processing involved and less memory needed as packets are not stored once sent. + /// </remarks> + Unreliable, + + /// <summary> + /// Requests data be sent reliably but with no fragmentation. + /// </summary> + /// <remarks> + /// Sending data reliably means that data is guaranteed to arrive and to arrive only once. Reliable delivery + /// typically requires more processing, more memory (as packets need to be stored in case they need resending), + /// a larger number of protocol bytes and can be slower than unreliable delivery. + /// </remarks> + Reliable, + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message00HostGameS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message00HostGameS2C.cs new file mode 100644 index 0000000..8402d10 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message00HostGameS2C.cs @@ -0,0 +1,20 @@ +using System; +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Messages.S2C +{ + public static class Message00HostGameS2C + { + public static void Serialize(IMessageWriter writer, int gameCode) + { + writer.StartMessage(MessageFlags.HostGame); + writer.Write(gameCode); + writer.EndMessage(); + } + + public static GameOptionsData Deserialize(IMessageReader reader) + { + throw new NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message01JoinGameS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message01JoinGameS2C.cs new file mode 100644 index 0000000..c455201 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message01JoinGameS2C.cs @@ -0,0 +1,50 @@ +using System; +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Messages.S2C +{ + public class Message01JoinGameS2C + { + public static void SerializeJoin(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId) + { + if (clear) + { + writer.Clear(MessageType.Reliable); + } + + writer.StartMessage(MessageFlags.JoinGame); + writer.Write(gameCode); + writer.Write(playerId); + writer.Write(hostId); + writer.EndMessage(); + } + + public static void SerializeError(IMessageWriter writer, bool clear, DisconnectReason reason, string? message = null) + { + if (clear) + { + writer.Clear(MessageType.Reliable); + } + + writer.StartMessage(MessageFlags.JoinGame); + writer.Write((int)reason); + + if (reason == DisconnectReason.Custom) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + writer.Write(message); + } + + writer.EndMessage(); + } + + public static void Deserialize(IMessageReader reader) + { + throw new System.NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message04RemovePlayerS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message04RemovePlayerS2C.cs new file mode 100644 index 0000000..77b447d --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message04RemovePlayerS2C.cs @@ -0,0 +1,29 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Messages.S2C +{ + public class Message04RemovePlayerS2C + { + public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId, DisconnectReason reason) + { + // Only a subset of DisconnectReason shows an unique message. + // ExitGame, Banned and Kicked. + if (clear) + { + writer.Clear(MessageType.Reliable); + } + + writer.StartMessage(MessageFlags.RemovePlayer); + writer.Write(gameCode); + writer.Write(playerId); + writer.Write(hostId); + writer.Write((byte)reason); + writer.EndMessage(); + } + + public static void Deserialize(IMessageReader reader) + { + throw new System.NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message07JoinedGameS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message07JoinedGameS2C.cs new file mode 100644 index 0000000..da6eb40 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message07JoinedGameS2C.cs @@ -0,0 +1,31 @@ +namespace Impostor.Api.Net.Messages.S2C +{ + public static class Message07JoinedGameS2C + { + public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId, int[] otherPlayerIds) + { + if (clear) + { + writer.Clear(MessageType.Reliable); + } + + writer.StartMessage(MessageFlags.JoinedGame); + writer.Write(gameCode); + writer.Write(playerId); + writer.Write(hostId); + writer.WritePacked(otherPlayerIds.Length); + + foreach (var id in otherPlayerIds) + { + writer.WritePacked(id); + } + + writer.EndMessage(); + } + + public static void Deserialize(IMessageReader reader) + { + throw new System.NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message10AlterGameS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message10AlterGameS2C.cs new file mode 100644 index 0000000..fa155df --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message10AlterGameS2C.cs @@ -0,0 +1,26 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api.Net.Messages.S2C +{ + public static class Message10AlterGameS2C + { + public static void Serialize(IMessageWriter writer, bool clear, int gameCode, bool isPublic) + { + if (clear) + { + writer.Clear(MessageType.Reliable); + } + + writer.StartMessage(MessageFlags.AlterGame); + writer.Write(gameCode); + writer.Write((byte)AlterGameTags.ChangePrivacy); + writer.Write(isPublic); + writer.EndMessage(); + } + + public static void Deserialize(IMessageReader reader) + { + throw new System.NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message11KickPlayerS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message11KickPlayerS2C.cs new file mode 100644 index 0000000..1e2b6ef --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message11KickPlayerS2C.cs @@ -0,0 +1,24 @@ +namespace Impostor.Api.Net.Messages.S2C +{ + public class Message11KickPlayerS2C + { + public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, bool isBan) + { + if (clear) + { + writer.Clear(MessageType.Reliable); + } + + writer.StartMessage(MessageFlags.KickPlayer); + writer.Write(gameCode); + writer.WritePacked(playerId); + writer.Write(isBan); + writer.EndMessage(); + } + + public static void Deserialize(IMessageReader reader) + { + throw new System.NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message12WaitForHostS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message12WaitForHostS2C.cs new file mode 100644 index 0000000..5964b1c --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message12WaitForHostS2C.cs @@ -0,0 +1,23 @@ +namespace Impostor.Api.Net.Messages.S2C +{ + public class Message12WaitForHostS2C + { + public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId) + { + if (clear) + { + writer.Clear(MessageType.Reliable); + } + + writer.StartMessage(MessageFlags.WaitForHost); + writer.Write(gameCode); + writer.Write(playerId); + writer.EndMessage(); + } + + public static void Deserialize(IMessageReader reader) + { + throw new System.NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message13RedirectS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message13RedirectS2C.cs new file mode 100644 index 0000000..4b93b0e --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message13RedirectS2C.cs @@ -0,0 +1,25 @@ +using System.Net; + +namespace Impostor.Api.Net.Messages.S2C +{ + public class Message13RedirectS2C + { + public static void Serialize(IMessageWriter writer, bool clear, IPEndPoint ipEndPoint) + { + if (clear) + { + writer.Clear(MessageType.Reliable); + } + + writer.StartMessage(MessageFlags.Redirect); + writer.Write(ipEndPoint.Address); + writer.Write((ushort)ipEndPoint.Port); + writer.EndMessage(); + } + + public static void Deserialize(IMessageReader reader) + { + throw new System.NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message16GetGameListS2C.cs b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message16GetGameListS2C.cs new file mode 100644 index 0000000..93386d7 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message16GetGameListS2C.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using Impostor.Api.Games; + +namespace Impostor.Api.Net.Messages.S2C +{ + public class Message16GetGameListS2C + { + public static void Serialize(IMessageWriter writer, int skeldGameCount, int miraHqGameCount, int polusGameCount, IEnumerable<IGame> games) + { + writer.StartMessage(MessageFlags.GetGameListV2); + + // Count + writer.StartMessage(1); + writer.Write(skeldGameCount); // The Skeld + writer.Write(miraHqGameCount); // Mira HQ + writer.Write(polusGameCount); // Polus + writer.EndMessage(); + + // Listing + writer.StartMessage(0); + + foreach (var game in games) + { + writer.StartMessage(0); + writer.Write(game.PublicIp.Address); + writer.Write((ushort)game.PublicIp.Port); + writer.Write(game.Code); + writer.Write(game.Host.Client.Name); + writer.Write((byte)game.PlayerCount); + writer.WritePacked(1); // TODO: What does Age do? + writer.Write((byte)game.Options.MapId); + writer.Write((byte)game.Options.NumImpostors); + writer.Write((byte)game.Options.MaxPlayers); + writer.EndMessage(); + } + + writer.EndMessage(); + writer.EndMessage(); + } + + public static void Deserialize(IMessageReader reader) + { + throw new System.NotImplementedException(); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Plugins/IPlugin.cs b/Impostor-dev/src/Impostor.Api/Plugins/IPlugin.cs new file mode 100644 index 0000000..fd0f38f --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Plugins/IPlugin.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Impostor.Api.Events; + +namespace Impostor.Api.Plugins +{ + public interface IPlugin : IEventListener + { + ValueTask EnableAsync(); + + ValueTask DisableAsync(); + + ValueTask ReloadAsync(); + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Plugins/IPluginStartup.cs b/Impostor-dev/src/Impostor.Api/Plugins/IPluginStartup.cs new file mode 100644 index 0000000..aa6a35f --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Plugins/IPluginStartup.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Impostor.Api.Plugins +{ + public interface IPluginStartup + { + void ConfigureHost(IHostBuilder host); + + void ConfigureServices(IServiceCollection services); + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Plugins/ImpostorPluginAttribute.cs b/Impostor-dev/src/Impostor.Api/Plugins/ImpostorPluginAttribute.cs new file mode 100644 index 0000000..b31bd47 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Plugins/ImpostorPluginAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace Impostor.Api.Plugins +{ + [AttributeUsage(AttributeTargets.Class)] + public class ImpostorPluginAttribute : Attribute + { + public ImpostorPluginAttribute(string package, string name, string author, string version) + { + Package = package; + Name = name; + Author = author; + Version = version; + } + + public string Package { get; } + + public string Name { get; } + + public string Author { get; } + + public string Version { get; } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Plugins/PluginBase.cs b/Impostor-dev/src/Impostor.Api/Plugins/PluginBase.cs new file mode 100644 index 0000000..0384363 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Plugins/PluginBase.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; + +namespace Impostor.Api.Plugins +{ + public class PluginBase : IPlugin + { + public virtual ValueTask EnableAsync() + { + return default; + } + + public virtual ValueTask DisableAsync() + { + return default; + } + + public virtual ValueTask ReloadAsync() + { + return default; + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/ProjectRules.ruleset b/Impostor-dev/src/Impostor.Api/ProjectRules.ruleset new file mode 100644 index 0000000..4ba23c2 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/ProjectRules.ruleset @@ -0,0 +1,17 @@ +<RuleSet Name="Rules for Hello World project" Description="These rules focus on critical issues for the Hello World app." ToolsVersion="10.0"> + <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.OrderingRules"> + <Rule Id="SA1200" Action="None" /> + </Rules> + <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.DocumentationRules"> + <Rule Id="SA1600" Action="None" /> + <Rule Id="SA1601" Action="None" /> + <Rule Id="SA1602" Action="None" /> + <Rule Id="SA1633" Action="None" /> + </Rules> + <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.ReadabilityRules"> + <Rule Id="SA1101" Action="None" /> + </Rules> + <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.NamingRules"> + <Rule Id="SA1309" Action="None" /> + </Rules> +</RuleSet>
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Properties/AssemblyInfo.cs b/Impostor-dev/src/Impostor.Api/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c02e44a --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("Impostor.Server")]
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Api/Unity/Mathf.cs b/Impostor-dev/src/Impostor.Api/Unity/Mathf.cs new file mode 100644 index 0000000..4b03417 --- /dev/null +++ b/Impostor-dev/src/Impostor.Api/Unity/Mathf.cs @@ -0,0 +1,54 @@ +namespace Impostor.Api.Unity +{ + public static class Mathf + { + /// <summary> + /// <para>Clamps the given value between the given minimum float and maximum float values. Returns the given value if it is within the min and max range.</para> + /// </summary> + /// <param name="value">The floating point value to restrict inside the range defined by the min and max values.</param> + /// <param name="min">The minimum floating point value to compare against.</param> + /// <param name="max">The maximum floating point value to compare against.</param> + /// <returns> + /// <para>The float result between the min and max values.</para> + /// </returns> + public static float Clamp(float value, float min, float max) + { + if (value < (double)min) + { + value = min; + } + else if (value > (double)max) + { + value = max; + } + + return value; + } + + /// <summary> + /// <para>Clamps value between 0 and 1 and returns value.</para> + /// </summary> + /// <param name="value">Value.</param> + /// <returns>Clamped value.</returns> + public static float Clamp01(float value) + { + if (value < 0.0) + { + return 0.0f; + } + + return (double)value > 1.0 ? 1f : value; + } + + /// <summary> + /// <para>Linearly interpolates between a and b by t.</para> + /// </summary> + /// <param name="a">The start value.</param> + /// <param name="b">The end value.</param> + /// <param name="t">The interpolation value between the two floats.</param> + /// <returns> + /// <para>The interpolated float result between the two float values.</para> + /// </returns> + public static float Lerp(float a, float b, float t) => a + ((b - a) * Clamp01(t)); + } +}
\ No newline at end of file |