]> git.deb.at Git - rhonda/impostor.git/commitdiff
Basically support AMCI
authorlinmeideli <2558527373@qq.com>
Wed, 19 Aug 2026 04:27:18 +0000 (12:27 +0800)
committerminiduikboot <mini@duikbo.at>
Sun, 30 Aug 2026 16:48:37 +0000 (18:48 +0200)
13 files changed:
src/Impostor.Api.Innersloth.Generator/packages.lock.json
src/Impostor.Api/Games/IGame.cs
src/Impostor.Api/Innersloth/GameFilters/GameFilter.cs
src/Impostor.Api/Innersloth/GameFilters/ModFilter.cs [new file with mode: 0644]
src/Impostor.Api/Net/Messages/C2S/Message00HostGameC2S.cs
src/Impostor.Api/Net/Messages/MessageFlags.cs
src/Impostor.Api/packages.lock.json
src/Impostor.Server/Http/ListingManager.cs
src/Impostor.Server/Net/Client.cs
src/Impostor.Server/Net/Manager/GameManager.cs
src/Impostor.Server/Net/State/Game.cs
src/Impostor.Server/Recorder/ClientRecorder.cs
src/Impostor.Server/packages.lock.json

index 4448822cd5ba74a8d7836448162951b77eb92a7d..fd80d2fde90e66df9dea3f031c89f90a6ec6ef7e 100644 (file)
           "System.Runtime.CompilerServices.Unsafe": "4.5.3"
         }
       }
-    }
+    },
+    ".NETStandard,Version=v2.0/win-x64": {}
   }
 }
\ No newline at end of file
index 71c764a3a8cc2a4bdabc01c7926c73be2c4756e1..e1bdc21660aae22510bfd1b2a442fbccdba216e2 100644 (file)
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Net;
 using System.Threading.Tasks;
 using Impostor.Api.Innersloth;
@@ -47,6 +48,11 @@ namespace Impostor.Api.Games
         /// </remarks>
         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);
 
         T? FindObjectByNetId<T>(uint netId)
index 43e7c916a7e541af8102b4d4289a4572e989bec5..7357f5f3651b1db617e61783fdef6af109b3d72c 100644 (file)
@@ -73,6 +73,9 @@ namespace Impostor.Api.Innersloth.GameFilters
                     case "bool":
                         return JsonSerializer.Deserialize<BoolGameFilter>(filterString)
                                ?? throw new InvalidOperationException("Deserialization returned null for BoolGameFilter");
+                    case "mod":
+                        return JsonSerializer.Deserialize<ModFilter>(filterString)
+                               ?? throw new InvalidOperationException("Deserialization returned null for ModFilter");
                     default:
                         throw new InvalidOperationException("No type matches subfilter");
                 }
diff --git a/src/Impostor.Api/Innersloth/GameFilters/ModFilter.cs b/src/Impostor.Api/Innersloth/GameFilters/ModFilter.cs
new file mode 100644 (file)
index 0000000..71e282d
--- /dev/null
@@ -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; }
+    }
+}
index 8b802c2211b78fa99d734d49f180b769f6013fac..6bc17584f7828ae9610e0cbe3642cfd92e53b625 100644 (file)
@@ -1,4 +1,4 @@
-using Impostor.Api.Innersloth;
+using Impostor.Api.Innersloth;
 using Impostor.Api.Innersloth.GameOptions;
 
 namespace Impostor.Api.Net.Messages.C2S
@@ -20,5 +20,11 @@ namespace Impostor.Api.Net.Messages.C2S
             crossplayFlags = (CrossplayFlags)reader.ReadInt32();
             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);
+        }
     }
 }
index 4faba36663744b0edbacb3c065ce09bb06532e02..a822fbd011c6c59f51f41c6478b83e35f1b769be 100644 (file)
@@ -29,6 +29,7 @@ namespace Impostor.Api.Net.Messages
         public const byte QueryPlatformIds = 22;
         public const byte QueryLobbyInfo = 23;
         public const byte EndGameHostMigration = 24;
+        public const byte HostModdedGame = 25;
         public const byte PackedGameDataTo = 26;
 
         private static readonly Dictionary<byte, string> FlagCache;
index c209cfaba8e165f949cfcd50b88c21faae0b288f..ab47122a50bedbd01f2d4fb1484ff74a0db259b0 100644 (file)
           "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": {
         "type": "Direct",
         "requested": "[1.2.0-beta.556, )",
         "resolved": "4.5.0",
         "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g=="
       }
-    }
+    },
+    "net8.0/win-x64": {}
   }
 }
\ No newline at end of file
index 0221d3d869034dd63db31263f5062326e6a25814..f92b373bed51e9b6b729b5c4695d7872b6b1f3be 100644 (file)
@@ -59,6 +59,10 @@ public sealed class ListingManager
             {
                 continue;
             }
