Skip to content

Commit

Permalink
BeforeGameCreatedEvent (Impostor#266)
Browse files Browse the repository at this point in the history
Co-authored-by: js6pak <[email protected]>
  • Loading branch information
Minorusama and js6pak authored Apr 13, 2021
1 parent 0bb5442 commit d47343d
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 12 deletions.
25 changes: 25 additions & 0 deletions src/Impostor.Api/Events/Game/IGameCreationEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Impostor.Api.Games;
using Impostor.Api.Net;

namespace Impostor.Api.Events
{
/// <summary>
/// Called just before a new <see cref="IGame"/> is created.
/// </summary>
public interface IGameCreationEvent : IEventCancelable
{
/// <summary>
/// Gets the client that requested creation of the game.
/// </summary>
/// <remarks>
/// Will be null if game creation was requested by a plugin.
/// </remarks>
IClient? Client { get; }

/// <summary>
/// Gets or sets the desired <see cref="Games.GameCode"/>.
/// </summary>
/// <exception cref="ImpostorException">If the GameCode is invalid or already used in another game.</exception>
GameCode? GameCode { get; set; }
}
}
6 changes: 4 additions & 2 deletions src/Impostor.Api/Games/GameCode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using Impostor.Api.Innersloth;

namespace Impostor.Api.Games
Expand All @@ -14,13 +14,15 @@ public GameCode(int value)
public GameCode(string code)
{
Value = GameCodeParser.GameNameToInt(code);
Code = code;
Code = code.ToUpperInvariant();
}

public string Code { get; }

public int Value { get; }

public bool IsInvalid => Value == -1;

public static implicit operator string(GameCode code) => code.Code;

public static implicit operator int(GameCode code) => code.Value;
Expand Down
8 changes: 7 additions & 1 deletion src/Impostor.Api/Games/Managers/IGameManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ public interface IGameManager

IGame? Find(GameCode code);

ValueTask<IGame> CreateAsync(GameOptionsData options);
/// <summary>
/// Creates a new game.
/// </summary>
/// <param name="options">Game options.</param>
/// <returns>Created game or null if creation was cancelled by a plugin.</returns>
/// <exception cref="ImpostorException">Thrown when game creation failed.</exception>
ValueTask<IGame?> CreateAsync(GameOptionsData options);
}
}
13 changes: 10 additions & 3 deletions src/Impostor.Plugins.Example/ExamplePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,17 @@ public override async ValueTask EnableAsync()
_logger.LogInformation("Example is being enabled.");

var game = await _gameManager.CreateAsync(new GameOptionsData());
game.DisplayName = "Example game";
await game.SetPrivacyAsync(true);
if (game == null)
{
_logger.LogWarning("Example game creation was cancelled");
}
else
{
game.DisplayName = "Example game";
await game.SetPrivacyAsync(true);

_logger.LogInformation("Created game {0}.", game.Code.Code);
_logger.LogInformation("Created game {0}.", game.Code.Code);
}
}

public override ValueTask DisableAsync()
Expand Down
24 changes: 24 additions & 0 deletions src/Impostor.Plugins.Example/Handlers/GameEventListener.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Impostor.Api.Events;
using Impostor.Api.Games;
using Impostor.Api.Innersloth;
using Microsoft.Extensions.Logging;

namespace Impostor.Plugins.Example.Handlers
Expand All @@ -12,6 +14,28 @@ public GameEventListener(ILogger<GameEventListener> logger)
_logger = logger;
}

[EventListener]
public void OnGameCreated(IGameCreationEvent e)
{
_logger.LogInformation("Game creation requested by {client}", e.Client == null ? "a plugin" : e.Client.Name);

if (e.Client != null)
{
var gameCode = GameCode.From(e.Client.Name);

if (!gameCode.IsInvalid)
{
e.GameCode = gameCode;
}

if (e.Client.Name == "dima")
{
e.IsCancelled = true;
e.Client.DisconnectAsync(DisconnectReason.Custom, "No you dont >:(");
}
}
}

