From 61cbd5c4d80f0f6d94afeb71c74b1f7c45a63110 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Tue, 25 Mar 2025 19:35:53 +0100 Subject: [PATCH 01/10] Add new network version --- src/Impostor.Server/Net/Manager/CompatibilityManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Impostor.Server/Net/Manager/CompatibilityManager.cs b/src/Impostor.Server/Net/Manager/CompatibilityManager.cs index 09f1513b..33034360 100644 --- a/src/Impostor.Server/Net/Manager/CompatibilityManager.cs +++ b/src/Impostor.Server/Net/Manager/CompatibilityManager.cs @@ -21,6 +21,7 @@ internal class CompatibilityManager : ICompatibilityManager new GameVersion(2024, 4, 1), // 2024.8.13 new GameVersion(2024, 4, 2), // 2024.9.4 new GameVersion(2024, 8, 10), // 2024.10.29 + new GameVersion(2024, 8, 11), // 16.0.0 (2025-03-25) }, }; From 3e2c3f152534a0e160f4a480d6694dc6776eca37 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Tue, 25 Mar 2025 19:48:54 +0100 Subject: [PATCH 02/10] Update Game Options Creating a game is now possible --- .../GameOptions/HideNSeekGameOptions.cs | 17 ++++++++++++++++- .../Innersloth/GameOptions/NormalGameOptions.cs | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Impostor.Api/Innersloth/GameOptions/HideNSeekGameOptions.cs b/src/Impostor.Api/Innersloth/GameOptions/HideNSeekGameOptions.cs index 87b01adc..10df4259 100644 --- a/src/Impostor.Api/Innersloth/GameOptions/HideNSeekGameOptions.cs +++ b/src/Impostor.Api/Innersloth/GameOptions/HideNSeekGameOptions.cs @@ -2,7 +2,7 @@ namespace Impostor.Api.Innersloth.GameOptions; public class HideNSeekGameOptions : IGameOptions { - public const int LatestVersion = 8; + public const int LatestVersion = 9; public HideNSeekGameOptions(byte version = LatestVersion) { @@ -93,6 +93,11 @@ public class HideNSeekGameOptions : IGameOptions public float MaxPingTime { get; set; } = 6f; + /// + /// Gets or sets the experience level of people in the lobby: Beginner, Intermediate or Expert. + /// + public byte Tag { get; set; } = 0; + public static HideNSeekGameOptions Deserialize(IMessageReader reader, byte version) { var options = new HideNSeekGameOptions(version); @@ -133,6 +138,11 @@ public class HideNSeekGameOptions : IGameOptions MaxPingTime = reader.ReadSingle(); CrewmateTimeInVent = reader.ReadSingle(); + if (Version >= 9) + { + Tag = reader.ReadByte(); + } + if (Version > LatestVersion) { IGameOptions.ThrowUnknownVersion(Version); @@ -172,6 +182,11 @@ public class HideNSeekGameOptions : IGameOptions writer.Write(MaxPingTime); writer.Write(CrewmateTimeInVent); + if (Version >= 9) + { + writer.Write((byte)Tag); + } + if (Version > LatestVersion) { IGameOptions.ThrowUnknownVersion(Version); diff --git a/src/Impostor.Api/Innersloth/GameOptions/NormalGameOptions.cs b/src/Impostor.Api/Innersloth/GameOptions/NormalGameOptions.cs index 7095f6de..61fdd5f6 100644 --- a/src/Impostor.Api/Innersloth/GameOptions/NormalGameOptions.cs +++ b/src/Impostor.Api/Innersloth/GameOptions/NormalGameOptions.cs @@ -4,7 +4,7 @@ namespace Impostor.Api.Innersloth.GameOptions; public class NormalGameOptions : IGameOptions { - public const int LatestVersion = 8; + public const int LatestVersion = 9; public NormalGameOptions(byte version = LatestVersion) { @@ -128,6 +128,11 @@ public class NormalGameOptions : IGameOptions /// public TaskBarUpdate TaskBarUpdate { get; set; } = TaskBarUpdate.Always; + /// + /// Gets or sets the experience level of people in the lobby: Beginner, Intermediate or Expert. + /// + public byte Tag { get; set; } = 0; + public RoleOptionsCollection RoleOptions { get; set; } public static NormalGameOptions Deserialize(IMessageReader reader, byte version) @@ -173,6 +178,11 @@ public class NormalGameOptions : IGameOptions AnonymousVotes = reader.ReadBoolean(); TaskBarUpdate = (TaskBarUpdate)reader.ReadByte(); + if (Version >= 9) + { + Tag = reader.ReadByte(); + } + RoleOptions.Deserialize(reader); if (Version > LatestVersion) @@ -217,6 +227,11 @@ public class NormalGameOptions : IGameOptions writer.Write(AnonymousVotes); writer.Write((byte)TaskBarUpdate); + if (Version >= 9) + { + writer.Write((byte)Tag); + } + RoleOptions.Serialize(writer); if (Version > LatestVersion) From 7e85419b82b14e7a437674d44846d85a285d9d09 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Wed, 26 Mar 2025 00:50:24 +0100 Subject: [PATCH 03/10] Add endpoint to find games by code This is used in the new "Enter Code" flow 3 extra fields are added to GameListing, including a Base 64 serialization method for game options --- src/Impostor.Server/Http/GamesController.cs | 104 ++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/Impostor.Server/Http/GamesController.cs b/src/Impostor.Server/Http/GamesController.cs index e64a498a..58c3abb7 100644 --- a/src/Impostor.Server/Http/GamesController.cs +++ b/src/Impostor.Server/Http/GamesController.cs @@ -97,6 +97,21 @@ public sealed class GamesController : ControllerBase return Ok(_hostServer); } + [HttpGet("{gameId}")] + public IActionResult Show([FromRoute] int gameId) + { + var code = new GameCode(gameId); + var game = _gameManager.Find(code); + + // If the game was not found, print an error message. + if (game == null) + { + return NotFound(new FindGameByCodeResponse(new MatchmakerError(DisconnectReason.GameNotFound))); + } + + return Ok(new FindGameByCodeResponse(GameListingV2.From(game))); + } + private static uint ConvertAddressToNumber(IPAddress address) { #pragma warning disable CS0618 // Among Us only supports IPv4 @@ -210,4 +225,93 @@ public sealed class GamesController : ControllerBase }; } } + + private class FindGameByCodeResponse + { + [SetsRequiredMembers] + public FindGameByCodeResponse(MatchmakerError error) => (Errors, Game) = (new[] { error }, null); + + [SetsRequiredMembers] + public FindGameByCodeResponse(GameListingV2 game) => (Errors, Game) = (null, game); + + [JsonPropertyName("Errors")] + public required MatchmakerError[]? Errors { get; init; } + + [JsonPropertyName("Game")] + public required GameListingV2? Game { get; init; } + } + + private class GameListingV2 + { + [JsonPropertyName("IP")] + public required uint Ip { get; init; } + + [JsonPropertyName("Port")] + public required ushort Port { get; init; } + + [JsonPropertyName("GameId")] + public required int GameId { get; init; } + + [JsonPropertyName("PlayerCount")] + public required int PlayerCount { get; init; } + + [JsonPropertyName("HostName")] + public required string HostName { get; init; } + + [JsonPropertyName("TrueHostName")] + public required string TrueHostName { get; init; } + + [JsonPropertyName("HostPlatformName")] + public required string HostPlatformName { get; init; } + + [JsonPropertyName("Platform")] + public required Platforms Platform { get; init; } + + [JsonPropertyName("QuickChat")] + public required QuickChatModes QuickChat { get; init; } + + [JsonPropertyName("Age")] + public required int Age { get; init; } + + [JsonPropertyName("MaxPlayers")] + public required int MaxPlayers { get; init; } + + [JsonPropertyName("NumImpostors")] + public required int NumImpostors { get; init; } + + [JsonPropertyName("MapId")] + public required MapTypes MapId { get; init; } + + [JsonPropertyName("Language")] + public required GameKeywords Language { get; init; } + + [JsonPropertyName("Options")] + public required string Options { get; init; } + + public static GameListingV2 From(IGame game) + { + var platform = game.Host?.Client.PlatformSpecificData; + + return new GameListingV2 + { + Ip = ConvertAddressToNumber(game.PublicIp.Address), + Port = (ushort)game.PublicIp.Port, + GameId = game.Code, + PlayerCount = game.PlayerCount, + HostName = game.DisplayName ?? game.Host?.Client.Name ?? "Unknown host", + TrueHostName = game.DisplayName ?? game.Host?.Client.Name ?? "Unknown host", + HostPlatformName = platform?.PlatformName ?? string.Empty, + Platform = platform?.Platform ?? Platforms.Unknown, + QuickChat = game.Host?.Client.ChatMode ?? QuickChatModes.QuickChatOnly, + Age = 0, + MaxPlayers = game.Options.MaxPlayers, + NumImpostors = game.Options.NumImpostors, + MapId = game.Options.Map, + Language = game.Options.Keywords, + + // TODO don't hardcode + Options = "CXQAAAEAAA8AAQAAAgAAwD8AAEA/AAAAQAAAoEECAgQCAAAAAgEtAAAALQAAAAAPAAABAQAHBQABZAMAAAAKHgIAAWQCAAAKDwQAAkYDAAAjDwADAAFkAgAADw8IAAJkAgAACgEJAAFkAgAACh4KAAFkAwAADxQB", + }; + } + } } From 6bd13c03999605437ca214a5d071baa333cbe061 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Wed, 26 Mar 2025 19:58:32 +0100 Subject: [PATCH 04/10] Forbid crossplay between 2024.10.29 and 16.0 Innersloth also forbids it, and it's likely not possible due to the new GameOption version --- src/Impostor.Server/Net/Manager/CompatibilityManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Impostor.Server/Net/Manager/CompatibilityManager.cs b/src/Impostor.Server/Net/Manager/CompatibilityManager.cs index 33034360..826d6145 100644 --- a/src/Impostor.Server/Net/Manager/CompatibilityManager.cs +++ b/src/Impostor.Server/Net/Manager/CompatibilityManager.cs @@ -21,6 +21,9 @@ internal class CompatibilityManager : ICompatibilityManager new GameVersion(2024, 4, 1), // 2024.8.13 new GameVersion(2024, 4, 2), // 2024.9.4 new GameVersion(2024, 8, 10), // 2024.10.29 + }, + new[] + { new GameVersion(2024, 8, 11), // 16.0.0 (2025-03-25) }, }; From 06289d46c0c403c69a776b06cb6fed2c90a55d24 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Wed, 26 Mar 2025 20:01:50 +0100 Subject: [PATCH 05/10] Bump minor version At this point it doesn't seem necessary to drop support for old Amoguses, so just bump the minor version for support purposes --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c8c928f5..623f4cff 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ net8.0 11 - 1.10.2 + 1.10.3 dev true true From 28e933092ac2e7cb821a7761194913ff0499bcaf Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Wed, 26 Mar 2025 20:52:17 +0100 Subject: [PATCH 06/10] Stop hardcoding the GameOptions in GameListingV2 To prevent needing to pull in Impostor.Hazel, use an extension method instead. Thanks Niko for researching this part Co-Authored-By: Niko233 <139348239+NikoCat233@users.noreply.github.com> --- .../Extensions/IGameOptionsExtensions.cs | 24 +++++++++++++++++++ src/Impostor.Server/Http/GamesController.cs | 5 ++-- 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/Impostor.Server/Extensions/IGameOptionsExtensions.cs diff --git a/src/Impostor.Server/Extensions/IGameOptionsExtensions.cs b/src/Impostor.Server/Extensions/IGameOptionsExtensions.cs new file mode 100644 index 00000000..ca54b7dc --- /dev/null +++ b/src/Impostor.Server/Extensions/IGameOptionsExtensions.cs @@ -0,0 +1,24 @@ +using System; +using Impostor.Api.Innersloth.GameOptions; +using Impostor.Hazel; + +namespace Impostor.Server.Extensions; + +public static class IGameOptionsExtensions +{ + public static byte[] ToBytes(this IGameOptions gameOptions) + { + using var writer = MessageWriter.Get(MessageType.Unreliable); + writer.Write(gameOptions.Version); + writer.StartMessage(0); + writer.Write((byte)gameOptions.GameMode); + gameOptions.Serialize(writer); + writer.EndMessage(); + return writer.ToByteArray(false); + } + + public static string ToBase64String(this IGameOptions gameOptions) + { + return Convert.ToBase64String(gameOptions.ToBytes()); + } +} diff --git a/src/Impostor.Server/Http/GamesController.cs b/src/Impostor.Server/Http/GamesController.cs index 58c3abb7..d7310e5c 100644 --- a/src/Impostor.Server/Http/GamesController.cs +++ b/src/Impostor.Server/Http/GamesController.cs @@ -9,6 +9,7 @@ using Impostor.Api.Config; using Impostor.Api.Games; using Impostor.Api.Games.Managers; using Impostor.Api.Innersloth; +using Impostor.Server.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -308,9 +309,7 @@ public sealed class GamesController : ControllerBase NumImpostors = game.Options.NumImpostors, MapId = game.Options.Map, Language = game.Options.Keywords, - - // TODO don't hardcode - Options = "CXQAAAEAAA8AAQAAAgAAwD8AAEA/AAAAQAAAoEECAgQCAAAAAgEtAAAALQAAAAAPAAABAQAHBQABZAMAAAAKHgIAAWQCAAAKDwQAAkYDAAAjDwADAAFkAgAADw8IAAJkAgAACgEJAAFkAgAACh4KAAFkAwAADxQB", + Options = game.Options.ToBase64String(), }; } } From fc2679a8d264098b4493423dbfcad5067544ee0a Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Wed, 26 Mar 2025 21:18:50 +0100 Subject: [PATCH 07/10] Add stub for Find Lobby endpoint Co-Authored-By: Niko233 <139348239+NikoCat233@users.noreply.github.com> --- src/Impostor.Server/Http/GamesController.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Impostor.Server/Http/GamesController.cs b/src/Impostor.Server/Http/GamesController.cs index d7310e5c..e1aad79c 100644 --- a/src/Impostor.Server/Http/GamesController.cs +++ b/src/Impostor.Server/Http/GamesController.cs @@ -113,6 +113,23 @@ public sealed class GamesController : ControllerBase return Ok(new FindGameByCodeResponse(GameListingV2.From(game))); } + [HttpGet("filtered")] + public IActionResult ShowFilteredLobbies() + { + // TODO: implement this stub + var response = new + { + games = Array.Empty(), + metadata = new + { + allGamesCount = _gameManager.Games.Count(), + matchingGamesCount = 0, + }, + }; + + return Ok(response); + } + private static uint ConvertAddressToNumber(IPAddress address) { #pragma warning disable CS0618 // Among Us only supports IPv4 From cd220530acea2726ced8b215ebfc2932d34d6631 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Tue, 1 Apr 2025 23:48:32 +0200 Subject: [PATCH 08/10] Remove GameListingV2 The game ignores unknown fields when deserializing, and as only fields were added it's acceptable to merge the structs together --- src/Impostor.Server/Http/GamesController.cs | 74 ++------------------- 1 file changed, 7 insertions(+), 67 deletions(-) diff --git a/src/Impostor.Server/Http/GamesController.cs b/src/Impostor.Server/Http/GamesController.cs index e1aad79c..2f08f7ba 100644 --- a/src/Impostor.Server/Http/GamesController.cs +++ b/src/Impostor.Server/Http/GamesController.cs @@ -110,7 +110,7 @@ public sealed class GamesController : ControllerBase return NotFound(new FindGameByCodeResponse(new MatchmakerError(DisconnectReason.GameNotFound))); } - return Ok(new FindGameByCodeResponse(GameListingV2.From(game))); + return Ok(new FindGameByCodeResponse(GameListing.From(game))); } [HttpGet("filtered")] @@ -119,7 +119,7 @@ public sealed class GamesController : ControllerBase // TODO: implement this stub var response = new { - games = Array.Empty(), + games = Array.Empty(), metadata = new { allGamesCount = _gameManager.Games.Count(), @@ -184,82 +184,22 @@ public sealed class GamesController : ControllerBase public required DisconnectReason Reason { get; init; } } - private class GameListing - { - [JsonPropertyName("IP")] - public required uint Ip { get; init; } - - [JsonPropertyName("Port")] - public required ushort Port { get; init; } - - [JsonPropertyName("GameId")] - public required int GameId { get; init; } - - [JsonPropertyName("PlayerCount")] - public required int PlayerCount { get; init; } - - [JsonPropertyName("HostName")] - public required string HostName { get; init; } - - [JsonPropertyName("HostPlatformName")] - public required string HostPlatformName { get; init; } - - [JsonPropertyName("Platform")] - public required Platforms Platform { get; init; } - - [JsonPropertyName("Age")] - public required int Age { get; init; } - - [JsonPropertyName("MaxPlayers")] - public required int MaxPlayers { get; init; } - - [JsonPropertyName("NumImpostors")] - public required int NumImpostors { get; init; } - - [JsonPropertyName("MapId")] - public required MapTypes MapId { get; init; } - - [JsonPropertyName("Language")] - public required GameKeywords Language { get; init; } - - public static GameListing From(IGame game) - { - var platform = game.Host?.Client.PlatformSpecificData; - - return new GameListing - { - Ip = ConvertAddressToNumber(game.PublicIp.Address), - Port = (ushort)game.PublicIp.Port, - GameId = game.Code, - PlayerCount = game.PlayerCount, - HostName = game.DisplayName ?? game.Host?.Client.Name ?? "Unknown host", - HostPlatformName = platform?.PlatformName ?? string.Empty, - Platform = platform?.Platform ?? Platforms.Unknown, - Age = 0, - MaxPlayers = game.Options.MaxPlayers, - NumImpostors = game.Options.NumImpostors, - MapId = game.Options.Map, - Language = game.Options.Keywords, - }; - } - } - private class FindGameByCodeResponse { [SetsRequiredMembers] public FindGameByCodeResponse(MatchmakerError error) => (Errors, Game) = (new[] { error }, null); [SetsRequiredMembers] - public FindGameByCodeResponse(GameListingV2 game) => (Errors, Game) = (null, game); + public FindGameByCodeResponse(GameListing game) => (Errors, Game) = (null, game); [JsonPropertyName("Errors")] public required MatchmakerError[]? Errors { get; init; } [JsonPropertyName("Game")] - public required GameListingV2? Game { get; init; } + public required GameListing? Game { get; init; } } - private class GameListingV2 + private class GameListing { [JsonPropertyName("IP")] public required uint Ip { get; init; } @@ -306,11 +246,11 @@ public sealed class GamesController : ControllerBase [JsonPropertyName("Options")] public required string Options { get; init; } - public static GameListingV2 From(IGame game) + public static GameListing From(IGame game) { var platform = game.Host?.Client.PlatformSpecificData; - return new GameListingV2 + return new GameListing { Ip = ConvertAddressToNumber(game.PublicIp.Address), Port = (ushort)game.PublicIp.Port, From 723495e3907ff890a79ac15639dff37c2b4b4185 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Tue, 1 Apr 2025 23:49:46 +0200 Subject: [PATCH 09/10] Add GameTags enum Can't be generated from game files at the moment unfortunately --- .../Innersloth/GameOptions/GameTags.cs | 14 ++++++++++++++ .../Innersloth/GameOptions/HideNSeekGameOptions.cs | 4 ++-- .../Innersloth/GameOptions/NormalGameOptions.cs | 4 ++-- 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 src/Impostor.Api/Innersloth/GameOptions/GameTags.cs diff --git a/src/Impostor.Api/Innersloth/GameOptions/GameTags.cs b/src/Impostor.Api/Innersloth/GameOptions/GameTags.cs new file mode 100644 index 00000000..b7db653e --- /dev/null +++ b/src/Impostor.Api/Innersloth/GameOptions/GameTags.cs @@ -0,0 +1,14 @@ +namespace Impostor.Api.Innersloth.GameOptions; + +// TODO: generate this enum based on game data + +/// +/// Tag that can be added to indicate the expected skill level of players in this lobby. +/// +public enum GameTags +{ + None = 0, + Beginner = 1, + Intermediate = 2, + Expert = 3, +} diff --git a/src/Impostor.Api/Innersloth/GameOptions/HideNSeekGameOptions.cs b/src/Impostor.Api/Innersloth/GameOptions/HideNSeekGameOptions.cs index 10df4259..a3d494a8 100644 --- a/src/Impostor.Api/Innersloth/GameOptions/HideNSeekGameOptions.cs +++ b/src/Impostor.Api/Innersloth/GameOptions/HideNSeekGameOptions.cs @@ -96,7 +96,7 @@ public class HideNSeekGameOptions : IGameOptions /// /// Gets or sets the experience level of people in the lobby: Beginner, Intermediate or Expert. /// - public byte Tag { get; set; } = 0; + public GameTags Tag { get; set; } = 0; public static HideNSeekGameOptions Deserialize(IMessageReader reader, byte version) { @@ -140,7 +140,7 @@ public class HideNSeekGameOptions : IGameOptions if (Version >= 9) { - Tag = reader.ReadByte(); + Tag = (GameTags)reader.ReadByte(); } if (Version > LatestVersion) diff --git a/src/Impostor.Api/Innersloth/GameOptions/NormalGameOptions.cs b/src/Impostor.Api/Innersloth/GameOptions/NormalGameOptions.cs index 61fdd5f6..4488b173 100644 --- a/src/Impostor.Api/Innersloth/GameOptions/NormalGameOptions.cs +++ b/src/Impostor.Api/Innersloth/GameOptions/NormalGameOptions.cs @@ -131,7 +131,7 @@ public class NormalGameOptions : IGameOptions /// /// Gets or sets the experience level of people in the lobby: Beginner, Intermediate or Expert. /// - public byte Tag { get; set; } = 0; + public GameTags Tag { get; set; } = 0; public RoleOptionsCollection RoleOptions { get; set; } @@ -180,7 +180,7 @@ public class NormalGameOptions : IGameOptions if (Version >= 9) { - Tag = reader.ReadByte(); + Tag = (GameTags)reader.ReadByte(); } RoleOptions.Deserialize(reader); From dab8751f1db2d9e65f6aa5f7a5998d692e15ab10 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Tue, 1 Apr 2025 23:50:17 +0200 Subject: [PATCH 10/10] Add note to old game methods These are no longer used, so warn people who do want to edit them. I don't foresee these getting removed soon though, as we do want to kick players on old versions for having an old game version --- src/Impostor.Server/Http/GamesController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Impostor.Server/Http/GamesController.cs b/src/Impostor.Server/Http/GamesController.cs index 2f08f7ba..2f0eecbd 100644 --- a/src/Impostor.Server/Http/GamesController.cs +++ b/src/Impostor.Server/Http/GamesController.cs @@ -51,6 +51,7 @@ public sealed class GamesController : ControllerBase [HttpGet] public IActionResult Index(int mapId, GameKeywords lang, int numImpostors, [FromHeader] AuthenticationHeaderValue authorization) { + // NOTE: this method is no longer used by Among Us 16.0.0 and is only kept for backwards compatibility if (authorization.Scheme != "Bearer" || authorization.Parameter == null) { return BadRequest(); @@ -76,6 +77,7 @@ public sealed class GamesController : ControllerBase [HttpPost] public IActionResult Post(int gameId) { + // NOTE: this method is no longer used by Among Us 16.0.0 and is only kept for backwards compatibility var code = new GameCode(gameId); var game = _gameManager.Find(code);