mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-20 01:46:11 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
202b955db1 | ||
|
|
1cd298608a | ||
|
|
f0689ac2e2 | ||
|
|
2cad4ffa14 | ||
|
|
e9437e781b |
No files matched your search
@@ -40,6 +40,8 @@ Impostor has an Anticheat that makes it possible to kick cheaters from games aut
|
||||
| **EnableRoleChecks** | `true` | Enables checks that check if players have the correct role when performing certain role abilities like venting or murdering. |
|
||||
| **EnableTargetChecks** | `true` | Enables checks that check if certain packets to everyone that should only have been sent to certain players or vice versa. This includes sending votes or network objects. |
|
||||
| **ForbidProtocolExtensions** | `true` | If disabled allows players to send network packets that go beyond the network packets sent by the vanilla game. This is necessary for most mods that need all players to install it. |
|
||||
| **EnablePacketSizeChecks** | `true` | Enables checks that verify if network messages exceed the maximum allowed size. |
|
||||
| **PacketSizeLimit** | `1203` | The maximum allowed size (in bytes) for a network message. The official servers of Among Us requires that a Hazel packet is < 1204 bytes, excluding headers. |
|
||||
|
||||
### Compatibility
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<VersionPrefix>1.10.6</VersionPrefix>
|
||||
<VersionPrefix>1.10.7</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
|
||||
@@ -85,6 +85,14 @@ public sealed class SourceGenerator : IIncrementalGenerator
|
||||
enumGenerator.Generate("SystemTypes", underlyingType: CSharpEnumUnderlyingType.Byte);
|
||||
enumGenerator.Generate("Language", sourceName: "SupportedLangs");
|
||||
enumGenerator.Generate("TaskTypes");
|
||||
enumGenerator.Generate("BoolOptionNames", "Impostor.Api.Innersloth.GameFilters");
|
||||
enumGenerator.Generate("ByteOptionNames", "Impostor.Api.Innersloth.GameFilters");
|
||||
enumGenerator.Generate("CategorizedOptionNames", "Impostor.Api.Innersloth.GameFilters");
|
||||
enumGenerator.Generate("FloatArrayOptionNames", "Impostor.Api.Innersloth.GameFilters");
|
||||
enumGenerator.Generate("FloatOptionNames", "Impostor.Api.Innersloth.GameFilters");
|
||||
enumGenerator.Generate("Int32ArrayOptionNames", "Impostor.Api.Innersloth.GameFilters");
|
||||
enumGenerator.Generate("Int32OptionNames", "Impostor.Api.Innersloth.GameFilters");
|
||||
enumGenerator.Generate("UInt32OptionNames", "Impostor.Api.Innersloth.GameFilters");
|
||||
|
||||
enumGenerator.Generate("RpcCalls", "Impostor.Api.Net.Inner", underlyingType: CSharpEnumUnderlyingType.Byte);
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ public enum CheatCategory
|
||||
/// <summary>A packet was sent on an invalid network object, like a PlayerControl without PlayerInfo.</summary>
|
||||
InvalidObject,
|
||||
|
||||
/// <summary>A packet was sent that exceeded the maximum allowed RPC size.</summary>
|
||||
PacketSize,
|
||||
|
||||
/// <summary>Legacy category for unsorted anticheat checks.</summary>
|
||||
Other,
|
||||
}
|
||||
@@ -29,5 +29,9 @@ namespace Impostor.Api.Config
|
||||
public bool EnableTargetChecks { get; set; } = true;
|
||||
|
||||
public bool ForbidProtocolExtensions { get; set; } = true;
|
||||
|
||||
public bool EnablePacketSizeChecks { get; set; } = true;
|
||||
|
||||
public int PacketSizeLimit { get; set; } = 1203;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" PrivateAssets="all" />
|
||||
<PackageReference Include="Impostor.Hazel.Abstractions" Version="1.0.0" />
|
||||
<PackageReference Include="Impostor.Hazel.Abstractions" Version="1.0.1" />
|
||||
|
||||
<ProjectReference Include="..\Impostor.Api.Innersloth.Generator\Impostor.Api.Innersloth.Generator.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
|
||||
<AdditionalFiles Include="Innersloth/Data/**/*.json" />
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"Invalid": 0,
|
||||
"VisualTasks": 1,
|
||||
"GhostsDoTasks": 2,
|
||||
"ConfirmImpostor": 3,
|
||||
"AnonymousVotes": 4,
|
||||
"IsDefaults": 5,
|
||||
"UseFlashlight": 6,
|
||||
"SeekerFinalVents": 7,
|
||||
"SeekerFinalMap": 8,
|
||||
"SeekerPings": 9,
|
||||
"ShowCrewmateNames": 10,
|
||||
"Roles": 11,
|
||||
"ShapeshifterLeaveSkin": 1000,
|
||||
"ImpostorsCanSeeProtect": 1100,
|
||||
"NoisemakerImpostorAlert": 1300
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Invalid": 0,
|
||||
"MapId": 1
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"KillCooldown": 0,
|
||||
"PlayerSpeed": 1,
|
||||
"EmergencyMeetingCount": 2,
|
||||
"EmergencyMeetingCooldown": 3,
|
||||
"DiscussionTime": 4,
|
||||
"VotingTime": 5,
|
||||
"CrewmateVision": 6,
|
||||
"HidingTime": 7,
|
||||
"MaxTimeInVent": 8,
|
||||
"ImposterVision": 9,
|
||||
"FinalHideTime": 10,
|
||||
"FinalHideImposterSpeed": 11
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Invalid": 0,
|
||||
"KillDistances": 1
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"Invalid": 0,
|
||||
"KillCooldown": 1,
|
||||
"PlayerSpeedMod": 2,
|
||||
"ImpostorLightMod": 3,
|
||||
"CrewLightMod": 4,
|
||||
"CrewmateTimeInVent": 100,
|
||||
"FinalEscapeTime": 101,
|
||||
"EscapeTime": 102,
|
||||
"SeekerFinalSpeed": 103,
|
||||
"MaxPingTime": 104,
|
||||
"CrewmateFlashlightSize": 105,
|
||||
"ImpostorFlashlightSize": 106,
|
||||
"ShapeshifterCooldown": 1000,
|
||||
"ShapeshifterDuration": 1001,
|
||||
"ProtectionDurationSeconds": 1100,
|
||||
"GuardianAngelCooldown": 1101,
|
||||
"ScientistCooldown": 1200,
|
||||
"ScientistBatteryCharge": 1201,
|
||||
"EngineerCooldown": 1300,
|
||||
"EngineerInVentMaxTime": 1301,
|
||||
"PhantomCooldown": 1500,
|
||||
"PhantomDuration": 1501,
|
||||
"TrackerCooldown": 1550,
|
||||
"TrackerDuration": 1551,
|
||||
"TrackerDelay": 1552,
|
||||
"NoisemakerAlertDuration": 1600
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Invalid": 0,
|
||||
"MaxImpostors": 1,
|
||||
"MinPlayers": 2
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"Invalid": 0,
|
||||
"NumImpostors": 1,
|
||||
"KillDistance": 2,
|
||||
"NumEmergencyMeetings": 3,
|
||||
"EmergencyCooldown": 4,
|
||||
"DiscussionTime": 5,
|
||||
"VotingTime": 6,
|
||||
"MaxImpostors": 7,
|
||||
"MinPlayers": 8,
|
||||
"MaxPlayers": 9,
|
||||
"NumCommonTasks": 10,
|
||||
"NumShortTasks": 11,
|
||||
"NumLongTasks": 12,
|
||||
"TaskBarMode": 13,
|
||||
"Tag": 14,
|
||||
"CrewmatesRemainingForVitals": 100,
|
||||
"CrewmateVentUses": 101,
|
||||
"ImpostorPlayerID": 102,
|
||||
"RulePreset": 200
|
||||
}
|
||||
@@ -1624,4 +1624,4 @@
|
||||
"DetectiveNotesTitlePostIt": 4080,
|
||||
"DetectiveNotesPostIt": 4081,
|
||||
"DetectiveNotesSuspectNumber": 4082
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Invalid": 0,
|
||||
"Keywords": 1
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dumpostorVersion": "1.0.1-dev",
|
||||
"gameVersion": "2025.10.14",
|
||||
"gameVersion": "2024.10.29",
|
||||
"platformType": "StandaloneItch"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class BoolGameFilter : ISubFilter
|
||||
{
|
||||
[JsonPropertyName("FilterType")]
|
||||
public string FilterType { get; } = "bool";
|
||||
|
||||
[JsonPropertyName("AcceptedValues")]
|
||||
public required List<bool> AcceptedValues { get; set; }
|
||||
|
||||
[JsonPropertyName("OptionEnum")]
|
||||
public required BoolOptionNames OptionEnum { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class CategorizedGameFilter : ISubFilter
|
||||
{
|
||||
[JsonPropertyName("FilterType")]
|
||||
public string FilterType { get; } = "cat";
|
||||
|
||||
[JsonPropertyName("AcceptedValues")]
|
||||
public required List<int> AcceptedValues { get; set; }
|
||||
|
||||
[JsonPropertyName("OptionEnum")]
|
||||
public required CategorizedOptionNames OptionEnum { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class ChatModeGameFilter : ISubFilter
|
||||
{
|
||||
[JsonPropertyName("FilterType")]
|
||||
public string FilterType { get; } = "chat";
|
||||
|
||||
[JsonPropertyName("AcceptedValues")]
|
||||
public required byte AcceptedValues { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class GameFilter
|
||||
{
|
||||
[JsonConstructor]
|
||||
public GameFilter(string optionType, string key, string subFilterString)
|
||||
{
|
||||
this.OptionType = optionType;
|
||||
this.Key = key;
|
||||
this.SubFilterString = subFilterString;
|
||||
this.SubFilter = ResolveSubFilter(OptionType, SubFilterString);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public ISubFilter SubFilter { get; set; }
|
||||
|
||||
[JsonPropertyName("OptionType")]
|
||||
public required string OptionType { get; set; }
|
||||
|
||||
[JsonPropertyName("Key")]
|
||||
public required string Key { get; set; }
|
||||
|
||||
[JsonPropertyName("SubFilterString")]
|
||||
public required string SubFilterString { get; set; }
|
||||
|
||||
public void ModifySubFilter(ISubFilter subFilter)
|
||||
{
|
||||
this.SubFilter = subFilter;
|
||||
}
|
||||
|
||||
[OnSerializing]
|
||||
internal void OnSerializing(StreamingContext context)
|
||||
{
|
||||
this.SubFilterString = JsonSerializer.Serialize(this.SubFilter);
|
||||
}
|
||||
|
||||
[OnDeserialized]
|
||||
internal void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
this.SubFilter = this.ResolveSubFilter(this.OptionType, this.SubFilterString);
|
||||
}
|
||||
|
||||
private ISubFilter ResolveSubFilter(string type, string filterString)
|
||||
{
|
||||
if (type != null)
|
||||
{
|
||||
switch (type.ToLower())
|
||||
{
|
||||
case "int":
|
||||
return JsonSerializer.Deserialize<IntGameFilter>(filterString)
|
||||
?? throw new InvalidOperationException("Deserialization returned null for IntGameFilter");
|
||||
case "platform":
|
||||
return JsonSerializer.Deserialize<PlatformGameFilter>(filterString)
|
||||
?? throw new InvalidOperationException("Deserialization returned null for PlatformGameFilter");
|
||||
case "cat":
|
||||
return JsonSerializer.Deserialize<CategorizedGameFilter>(filterString)
|
||||
?? throw new InvalidOperationException("Deserialization returned null for CategorizedGameFilter");
|
||||
case "languages":
|
||||
return JsonSerializer.Deserialize<LanguageFilter>(filterString)
|
||||
?? throw new InvalidOperationException("Deserialization returned null for LanguageFilter");
|
||||
case "chat":
|
||||
return JsonSerializer.Deserialize<ChatModeGameFilter>(filterString)
|
||||
?? throw new InvalidOperationException("Deserialization returned null for ChatModeGameFilter");
|
||||
case "map":
|
||||
return JsonSerializer.Deserialize<MapGameFilter>(filterString)
|
||||
?? throw new InvalidOperationException("Deserialization returned null for MapGameFilter");
|
||||
case "bool":
|
||||
return JsonSerializer.Deserialize<BoolGameFilter>(filterString)
|
||||
?? throw new InvalidOperationException("Deserialization returned null for BoolGameFilter");
|
||||
default:
|
||||
throw new InvalidOperationException("No type matches subfilter");
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Must provide type for sub filter");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class GameFilterSet
|
||||
{
|
||||
[JsonConstructor]
|
||||
public GameFilterSet(GameModes gameMode, List<GameFilter> filters)
|
||||
{
|
||||
GameMode = gameMode;
|
||||
Filters = filters;
|
||||
}
|
||||
|
||||
[JsonPropertyName("GameMode")]
|
||||
public required GameModes GameMode { get; set; }
|
||||
|
||||
[JsonPropertyName("Filters")]
|
||||
public required List<GameFilter> Filters { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class GameFiltersList
|
||||
{
|
||||
[JsonPropertyName("FilterSets")]
|
||||
public required List<GameFilterSet> FilterSets { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
public interface ISubFilter
|
||||
{
|
||||
string FilterType { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class IntGameFilter : ISubFilter
|
||||
{
|
||||
[JsonPropertyName("FilterType")]
|
||||
public string FilterType { get; } = "int";
|
||||
|
||||
[JsonPropertyName("AcceptedValues")]
|
||||
public required List<int> AcceptedValues { get; set; }
|
||||
|
||||
[JsonPropertyName("OptionEnum")]
|
||||
public required Int32OptionNames OptionEnum { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class LanguageFilter : ISubFilter
|
||||
{
|
||||
[JsonPropertyName("FilterType")]
|
||||
public string FilterType { get; } = "languages";
|
||||
|
||||
[JsonPropertyName("AcceptedValues")]
|
||||
public required uint AcceptedValues { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class MapGameFilter : ISubFilter
|
||||
{
|
||||
[JsonPropertyName("FilterType")]
|
||||
public string FilterType { get; } = "map";
|
||||
|
||||
[JsonPropertyName("AcceptedValues")]
|
||||
public required byte AcceptedValues { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameFilters
|
||||
{
|
||||
[Serializable]
|
||||
public class PlatformGameFilter : ISubFilter
|
||||
{
|
||||
[JsonPropertyName("FilterType")]
|
||||
public string FilterType { get; } = "platform";
|
||||
|
||||
[JsonPropertyName("AcceptedValues")]
|
||||
public required uint AcceptedValues { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,8 @@ public interface IGameOptions
|
||||
/// </summary>
|
||||
public bool IsDefaults { get; set; }
|
||||
|
||||
public GameTags Tag { get; set; }
|
||||
|
||||
public void Serialize(IMessageWriter writer);
|
||||
|
||||
public static void EnsureVersionIsModular<TCaller>(byte version)
|
||||
|
||||
@@ -145,6 +145,8 @@ public class LegacyGameOptionsData : IGameOptions
|
||||
/// </summary>
|
||||
public bool IsDefaults { get; set; } = true;
|
||||
|
||||
public GameTags Tag { get; set; } = 0;
|
||||
|
||||
public static LegacyGameOptionsData Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new LegacyGameOptionsData(version);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"net8.0": {
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"requested": "[1.0.1, )",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Direct",
|
||||
|
||||
@@ -42,19 +42,19 @@
|
||||
},
|
||||
"Impostor.Hazel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "1.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "7.0.1",
|
||||
"Impostor.Hazel.Abstractions": "1.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "8.0.0",
|
||||
"Serilog": "2.12.0"
|
||||
}
|
||||
},
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -588,7 +588,7 @@
|
||||
"impostor.api": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "[1.0.0, )",
|
||||
"Impostor.Hazel.Abstractions": "[1.0.1, )",
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
|
||||
@@ -597,8 +597,8 @@
|
||||
"impostor.server": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Api": "[1.9.0-dev, )",
|
||||
"Impostor.Hazel": "[1.0.0, )",
|
||||
"Impostor.Api": "[1.10.7-dev, )",
|
||||
"Impostor.Hazel": "[1.0.1, )",
|
||||
"Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Hosting": "[8.0.0, )",
|
||||
"Microsoft.Extensions.ObjectPool": "[8.0.1, )",
|
||||
|
||||
@@ -13,19 +13,19 @@
|
||||
},
|
||||
"Impostor.Hazel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "1.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "7.0.1",
|
||||
"Impostor.Hazel.Abstractions": "1.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "8.0.0",
|
||||
"Serilog": "2.12.0"
|
||||
}
|
||||
},
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -97,8 +97,8 @@
|
||||
},
|
||||
"Microsoft.Extensions.ObjectPool": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.1",
|
||||
"contentHash": "dDFItid/TGJljAnqbUQ0PkAl6ShIG0htwRR8J0cG/5Il6lIZPfkIZMwrZTWiF23AiQcqt6suPaUy2ih6dsCB9Q=="
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "4pm+XgxSukskwjzDDfSjG4KNUIOdFF2VaqZZDtTzoyQMOVSnlV6ZM8a9aVu5dg9LVZTB//utzSc8fOi0b0Mb2Q=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
@@ -132,7 +132,7 @@
|
||||
"impostor.api": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "[1.0.0, )",
|
||||
"Impostor.Hazel.Abstractions": "[1.0.1, )",
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
|
||||
@@ -141,8 +141,8 @@
|
||||
"impostor.client": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Api": "[1.9.0-dev, )",
|
||||
"Impostor.Hazel": "[1.0.0, )"
|
||||
"Impostor.Api": "[1.10.7-dev, )",
|
||||
"Impostor.Hazel": "[1.0.1, )"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
|
||||
<PackageReference Include="Impostor.Hazel" Version="1.0.0" />
|
||||
<PackageReference Include="Impostor.Hazel" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -4,20 +4,20 @@
|
||||
"net8.0": {
|
||||
"Impostor.Hazel": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
|
||||
"requested": "[1.0.1, )",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "1.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "7.0.1",
|
||||
"Impostor.Hazel.Abstractions": "1.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "8.0.0",
|
||||
"Serilog": "2.12.0"
|
||||
}
|
||||
},
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -89,8 +89,8 @@
|
||||
},
|
||||
"Microsoft.Extensions.ObjectPool": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.1",
|
||||
"contentHash": "dDFItid/TGJljAnqbUQ0PkAl6ShIG0htwRR8J0cG/5Il6lIZPfkIZMwrZTWiF23AiQcqt6suPaUy2ih6dsCB9Q=="
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "4pm+XgxSukskwjzDDfSjG4KNUIOdFF2VaqZZDtTzoyQMOVSnlV6ZM8a9aVu5dg9LVZTB//utzSc8fOi0b0Mb2Q=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
@@ -124,7 +124,7 @@
|
||||
"impostor.api": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "[1.0.0, )",
|
||||
"Impostor.Hazel.Abstractions": "[1.0.1, )",
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"net8.0": {
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -102,7 +102,7 @@
|
||||
"impostor.api": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "[1.0.0, )",
|
||||
"Impostor.Hazel.Abstractions": "[1.0.1, )",
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"net8.0": {
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -102,7 +102,7 @@
|
||||
"impostor.api": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "[1.0.0, )",
|
||||
"Impostor.Hazel.Abstractions": "[1.0.1, )",
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Impostor.Server.Http;
|
||||
|
||||
[Route("/api/filters")]
|
||||
public sealed class FiltersController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Value = new[]
|
||||
{
|
||||
"Tags",
|
||||
|
||||
// "PlayerSpeed",
|
||||
// "Roles",
|
||||
// "KillCooldown",
|
||||
// "VotingTime",
|
||||
"NumImposters",
|
||||
|
||||
// "VisualTasks",
|
||||
// "AnonymousVotes",
|
||||
// "ConfirmEjects",
|
||||
// "DiscussionTime",
|
||||
// "EmergencyCooldown",
|
||||
// "NumEmergencyMeetings",
|
||||
// "NumCommonTasks",
|
||||
// "NumShortTasks",
|
||||
// "NumLongTasks",
|
||||
// "KillDistance",
|
||||
};
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult GetFilters([FromHeader] AuthenticationHeaderValue authorization)
|
||||
{
|
||||
if (authorization.Scheme != "Bearer" || authorization.Parameter == null)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
TokenController.Token? token = null;
|
||||
try
|
||||
{
|
||||
token = JsonSerializer.Deserialize<TokenController.Token>(Convert.FromBase64String(authorization.Parameter));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
filters = Value,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,16 @@ using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Web;
|
||||
using Impostor.Api.Config;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameFilters;
|
||||
using Impostor.Hazel.UPnP;
|
||||
using Impostor.Server.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Http;
|
||||
@@ -25,6 +29,7 @@ public sealed class GamesController : ControllerBase
|
||||
private readonly IGameManager _gameManager;
|
||||
private readonly ListingManager _listingManager;
|
||||
private readonly HostServer _hostServer;
|
||||
private readonly ILogger<GamesController> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GamesController"/> class.
|
||||
@@ -32,12 +37,13 @@ public sealed class GamesController : ControllerBase
|
||||
/// <param name="gameManager">GameManager containing a list of games.</param>
|
||||
/// <param name="listingManager">ListingManager responsible for filtering.</param>
|
||||
/// <param name="serverConfig">Impostor configuration section containing the public ip address of this server.</param>
|
||||
public GamesController(IGameManager gameManager, ListingManager listingManager, IOptions<ServerConfig> serverConfig)
|
||||
public GamesController(IGameManager gameManager, ListingManager listingManager, IOptions<ServerConfig> serverConfig, ILogger<GamesController> logger)
|
||||
{
|
||||
_gameManager = gameManager;
|
||||
_listingManager = listingManager;
|
||||
var config = serverConfig.Value;
|
||||
_hostServer = HostServer.From(IPAddress.Parse(config.ResolvePublicIp()), config.PublicPort);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -116,20 +122,51 @@ public sealed class GamesController : ControllerBase
|
||||
}
|
||||
|
||||
[HttpGet("filtered")]
|
||||
public IActionResult ShowFilteredLobbies()
|
||||
public IActionResult ShowFilteredLobbies([FromQuery] string filter)
|
||||
{
|
||||
// TODO: implement this stub
|
||||
var response = new
|
||||
if (string.IsNullOrEmpty(filter))
|
||||
{
|
||||
games = Array.Empty<GameListing>(),
|
||||
metadata = new
|
||||
{
|
||||
allGamesCount = _gameManager.Games.Count(),
|
||||
matchingGamesCount = 0,
|
||||
},
|
||||
};
|
||||
return BadRequest(new MatchmakerResponse(new MatchmakerError(DisconnectReason.ServerError, "filter query parameter not provided")));
|
||||
}
|
||||
|
||||
return Ok(response);
|
||||
try
|
||||
{
|
||||
var decodedFilter = HttpUtility.UrlDecode(filter);
|
||||
var filtersList = JsonSerializer.Deserialize<GameFiltersList>(decodedFilter);
|
||||
|
||||
// filterSets wont be null. It must at least have ChatFilter and LangFilter
|
||||
// Vanilla game only builds one filterSet and InnerSloth officials only handles first one (though you can send multiple filter sets. sloths only handle the first)
|
||||
if (filtersList == null || filtersList.FilterSets.Count != 1
|
||||
|| filtersList.FilterSets[0].Filters.Count < 2
|
||||
|| !filtersList.FilterSets[0].Filters.Any(x => x.OptionType == "languages")
|
||||
|| !filtersList.FilterSets[0].Filters.Any(x => x.OptionType == "chat"))
|
||||
{
|
||||
return BadRequest(new MatchmakerResponse(new MatchmakerError(DisconnectReason.ServerError, "Invalid filterSets")));
|
||||
}
|
||||
|
||||
var filteredGames = _listingManager.FindListingsV2(HttpContext, filtersList);
|
||||
var gameListings = filteredGames.Select(GameListing.From).ToList();
|
||||
|
||||
var response = new
|
||||
{
|
||||
games = gameListings,
|
||||
metadata = new
|
||||
{
|
||||
allGamesCount = _gameManager.Games.Count(),
|
||||
matchingGamesCount = gameListings.Count,
|
||||
},
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
return BadRequest(new MatchmakerResponse(new MatchmakerError(DisconnectReason.ServerError, "Unable to deserialize filter json" + ex)));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new MatchmakerResponse(new MatchmakerError(DisconnectReason.ServerError, "Unknown exception caught in filter" + ex)));
|
||||
}
|
||||
}
|
||||
|
||||
private static uint ConvertAddressToNumber(IPAddress address)
|
||||
@@ -177,13 +214,18 @@ public sealed class GamesController : ControllerBase
|
||||
private class MatchmakerError
|
||||
{
|
||||
[SetsRequiredMembers]
|
||||
public MatchmakerError(DisconnectReason reason)
|
||||
public MatchmakerError(DisconnectReason reason, string message = "")
|
||||
{
|
||||
Reason = reason;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
[JsonPropertyName("Reason")]
|
||||
public required DisconnectReason Reason { get; init; }
|
||||
|
||||
[JsonPropertyName("Message")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||
public required string Message { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
private class FindGameByCodeResponse
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Impostor.Api.Config;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Http;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameFilters;
|
||||
using Impostor.Api.Net.Manager;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -103,6 +105,122 @@ public sealed class ListingManager
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IGame> FindListingsV2(HttpContext ctx, GameFiltersList filtersList, int maxListings = 10)
|
||||
{
|
||||
if (filtersList == null || filtersList.FilterSets.Count == 0)
|
||||
{
|
||||
return Enumerable.Empty<IGame>();
|
||||
}
|
||||
|
||||
var result = new List<IGame>();
|
||||
var resultCount = 0;
|
||||
|
||||
// Apply custom filters from API
|
||||
var filters = _listingFilters.Select(f => f.GetFilter(ctx)).ToArray();
|
||||
|
||||
foreach (var game in _gameManager.Games)
|
||||
{
|
||||
if (!game.IsPublic || game.GameState != GameStates.NotStarted || game.PlayerCount >= game.Options.MaxPlayers)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!filters.All(filter => filter(game)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var matchesAnyFilterSet = false;
|
||||
foreach (var filterSet in filtersList.FilterSets)
|
||||
{
|
||||
// Not sure how to handle normal fools and seek fools
|
||||
if (game.Options.GameMode != filterSet.GameMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var matchesAllFilters = true;
|
||||
|
||||
// Hard coded to only check map, languages, impostornum, chatmode, tags
|
||||
foreach (var filter in filterSet.Filters)
|
||||
{
|
||||
switch (filter.OptionType)
|
||||
{
|
||||
case "map":
|
||||
if (filter.SubFilter is MapGameFilter mapFilter && ((1 << (int)game.Options.Map) & mapFilter.AcceptedValues) == 0)
|
||||
{
|
||||
matchesAllFilters = false;
|
||||
}
|
||||
|
||||
break;
|
||||
case "languages":
|
||||
if (filter.SubFilter is LanguageFilter langFilter && game.Options.Keywords != (GameKeywords)langFilter.AcceptedValues)
|
||||
{
|
||||
matchesAllFilters = false;
|
||||
}
|
||||
|
||||
break;
|
||||
case "int":
|
||||
if (filter.SubFilter is IntGameFilter intFilter)
|
||||
{
|
||||
switch (intFilter.OptionEnum)
|
||||
{
|
||||
case Int32OptionNames.NumImpostors:
|
||||
if (!intFilter.AcceptedValues.Contains(game.Options.NumImpostors))
|
||||
{
|
||||
matchesAllFilters = false;
|
||||
}
|
||||
|
||||
break;
|
||||
case Int32OptionNames.Tag:
|
||||
if (!intFilter.AcceptedValues.Contains((int)game.Options.Tag))
|
||||
{
|
||||
matchesAllFilters = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case "chat":
|
||||
if (filter.SubFilter is ChatModeGameFilter chatFilter && game.Host != null)
|
||||
{
|
||||
if (((byte)game.Host.Client.ChatMode & chatFilter.AcceptedValues) == 0)
|
||||
{
|
||||
matchesAllFilters = false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!matchesAllFilters)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchesAllFilters)
|
||||
{
|
||||
matchesAnyFilterSet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchesAnyFilterSet)
|
||||
{
|
||||
result.Add(game);
|
||||
if (++resultCount >= maxListings)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool IsGameDesired(IGame game, int map, int impostorCount, GameKeywords language)
|
||||
{
|
||||
if ((map & (1 << (int)game.Options.Map)) == 0)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
|
||||
<PackageReference Include="Impostor.Hazel" Version="1.0.0" />
|
||||
<PackageReference Include="Impostor.Hazel" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace Impostor.Server.Net
|
||||
CheatingHostMode.Never => true,
|
||||
_ => true,
|
||||
},
|
||||
CheatCategory.PacketSize => _antiCheatConfig.EnablePacketSizeChecks,
|
||||
CheatCategory.Other => true,
|
||||
_ => LogUnknownCategory(category),
|
||||
};
|
||||
@@ -267,10 +268,30 @@ namespace Impostor.Server.Net
|
||||
|
||||
case MessageFlags.PackedGameDataTo:
|
||||
{
|
||||
if (Player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var game = Player.Game;
|
||||
|
||||
// Innersloth Special: this message uses PackedInt32 instead of a normal int32
|
||||
var code = reader.ReadPackedInt32();
|
||||
|
||||
if (code != game.Code.Value)
|
||||
{
|
||||
_logger.LogWarning("gcm2 {0} {1}", code, game.Code.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
// We're limiting this to hosts right now. If you have a use case for this for
|
||||
// players to use this feature, we're open to changing this.
|
||||
if (!IsPacketAllowed(reader, true, flag))
|
||||
if (game.HostId != Id)
|
||||
{
|
||||
await ReportCheatAsync(
|
||||
new CheatContext(MessageFlags.FlagToString(flag)),
|
||||
CheatCategory.MustBeHost,
|
||||
"Client sent a PackedGameDataTo message");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -292,7 +313,7 @@ namespace Impostor.Server.Net
|
||||
return;
|
||||
}
|
||||
|
||||
if (packed.ReadInt32() != Player!.Game.Code)
|
||||
if (packed.ReadInt32() != game.Code.Value)
|
||||
{
|
||||
_logger.LogWarning("PackedGameDataTo contained GameDataTo for the wrong game.");
|
||||
return;
|
||||
@@ -445,7 +466,8 @@ namespace Impostor.Server.Net
|
||||
var game = Player.Game;
|
||||
|
||||
// GameCode must match code of the current game assigned to the player.
|
||||
if (message.ReadInt32() != game.Code)
|
||||
var code = message.ReadInt32();
|
||||
if (code != game.Code.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Config;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Hazel;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Net.Hazel
|
||||
{
|
||||
internal class HazelConnection : IHazelConnection
|
||||
{
|
||||
private readonly ILogger<HazelConnection> _logger;
|
||||
private readonly AntiCheatConfig _antiCheatConfig;
|
||||
|
||||
public HazelConnection(Connection innerConnection, ILogger<HazelConnection> logger)
|
||||
public HazelConnection(Connection innerConnection, ILogger<HazelConnection> logger, IOptions<AntiCheatConfig> antiCheatOptions)
|
||||
{
|
||||
_logger = logger;
|
||||
_antiCheatConfig = antiCheatOptions.Value;
|
||||
InnerConnection = innerConnection;
|
||||
innerConnection.DataReceived = ConnectionOnDataReceived;
|
||||
innerConnection.Disconnected = ConnectionOnDisconnected;
|
||||
@@ -58,6 +63,19 @@ namespace Impostor.Server.Net.Hazel
|
||||
return;
|
||||
}
|
||||
|
||||
// Check raw message size against the configured limit.
|
||||
// Innersloth requires full packet ≤ 1200 bytes (1168 bytes payload after 32 bytes IP+UDP headers).
|
||||
if (e.Message.Length > _antiCheatConfig.PacketSizeLimit)
|
||||
{
|
||||
if (await Client.ReportCheatAsync(
|
||||
new CheatContext("RootMessage"),
|
||||
CheatCategory.PacketSize,
|
||||
$"Received a message that is too large, length: {e.Message.Length}"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (e.Message.Position >= e.Message.Length)
|
||||
|
||||
@@ -42,6 +42,8 @@ internal class CompatibilityManager : ICompatibilityManager
|
||||
new GameVersion(2026, 2, 2), // 17.2.2 (2026-03-17, build 6768)
|
||||
new GameVersion(2026, 1, 12), // 17.3 (2026-03-31, build 6803)
|
||||
new GameVersion(2026, 3, 17), // 17.3.1 (2026-04-08, build 6841, mobile only)
|
||||
new GameVersion(2026, 3, 18), // 17.4 (2026-06-05, build 7044, pc only)
|
||||
new GameVersion(2026, 4, 23), // 17.4 (2026-06-05, build 7045, mobile only)
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Config;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Net.Messages.C2S;
|
||||
using Impostor.Hazel;
|
||||
@@ -11,6 +12,7 @@ using Impostor.Server.Net.Hazel;
|
||||
using Impostor.Server.Net.Manager;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Net
|
||||
{
|
||||
@@ -20,18 +22,21 @@ namespace Impostor.Server.Net
|
||||
private readonly ClientManager _clientManager;
|
||||
private readonly ObjectPool<MessageReader> _readerPool;
|
||||
private readonly ILogger<HazelConnection> _connectionLogger;
|
||||
private readonly IOptions<AntiCheatConfig> _antiCheatOptions;
|
||||
private UdpConnectionListener? _connection;
|
||||
|
||||
public Matchmaker(
|
||||
IEventManager eventManager,
|
||||
ClientManager clientManager,
|
||||
ObjectPool<MessageReader> readerPool,
|
||||
ILogger<HazelConnection> connectionLogger)
|
||||
ILogger<HazelConnection> connectionLogger,
|
||||
IOptions<AntiCheatConfig> antiCheatOptions)
|
||||
{
|
||||
_eventManager = eventManager;
|
||||
_clientManager = clientManager;
|
||||
_readerPool = readerPool;
|
||||
_connectionLogger = connectionLogger;
|
||||
_antiCheatOptions = antiCheatOptions;
|
||||
}
|
||||
|
||||
public async ValueTask StartAsync(IPEndPoint ipEndPoint)
|
||||
@@ -64,7 +69,7 @@ namespace Impostor.Server.Net
|
||||
// Handshake.
|
||||
HandshakeC2S.Deserialize(e.HandshakeData, out var clientVersion, out var name, out var language, out var chatMode, out var platformSpecificData);
|
||||
|
||||
var connection = new HazelConnection(e.Connection, _connectionLogger);
|
||||
var connection = new HazelConnection(e.Connection, _connectionLogger, _antiCheatOptions);
|
||||
|
||||
await _eventManager.CallAsync(new ClientConnectionEvent(connection, e.HandshakeData));
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
"EnableOwnershipChecks": true,
|
||||
"EnableRoleChecks": true,
|
||||
"EnableTargetChecks": true,
|
||||
"ForbidProtocolExtensions": true
|
||||
"ForbidProtocolExtensions": true,
|
||||
"EnablePacketSizeChecks": true,
|
||||
"PacketSizeLimit": 1203
|
||||
},
|
||||
"Timeout": {
|
||||
"SpawnTimeout": 2500,
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"net8.0": {
|
||||
"Impostor.Hazel": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
|
||||
"requested": "[1.0.1, )",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "1.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "7.0.1",
|
||||
"Impostor.Hazel.Abstractions": "1.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "8.0.0",
|
||||
"Serilog": "2.12.0"
|
||||
}
|
||||
},
|
||||
@@ -97,8 +97,8 @@
|
||||
},
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -414,7 +414,7 @@
|
||||
"impostor.api": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "[1.0.0, )",
|
||||
"Impostor.Hazel.Abstractions": "[1.0.1, )",
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
|
||||
|
||||
@@ -46,19 +46,19 @@
|
||||
},
|
||||
"Impostor.Hazel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "1.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "7.0.1",
|
||||
"Impostor.Hazel.Abstractions": "1.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "8.0.0",
|
||||
"Serilog": "2.12.0"
|
||||
}
|
||||
},
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -529,7 +529,7 @@
|
||||
"impostor.api": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "[1.0.0, )",
|
||||
"Impostor.Hazel.Abstractions": "[1.0.1, )",
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
|
||||
@@ -538,8 +538,8 @@
|
||||
"impostor.server": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Api": "[1.9.0-dev, )",
|
||||
"Impostor.Hazel": "[1.0.0, )",
|
||||
"Impostor.Api": "[1.10.7-dev, )",
|
||||
"Impostor.Hazel": "[1.0.1, )",
|
||||
"Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Hosting": "[8.0.0, )",
|
||||
"Microsoft.Extensions.ObjectPool": "[8.0.1, )",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Impostor.Hazel" Version="1.0.0" />
|
||||
<PackageReference Include="Impostor.Hazel" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -4,13 +4,13 @@
|
||||
"net8.0": {
|
||||
"Impostor.Hazel": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
|
||||
"requested": "[1.0.1, )",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "1.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "7.0.1",
|
||||
"Impostor.Hazel.Abstractions": "1.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "8.0.0",
|
||||
"Serilog": "2.12.0"
|
||||
}
|
||||
},
|
||||
@@ -31,8 +31,8 @@
|
||||
},
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -41,8 +41,8 @@
|
||||
},
|
||||
"Microsoft.Extensions.ObjectPool": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.1",
|
||||
"contentHash": "dDFItid/TGJljAnqbUQ0PkAl6ShIG0htwRR8J0cG/5Il6lIZPfkIZMwrZTWiF23AiQcqt6suPaUy2ih6dsCB9Q=="
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "4pm+XgxSukskwjzDDfSjG4KNUIOdFF2VaqZZDtTzoyQMOVSnlV6ZM8a9aVu5dg9LVZTB//utzSc8fOi0b0Mb2Q=="
|
||||
},
|
||||
"Serilog": {
|
||||
"type": "Transitive",
|
||||
|
||||
@@ -13,19 +13,19 @@
|
||||
},
|
||||
"Impostor.Hazel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "1.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "7.0.1",
|
||||
"Impostor.Hazel.Abstractions": "1.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.ObjectPool": "8.0.0",
|
||||
"Serilog": "2.12.0"
|
||||
}
|
||||
},
|
||||
"Impostor.Hazel.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
@@ -402,7 +402,7 @@
|
||||
"impostor.api": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Hazel.Abstractions": "[1.0.0, )",
|
||||
"Impostor.Hazel.Abstractions": "[1.0.1, )",
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
|
||||
@@ -411,8 +411,8 @@
|
||||
"impostor.server": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Impostor.Api": "[1.9.0-dev, )",
|
||||
"Impostor.Hazel": "[1.0.0, )",
|
||||
"Impostor.Api": "[1.10.7-dev, )",
|
||||
"Impostor.Hazel": "[1.0.1, )",
|
||||
"Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )",
|
||||
"Microsoft.Extensions.Hosting": "[8.0.0, )",
|
||||
"Microsoft.Extensions.ObjectPool": "[8.0.1, )",
|
||||
|
||||
Reference in new issue
Block a user