Basically support AMCI

This commit is contained in:
linmeideli authored and miniduikboot committed 2026-08-30 18:48:37 +02:00
1 parent 8f41030faf
commit 510ed07389
13 files changed
+107 -16

No files matched your search

@@ -151,6 +151,7 @@
"System.Runtime.CompilerServices.Unsafe": "4.5.3" "System.Runtime.CompilerServices.Unsafe": "4.5.3"
} }
} }
} },
".NETStandard,Version=v2.0/win-x64": {}
} }
} }
+7 -1
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Impostor.Api.Innersloth; using Impostor.Api.Innersloth;
@@ -47,6 +48,11 @@ namespace Impostor.Api.Games
/// </remarks> /// </remarks>
bool IsHostAuthoritive { get; } bool IsHostAuthoritive { get; }
/// <summary>
/// Gets the mod GUID that the game was registered with through the AMCI.
/// </summary>
Guid? ModGuid { get; }
IClientPlayer? GetClientPlayer(int clientId); IClientPlayer? GetClientPlayer(int clientId);
T? FindObjectByNetId<T>(uint netId) T? FindObjectByNetId<T>(uint netId)
@@ -73,6 +73,9 @@ namespace Impostor.Api.Innersloth.GameFilters
case "bool": case "bool":
return JsonSerializer.Deserialize<BoolGameFilter>(filterString) return JsonSerializer.Deserialize<BoolGameFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for BoolGameFilter"); ?? throw new InvalidOperationException("Deserialization returned null for BoolGameFilter");
case "mod":
return JsonSerializer.Deserialize<ModFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for ModFilter");
default: default:
throw new InvalidOperationException("No type matches subfilter"); throw new InvalidOperationException("No type matches subfilter");
} }
@@ -0,0 +1,16 @@
using System;
using System.Text.Json.Serialization;
// https://github.com/Innersloth-LLC/AmongUsModdingInformation
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class ModFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "mod";
[JsonPropertyName("AcceptedValues")]
public required Guid AcceptedValues { get; set; }
}
}
@@ -1,4 +1,4 @@
using Impostor.Api.Innersloth; using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.GameOptions; using Impostor.Api.Innersloth.GameOptions;
namespace Impostor.Api.Net.Messages.C2S namespace Impostor.Api.Net.Messages.C2S
@@ -20,5 +20,11 @@ namespace Impostor.Api.Net.Messages.C2S
crossplayFlags = (CrossplayFlags)reader.ReadInt32(); crossplayFlags = (CrossplayFlags)reader.ReadInt32();
gameFilterOptions = GameFilterOptions.Deserialize(reader); gameFilterOptions = GameFilterOptions.Deserialize(reader);
} }
public static void DeserializeModded(IMessageReader reader, out IGameOptions gameOptions, out CrossplayFlags crossplayFlags, out GameFilterOptions gameFilterOptions, out Guid modGuid)
{
Deserialize(reader, out gameOptions, out crossplayFlags, out gameFilterOptions);
modGuid = new Guid(reader.ReadBytes(16).Span);
}
} }
} }
@@ -29,6 +29,7 @@ namespace Impostor.Api.Net.Messages
public const byte QueryPlatformIds = 22; public const byte QueryPlatformIds = 22;
public const byte QueryLobbyInfo = 23; public const byte QueryLobbyInfo = 23;
public const byte EndGameHostMigration = 24; public const byte EndGameHostMigration = 24;
public const byte HostModdedGame = 25;
public const byte PackedGameDataTo = 26; public const byte PackedGameDataTo = 26;
private static readonly Dictionary<byte, string> FlagCache; private static readonly Dictionary<byte, string> FlagCache;
+8 -1
View File
@@ -40,6 +40,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
} }
}, },
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.30, )",
"resolved": "8.0.30",
"contentHash": "SfmwuZjUPBTEoBMAsXZ/hWkaEK9oL+Y/UtAx7N6RyWHfcMt9J0ExrNPuOgTyPbCXIyiTBFsxFNrPLEZNPmJcvA=="
},
"StyleCop.Analyzers": { "StyleCop.Analyzers": {
"type": "Direct", "type": "Direct",
"requested": "[1.2.0-beta.556, )", "requested": "[1.2.0-beta.556, )",
@@ -117,6 +123,7 @@
"resolved": "4.5.0", "resolved": "4.5.0",
"contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==" "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g=="
} }
} },
"net8.0/win-x64": {}
} }
} }
@@ -59,6 +59,10 @@ public sealed class ListingManager
{ {
continue; continue;
} }
if (game.ModGuid != null)
{
continue;
}
if (!_compatibilityConfig.AllowVersionMixing && if (!_compatibilityConfig.AllowVersionMixing &&
game.Host != null && game.Host != null &&
@@ -137,6 +141,10 @@ public sealed class ListingManager
{ {
continue; continue;
} }
if (game.ModGuid != null && !filterSet.Filters.Any(f => f.OptionType == "mod"))
{
continue;
}
var matchesAllFilters = true; var matchesAllFilters = true;
@@ -191,6 +199,13 @@ public sealed class ListingManager
} }
} }
break;
case "mod":
if (filter.SubFilter is ModFilter modFilter && game.ModGuid != modFilter.AcceptedValues)
{
matchesAllFilters = false;
}
break; break;
} }
+27 -3
View File
@@ -5,6 +5,7 @@ using Impostor.Api;
using Impostor.Api.Config; using Impostor.Api.Config;
using Impostor.Api.Games; using Impostor.Api.Games;
using Impostor.Api.Innersloth; using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.GameOptions;
using Impostor.Api.Net; using Impostor.Api.Net;
using Impostor.Api.Net.Custom; using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Messages; using Impostor.Api.Net.Messages;
@@ -41,6 +42,11 @@ namespace Impostor.Server.Net
{ {
return false; return false;
} }
if (Player != null && Player.Game.ModGuid != null)
{
return false;
}
if (Player != null && Player.IsHost) if (Player != null && Player.IsHost)
{ {
@@ -124,12 +130,25 @@ namespace Impostor.Server.Net
switch (flag) switch (flag)
{ {
case MessageFlags.HostGame: case MessageFlags.HostGame:
case MessageFlags.HostModdedGame:
{ {
// Read game settings. IGameOptions gameOptions;
Message00HostGameC2S.Deserialize(reader, out var gameOptions, out _, out var gameFilterOptions); GameFilterOptions gameFilterOptions;
Guid? modGuid = null;
if (flag == MessageFlags.HostModdedGame)
{
Message00HostGameC2S.DeserializeModded(reader, out gameOptions, out _, out gameFilterOptions, out var parsedModGuid);
modGuid = parsedModGuid;
}
else
{
// Read game settings.
Message00HostGameC2S.Deserialize(reader, out gameOptions, out _, out gameFilterOptions);
}
// Create game. // Create game.
var game = await _gameManager.CreateAsync(this, gameOptions, gameFilterOptions); var game = await _gameManager.CreateAsync(this, gameOptions, gameFilterOptions, modGuid);
if (game == null) if (game == null)
{ {
@@ -137,6 +156,11 @@ namespace Impostor.Server.Net
return; return;
} }
if (modGuid != null)
{
_logger.LogInformation("Client {Name} ({Id}) hosted a modded game with mod GUID {ModGuid}.", Name, Id, modGuid);
}
// Code in the packet below will be used in JoinGame. // Code in the packet below will be used in JoinGame.
using (var writer = MessageWriter.Get(MessageType.Reliable)) using (var writer = MessageWriter.Get(MessageType.Reliable))
{ {
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -85,7 +85,7 @@ namespace Impostor.Server.Net.Manager
await _eventManager.CallAsync(new GameDestroyedEvent(game)); await _eventManager.CallAsync(new GameDestroyedEvent(game));
} }
public async ValueTask<IGame?> CreateAsync(IClient? owner, IGameOptions options, GameFilterOptions filterOptions) public async ValueTask<IGame?> CreateAsync(IClient? owner, IGameOptions options, GameFilterOptions filterOptions, Guid? modGuid = null)
{ {
if (owner != null && !_gamesCreatedBy.TryAdd(owner, null)) if (owner != null && !_gamesCreatedBy.TryAdd(owner, null))
{ {
@@ -101,11 +101,11 @@ namespace Impostor.Server.Net.Manager
return null; return null;
} }
var (success, game) = await TryCreateAsync(options, filterOptions, owner, @event.GameCode); var (success, game) = await TryCreateAsync(options, filterOptions, owner, @event.GameCode, modGuid);
for (var i = 0; i < 10 && !success; i++) for (var i = 0; i < 10 && !success; i++)
{ {
(success, game) = await TryCreateAsync(options, filterOptions, owner); (success, game) = await TryCreateAsync(options, filterOptions, owner, modGuid: modGuid);
} }
if (owner != null) if (owner != null)
@@ -126,10 +126,12 @@ namespace Impostor.Server.Net.Manager
return CreateAsync(null, options, filterOptions); return CreateAsync(null, options, filterOptions);
} }
private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(IGameOptions options, GameFilterOptions filterOptions, IClient? owner, GameCode? desiredGameCode = null) private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(IGameOptions options, GameFilterOptions filterOptions, IClient? owner, GameCode? desiredGameCode = null, Guid? modGuid = null)
{ {
var gameCode = desiredGameCode ?? _gameCodeFactory.Create(); var gameCode = desiredGameCode ?? _gameCodeFactory.Create();
var game = ActivatorUtilities.CreateInstance<Game>(_serviceProvider, _publicIp, gameCode, options, filterOptions); var game = modGuid.HasValue
? ActivatorUtilities.CreateInstance<Game>(_serviceProvider, _publicIp, gameCode, options, filterOptions, modGuid.Value)
: ActivatorUtilities.CreateInstance<Game>(_serviceProvider, _publicIp, gameCode, options, filterOptions);
if (!_games.TryAdd(gameCode, game)) if (!_games.TryAdd(gameCode, game))
{ {
+5 -1
View File
@@ -47,7 +47,8 @@ namespace Impostor.Server.Net.State
IEventManager eventManager, IEventManager eventManager,
ICompatibilityManager compatibilityManager, ICompatibilityManager compatibilityManager,
IOptions<CompatibilityConfig> compatibilityConfig, IOptions<CompatibilityConfig> compatibilityConfig,
IOptions<TimeoutConfig> timeoutConfig) IOptions<TimeoutConfig> timeoutConfig,
Guid? modGuid = null)
{ {
_logger = logger; _logger = logger;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
@@ -62,6 +63,7 @@ namespace Impostor.Server.Net.State
GameNet = new GameNet(); GameNet = new GameNet();
Options = options; Options = options;
FilterOptions = filterOptions; FilterOptions = filterOptions;
ModGuid = modGuid;
_clientManager = clientManager; _clientManager = clientManager;
_eventManager = eventManager; _eventManager = eventManager;
_compatibilityManager = compatibilityManager; _compatibilityManager = compatibilityManager;
@@ -96,6 +98,8 @@ namespace Impostor.Server.Net.State
public bool IsHostAuthoritive => Host != null && Host.Client.GameVersion.HasDisableServerAuthorityFlag; public bool IsHostAuthoritive => Host != null && Host.Client.GameVersion.HasDisableServerAuthorityFlag;
public Guid? ModGuid { get; }
internal GameNet GameNet { get; } internal GameNet GameNet { get; }
public bool TryGetPlayer(int id, [MaybeNullWhen(false)] out ClientPlayer player) public bool TryGetPlayer(int id, [MaybeNullWhen(false)] out ClientPlayer player)
@@ -1,4 +1,4 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Impostor.Api.Config; using Impostor.Api.Config;
using Impostor.Api.Innersloth; using Impostor.Api.Innersloth;
using Impostor.Api.Net.Custom; using Impostor.Api.Net.Custom;
@@ -52,7 +52,7 @@ namespace Impostor.Server.Recorder
await base.HandleMessageAsync(reader, messageType); await base.HandleMessageAsync(reader, messageType);
// Player created a game. // Player created a game.
if (reader.Tag == MessageFlags.HostGame) if (reader.Tag is MessageFlags.HostGame or MessageFlags.HostModdedGame)
{ {
_createdGame = true; _createdGame = true;
} }
+6
View File
@@ -56,6 +56,12 @@
"resolved": "8.0.1", "resolved": "8.0.1",
"contentHash": "X21b8D6z58EG34OMLH955750mFaEaGs5xLGe9Vyi3059Cgb3sk+p2k/JlQXaieEBRrk+SVlFzDei3Q6PdVzJOA==" "contentHash": "X21b8D6z58EG34OMLH955750mFaEaGs5xLGe9Vyi3059Cgb3sk+p2k/JlQXaieEBRrk+SVlFzDei3Q6PdVzJOA=="
}, },
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.30, )",
"resolved": "8.0.30",
"contentHash": "SfmwuZjUPBTEoBMAsXZ/hWkaEK9oL+Y/UtAx7N6RyWHfcMt9J0ExrNPuOgTyPbCXIyiTBFsxFNrPLEZNPmJcvA=="
},
"Serilog.Extensions.Hosting": { "Serilog.Extensions.Hosting": {
"type": "Direct", "type": "Direct",
"requested": "[8.0.0, )", "requested": "[8.0.0, )",