--- /dev/null
+using System;
+
+namespace Impostor.Server.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; }
+
+ /// <summary>
+ /// The order of the priority.
+ /// </summary>
+ public int PriorityOrder { get; set; } = 100;
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Server.Events
+{
+ public enum EventPriority
+ {
+ Lowest = 0,
+ Low = 1,
+ Normal = 2,
+ High = 3,
+ Highest = 4,
+ Monitor = 5
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Games;
+
+namespace Impostor.Server.Events
+{
+ /// <summary>
+ /// Called whenever a new <see cref="IGame"/> is created.
+ /// </summary>
+ public sealed class GameCreatedEvent : IGameEvent
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GameCreatedEvent"/> class.
+ /// </summary>
+ /// <param name="game">Instance of the game.</param>
+ public GameCreatedEvent(IGame game)
+ {
+ Game = game;
+ }
+
+ /// <inheritdoc/>
+ public IGame Game { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Games;
+
+namespace Impostor.Server.Events
+{
+ /// <summary>
+ /// Called whenever a new <see cref="IGame"/> is destroyed.
+ /// </summary>
+ public sealed class GameDestroyedEvent : IGameEvent
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GameDestroyedEvent"/> class.
+ /// </summary>
+ /// <param name="game">Instance of the game.</param>
+ public GameDestroyedEvent(IGame game)
+ {
+ Game = game;
+ }
+
+ /// <inheritdoc/>
+ public IGame Game { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Games;
+
+namespace Impostor.Server.Events
+{
+ public interface IGameEvent : IEvent
+ {
+ IGame Game { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Games;
+using Impostor.Server.Net;
+
+namespace Impostor.Server.Events
+{
+ public class PlayerJoinedGameEvent : IGameEvent
+ {
+ public PlayerJoinedGameEvent(IGame game, IClientPlayer player)
+ {
+ Game = game;
+ Player = player;
+ }
+
+ public IGame Game { get; }
+
+ public IClientPlayer Player { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Server.Games;
+using Impostor.Server.Net;
+
+namespace Impostor.Server.Events
+{
+ public class PlayerLeftGameEvent : IGameEvent
+ {
+ public PlayerLeftGameEvent(IGame game, IClientPlayer player, bool isBan)
+ {
+ Game = game;
+ Player = player;
+ IsBan = isBan;
+ }
+
+ public IGame Game { get; }
+
+ public IClientPlayer Player { get; }
+
+ public bool IsBan { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Server.Events
+{
+ public interface IEvent
+ {
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Server.Events
+{
+ public interface IEventCancelable : IEvent
+ {
+ /// <summary>
+ /// True if the event was cancelled.
+ /// </summary>
+ bool IsCancelled { get; set; }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Server.Events
+{
+ public interface IEventListener
+ {
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Threading.Tasks;
+
+namespace Impostor.Server.Events.Managers
+{
+ public interface IEventManager
+ {
+ /// <summary>
+ /// Register a temporary event listener.
+ /// </summary>
+ /// <param name="callback">Event callback.</param>
+ /// <returns>Disposable that unregisters the callback from the event manager.</returns>
+ /// <typeparam name="TEvent">Type of the event.</typeparam>
+ IDisposable Register<TEvent>(Func<IServiceProvider, TEvent, ValueTask> callback)
+ where TEvent : IEvent;
+
+ /// <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
--- /dev/null
+using System;
+using System.Runtime.Serialization;
+
+namespace Impostor.Server
+{
+ 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
--- /dev/null
+using System.Linq;
+using Impostor.Server.Games.Managers;
+using Impostor.Shared.Innersloth.Data;
+
+namespace Impostor.Server.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
--- /dev/null
+using System;
+using Impostor.Shared.Innersloth;
+
+namespace Impostor.Server.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
--- /dev/null
+namespace Impostor.Server.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
--- /dev/null
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Impostor.Server.Net;
+
+namespace Impostor.Server.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
--- /dev/null
+using System.Collections.Generic;
+using System.Net;
+using Impostor.Server.Net;
+using Impostor.Server.Net.Messages;
+using Impostor.Shared.Innersloth;
+using Impostor.Shared.Innersloth.Data;
+
+namespace Impostor.Server.Games
+{
+ public interface IGame
+ {
+ GameOptionsData Options { get; }
+
+ GameCode Code { get; }
+
+ GameStates GameState { 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; }
+
+ IGameMessageWriter CreateMessage(MessageType type);
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Generic;
+
+namespace Impostor.Server.Games.Managers
+{
+ public interface IGameManager
+ {
+ IEnumerable<IGame> Games { get; }
+
+ IGame? Find(GameCode code);
+ }
+}
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net5.0</TargetFramework>
+ <RootNamespace>Impostor.Server</RootNamespace>
+ <Nullable>enable</Nullable>
+ <DebugType Condition=" '$(Configuration)' == 'Release' ">None</DebugType>
+ <CodeAnalysisRuleSet>ProjectRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Impostor.Shared\Impostor.Shared.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-rc.1.20451.14" />
+ <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-rc.1.20451.14" />
+ <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
--- /dev/null
+<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
--- /dev/null
+using System;
+using System.Threading.Tasks;
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.Net
+{
+ public static class GameMessageWriterExtensions
+ {
+ public static ValueTask SendToAllExceptAsync(this IGameMessageWriter writer, LimboStates states, int? id)
+ {
+ return id.HasValue
+ ? writer.SendToAllExceptAsync(id.Value, states)
+ : writer.SendToAllAsync(states);
+ }
+
+ public static ValueTask SendToAllExceptAsync(this IGameMessageWriter writer, LimboStates states, IClient client)
+ {
+ if (client == null)
+ {
+ throw new ArgumentNullException(nameof(client));
+ }
+
+ return writer.SendToAllExceptAsync(client.Id, states);
+ }
+
+ public static ValueTask SendToAsync(this IGameMessageWriter writer, IClient client)
+ {
+ if (client == null)
+ {
+ throw new ArgumentNullException(nameof(client));
+ }
+
+ return writer.SendToAsync(client.Id);
+ }
+
+ public static ValueTask SendToAsync(this IGameMessageWriter writer, IClientPlayer player)
+ {
+ return SendToAsync(writer, player.Client);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Generic;
+
+namespace Impostor.Server.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>
+ IConnection? Connection { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether the client is a bot.
+ /// </summary>
+ bool IsBot { 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; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Threading.Tasks;
+using Impostor.Server.Games;
+
+namespace Impostor.Server.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; }
+
+ ValueTask KickAsync();
+
+ ValueTask BanAsync();
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Net;
+using Impostor.Server.Games;
+using Impostor.Server.Net.Messages;
+
+namespace Impostor.Server.Net
+{
+ /// <summary>
+ /// Represents the connection of the client.
+ /// </summary>
+ public interface IConnection
+ {
+ /// <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; }
+
+ /// <summary>
+ /// Create a message writer that can be send to the connection.
+ /// </summary>
+ /// <remarks>
+ /// Be aware when implementing a custom connection handler that this method is not called when a message
+ /// is being send in <see cref="IGame"/>.
+ /// </remarks>
+ /// <param name="messageType">Type of the message.</param>
+ /// <returns>Message writer for the current connection.</returns>
+ IConnectionMessageWriter CreateMessage(MessageType messageType);
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Impostor.Server.Net
+{
+ [Flags]
+ public enum LimboStates
+ {
+ PreSpawn = 1,
+ NotLimbo = 2,
+ WaitingForHost = 4,
+ All = PreSpawn | NotLimbo | WaitingForHost
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Generic;
+
+namespace Impostor.Server.Net.Manager
+{
+ public interface IClientManager
+ {
+ IEnumerable<IClient> Clients { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Threading.Tasks;
+
+namespace Impostor.Server.Net.Messages
+{
+ /// <summary>
+ /// Represents the message writer for <see cref="IConnection"/>.
+ /// </summary>
+ public interface IConnectionMessageWriter : IMessageWriter
+ {
+ /// <summary>
+ /// Gets the connection where the message writer belongs to.
+ /// </summary>
+ public IConnection Connection { get; }
+
+ /// <summary>
+ /// Sends the message to the <see cref="Connection"/>.
+ /// </summary>
+ /// <returns>Task.</returns>
+ ValueTask SendAsync();
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Threading.Tasks;
+using Impostor.Server.Games;
+
+namespace Impostor.Server.Net.Messages
+{
+ /// <summary>
+ /// Represents the message writer for <see cref="IGame"/>.
+ /// </summary>
+ public interface IGameMessageWriter : IMessageWriter
+ {
+ /// <summary>
+ /// Send the message to all players.
+ /// </summary>
+ /// <param name="states">Required limbo state of the player.</param>
+ /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
+ ValueTask SendToAllAsync(LimboStates states = LimboStates.NotLimbo);
+
+ /// <summary>
+ /// Send the message to all players except one.
+ /// </summary>
+ /// <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(int senderId, LimboStates states = LimboStates.NotLimbo);
+
+ /// <summary>
+ /// Send a message to a specific player.
+ /// </summary>
+ /// <param name="id">ID of the client.</param>
+ /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
+ ValueTask SendToAsync(int id);
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Server.Net.Messages
+{
+ public interface IMessage
+ {
+ MessageType Type { get; }
+
+ IMessageReader CreateReader();
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Impostor.Server.Net.Messages
+{
+ public interface IMessageReader
+ {
+ /// <summary>
+ /// Gets the tag of the message.
+ /// </summary>
+ byte Tag { get; }
+
+ /// <summary>
+ /// Gets the buffer of the message.
+ /// </summary>
+ ReadOnlyMemory<byte> Buffer { 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);
+
+ IMessageReader Slice(int start);
+
+ IMessageReader Slice(int start, int length);
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Net;
+using Impostor.Server.Games;
+
+namespace Impostor.Server.Net.Messages
+{
+ /// <summary>
+ /// Base message writer.
+ /// </summary>
+ public interface IMessageWriter : IDisposable
+ {
+ /// <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);
+
+ /// <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
--- /dev/null
+namespace Impostor.Server.Net.Messages
+{
+ /// <summary>
+ /// Specifies how a message should be sent between connections.
+ /// </summary>
+ public enum MessageType
+ {
+ /// <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
--- /dev/null
+using System.Threading.Tasks;
+using Impostor.Server.Events;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace Impostor.Server.Plugins
+{
+ public interface IPlugin : IEventListener
+ {
+ ValueTask EnableAsync();
+
+ ValueTask DisableAsync();
+
+ ValueTask ReloadAsync();
+
+ void ConfigureHost(IHostBuilder host);
+
+ void ConfigureServices(IServiceCollection services);
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace Impostor.Server.Plugins
+{
+ public class PluginBase : IPlugin
+ {
+ public virtual ValueTask EnableAsync()
+ {
+ return default;
+ }
+
+ public virtual ValueTask DisableAsync()
+ {
+ return default;
+ }
+
+ public virtual ValueTask ReloadAsync()
+ {
+ return default;
+ }
+
+ public virtual void ConfigureHost(IHostBuilder host)
+ {
+ }
+
+ public virtual void ConfigureServices(IServiceCollection services)
+ {
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<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="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
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\Impostor.Server.Api\Impostor.Server.Api.csproj" />
+ <ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
+++ /dev/null
-using System;
-
-namespace Impostor.Server.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; }
-
- /// <summary>
- /// The order of the priority.
- /// </summary>
- public int PriorityOrder { get; set; } = 100;
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace Impostor.Server.Events
-{
- public enum EventPriority
- {
- Lowest = 0,
- Low = 1,
- Normal = 2,
- High = 3,
- Highest = 4,
- Monitor = 5
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Server.Games;
-
-namespace Impostor.Server.Events
-{
- /// <summary>
- /// Called whenever a new <see cref="IGame"/> is created.
- /// </summary>
- public sealed class GameCreatedEvent : IGameEvent
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="GameCreatedEvent"/> class.
- /// </summary>
- /// <param name="game">Instance of the game.</param>
- public GameCreatedEvent(IGame game)
- {
- Game = game;
- }
-
- /// <inheritdoc/>
- public IGame Game { get; }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Server.Games;
-
-namespace Impostor.Server.Events
-{
- /// <summary>
- /// Called whenever a new <see cref="IGame"/> is destroyed.
- /// </summary>
- public sealed class GameDestroyedEvent : IGameEvent
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="GameDestroyedEvent"/> class.
- /// </summary>
- /// <param name="game">Instance of the game.</param>
- public GameDestroyedEvent(IGame game)
- {
- Game = game;
- }
-
- /// <inheritdoc/>
- public IGame Game { get; }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Server.Games;
-
-namespace Impostor.Server.Events
-{
- public interface IGameEvent : IEvent
- {
- IGame Game { get; }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Server.Games;
-using Impostor.Server.Net;
-
-namespace Impostor.Server.Events
-{
- public class PlayerJoinedGameEvent : IGameEvent
- {
- public PlayerJoinedGameEvent(IGame game, IClientPlayer player)
- {
- Game = game;
- Player = player;
- }
-
- public IGame Game { get; }
-
- public IClientPlayer Player { get; }
- }
-}
\ No newline at end of file
+++ /dev/null
-using Impostor.Server.Games;
-using Impostor.Server.Net;
-
-namespace Impostor.Server.Events
-{
- public class PlayerLeftGameEvent : IGameEvent
- {
- public PlayerLeftGameEvent(IGame game, IClientPlayer player, bool isBan)
- {
- Game = game;
- Player = player;
- IsBan = isBan;
- }
-
- public IGame Game { get; }
-
- public IClientPlayer Player { get; }
-
- public bool IsBan { get; }
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace Impostor.Server.Events
-{
- public interface IEvent
- {
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace Impostor.Server.Events
-{
- public interface IEventCancelable : IEvent
- {
- /// <summary>
- /// True if the event was cancelled.
- /// </summary>
- bool IsCancelled { get; set; }
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace Impostor.Server.Events
-{
- public interface IEventListener
- {
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Threading.Tasks;
-
-namespace Impostor.Server.Events.Managers
-{
- public interface IEventManager
- {
- /// <summary>
- /// Register a temporary event listener.
- /// </summary>
- /// <param name="callback">Event callback.</param>
- /// <returns>Disposable that unregisters the callback from the event manager.</returns>
- /// <typeparam name="TEvent">Type of the event.</typeparam>
- IDisposable Register<TEvent>(Func<IServiceProvider, TEvent, ValueTask> callback)
- where TEvent : IEvent;
-
- /// <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
+++ /dev/null
-using System;
-using System.Runtime.Serialization;
-
-namespace Impostor.Server
-{
- 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
+++ /dev/null
-using System.Linq;
-using Impostor.Server.Games.Managers;
-using Impostor.Shared.Innersloth.Data;
-
-namespace Impostor.Server.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
+++ /dev/null
-using System;
-using Impostor.Shared.Innersloth;
-
-namespace Impostor.Server.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
+++ /dev/null
-namespace Impostor.Server.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
+++ /dev/null
-using System;
-using System.Diagnostics.CodeAnalysis;
-using Impostor.Server.Net;
-
-namespace Impostor.Server.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
+++ /dev/null
-using System.Collections.Generic;
-using System.Net;
-using Impostor.Server.Net;
-using Impostor.Server.Net.Messages;
-using Impostor.Shared.Innersloth;
-using Impostor.Shared.Innersloth.Data;
-
-namespace Impostor.Server.Games
-{
- public interface IGame
- {
- GameOptionsData Options { get; }
-
- GameCode Code { get; }
-
- GameStates GameState { 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; }
-
- IGameMessageWriter CreateMessage(MessageType type);
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Impostor.Server.Games.Managers
-{
- public interface IGameManager
- {
- IEnumerable<IGame> Games { get; }
-
- IGame? Find(GameCode code);
- }
-}
\ No newline at end of file
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
- <RootNamespace>Impostor.Server</RootNamespace>
- <Nullable>enable</Nullable>
- <DebugType Condition=" '$(Configuration)' == 'Release' ">None</DebugType>
- <CodeAnalysisRuleSet>ProjectRules.ruleset</CodeAnalysisRuleSet>
- </PropertyGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\Impostor.Shared\Impostor.Shared.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-rc.1.20451.14" />
- <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-rc.1.20451.14" />
- <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
+++ /dev/null
-<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
+++ /dev/null
-using System;
-using System.Threading.Tasks;
-using Impostor.Server.Net.Messages;
-
-namespace Impostor.Server.Net
-{
- public static class GameMessageWriterExtensions
- {
- public static ValueTask SendToAllExceptAsync(this IGameMessageWriter writer, LimboStates states, int? id)
- {
- return id.HasValue
- ? writer.SendToAllExceptAsync(id.Value, states)
- : writer.SendToAllAsync(states);
- }
-
- public static ValueTask SendToAllExceptAsync(this IGameMessageWriter writer, LimboStates states, IClient client)
- {
- if (client == null)
- {
- throw new ArgumentNullException(nameof(client));
- }
-
- return writer.SendToAllExceptAsync(client.Id, states);
- }
-
- public static ValueTask SendToAsync(this IGameMessageWriter writer, IClient client)
- {
- if (client == null)
- {
- throw new ArgumentNullException(nameof(client));
- }
-
- return writer.SendToAsync(client.Id);
- }
-
- public static ValueTask SendToAsync(this IGameMessageWriter writer, IClientPlayer player)
- {
- return SendToAsync(writer, player.Client);
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Impostor.Server.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>
- IConnection? Connection { get; }
-
- /// <summary>
- /// Gets a value indicating whether the client is a bot.
- /// </summary>
- bool IsBot { 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; }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Threading.Tasks;
-using Impostor.Server.Games;
-
-namespace Impostor.Server.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; }
-
- ValueTask KickAsync();
-
- ValueTask BanAsync();
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Net;
-using Impostor.Server.Games;
-using Impostor.Server.Net.Messages;
-
-namespace Impostor.Server.Net
-{
- /// <summary>
- /// Represents the connection of the client.
- /// </summary>
- public interface IConnection
- {
- /// <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; }
-
- /// <summary>
- /// Create a message writer that can be send to the connection.
- /// </summary>
- /// <remarks>
- /// Be aware when implementing a custom connection handler that this method is not called when a message
- /// is being send in <see cref="IGame"/>.
- /// </remarks>
- /// <param name="messageType">Type of the message.</param>
- /// <returns>Message writer for the current connection.</returns>
- IConnectionMessageWriter CreateMessage(MessageType messageType);
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-
-namespace Impostor.Server.Net
-{
- [Flags]
- public enum LimboStates
- {
- PreSpawn = 1,
- NotLimbo = 2,
- WaitingForHost = 4,
- All = PreSpawn | NotLimbo | WaitingForHost
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Collections.Generic;
-
-namespace Impostor.Server.Net.Manager
-{
- public interface IClientManager
- {
- IEnumerable<IClient> Clients { get; }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Threading.Tasks;
-
-namespace Impostor.Server.Net.Messages
-{
- /// <summary>
- /// Represents the message writer for <see cref="IConnection"/>.
- /// </summary>
- public interface IConnectionMessageWriter : IMessageWriter
- {
- /// <summary>
- /// Gets the connection where the message writer belongs to.
- /// </summary>
- public IConnection Connection { get; }
-
- /// <summary>
- /// Sends the message to the <see cref="Connection"/>.
- /// </summary>
- /// <returns>Task.</returns>
- ValueTask SendAsync();
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Threading.Tasks;
-using Impostor.Server.Games;
-
-namespace Impostor.Server.Net.Messages
-{
- /// <summary>
- /// Represents the message writer for <see cref="IGame"/>.
- /// </summary>
- public interface IGameMessageWriter : IMessageWriter
- {
- /// <summary>
- /// Send the message to all players.
- /// </summary>
- /// <param name="states">Required limbo state of the player.</param>
- /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
- ValueTask SendToAllAsync(LimboStates states = LimboStates.NotLimbo);
-
- /// <summary>
- /// Send the message to all players except one.
- /// </summary>
- /// <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(int senderId, LimboStates states = LimboStates.NotLimbo);
-
- /// <summary>
- /// Send a message to a specific player.
- /// </summary>
- /// <param name="id">ID of the client.</param>
- /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
- ValueTask SendToAsync(int id);
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace Impostor.Server.Net.Messages
-{
- public interface IMessage
- {
- MessageType Type { get; }
-
- IMessageReader CreateReader();
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-
-namespace Impostor.Server.Net.Messages
-{
- public interface IMessageReader
- {
- /// <summary>
- /// Gets the tag of the message.
- /// </summary>
- byte Tag { get; }
-
- /// <summary>
- /// Gets the buffer of the message.
- /// </summary>
- ReadOnlyMemory<byte> Buffer { 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);
-
- IMessageReader Slice(int start);
-
- IMessageReader Slice(int start, int length);
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Net;
-using Impostor.Server.Games;
-
-namespace Impostor.Server.Net.Messages
-{
- /// <summary>
- /// Base message writer.
- /// </summary>
- public interface IMessageWriter : IDisposable
- {
- /// <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);
-
- /// <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
+++ /dev/null
-namespace Impostor.Server.Net.Messages
-{
- /// <summary>
- /// Specifies how a message should be sent between connections.
- /// </summary>
- public enum MessageType
- {
- /// <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
+++ /dev/null
-using System.Threading.Tasks;
-using Impostor.Server.Events;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-
-namespace Impostor.Server.Plugins
-{
- public interface IPlugin : IEventListener
- {
- ValueTask EnableAsync();
-
- ValueTask DisableAsync();
-
- ValueTask ReloadAsync();
-
- void ConfigureHost(IHostBuilder host);
-
- void ConfigureServices(IServiceCollection services);
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-
-namespace Impostor.Server.Plugins
-{
- public class PluginBase : IPlugin
- {
- public virtual ValueTask EnableAsync()
- {
- return default;
- }
-
- public virtual ValueTask DisableAsync()
- {
- return default;
- }
-
- public virtual ValueTask ReloadAsync()
- {
- return default;
- }
-
- public virtual void ConfigureHost(IHostBuilder host)
- {
- }
-
- public virtual void ConfigureServices(IServiceCollection services)
- {
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<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="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
</PropertyGroup>
<ItemGroup>
+ <ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
<ProjectReference Include="..\Impostor.Hazel\Impostor.Hazel.csproj" />
- <ProjectReference Include="..\Impostor.Server.Api\Impostor.Server.Api.csproj" />
<ProjectReference Include="..\Impostor.Shared\Impostor.Shared.csproj" />
</ItemGroup>
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Server", "Impostor.Server\Impostor.Server.csproj", "{1B0390AF-A4F3-4FE4-B093-708B0135C0B3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Server.Api", "Impostor.Server.Api\Impostor.Server.Api.csproj", "{E096A7D7-D693-4A13-A526-38CC574D84F8}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Api", "Impostor.Api\Impostor.Api.csproj", "{E096A7D7-D693-4A13-A526-38CC574D84F8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "patcher", "patcher", "{94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}"
EndProject
{D16B8DE9-8EE2-4F6A-9352-305D5AB0233D} = {56DD9707-D811-4056-9E2C-8A9CC2479B07}
{804CF172-0C87-4423-9688-BD97D549891E} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}
{1B0390AF-A4F3-4FE4-B093-708B0135C0B3} = {F2B205ED-4250-412E-9992-B11B7D6CE136}
- {E096A7D7-D693-4A13-A526-38CC574D84F8} = {F2B205ED-4250-412E-9992-B11B7D6CE136}
{7C3EB599-2292-4532-B280-D5BED1094DD4} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}
{ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7} = {36AA9913-E6EA-4A6C-90E6-2FD3CC2E3124}