[EventListener]
public void OnGameCreated(IGameCreatedEvent e)
{
Expand Down
46 changes: 46 additions & 0 deletions src/Impostor.Server/Events/Game/GameCreationEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Impostor.Api;
using Impostor.Api.Events;
using Impostor.Api.Games;
using Impostor.Api.Games.Managers;
using Impostor.Api.Net;

namespace Impostor.Server.Events
{
public class GameCreationEvent : IGameCreationEvent
{
private readonly IGameManager _gameManager;
private GameCode? _gameCode;

public GameCreationEvent(IGameManager gameManager, IClient? client)
{
_gameManager = gameManager;
Client = client;
}

public IClient? Client { get; }

public GameCode? GameCode
{
get => _gameCode;
set
{
if (value.HasValue)
{
if (value.Value.IsInvalid)
{
throw new ImpostorException("GameCode is invalid.");
}

if (_gameManager.Find(value.Value) != null)
{
throw new ImpostorException($"GameCode [{value.Value.Code}] is already used.");
}
}

_gameCode = value;
}
}

public bool IsCancelled { get; set; }
}
}
8 changes: 7 additions & 1 deletion src/Impostor.Server/Net/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,13 @@ public override async ValueTask HandleMessageAsync(IMessageReader reader, Messag
var gameInfo = Message00HostGameC2S.Deserialize(reader, out _);

// Create game.
var game = await _gameManager.CreateAsync(gameInfo);
var game = await _gameManager.CreateAsync(this, gameInfo);

if (game == null)
{
await DisconnectAsync(DisconnectReason.GameMissing);
return;
}

// Code in the packet below will be used in JoinGame.
using (var writer = MessageWriter.Get(MessageType.Reliable))
Expand Down
22 changes: 18 additions & 4 deletions src/Impostor.Server/Net/Manager/GameManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Impostor.Api.Games;
using Impostor.Api.Games.Managers;
using Impostor.Api.Innersloth;
using Impostor.Api.Net;
using Impostor.Server.Config;
using Impostor.Server.Events;
using Impostor.Server.Net.Redirector;
Expand Down Expand Up @@ -110,10 +111,18 @@ public async ValueTask RemoveAsync(GameCode gameCode)
await _eventManager.CallAsync(new GameDestroyedEvent(game));
}

public async ValueTask<IGame> CreateAsync(GameOptionsData options)
public async ValueTask<IGame?> CreateAsync(IClient? owner, GameOptionsData options)
{
var @event = new GameCreationEvent(this, owner);
await _eventManager.CallAsync(@event);

if (@event.IsCancelled)
{
return null;
}

// TODO: Prevent duplicates when using server redirector using INodeProvider.
var (success, game) = await TryCreateAsync(options);
var (success, game) = await TryCreateAsync(options, @event.GameCode);

for (var i = 0; i < 10 && !success; i++)
{
Expand All @@ -128,9 +137,14 @@ public async ValueTask<IGame> CreateAsync(GameOptionsData options)
return game;
}

private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(GameOptionsData options)
public ValueTask<IGame?> CreateAsync(GameOptionsData options)
{
return CreateAsync(null, options);
}

private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(GameOptionsData options, GameCode? desiredGameCode = null)
{
var gameCode = _gameCodeFactory.Create();
var gameCode = desiredGameCode ?? _gameCodeFactory.Create();
var gameCodeStr = gameCode.Code;
var game = ActivatorUtilities.CreateInstance<Game>(_serviceProvider, _publicIp, gameCode, options);

Expand Down
2 changes: 1 addition & 1 deletion src/Impostor.Tools.ServerReplay/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private static async Task ParsePacket(BinaryReader reader)
case RecordedPacketType.GameCreated:
_gameCodeFactory.Result = GameCode.From(reader.ReadString());

await _gameManager.CreateAsync(GameOptions[clientId]);
await _gameManager.CreateAsync(Connections[clientId].Client, GameOptions[clientId]);

GameOptions.Remove(clientId);
break;
Expand Down

0 comments on commit d47343d

Please sign in to comment.