mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-19 15:51:42 +00:00
Implement event to detect game option changes
This can be triggered by both RPC 02 and a(nother) plugin calling SyncSettings to force certain settings on the host. Users of this API could cause this event to recurse by calling SyncSettingsAsync inside an event handler, so we prevent such reasonable foreseeable misuse.
This commit is contained in:
1 parent
66763a4066
commit
5edb409388
5 files changed
+102
No files matched your search
@@ -0,0 +1,51 @@
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Net.Messages.Rpcs;
|
||||
|
||||
namespace Impostor.Api.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// This event is triggered when game options are going to be changed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Be careful when calling SyncSettingsAsync while handling this event,
|
||||
/// as it may call this event again in some cases.
|
||||
/// </remarks>
|
||||
public interface IGameOptionsChangedEvent : IGameEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Lists the possible reasons that the game options may have changed.
|
||||
/// </summary>
|
||||
public enum ChangeReason
|
||||
{
|
||||
/// <summary>
|
||||
/// The options were changed by the host using
|
||||
/// <see cref="Rpc02SyncSettings"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This event does not change the message sent to other players,
|
||||
/// so changes made to Game.Options are not synced until
|
||||
/// SyncSettingsAsync is called afterwards.
|
||||
/// </remarks>
|
||||
Rpc,
|
||||
|
||||
/// <summary>
|
||||
/// The options were changed by a plugin using the
|
||||
/// SyncSettingsAsync method in <see cref="IGame"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This event is called after serializing the settings, which
|
||||
/// means that changes made by event handlers will not be synced
|
||||
/// over.
|
||||
///
|
||||
/// We strongly recommend against calling SyncSettingsAsync while
|
||||
/// handling this event.
|
||||
/// </remarks>
|
||||
Api,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason that the game options have changed.
|
||||
/// </summary>
|
||||
public ChangeReason ChangedBy { get; }
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,16 @@ namespace Impostor.Plugins.Example.Handlers
|
||||
);
|
||||
}
|
||||
|
||||
[EventListener]
|
||||
public async void OnGameOptionsChanged(IGameOptionsChangedEvent e)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Game {code} > new options because of {source}",
|
||||
e.Game.Code,
|
||||
e.ChangedBy
|
||||
);
|
||||
}
|
||||
|
||||
[EventListener]
|
||||
public void OnPlayerJoined(IGamePlayerJoinedEvent e)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Impostor.Api.Events;
|
||||
using Impostor.Api.Games;
|
||||
using static Impostor.Api.Events.IGameOptionsChangedEvent;
|
||||
|
||||
namespace Impostor.Server.Events
|
||||
{
|
||||
public class GameOptionsChangedEvent : IGameOptionsChangedEvent
|
||||
{
|
||||
public GameOptionsChangedEvent(IGame game, ChangeReason changedBy)
|
||||
{
|
||||
Game = game;
|
||||
ChangedBy = changedBy;
|
||||
}
|
||||
|
||||
public ChangeReason ChangedBy { get; }
|
||||
|
||||
public IGame Game { get; }
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ using Impostor.Api.Net.Inner;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
using Impostor.Api.Net.Messages.Rpcs;
|
||||
using Impostor.Api.Utils;
|
||||
using Impostor.Server.Events;
|
||||
using Impostor.Server.Events.Player;
|
||||
using Impostor.Server.Net.Inner.Objects.Components;
|
||||
using Impostor.Server.Net.State;
|
||||
@@ -142,6 +143,9 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
|
||||
Rpc02SyncSettings.DeserializeInto(reader, Game.Options);
|
||||
await _eventManager.CallAsync(new GameOptionsChangedEvent(
|
||||
Game,
|
||||
Api.Events.IGameOptionsChangedEvent.ChangeReason.Rpc));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,16 @@ using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Inner;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Hazel;
|
||||
using Impostor.Server.Events;
|
||||
using Impostor.Server.Net.Inner;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
{
|
||||
internal partial class Game : IGame
|
||||
{
|
||||
private bool alreadyCallingOptionsChangedEvent = false;
|
||||
|
||||
IClientPlayer? IGame.Host => Host;
|
||||
|
||||
IGameNet IGame.GameNet => GameNet;
|
||||
@@ -59,6 +63,20 @@ namespace Impostor.Server.Net.State
|
||||
writer.EndMessage();
|
||||
|
||||
await SendToAllAsync(writer);
|
||||
|
||||
// Prevent bad plugins from causing a server crash by recursing into this function
|
||||
if (alreadyCallingOptionsChangedEvent)
|
||||
{
|
||||
_logger.LogError("Plugin called SyncSettingsAsync while processing a GameOptionsChangedEvent, aborting to prevent recursion");
|
||||
}
|
||||
else
|
||||
{
|
||||
alreadyCallingOptionsChangedEvent = true;
|
||||
await _eventManager.CallAsync(new GameOptionsChangedEvent(
|
||||
this,
|
||||
Api.Events.IGameOptionsChangedEvent.ChangeReason.Api));
|
||||
alreadyCallingOptionsChangedEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask SetPrivacyAsync(bool isPublic)
|
||||
|
||||
Reference in new issue
Block a user