--- /dev/null
+using Impostor.Api.Net;
+
+namespace Impostor.Api.Events.Client
+{
+ /// <summary>
+ /// Called just after a <see cref="IClient"/> is created and connected.
+ /// </summary>
+ public interface IClientConnectedEvent : IClientEvent
+ {
+ IClient Client { get; }
+ }
+}
--- /dev/null
+using Impostor.Api.Net;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Events.Client
+{
+ /// <summary>
+ /// Called just before a <see cref="IHazelConnection"/> is registered.
+ /// </summary>
+ public interface IClientConnectionEvent : IClientEvent
+ {
+ IMessageReader HandshakeData { get; }
+ }
+}
--- /dev/null
+using Impostor.Api.Net;
+
+namespace Impostor.Api.Events.Client
+{
+ public interface IClientEvent : IEvent
+ {
+ IHazelConnection Connection { get; }
+ }
+}
--- /dev/null
+using Impostor.Api.Games;
+using Impostor.Api.Net;
+
+namespace Impostor.Api.Events
+{
+ /// <summary>
+ /// Called just before a <see cref="IClientPlayer"/> joins a game.
+ /// </summary>
+ public interface IGamePlayerJoiningEvent : IGameEvent
+ {
+ IClientPlayer Player { get; }
+
+ GameJoinResult? JoinResult { get; set; }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Impostor.Api.Events
+{
+ /// <summary>
+ /// Disposes multiple <see cref="IDisposable" />.
+ /// </summary>
+ public class MultiDisposable : IDisposable
+ {
+ private readonly IEnumerable<IDisposable> _disposables;
+
+ public MultiDisposable(IEnumerable<IDisposable> disposables)
+ {
+ _disposables = disposables;
+ }
+
+ public MultiDisposable(params IDisposable[] disposables) : this(disposables.AsEnumerable())
+ {
+ }
+
+ public void Dispose()
+ {
+ foreach (var disposable in _disposables)
+ {
+ disposable?.Dispose();
+ }
+ }
+ }
+}
/// <param name="id">ID of the client.</param>
/// <returns>A <see cref="ValueTask" /> representing the asynchronous operation.</returns>
ValueTask SendToAsync(IMessageWriter writer, int id);
+
+ /// <summary>
+ /// Creates a MessageWriter with GameData header.
+ /// </summary>
+ /// <remarks>Use with caution, recommended only for advanced developers.</remarks>
+ /// <param name="targetNetId">Net id of the InnerNetObject.</param>
+ /// <param name="callId">Rpc id of the message.</param>
+ /// <param name="targetClientId">Client id of GameDataTo's target.</param>
+ /// <param name="type">Message type of the message.</param>
+ /// <returns>A <see cref="IMessageWriter" /> that you can fill with rpc data and send using <see cref="FinishRpcAsync"/>.</returns>
+ IMessageWriter StartRpc(uint targetNetId, RpcCalls callId, int? targetClientId, MessageType type = MessageType.Reliable);
+
+ /// <summary>
+ /// Finishes rpc message and sends it to the target.
+ /// </summary>
+ /// <param name="writer">Message writer of the rpc.</param>
+ /// <param name="targetClientId">Id of the receiving client.</param>
+ /// <returns>A <see cref="ValueTask" /> representing the asynchronous operation.</returns>
+ ValueTask FinishRpcAsync(IMessageWriter writer, int? targetClientId = null);
}
}
+++ /dev/null
-using System;
-
-namespace Impostor.Api
-{
- public interface IDateTimeProvider
- {
- DateTimeOffset UtcNow { get; }
- }
-}
--- /dev/null
+namespace Impostor.Api.Net.Custom
+{
+ public interface ICustomMessage
+ {
+ byte Id { get; }
+ }
+}
--- /dev/null
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Impostor.Api.Net.Custom
+{
+ public interface ICustomMessageManager<T>
+ where T : ICustomMessage
+ {
+ bool TryGet(byte id, [MaybeNullWhen(false)] out T message);
+
+ /// <summary>
+ /// Register a custom message.
+ /// </summary>
+ /// <param name="message"><typeparamref name="T"/> message.</param>
+ /// <returns>Disposable that unregisters the message.</returns>
+ IDisposable Register(T message);
+ }
+}
--- /dev/null
+using System.Threading.Tasks;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Net.Custom
+{
+ public interface ICustomRootMessage : ICustomMessage
+ {
+ ValueTask HandleMessageAsync(IClient client, IMessageReader reader, MessageType messageType);
+ }
+}
--- /dev/null
+using System.Threading.Tasks;
+using Impostor.Api.Net.Inner;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Net.Custom
+{
+ public interface ICustomRpc : ICustomMessage
+ {
+ ValueTask<bool> HandleRpcAsync(IInnerNetObject innerNetObject, IClientPlayer sender, IClientPlayer? target, IMessageReader reader);
+ }
+}
public MessageType SendOption { get; }
+ /// <summary>
+ /// Copies the contents of this writer into a new array.
+ /// </summary>
+ /// <param name="includeHeader">Whether to include message header in the array.</param>
+ /// <returns>An array containing the data in the current writer.</returns>
+ byte[] ToByteArray(bool includeHeader);
+
/// <summary>
/// Writes a boolean to the message.
/// </summary>
--- /dev/null
+using System;
+
+namespace Impostor.Api.Utils
+{
+ public interface IDateTimeProvider
+ {
+ DateTimeOffset UtcNow { get; }
+ }
+}
--- /dev/null
+namespace Impostor.Api.Utils
+{
+ public interface IServerEnvironment
+ {
+ string Version { get; }
+ }
+}
--- /dev/null
+using Impostor.Api.Events.Client;
+using Impostor.Api.Net;
+
+namespace Impostor.Server.Events.Client
+{
+ public class ClientConnectedEvent : IClientConnectedEvent
+ {
+ public ClientConnectedEvent(IHazelConnection connection, IClient client)
+ {
+ Connection = connection;
+ Client = client;
+ }
+
+ public IHazelConnection Connection { get; }
+
+ public IClient Client { get; }
+ }
+}
--- /dev/null
+using Impostor.Api.Events.Client;
+using Impostor.Api.Net;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Server.Events.Client
+{
+ public class ClientConnectionEvent : IClientConnectionEvent
+ {
+ public ClientConnectionEvent(IHazelConnection connection, IMessageReader handshakeData)
+ {
+ Connection = connection;
+ HandshakeData = handshakeData;
+ }
+
+ public IHazelConnection Connection { get; }
+
+ public IMessageReader HandshakeData { get; }
+ }
+}
}
var eventListeners = RegisteredEventListener.FromType(listener.GetType());
- var disposes = new IDisposable[eventListeners.Count];
+ var disposes = new List<IDisposable>(eventListeners.Count);
foreach (var eventListener in eventListeners)
{
wrappedEventListener.EventType,
_ => new TemporaryEventRegister());
- register.Add(wrappedEventListener);
+ disposes.Add(register.Add(wrappedEventListener));
}
if (eventListeners.Count > 0)
--- /dev/null
+using Impostor.Api.Events;
+using Impostor.Api.Games;
+using Impostor.Api.Net;
+
+namespace Impostor.Server.Events
+{
+ public class GamePlayerJoiningEvent : IGamePlayerJoiningEvent
+ {
+ public GamePlayerJoiningEvent(IGame game, IClientPlayer player)
+ {
+ Game = game;
+ Player = player;
+ }
+
+ public IGame Game { get; }
+
+ public IClientPlayer Player { get; }
+
+ public GameJoinResult? JoinResult { get; set; }
+ }
+}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-
-namespace Impostor.Server.Events
-{
- /// <summary>
- /// Disposes multiple <see cref="IDisposable" />.
- /// </summary>
- internal class MultiDisposable : IDisposable
- {
- private readonly IEnumerable<IDisposable> _disposables;
-
- public MultiDisposable(IEnumerable<IDisposable> disposables)
- {
- _disposables = disposables;
- }
-
- public void Dispose()
- {
- foreach (var disposable in _disposables)
- {
- disposable?.Dispose();
- }
- }
- }
-}
using Impostor.Api.Games;
using Impostor.Api.Innersloth;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.C2S;
using Impostor.Api.Net.Messages.S2C;
private readonly AntiCheatConfig _antiCheatConfig;
private readonly ClientManager _clientManager;
private readonly GameManager _gameManager;
+ private readonly ICustomMessageManager<ICustomRootMessage> _customMessageManager;
- public Client(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, string name, int gameVersion, IHazelConnection connection)
+ public Client(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, string name, int gameVersion, IHazelConnection connection)
: base(name, gameVersion, connection)
{
_logger = logger;
_antiCheatConfig = antiCheatOptions.Value;
_clientManager = clientManager;
_gameManager = gameManager;
+ _customMessageManager = customMessageManager;
}
public override async ValueTask<bool> ReportCheatAsync(CheatContext context, string message)
var toPlayer = flag == MessageFlags.GameDataTo;
- // Handle packet.
- using var readerCopy = reader.Copy();
+ var position = reader.Position;
+ var verified = await Player!.Game.HandleGameDataAsync(reader, Player, toPlayer);
+ reader.Seek(position);
- var verified = await Player!.Game.HandleGameDataAsync(readerCopy, Player, toPlayer);
if (verified)
{
// Broadcast packet to all other players.
}
default:
+ if (_customMessageManager.TryGet(flag, out var customRootMessage))
+ {
+ await customRootMessage.HandleMessageAsync(this, reader, messageType);
+ break;
+ }
+
_logger.LogWarning("Server received unknown flag {0}.", flag);
break;
}
--- /dev/null
+using System;
+using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
+using Impostor.Api;
+using Impostor.Api.Net.Custom;
+
+namespace Impostor.Server.Net.Custom
+{
+ public class CustomMessageManager<T> : ICustomMessageManager<T>
+ where T : ICustomMessage
+ {
+ private readonly ConcurrentDictionary<byte, T> _messages = new ConcurrentDictionary<byte, T>();
+
+ public bool TryGet(byte id, [MaybeNullWhen(false)] out T message)
+ {
+ return _messages.TryGetValue(id, out message);
+ }
+
+ public IDisposable Register(T message)
+ {
+ if (!_messages.TryAdd(message.Id, message))
+ {
+ throw new ImpostorException($"Message with id: {message.Id} was already registered");
+ }
+
+ return new UnregisterDisposable(this, message);
+ }
+
+ private class UnregisterDisposable : IDisposable
+ {
+ private readonly CustomMessageManager<T> _manager;
+ private readonly T _message;
+
+ private bool _disposed;
+
+ public UnregisterDisposable(CustomMessageManager<T> manager, T message)
+ {
+ _manager = manager;
+ _message = message;
+ }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException("Tried to dispose already disposed object");
+ }
+
+ _manager._messages.TryRemove(_message.Id, out _);
+ _disposed = true;
+ }
+ }
+ }
+}
using System.Threading.Tasks;
using Impostor.Api.Games;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Messages;
using Impostor.Server.Net.State;
{
private const int HostInheritId = -2;
- protected InnerNetObject(Game game)
+ private readonly ICustomMessageManager<ICustomRpc> _customMessageManager;
+
+ protected InnerNetObject(ICustomMessageManager<ICustomRpc> customMessageManager, Game game)
{
+ _customMessageManager = customMessageManager;
Game = game;
}
public abstract ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer? target, IMessageReader reader, bool initialState);
- public abstract ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader);
+ public virtual async ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
+ {
+ return await HandleCustomRpcAsync(sender, target, call, reader) ?? await UnregisteredCall(call, sender);
+ }
+
+ protected async ValueTask<bool?> HandleCustomRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
+ {
+ if (_customMessageManager.TryGet((byte)call, out var customRpc))
+ {
+ return await customRpc.HandleRpcAsync(this, sender, target, reader);
+ }
+
+ return null;
+ }
- public virtual ValueTask OnSpawnAsync()
+ internal virtual ValueTask OnSpawnAsync()
{
return default;
}
using Impostor.Api;
using Impostor.Api.Events.Managers;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.Rpcs;
private ushort _lastSequenceId;
private bool _spawnSnapAllowed;
- public InnerCustomNetworkTransform(Game game, ILogger<InnerCustomNetworkTransform> logger, InnerPlayerControl playerControl, IEventManager eventManager, ObjectPool<PlayerMovementEvent> pool) : base(game)
+ public InnerCustomNetworkTransform(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerCustomNetworkTransform> logger, InnerPlayerControl playerControl, IEventManager eventManager, ObjectPool<PlayerMovementEvent> pool) : base(customMessageManager, game)
{
_logger = logger;
_playerControl = playerControl;
return true;
}
- return await UnregisteredCall(call, sender);
+ return await base.HandleRpcAsync(sender, target, call, reader);
}
internal async ValueTask SetPositionAsync(IClientPlayer sender, Vector2 position, Vector2 velocity)
using System;
using System.Threading.Tasks;
using Impostor.Api.Events.Managers;
-using Impostor.Api.Innersloth;
-using Impostor.Api.Innersloth.Maps;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.Rpcs;
private readonly InnerPlayerControl _playerControl;
private readonly IEventManager _eventManager;
- public InnerPlayerPhysics(Game game, ILogger<InnerPlayerPhysics> logger, InnerPlayerControl playerControl, IEventManager eventManager) : base(game)
+ public InnerPlayerPhysics(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerPlayerPhysics> logger, InnerPlayerControl playerControl, IEventManager eventManager) : base(customMessageManager, game)
{
_logger = logger;
_playerControl = playerControl;
break;
default:
- return await UnregisteredCall(call, sender);
+ return await base.HandleRpcAsync(sender, target, call, reader);
}
return true;
using System.Threading.Tasks;
using Impostor.Api;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Inner.Objects;
using Impostor.Api.Net.Messages;
private readonly ILogger<InnerVoteBanSystem> _logger;
private readonly Dictionary<int, int[]> _votes;
- public InnerVoteBanSystem(Game game, ILogger<InnerVoteBanSystem> logger) : base(game)
+ public InnerVoteBanSystem(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerVoteBanSystem> logger) : base(customMessageManager, game)
{
_logger = logger;
_votes = new Dictionary<int, int[]>();
return true;
}
- return await UnregisteredCall(call, sender);
+ return await base.HandleRpcAsync(sender, target, call, reader);
}
}
}
using System.Threading.Tasks;
using Impostor.Api;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Inner.Objects;
using Impostor.Api.Net.Messages;
private readonly ILogger<InnerGameData> _logger;
private readonly ConcurrentDictionary<byte, InnerPlayerInfo> _allPlayers;
- public InnerGameData(Game game, ILogger<InnerGameData> logger, IServiceProvider serviceProvider) : base(game)
+ public InnerGameData(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerGameData> logger, IServiceProvider serviceProvider) : base(customMessageManager, game)
{
_logger = logger;
_allPlayers = new ConcurrentDictionary<byte, InnerPlayerInfo>();
}
default:
- return await UnregisteredCall(call, sender);
+ return await base.HandleRpcAsync(sender, target, call, reader);
}
return true;
using System;
using System.Threading.Tasks;
using Impostor.Api.Net;
-using Impostor.Api.Net.Inner;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner.Objects;
using Impostor.Api.Net.Messages;
using Impostor.Server.Net.State;
{
internal class InnerLobbyBehaviour : InnerNetObject, IInnerLobbyBehaviour
{
- public InnerLobbyBehaviour(Game game) : base(game)
+ public InnerLobbyBehaviour(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game)
{
Components.Add(this);
}
{
throw new NotImplementedException();
}
-
- public override ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
- {
- throw new NotImplementedException();
- }
}
}
using Impostor.Api.Events.Managers;
using Impostor.Api.Innersloth;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.Rpcs;
[AllowNull]
private PlayerVoteArea[] _playerStates;
- public InnerMeetingHud(Game game, ILogger<InnerMeetingHud> logger, IEventManager eventManager) : base(game)
+ public InnerMeetingHud(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerMeetingHud> logger, IEventManager eventManager) : base(customMessageManager, game)
{
_logger = logger;
_eventManager = eventManager;
}
default:
- return await UnregisteredCall(call, sender);
+ return await base.HandleRpcAsync(sender, target, call, reader);
}
return true;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Customization;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Inner.Objects;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.Rpcs;
+using Impostor.Api.Utils;
using Impostor.Server.Events.Player;
using Impostor.Server.Net.Inner.Objects.Components;
using Impostor.Server.Net.State;
private readonly IEventManager _eventManager;
private readonly IDateTimeProvider _dateTimeProvider;
- public InnerPlayerControl(Game game, ILogger<InnerPlayerControl> logger, IServiceProvider serviceProvider, IEventManager eventManager, IDateTimeProvider dateTimeProvider) : base(game)
+ public InnerPlayerControl(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerPlayerControl> logger, IServiceProvider serviceProvider, IEventManager eventManager, IDateTimeProvider dateTimeProvider) : base(customMessageManager, game)
{
_logger = logger;
_eventManager = eventManager;
}
default:
- return await UnregisteredCall(call, sender);
+ return await base.HandleRpcAsync(sender, target, call, reader);
}
return true;
using System;
using System.Collections.Generic;
-using Impostor.Api;
using Impostor.Api.Games;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Customization;
using Impostor.Api.Net.Messages;
+using Impostor.Api.Utils;
namespace Impostor.Server.Net.Inner.Objects
{
using System.Numerics;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Maps;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner.Objects.ShipStatus;
using Impostor.Server.Net.Inner.Objects.Systems;
using Impostor.Server.Net.Inner.Objects.Systems.ShipStatus;
{
internal class InnerAirshipStatus : InnerShipStatus, IInnerAirshipStatus
{
- public InnerAirshipStatus(Game game) : base(game)
+ public InnerAirshipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game)
{
}
using System.Numerics;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Maps;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner.Objects.ShipStatus;
using Impostor.Server.Net.Inner.Objects.Systems;
using Impostor.Server.Net.Inner.Objects.Systems.ShipStatus;
{
internal class InnerMiraShipStatus : InnerShipStatus, IInnerMiraShipStatus
{
- public InnerMiraShipStatus(Game game) : base(game)
+ public InnerMiraShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game)
{
}
using System.Numerics;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Maps;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner.Objects.ShipStatus;
using Impostor.Server.Net.Inner.Objects.Systems;
using Impostor.Server.Net.Inner.Objects.Systems.ShipStatus;
{
internal class InnerPolusShipStatus : InnerShipStatus, IInnerPolusShipStatus
{
- public InnerPolusShipStatus(Game game) : base(game)
+ public InnerPolusShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game)
{
}
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Maps;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Inner.Objects.ShipStatus;
using Impostor.Api.Net.Messages;
{
private readonly Dictionary<SystemTypes, ISystemType> _systems = new Dictionary<SystemTypes, ISystemType>();
- protected InnerShipStatus(Game game) : base(game)
+ protected InnerShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game)
{
Components.Add(this);
}
public abstract Vector2 MeetingSpawnCenter { get; }
- public override ValueTask OnSpawnAsync()
+ internal override ValueTask OnSpawnAsync()
{
for (var i = 0; i < Doors.Count; i++)
{
}
default:
- return await UnregisteredCall(call, sender);
+ return await base.HandleRpcAsync(sender, target, call, reader);
}
return true;
using System.Numerics;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Maps;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner.Objects.ShipStatus;
using Impostor.Server.Net.Inner.Objects.Systems;
using Impostor.Server.Net.Inner.Objects.Systems.ShipStatus;
{
internal class InnerSkeldShipStatus : InnerShipStatus, IInnerSkeldShipStatus
{
- public InnerSkeldShipStatus(Game game) : base(game)
+ public InnerSkeldShipStatus(ICustomMessageManager<ICustomRpc> customMessageManager, Game game) : base(customMessageManager, game)
{
}
using Impostor.Api.Net.Messages.S2C;
using Impostor.Hazel;
using Impostor.Server.Config;
+using Impostor.Server.Events.Client;
using Impostor.Server.Net.Factories;
using Microsoft.Extensions.Logging;
};
private readonly ILogger<ClientManager> _logger;
+ private readonly IEventManager _eventManager;
private readonly ConcurrentDictionary<int, ClientBase> _clients;
private readonly IClientFactory _clientFactory;
private int _idLast;
- public ClientManager(ILogger<ClientManager> logger, IClientFactory clientFactory)
+ public ClientManager(ILogger<ClientManager> logger, IEventManager eventManager, IClientFactory clientFactory)
{
_logger = logger;
+ _eventManager = eventManager;
_clientFactory = clientFactory;
_clients = new ConcurrentDictionary<int, ClientBase>();
}
client.Id = id;
_logger.LogTrace("Client connected.");
_clients.TryAdd(id, client);
+
+ await _eventManager.CallAsync(new ClientConnectedEvent(connection, client));
}
public void Remove(IClient client)
using Impostor.Api.Net.Messages.C2S;
using Impostor.Hazel;
using Impostor.Hazel.Udp;
+using Impostor.Server.Events.Client;
using Impostor.Server.Net.Hazel;
using Impostor.Server.Net.Manager;
using Microsoft.Extensions.Logging;
{
internal class Matchmaker
{
+ private readonly IEventManager _eventManager;
private readonly ClientManager _clientManager;
private readonly ObjectPool<MessageReader> _readerPool;
- private readonly ILogger<Matchmaker> _logger;
private readonly ILogger<HazelConnection> _connectionLogger;
private UdpConnectionListener? _connection;
public Matchmaker(
- ILogger<Matchmaker> logger,
+ IEventManager eventManager,
ClientManager clientManager,
ObjectPool<MessageReader> readerPool,
ILogger<HazelConnection> connectionLogger)
{
- _logger = logger;
+ _eventManager = eventManager;
_clientManager = clientManager;
_readerPool = readerPool;
_connectionLogger = connectionLogger;
var connection = new HazelConnection(e.Connection, _connectionLogger);
+ await _eventManager.CallAsync(new ClientConnectionEvent(connection, e.HandshakeData));
+
// Register client
await _clientManager.RegisterConnectionAsync(connection, name, clientVersion);
}
{
internal partial class Game
{
- private const int FakeClientId = int.MaxValue - 1;
-
/// <summary>
/// Used for global object, spawned by the host.
/// </summary>
{
if (!await obj.HandleRpcAsync(sender, target, (RpcCalls)reader.ReadByte(), reader))
{
- return false;
+ parent.RemoveMessage(reader);
+ continue;
}
}
else
var innerNetObject = (InnerNetObject)ActivatorUtilities.CreateInstance(_serviceProvider, SpawnableObjects[objectId], this);
var ownerClientId = reader.ReadPackedInt32();
- // Prevent fake client from being broadcasted.
- // TODO: Remove message from stream properly.
- if (ownerClientId == FakeClientId)
- {
- return false;
- }
-
innerNetObject.SpawnFlags = (SpawnFlags)reader.ReadByte();
var components = innerNetObject.GetComponentsInChildren<InnerNetObject>();
return GameJoinResult.CreateSuccess(player);
}
+ var @event = new GamePlayerJoiningEvent(this, player);
+ await _eventManager.CallAsync(@event);
+
+ if (@event.JoinResult != null && !@event.JoinResult.Value.IsSuccess)
+ {
+ return @event.JoinResult.Value;
+ }
+
await HandleJoinGameNew(player, isNew);
return GameJoinResult.CreateSuccess(player);
}
}
}
- internal IMessageWriter StartRpc(uint targetNetId, RpcCalls callId, int targetClientId = -1, MessageType type = MessageType.Reliable)
+ public IMessageWriter StartRpc(uint targetNetId, RpcCalls callId, int? targetClientId = null, MessageType type = MessageType.Reliable)
{
var writer = MessageWriter.Get(type);
- if (targetClientId < 0)
+ if (targetClientId == null || targetClientId < 0)
{
writer.StartMessage(MessageFlags.GameData);
writer.Write(Code);
{
writer.StartMessage(MessageFlags.GameDataTo);
writer.Write(Code);
- writer.WritePacked(targetClientId);
+ writer.WritePacked(targetClientId.Value);
}
writer.StartMessage(GameDataTag.RpcFlag);
return writer;
}
- internal ValueTask FinishRpcAsync(IMessageWriter writer, int? targetClientId = null)
+ public ValueTask FinishRpcAsync(IMessageWriter writer, int? targetClientId = null)
{
writer.EndMessage();
writer.EndMessage();
using System;
using System.IO;
using System.Linq;
-using Impostor.Api;
using Impostor.Api.Events.Managers;
using Impostor.Api.Games;
using Impostor.Api.Games.Managers;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Manager;
using Impostor.Api.Net.Messages;
+using Impostor.Api.Utils;
using Impostor.Hazel.Extensions;
using Impostor.Server.Config;
using Impostor.Server.Events;
using Impostor.Server.Net;
+using Impostor.Server.Net.Custom;
using Impostor.Server.Net.Factories;
using Impostor.Server.Net.Manager;
using Impostor.Server.Net.Messages;
.Get<AuthServerConfig>() ?? new AuthServerConfig();
services.AddSingleton<ServerEnvironment>();
+ services.AddSingleton<IServerEnvironment>(p => p.GetRequiredService<ServerEnvironment>());
services.AddSingleton<IDateTimeProvider, RealDateTimeProvider>();
services.Configure<DebugConfig>(host.Configuration.GetSection(DebugConfig.Section));
services.AddEventPools();
services.AddHazel();
+ services.AddSingleton<ICustomMessageManager<ICustomRootMessage>, CustomMessageManager<ICustomRootMessage>>();
+ services.AddSingleton<ICustomMessageManager<ICustomRpc>, CustomMessageManager<ICustomRpc>>();
services.AddSingleton<IMessageWriterProvider, MessageWriterProvider>();
services.AddSingleton<IGameCodeFactory, GameCodeFactory>();
services.AddSingleton<IEventManager, EventManager>();
using System;
-using Impostor.Api;
+using Impostor.Api.Utils;
namespace Impostor.Server
{
using System.Threading.Tasks;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Messages;
using Impostor.Server.Config;
using Impostor.Server.Net;
private bool _createdGame;
private bool _recordAfter;
- public ClientRecorder(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, string name, int gameVersion, HazelConnection connection, PacketRecorder recorder)
- : base(logger, antiCheatOptions, clientManager, gameManager, name, gameVersion, connection)
+ public ClientRecorder(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, GameManager gameManager, string name, int gameVersion, HazelConnection connection, PacketRecorder recorder)
+ : base(logger, antiCheatOptions, clientManager, gameManager, customMessageManager, name, gameVersion, connection)
{
_recorder = recorder;
_isFirst = true;
--- /dev/null
+using System;
+using Impostor.Api.Utils;
+
+namespace Impostor.Server.Utils
+{
+ public class RealDateTimeProvider : IDateTimeProvider
+ {
+ public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
+ }
+}
+using Impostor.Api.Utils;
+
namespace Impostor.Server.Utils
{
- public class ServerEnvironment
+ public class ServerEnvironment : IServerEnvironment
{
+ public string Version { get; } = DotnetUtils.Version;
+
public bool IsReplay { get; init; }
}
}
using System;
-using Impostor.Api;
+using Impostor.Api.Utils;
namespace Impostor.Tools.ServerReplay
{
using System.IO;
using System.Net;
using System.Threading.Tasks;
-using Impostor.Api;
using Impostor.Api.Events.Managers;
using Impostor.Api.Games;
using Impostor.Api.Games.Managers;
using Impostor.Api.Innersloth;
using Impostor.Api.Net;
+using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.C2S;
+using Impostor.Api.Utils;
using Impostor.Hazel;
using Impostor.Hazel.Extensions;
using Impostor.Server;
using Impostor.Server.Events;
using Impostor.Server.Net;
+using Impostor.Server.Net.Custom;
using Impostor.Server.Net.Factories;
using Impostor.Server.Net.Manager;
using Impostor.Server.Net.Redirector;
services.AddEventPools();
services.AddHazel();
+ services.AddSingleton<ICustomMessageManager<ICustomRootMessage>, CustomMessageManager<ICustomRootMessage>>();
+ services.AddSingleton<ICustomMessageManager<ICustomRpc>, CustomMessageManager<ICustomRpc>>();
return services.BuildServiceProvider();
}
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.SpacingRules">
<Rule Id="SA1009" Action="None" />
</Rules>
+ <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.OrderingRules">
+ <Rule Id="SA1202" Action="Info" />
+ </Rules>
</RuleSet>