+            if (game.ModGuid != null)
+            {
+                continue;
+            }
 
             if (!_compatibilityConfig.AllowVersionMixing &&
                 game.Host != null &&
@@ -137,6 +141,10 @@ public sealed class ListingManager
                 {
                     continue;
                 }
+                if (game.ModGuid != null && !filterSet.Filters.Any(f => f.OptionType == "mod"))
+                {
+                    continue;
+                }
 
                 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;
                     }
 
index 97bb7c27577fd38fedb36a18ff478ec3ac138d87..6d83016104257d953fa844edc532a021a1b4723b 100644 (file)
@@ -5,6 +5,7 @@ using Impostor.Api;
 using Impostor.Api.Config;
 using Impostor.Api.Games;
 using Impostor.Api.Innersloth;
+using Impostor.Api.Innersloth.GameOptions;
 using Impostor.Api.Net;
 using Impostor.Api.Net.Custom;
 using Impostor.Api.Net.Messages;
@@ -41,6 +42,11 @@ namespace Impostor.Server.Net
             {
                 return false;
             }
+            
+            if (Player != null && Player.Game.ModGuid != null)
+            {
+                return false;
+            }
 
             if (Player != null && Player.IsHost)
             {
@@ -124,12 +130,25 @@ namespace Impostor.Server.Net
             switch (flag)
             {
                 case MessageFlags.HostGame:
+                case MessageFlags.HostModdedGame:
                 {
-                    // Read game settings.
-                    Message00HostGameC2S.Deserialize(reader, out var gameOptions, out _, out var gameFilterOptions);
+                    IGameOptions gameOptions;
+                    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.
-                    var game = await _gameManager.CreateAsync(this, gameOptions, gameFilterOptions);
+                    var game = await _gameManager.CreateAsync(this, gameOptions, gameFilterOptions, modGuid);
 
                     if (game == null)
                     {
@@ -137,6 +156,11 @@ namespace Impostor.Server.Net
                         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.
                     using (var writer = MessageWriter.Get(MessageType.Reliable))
                     {
index d8d5e97d3719fd009c597d90f44b039ef949d095..19d5f3364da3907a1816dd768fbc51f792de6be8 100644 (file)
@@ -1,4 +1,4 @@
-using System;
+using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Linq;
@@ -85,7 +85,7 @@ namespace Impostor.Server.Net.Manager
             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))
             {
@@ -101,11 +101,11 @@ namespace Impostor.Server.Net.Manager
                 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++)
             {
-                (success, game) = await TryCreateAsync(options, filterOptions, owner);
+                (success, game) = await TryCreateAsync(options, filterOptions, owner, modGuid: modGuid);
             }
 
             if (owner != null)
@@ -126,10 +126,12 @@ namespace Impostor.Server.Net.Manager
             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 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))
             {
index 07fd480b9ec805eb81591d815d0f994430b12195..9fec3c8afef8ebb3b034b45397d613e41944d3ee 100644 (file)
@@ -47,7 +47,8 @@ namespace Impostor.Server.Net.State
             IEventManager eventManager,
             ICompatibilityManager compatibilityManager,
             IOptions<CompatibilityConfig> compatibilityConfig,
-            IOptions<TimeoutConfig> timeoutConfig)
+            IOptions<TimeoutConfig> timeoutConfig,
+            Guid? modGuid = null)
         {
             _logger = logger;
             _serviceProvider = serviceProvider;
@@ -62,6 +63,7 @@ namespace Impostor.Server.Net.State
             GameNet = new GameNet();
             Options = options;
             FilterOptions = filterOptions;
+            ModGuid = modGuid;
             _clientManager = clientManager;
             _eventManager = eventManager;
             _compatibilityManager = compatibilityManager;
@@ -96,6 +98,8 @@ namespace Impostor.Server.Net.State
 
         public bool IsHostAuthoritive => Host != null && Host.Client.GameVersion.HasDisableServerAuthorityFlag;
 
+        public Guid? ModGuid { get; }
+
         internal GameNet GameNet { get; }
 
         public bool TryGetPlayer(int id, [MaybeNullWhen(false)] out ClientPlayer player)
index 4671dd3f7b0f7230cdfe1b3cef54596a556fc09b..428b1fd99a60cc103a6034dcabae6fbf921a06fa 100644 (file)
@@ -1,4 +1,4 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
 using Impostor.Api.Config;
 using Impostor.Api.Innersloth;
 using Impostor.Api.Net.Custom;
@@ -52,7 +52,7 @@ namespace Impostor.Server.Recorder
             await base.HandleMessageAsync(reader, messageType);
 
             // Player created a game.
-            if (reader.Tag == MessageFlags.HostGame)
+            if (reader.Tag is MessageFlags.HostGame or MessageFlags.HostModdedGame)
             {
                 _createdGame = true;
             }
index e4565d789292a953b06d879cfab00e80d2f235f0..6cd84f2dbc09c298f543690c5684b9fc9ba0ac42 100644 (file)
         "resolved": "8.0.1",
         "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": {
         "type": "Direct",
         "requested": "[8.0.0, )",