summaryrefslogtreecommitdiff
path: root/Impostor-dev/src/Impostor.Api
diff options
context:
space:
mode:
Diffstat (limited to 'Impostor-dev/src/Impostor.Api')
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Attributes/EventListenerAttribute.cs34
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/EventPriority.cs12
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGameAlterEvent.cs7
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGameCreatedEvent.cs11
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGameDestroyedEvent.cs11
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGameEndedEvent.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGameEvent.cs12
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGamePlayerJoinedEvent.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGamePlayerLeftEvent.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGameStartedEvent.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/IGameStartingEvent.cs11
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingEndedEvent.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingEvent.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Meeting/IMeetingStartedEvent.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerChatEvent.cs10
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerCompletedTaskEvent.cs10
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerDestroyedEvent.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerEvent.cs19
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerExileEvent.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerMurderEvent.cs12
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerSetStartCounterEvent.cs10
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerSpawnedEvent.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerStartMeetingEvent.cs12
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Game/Player/IPlayerVentEvent.cs17
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/IEvent.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/IEventCancelable.cs10
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/IEventListener.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/IManualEventListener.cs13
-rw-r--r--Impostor-dev/src/Impostor.Api/Events/Managers/IEventManager.cs35
-rw-r--r--Impostor-dev/src/Impostor.Api/Exceptions/ImpostorCheatException.cs24
-rw-r--r--Impostor-dev/src/Impostor.Api/Exceptions/ImpostorConfigException.cs24
-rw-r--r--Impostor-dev/src/Impostor.Api/Exceptions/ImpostorException.cs24
-rw-r--r--Impostor-dev/src/Impostor.Api/Exceptions/ImpostorProtocolException.cs24
-rw-r--r--Impostor-dev/src/Impostor.Api/Extensions/SpanReaderExtensions.cs70
-rw-r--r--Impostor-dev/src/Impostor.Api/Extensions/SystemTypesExtensions.cs12
-rw-r--r--Impostor-dev/src/Impostor.Api/Games/Extensions/GameExtensions.cs42
-rw-r--r--Impostor-dev/src/Impostor.Api/Games/Extensions/GameManagerExtensions.cs14
-rw-r--r--Impostor-dev/src/Impostor.Api/Games/GameCode.cs74
-rw-r--r--Impostor-dev/src/Impostor.Api/Games/GameJoinError.cs48
-rw-r--r--Impostor-dev/src/Impostor.Api/Games/GameJoinResult.cs48
-rw-r--r--Impostor-dev/src/Impostor.Api/Games/IGame.cs88
-rw-r--r--Impostor-dev/src/Impostor.Api/Games/IGameCodeFactory.cs7
-rw-r--r--Impostor-dev/src/Impostor.Api/Games/Managers/IGameManager.cs11
-rw-r--r--Impostor-dev/src/Impostor.Api/Impostor.Api.csproj38
-rw-r--r--Impostor-dev/src/Impostor.Api/Impostor.Api.csproj.DotSettings8
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/AlterGameTags.cs7
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/ChatNoteType.cs7
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/Customization/ColorType.cs18
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/Customization/HatType.cs100
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/Customization/PetType.cs18
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/Customization/SkinType.cs22
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/DeathReason.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/DisconnectReason.cs54
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/FloatRange.cs22
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/GameCodeParser.cs142
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/GameKeywords.cs19
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/GameOptionsData.cs261
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/GameOverReason.cs15
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/GameStates.cs11
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/GameVersion.cs10
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/KillDistances.cs12
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/MapFlags.cs12
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/MapTypes.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/RegionInfo.cs48
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/ServerInfo.cs37
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/SystemTypeHelpers.cs28
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/SystemTypes.cs42
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/TaskBarUpdate.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/TaskTypes.cs49
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/TextBox.cs10
-rw-r--r--Impostor-dev/src/Impostor.Api/Innersloth/VentLocation.cs48
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/IClient.cs76
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/IClientPlayer.cs43
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/IConnection.cs7
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/IHazelConnection.cs41
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/IGameNet.cs18
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/IInnerNetObject.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/Components/IInnerCustomNetworkTransform.cs15
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/Components/IInnerPlayerPhysics.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerGameData.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerLobbyBehaviour.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerMeetingHud.cs6
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerPlayerControl.cs116
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerPlayerInfo.cs53
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerShipStatus.cs7
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/IInnerVoteBanSystem.cs7
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Inner/Objects/ITaskInfo.cs14
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/LimboStates.cs13
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Manager/IClientManager.cs9
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message00HostGameC2S.cs27
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message01JoinGameC2S.cs20
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message04RemovePlayerC2S.cs16
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message08EndGameC2S.cs18
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message10AlterGameC2S.cs20
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message11KickPlayerC2S.cs16
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/C2S/Message16GetGameListC2S.cs18
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/IMessageReader.cs68
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/IMessageWriter.cs127
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/IMessageWriterProvider.cs16
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/MessageFlags.cs22
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/MessageType.cs32
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message00HostGameS2C.cs20
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message01JoinGameS2C.cs50
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message04RemovePlayerS2C.cs29
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message07JoinedGameS2C.cs31
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message10AlterGameS2C.cs26
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message11KickPlayerS2C.cs24
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message12WaitForHostS2C.cs23
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message13RedirectS2C.cs25
-rw-r--r--Impostor-dev/src/Impostor.Api/Net/Messages/S2C/Message16GetGameListS2C.cs46
-rw-r--r--Impostor-dev/src/Impostor.Api/Plugins/IPlugin.cs14
-rw-r--r--Impostor-dev/src/Impostor.Api/Plugins/IPluginStartup.cs12
-rw-r--r--Impostor-dev/src/Impostor.Api/Plugins/ImpostorPluginAttribute.cs24
-rw-r--r--Impostor-dev/src/Impostor.Api/Plugins/PluginBase.cs22
-rw-r--r--Impostor-dev/src/Impostor.Api/ProjectRules.ruleset17
-rw-r--r--Impostor-dev/src/Impostor.Api/Properties/AssemblyInfo.cs3
-rw-r--r--Impostor-dev/src/Impostor.Api/Unity/Mathf.cs54
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