mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-19 15:51:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
703915f06f | ||
|
|
bf52365a1a | ||
|
|
e97c28574b | ||
|
|
8ee5796d89 | ||
|
|
7b77165421 | ||
|
|
196c01d5e3 | ||
|
|
fd40e469c5 | ||
|
|
27b2fc5876 | ||
|
|
8bbe6be4b0 | ||
|
|
4f372ecabb | ||
|
|
b09c40b7e1 | ||
|
|
5ee0fdb61f | ||
|
|
44ebc0c6b0 | ||
|
|
681126dfb5 | ||
|
|
9f627b7b01 | ||
|
|
510ed07389 | ||
|
|
8f41030faf | ||
|
|
f23dd8aa25 | ||
|
|
857e04dbc3 | ||
|
|
b78f86f0f3 | ||
|
|
db9c6ab66c | ||
|
|
a161aa7045 | ||
|
|
caab877c80 | ||
|
|
202b955db1 | ||
|
|
1cd298608a | ||
|
|
f0689ac2e2 | ||
|
|
2cad4ffa14 | ||
|
|
e9437e781b |
No files matched your search
@@ -36,10 +36,14 @@ Impostor has an Anticheat that makes it possible to kick cheaters from games aut
|
||||
| **EnableMustBeHostChecks** | `true` | Enables checks that check if players are the host before they can do actions that require them to be host of the game. This includes starting the game and spawning objects. |
|
||||
| **EnableColorLimitChecks** | `true` | Enables checks that checks if players request colors that are already in use. |
|
||||
| **EnableNameLimitChecks** | `true` | Enables checks that checks if player names have a length that is possible to set using the user interface. |
|
||||
| **EnableItemLimitChecks** | `true` | Enables checks for item counts that cannot be exceeded in the official clients. |
|
||||
| **EnableOwnershipChecks** | `true` | Enables checks that check if players are allowed to perform a certain action on themself or another player. |
|
||||
| **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. |
|
||||
| **EnableIdentifierChecks** | `true` | Enables checks that verify if the friendcode and puid a host claims for a player in their PlayerInfo match the ones known for that player. |
|
||||
| **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.9</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);
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETStandard,Version=v2.0/win-x64": {}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Impostor.Api;
|
||||
|
||||
// NOTE: when adding items to this enum, add them to the bottom for API stability
|
||||
public enum CheatCategory
|
||||
{
|
||||
/// <summary>A packet used a part of the network protocol that is unknown to Impostor, like a custom RPC.</summary>
|
||||
@@ -32,6 +33,15 @@ 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,
|
||||
|
||||
/// <summary>A packet was sent with more items than possible in the game.</summary>
|
||||
ItemLimits,
|
||||
|
||||
/// <summary>A packet contained an identifier (friendcode or puid) that does not match the one known for that player.</summary>
|
||||
Identifiers,
|
||||
}
|
||||
@@ -22,12 +22,20 @@ namespace Impostor.Api.Config
|
||||
|
||||
public bool EnableNameLimitChecks { get; set; } = true;
|
||||
|
||||
public bool EnableItemLimitChecks { get; set; } = true;
|
||||
|
||||
public bool EnableOwnershipChecks { get; set; } = true;
|
||||
|
||||
public bool EnableRoleChecks { get; set; } = true;
|
||||
|
||||
public bool EnableTargetChecks { get; set; } = true;
|
||||
|
||||
public bool EnableIdentifierChecks { get; set; } = true;
|
||||
|
||||
public bool ForbidProtocolExtensions { get; set; } = true;
|
||||
|
||||
public bool EnablePacketSizeChecks { get; set; } = true;
|
||||
|
||||
public int PacketSizeLimit { get; set; } = 1203;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Api.Events.Meeting
|
||||
{
|
||||
@@ -13,5 +13,15 @@ namespace Impostor.Api.Events.Meeting
|
||||
/// Gets a value indicating whether meeting result is a tie.
|
||||
/// </summary>
|
||||
bool IsTie { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a Judge overruled the vote result.
|
||||
/// </summary>
|
||||
bool WasOverruled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nonce of the winning Judge overrule, or 0 if the meeting wasn't overruled.
|
||||
/// </summary>
|
||||
ushort OverrideId { get; }
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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" />
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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,32 @@
|
||||
{
|
||||
"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,
|
||||
"ViperDissolveTime": 1601,
|
||||
"DetectiveSuspectLimit": 1602,
|
||||
"JudgeTaskRequirementPercentage": 1620,
|
||||
"SpiritGuideCooldownSeconds": 1640
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -11,5 +11,7 @@
|
||||
"Phantom": 9,
|
||||
"Tracker": 10,
|
||||
"Detective": 12,
|
||||
"Viper": 18
|
||||
"Viper": 18,
|
||||
"Judge": 19,
|
||||
"SpiritGuide": 21
|
||||
}
|
||||
@@ -59,5 +59,7 @@
|
||||
"CheckVanish": 62,
|
||||
"StartVanish": 63,
|
||||
"CheckAppear": 64,
|
||||
"StartAppear": 65
|
||||
"StartAppear": 65,
|
||||
"QueueOverruleVotes": 66,
|
||||
"SpiritGuideMessage": 67
|
||||
}
|
||||
@@ -626,6 +626,14 @@
|
||||
"SanctionSelfHarmPromotion": 1720,
|
||||
"SanctionRepeatOffender10": 1721,
|
||||
"SanctionUnknown": 1722,
|
||||
"ReplicatorAbility": 1800,
|
||||
"ReplicatorRole": 1801,
|
||||
"ReplicatorBlurb": 1802,
|
||||
"ReplicatorBlurbMed": 1803,
|
||||
"ReplicatorBlurbLong": 1804,
|
||||
"ReplicatorCooldown": 1805,
|
||||
"ReplicatorDuration": 1806,
|
||||
"ReplicatorQuantity": 1807,
|
||||
"ScreenShakeOption": 1900,
|
||||
"FeaturedItems": 1901,
|
||||
"FeaturedBundles": 1902,
|
||||
@@ -1576,6 +1584,14 @@
|
||||
"XboxCommsCreateGameError": 3702,
|
||||
"XboxCommsSearchError": 3703,
|
||||
"XboxCommsJoinError": 3704,
|
||||
"WatchAdBody": 3800,
|
||||
"WatchAdTitle": 3801,
|
||||
"WatchAd": 3802,
|
||||
"RewardAdTitle": 3803,
|
||||
"RewardAdBody1": 3804,
|
||||
"RewardAdBody2": 3805,
|
||||
"WatchAdErrorBody": 3806,
|
||||
"WatchAdErrorTitle": 3807,
|
||||
"DetectiveRole": 4000,
|
||||
"DetectiveAbility": 4001,
|
||||
"DetectiveAbilityNotes": 4002,
|
||||
@@ -1623,5 +1639,50 @@
|
||||
"DetectiveNotesPrepositionMaybe": 4079,
|
||||
"DetectiveNotesTitlePostIt": 4080,
|
||||
"DetectiveNotesPostIt": 4081,
|
||||
"DetectiveNotesSuspectNumber": 4082
|
||||
"DetectiveNotesSuspectNumber": 4082,
|
||||
"AprilFoolsMode": 4083,
|
||||
"MatchInfoGuideTitle": 4084,
|
||||
"MatchInfoGuidePlayersLabel": 4085,
|
||||
"MatchInfoGuideNoRolesMessage": 4086,
|
||||
"JudgeRole": 4090,
|
||||
"JudgeAbility": 4091,
|
||||
"JudgeBlurb": 4092,
|
||||
"JudgeBlurbMed": 4093,
|
||||
"JudgeBlurbLong": 4094,
|
||||
"JudgeTaskRequirement": 4095,
|
||||
"RolesHelp_Judge_01": 4096,
|
||||
"RolesHelp_Judge_02": 4097,
|
||||
"JudgeAbilityUnlocked": 4098,
|
||||
"JudgeAbilityProgress": 4099,
|
||||
"JudgeCourtAdjourned": 4100,
|
||||
"JudgeHasSpoken": 4101,
|
||||
"StatsJudgeSuccessfulOverrules": 4102,
|
||||
"StatsJudgeFailedOverrules": 4103,
|
||||
"QCJudges": 4104,
|
||||
"QCJudgeOverrule": 4105,
|
||||
"QCJudgePlease": 4106,
|
||||
"QCJudgeWillOverrule": 4107,
|
||||
"JudgeBeatYouToIt": 4108,
|
||||
"SpiritGuideRole": 4120,
|
||||
"SpiritGuideAbility": 4121,
|
||||
"SpiritGuideBlurb": 4122,
|
||||
"SpiritGuideBlurbMed": 4123,
|
||||
"SpiritGuideBlurbLong": 4124,
|
||||
"SpiritGuideCooldown": 4125,
|
||||
"SpiritGuideAbilityCancel": 4126,
|
||||
"SpiritGuideAbilitySend": 4127,
|
||||
"SpiritGuideAbilityRefresh": 4128,
|
||||
"StatsSpiritGuideSentMessage": 4129,
|
||||
"RolesHelp_SpiritGuide_01": 4130,
|
||||
"RolesHelp_SpiritGuide_02": 4131,
|
||||
"QCInfluencerMessage": 4132,
|
||||
"QCBecauseInfluencer": 4133,
|
||||
"QCAnyInfluencerMessage": 4134,
|
||||
"QCInfluencers": 4135,
|
||||
"RedeemCodeLabel": 4200,
|
||||
"CodeLengthLabel": 4201,
|
||||
"CodeErrorLabel": 4202,
|
||||
"CodeAlreadyRedeemedLabel": 4203,
|
||||
"CodeRedeemPopupLabel": 4204,
|
||||
"HorseModeToggle": 4300
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Invalid": 0,
|
||||
"Keywords": 1
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dumpostorVersion": "1.0.1-dev",
|
||||
"gameVersion": "2025.10.14",
|
||||
"dumpostorVersion": "1.0.2-dev",
|
||||
"gameVersion": "2026.9.29",
|
||||
"platformType": "StandaloneItch"
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"hatIds": [
|
||||
"hat_pk04_beanie",
|
||||
"hat_pk05_Cheese",
|
||||
"hat_pk05_Cherry",
|
||||
"hat_Chocolate",
|
||||
"hat_Zipper",
|
||||
"hat_pk02_Crown",
|
||||
"hat_babybean",
|
||||
"hat_Sorry",
|
||||
"hat_mareLwyd",
|
||||
"hat_fishingHat",
|
||||
"hat_schnapp",
|
||||
"hat_pk03_Traffic",
|
||||
"hat_Pot",
|
||||
"hat_AbominalHat",
|
||||
"hat_tombstone",
|
||||
"hat_pk04_MiniCrewmate",
|
||||
"hat_pk05_Wizardhat",
|
||||
"hat_pk05_Ellryhat",
|
||||
"hat_pk05_EllieToppat"
|
||||
],
|
||||
"visorIds": [
|
||||
"visor_pk01_AngeryVisor",
|
||||
"visor_pk01_DumStickerVisor",
|
||||
"visor_vr_Vr-Black",
|
||||
"visor_pk01_PaperMaskVisor",
|
||||
"visor_wash"
|
||||
],
|
||||
"petIds": [
|
||||
"pet_YuleGoatPet",
|
||||
"pet_HamPet",
|
||||
"pet_Lava",
|
||||
"pet_Snow",
|
||||
"pet_Bush"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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,87 @@
|
||||
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");
|
||||
case "mod":
|
||||
return JsonSerializer.Deserialize<ModFilter>(filterString)
|
||||
?? throw new InvalidOperationException("Deserialization returned null for ModFilter");
|
||||
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,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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public class HideNSeekGameOptions : IGameOptions
|
||||
{
|
||||
public const int LatestVersion = 10;
|
||||
public const int LatestVersion = 12;
|
||||
|
||||
public HideNSeekGameOptions(byte version = LatestVersion)
|
||||
{
|
||||
@@ -107,6 +107,11 @@ public class HideNSeekGameOptions : IGameOptions
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
if (Version > LatestVersion)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
|
||||
}
|
||||
|
||||
if (Version >= 8)
|
||||
{
|
||||
SpecialMode = (SpecialGameModes)reader.ReadByte();
|
||||
@@ -142,11 +147,6 @@ public class HideNSeekGameOptions : IGameOptions
|
||||
{
|
||||
Tag = (GameTags)reader.ReadByte();
|
||||
}
|
||||
|
||||
if (Version > LatestVersion)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
|
||||
@@ -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,7 +4,7 @@ namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public class NormalGameOptions : IGameOptions
|
||||
{
|
||||
public const int LatestVersion = 10;
|
||||
public const int LatestVersion = 12;
|
||||
|
||||
public NormalGameOptions(byte version = LatestVersion)
|
||||
{
|
||||
@@ -144,6 +144,11 @@ public class NormalGameOptions : IGameOptions
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
if (Version > LatestVersion)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
|
||||
}
|
||||
|
||||
if (Version >= 8)
|
||||
{
|
||||
SpecialMode = (SpecialGameModes)reader.ReadByte();
|
||||
@@ -184,11 +189,6 @@ public class NormalGameOptions : IGameOptions
|
||||
}
|
||||
|
||||
RoleOptions.Deserialize(reader);
|
||||
|
||||
if (Version > LatestVersion)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class JudgeRoleOptions : IRoleOptions
|
||||
{
|
||||
public JudgeRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.Judge;
|
||||
|
||||
public float JudgeTaskRequirementPercentage { get; set; } = 50f;
|
||||
|
||||
public static JudgeRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new JudgeRoleOptions(version);
|
||||
|
||||
options.JudgeTaskRequirementPercentage = (int)reader.ReadByte();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write((byte)JudgeTaskRequirementPercentage);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ public class RoleOptionsCollection
|
||||
RoleTypes.Tracker => TrackerRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Detective => DetectiveRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Viper => ViperRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Judge => JudgeRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.SpiritGuide => SpiritGuideRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(roleType), roleType, null),
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class SpiritGuideRoleOptions : IRoleOptions
|
||||
{
|
||||
public SpiritGuideRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.SpiritGuide;
|
||||
|
||||
public float SpiritGuideCooldownSeconds { get; set; } = 20f;
|
||||
|
||||
public static SpiritGuideRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new SpiritGuideRoleOptions(version);
|
||||
|
||||
options.SpiritGuideCooldownSeconds = reader.ReadByte();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write((byte)SpiritGuideCooldownSeconds);
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,22 @@ namespace Impostor.Api.Net
|
||||
/// </remarks>
|
||||
IDictionary<object, object> Items { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the friendcode of the player.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Impostor's HTTP server does not resolve friendcodes, so this value will remain null unless a plugin has written a value in this field.
|
||||
/// </remarks>
|
||||
string? FriendCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the puid of the player.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Impostor's HTTP server does not resolve puids, so this value will remain null unless a plugin has written a value in this field.
|
||||
/// </remarks>
|
||||
string? Puid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current game data of the <see cref="IClient" />.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Net.Inner;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Impostor.Api.Events.Player;
|
||||
|
||||
@@ -6,6 +6,24 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
{
|
||||
public interface IInnerMeetingHud : IInnerNetObject
|
||||
{
|
||||
public interface IJudgeOverrule
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the player id of the Judge that queued this overrule.
|
||||
/// </summary>
|
||||
byte JudgePlayerId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player id of the player the Judge wants to overrule.
|
||||
/// </summary>
|
||||
byte OverruledPlayerId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the overrule nonce used to match this overrule to the voting result.
|
||||
/// </summary>
|
||||
ushort OverruleNonce { get; }
|
||||
}
|
||||
|
||||
public interface IPlayerVoteArea
|
||||
{
|
||||
/// <summary>
|
||||
@@ -55,5 +73,10 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
/// Gets the player that started the meeting.
|
||||
/// </summary>
|
||||
IInnerPlayerInfo? Reporter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Judge overrules queued in this meeting.
|
||||
/// </summary>
|
||||
IReadOnlyCollection<IJudgeOverrule> JudgeOverrules { get; }
|
||||
}
|
||||
}
|
||||
@@ -43,5 +43,21 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
DateTimeOffset LastMurder { get; }
|
||||
|
||||
uint PlayerLevel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the friendcode of the player.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Impostor's HTTP server does not resolve friendcodes, so this value will remain null unless a plugin has written a value on the player's client.
|
||||
/// </remarks>
|
||||
string? FriendCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the puid of the player.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Impostor's HTTP server does not resolve puids, so this value will remain null unless a plugin has written a value on the player's client.
|
||||
/// </remarks>
|
||||
string? Puid { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.C2S
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.C2S
|
||||
{
|
||||
/// <summary>
|
||||
/// Special serialization rules for AU MCI HostGame messages.
|
||||
/// <see href="https://github.com/Innersloth-LLC/AmongUsModdingInformation?tab=readme-ov-file#all-client-mod-client-identification"/>
|
||||
/// for more information.
|
||||
/// </summary>
|
||||
public static class Message25HostModdedGameC2S
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, IGameOptions gameOptions, CrossplayFlags crossplayFlags, GameFilterOptions gameFilterOptions)
|
||||
{
|
||||
Message00HostGameC2S.Serialize(writer, gameOptions, crossplayFlags, gameFilterOptions);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out IGameOptions gameOptions, out CrossplayFlags crossplayFlags, out GameFilterOptions gameFilterOptions, out Guid modGuid)
|
||||
{
|
||||
Message00HostGameC2S.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 QueryLobbyInfo = 23;
|
||||
public const byte EndGameHostMigration = 24;
|
||||
public const byte HostModdedGame = 25;
|
||||
public const byte PackedGameDataTo = 26;
|
||||
|
||||
private static readonly Dictionary<byte, string> FlagCache;
|
||||
|
||||
@@ -2,6 +2,8 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc23VotingComplete
|
||||
{
|
||||
// Pre 18.0 version
|
||||
// TODO clean up when 17.4 and below are unsupported
|
||||
public static void Serialize(IMessageWriter writer, byte[] states, byte playerId, bool tie)
|
||||
{
|
||||
writer.WriteBytesAndSize(states);
|
||||
@@ -9,6 +11,15 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
writer.Write(tie);
|
||||
}
|
||||
|
||||
public static void Serialize(IMessageWriter writer, byte[] states, byte playerId, bool tie, bool wasOverruled, ushort overrideId)
|
||||
{
|
||||
writer.WriteBytesAndSize(states);
|
||||
writer.Write(playerId);
|
||||
writer.Write(tie);
|
||||
writer.Write(wasOverruled);
|
||||
writer.Write(overrideId);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out IMessageReader[] states, out byte playerId, out bool tie)
|
||||
{
|
||||
var length = reader.ReadPackedInt32();
|
||||
@@ -21,5 +32,21 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
playerId = reader.ReadByte();
|
||||
tie = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out IMessageReader[] states, out byte playerId, out bool tie, out bool wasOverruled, out ushort overrideId)
|
||||
{
|
||||
var length = reader.ReadPackedInt32();
|
||||
states = new IMessageReader[length];
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
states[i] = reader.ReadMessage();
|
||||
}
|
||||
|
||||
playerId = reader.ReadByte();
|
||||
tie = reader.ReadBoolean();
|
||||
|
||||
wasOverruled = reader.ReadBoolean();
|
||||
overrideId = reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc66QueueOverruleVotes
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, byte judgePlayerId, byte targetPlayerId, ushort overruleNonce)
|
||||
{
|
||||
writer.Write(judgePlayerId);
|
||||
writer.Write(targetPlayerId);
|
||||
writer.Write(overruleNonce);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out byte judgePlayerId, out byte targetPlayerId, out ushort overruleNonce)
|
||||
{
|
||||
judgePlayerId = reader.ReadByte();
|
||||
targetPlayerId = reader.ReadByte();
|
||||
overruleNonce = reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc67SpiritGuideMessage
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, byte[] images)
|
||||
{
|
||||
writer.WriteBytesAndSize(images);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out ReadOnlyMemory<byte> images)
|
||||
{
|
||||
images = reader.ReadBytesAndSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,8 @@ namespace Impostor.Api.Net.Messages.S2C
|
||||
writer.Write(player.Client.Name);
|
||||
player.Client.PlatformSpecificData.Serialize(writer);
|
||||
writer.WritePacked(player.Character?.PlayerInfo?.PlayerLevel ?? 1);
|
||||
|
||||
// ProductUserId and FriendCode are not yet known, so set them to an empty string
|
||||
writer.Write(string.Empty);
|
||||
writer.Write(string.Empty);
|
||||
writer.Write(player.Client.FriendCode ?? string.Empty);
|
||||
writer.Write(player.Client.Puid ?? string.Empty);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,8 @@ namespace Impostor.Api.Net.Messages.S2C
|
||||
writer.Write(ply.Client.Name);
|
||||
ply.Client.PlatformSpecificData.Serialize(writer);
|
||||
writer.WritePacked(ply.Character?.PlayerInfo?.PlayerLevel ?? 1);
|
||||
|
||||
// ProductUserId and FriendCode are not yet known, so set them to an empty string
|
||||
writer.Write(string.Empty);
|
||||
writer.Write(string.Empty);
|
||||
writer.Write(ply.Client.FriendCode ?? string.Empty);
|
||||
writer.Write(ply.Client.Puid ?? string.Empty);
|
||||
}
|
||||
|
||||
writer.EndMessage();
|
||||
|
||||
@@ -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",
|
||||
@@ -40,6 +40,12 @@
|
||||
"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, )",
|
||||
@@ -117,6 +123,7 @@
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"net8.0/win-x64": {}
|
||||
}
|
||||
}
|
||||
@@ -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, )"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Impostor.Api.Events.Meeting;
|
||||
using Impostor.Api.Events.Meeting;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
@@ -6,12 +6,14 @@ namespace Impostor.Server.Events.Meeting
|
||||
{
|
||||
public class MeetingEndedEvent : IMeetingEndedEvent
|
||||
{
|
||||
public MeetingEndedEvent(IGame game, IInnerMeetingHud meetingHud, IInnerPlayerControl? exiled, bool isTie)
|
||||
public MeetingEndedEvent(IGame game, IInnerMeetingHud meetingHud, IInnerPlayerControl? exiled, bool isTie, bool wasOverruled = false, ushort overrideId = 0)
|
||||
{
|
||||
Game = game;
|
||||
MeetingHud = meetingHud;
|
||||
Exiled = exiled;
|
||||
IsTie = isTie;
|
||||
WasOverruled = wasOverruled;
|
||||
OverrideId = overrideId;
|
||||
}
|
||||
|
||||
public IGame Game { get; }
|
||||
@@ -21,5 +23,9 @@ namespace Impostor.Server.Events.Meeting
|
||||
public IInnerPlayerControl? Exiled { get; }
|
||||
|
||||
public bool IsTie { get; }
|
||||
|
||||
public bool WasOverruled { get; }
|
||||
|
||||
public ushort OverrideId { get; }
|
||||
}
|
||||
}
|
||||
@@ -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,15 @@ 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.Server.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Http;
|
||||
@@ -25,6 +28,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 +36,14 @@ 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)
|
||||
/// <param name="logger">DI injected logger.</param>
|
||||
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
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -59,6 +60,11 @@ public sealed class ListingManager
|
||||
continue;
|
||||
}
|
||||
|
||||
if (game.ModGuid != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_compatibilityConfig.AllowVersionMixing &&
|
||||
game.Host != null &&
|
||||
_compatibilityManager.CanJoinGame(game.Host.Client.GameVersion, gameVersion) != GameJoinError.None)
|
||||
@@ -103,6 +109,134 @@ 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;
|
||||
}
|
||||
|
||||
if (game.ModGuid != null && !filterSet.Filters.Any(f => f.OptionType == "mod"))
|
||||
{
|
||||
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;
|
||||
case "mod":
|
||||
if (filter.SubFilter is ModFilter modFilter && game.ModGuid != modFilter.AcceptedValues)
|
||||
{
|
||||
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>
|
||||
|
||||
@@ -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;
|
||||
@@ -42,6 +43,11 @@ namespace Impostor.Server.Net
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Player != null && Player.Game.ModGuid != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Player != null && Player.IsHost)
|
||||
{
|
||||
var isHostCheatingAllowed = _antiCheatConfig.AllowCheatingHosts switch {
|
||||
@@ -74,12 +80,15 @@ namespace Impostor.Server.Net
|
||||
CheatCategory.Ownership => _antiCheatConfig.EnableOwnershipChecks,
|
||||
CheatCategory.Role => _antiCheatConfig.EnableRoleChecks,
|
||||
CheatCategory.Target => _antiCheatConfig.EnableTargetChecks,
|
||||
CheatCategory.Identifiers => _antiCheatConfig.EnableIdentifierChecks,
|
||||
CheatCategory.HostOnlyExtension => _antiCheatConfig.AllowHostOnlyExtensions switch {
|
||||
CheatingHostMode.Always => false,
|
||||
CheatingHostMode.IfRequested => !GameVersion.HasDisableServerAuthorityFlag,
|
||||
CheatingHostMode.Never => true,
|
||||
_ => true,
|
||||
},
|
||||
CheatCategory.PacketSize => _antiCheatConfig.EnablePacketSizeChecks,
|
||||
CheatCategory.ItemLimits => _antiCheatConfig.EnableItemLimitChecks,
|
||||
CheatCategory.Other => true,
|
||||
_ => LogUnknownCategory(category),
|
||||
};
|
||||
@@ -123,12 +132,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)
|
||||
{
|
||||
Message25HostModdedGameC2S.Deserialize(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)
|
||||
{
|
||||
@@ -136,6 +158,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))
|
||||
{
|
||||
@@ -267,10 +294,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 +339,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 +492,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;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ namespace Impostor.Server.Net
|
||||
|
||||
public ClientPlayer? Player { get; set; }
|
||||
|
||||
public string? FriendCode { get; set; }
|
||||
|
||||
public string? Puid { get; set; }
|
||||
|
||||
public ColorType? PreviousColor { get; set; } = null;
|
||||
|
||||
IClientPlayer? IClient.Player => Player;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
@@ -9,5 +9,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
IReadOnlyCollection<IInnerMeetingHud.IPlayerVoteArea> IInnerMeetingHud.PlayerStates => Array.AsReadOnly(_playerStates);
|
||||
|
||||
IInnerPlayerInfo? IInnerMeetingHud.Reporter => Reporter;
|
||||
|
||||
IReadOnlyCollection<IInnerMeetingHud.IJudgeOverrule> IInnerMeetingHud.JudgeOverrules => JudgeOverrules;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
internal partial class InnerMeetingHud
|
||||
{
|
||||
public sealed class JudgeOverrule : IInnerMeetingHud.IJudgeOverrule
|
||||
{
|
||||
public JudgeOverrule(byte judgePlayerId, byte overruledPlayerId, ushort overruleNonce)
|
||||
{
|
||||
JudgePlayerId = judgePlayerId;
|
||||
OverruledPlayerId = overruledPlayerId;
|
||||
OverruleNonce = overruleNonce;
|
||||
}
|
||||
|
||||
public byte JudgePlayerId { get; set; }
|
||||
|
||||
public byte OverruledPlayerId { get; set; }
|
||||
|
||||
public ushort OverruleNonce { get; set; }
|
||||
|
||||
public static JudgeOverrule Deserialize(IMessageReader reader)
|
||||
{
|
||||
var inner = reader.ReadMessage();
|
||||
return new JudgeOverrule(inner.Tag, inner.ReadByte(), inner.ReadUInt16());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Inner;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
using Impostor.Api.Net.Messages.Rpcs;
|
||||
using Impostor.Server.Events.Meeting;
|
||||
using Impostor.Server.Events.Player;
|
||||
@@ -22,10 +23,12 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
internal partial class InnerMeetingHud : InnerNetObject
|
||||
{
|
||||
private static readonly GameVersion JudgeMinVersion = new GameVersion(2026, 7, 15); // 18.0
|
||||
|
||||
private readonly ILogger<InnerMeetingHud> _logger;
|
||||
private readonly IEventManager _eventManager;
|
||||
|
||||
private readonly CancellationTokenSource _timerToken;
|
||||
private readonly List<JudgeOverrule> _judgeOverrules = new();
|
||||
|
||||
[AllowNull]
|
||||
private PlayerVoteArea[] _playerStates;
|
||||
@@ -65,6 +68,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public InnerPlayerInfo? Reporter { get; private set; }
|
||||
|
||||
public IReadOnlyCollection<IInnerMeetingHud.IJudgeOverrule> JudgeOverrules => _judgeOverrules;
|
||||
|
||||
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -108,6 +113,19 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sender.Client.GameVersion >= JudgeMinVersion)
|
||||
{
|
||||
var overruleQueueListLength = reader.ReadPackedInt32();
|
||||
if (overruleQueueListLength > 0)
|
||||
{
|
||||
_judgeOverrules.Clear();
|
||||
for (var i = 0; i < overruleQueueListLength; i++)
|
||||
{
|
||||
_judgeOverrules.Add(JudgeOverrule.Deserialize(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
|
||||
@@ -122,6 +140,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
|
||||
Rpc22Close.Deserialize(reader);
|
||||
_judgeOverrules.Clear();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -132,10 +151,23 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc23VotingComplete.Deserialize(reader, out var states, out var playerId, out var tie);
|
||||
foreach (var messageReader in states)
|
||||
if (sender.Client.GameVersion >= JudgeMinVersion)
|
||||
{
|
||||
messageReader.Dispose();
|
||||
Rpc23VotingComplete.Deserialize(reader, out var states, out var playerId, out var tie, out var wasOverruled, out var overrideId);
|
||||
|
||||
foreach (var messageReader in states)
|
||||
{
|
||||
messageReader.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Rpc23VotingComplete.Deserialize(reader, out var states, out var playerId, out var tie);
|
||||
|
||||
foreach (var messageReader in states)
|
||||
{
|
||||
messageReader.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// This would be a nice place to implement an anti cheat.
|
||||
@@ -161,6 +193,12 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.QueueOverruleVotes:
|
||||
{
|
||||
Rpc66QueueOverruleVotes.Deserialize(reader, out var judgePlayerId, out var targetPlayerId, out var overruleNonce);
|
||||
return await HandleQueueOverruleVotesAsync(sender, target, judgePlayerId, targetPlayerId, overruleNonce);
|
||||
}
|
||||
|
||||
default:
|
||||
return await base.HandleRpcAsync(sender, target, call, reader);
|
||||
}
|
||||
@@ -221,6 +259,87 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return true;
|
||||
}
|
||||
|
||||
private async ValueTask<bool> HandleQueueOverruleVotesAsync(ClientPlayer sender, ClientPlayer? target, byte judgePlayerId, byte targetPlayerId, ushort overruleNonce)
|
||||
{
|
||||
if (!await ValidateRole(RpcCalls.QueueOverruleVotes, sender, sender.Character?.PlayerInfo, RoleTypes.Judge) ||
|
||||
!await ValidateTarget(RpcCalls.QueueOverruleVotes, sender, target) ||
|
||||
!await ValidateHost(RpcCalls.QueueOverruleVotes, target!))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// A Judge can only queue an overrule for themself.
|
||||
if (judgePlayerId != sender.Character!.PlayerId)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.QueueOverruleVotes, CheatCategory.Ownership, "Client sent a Judge overrule for another player"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The client only generates nonces in the 1..65535 range.
|
||||
if (overruleNonce == 0)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.QueueOverruleVotes, CheatCategory.GameFlow, "Client sent a Judge overrule with an uninitialized nonce"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// A Judge can only overrule once per meeting.
|
||||
if (_judgeOverrules.Any(overrule => overrule.JudgePlayerId == judgePlayerId))
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.QueueOverruleVotes, CheatCategory.GameFlow, "Client sent more than one Judge overrule in the same meeting"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The overruled player must exist.
|
||||
if (Game.GameNet.GameData!.GetPlayerById(targetPlayerId) == null)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.QueueOverruleVotes, CheatCategory.InvalidObject, "Client sent a Judge overrule for an unknown player"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AddOrUpdateJudgeOverrule(judgePlayerId, targetPlayerId, overruleNonce);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AddOrUpdateJudgeOverrule(byte judgePlayerId, byte targetPlayerId, ushort overruleNonce)
|
||||
{
|
||||
var existing = _judgeOverrules.Find(overrule => overrule.JudgePlayerId == judgePlayerId);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.OverruledPlayerId = targetPlayerId;
|
||||
existing.OverruleNonce = overruleNonce;
|
||||
}
|
||||
else
|
||||
{
|
||||
_judgeOverrules.Add(new JudgeOverrule(judgePlayerId, targetPlayerId, overruleNonce));
|
||||
}
|
||||
}
|
||||
|
||||
private JudgeOverrule? GetWinningOverrule()
|
||||
{
|
||||
var gameData = Game.GameNet.GameData!;
|
||||
foreach (var overrule in _judgeOverrules)
|
||||
{
|
||||
var judge = gameData.GetPlayerById(overrule.JudgePlayerId);
|
||||
var target = gameData.GetPlayerById(overrule.OverruledPlayerId);
|
||||
|
||||
if (judge != null && target != null && !judge.Disconnected && !target.Disconnected)
|
||||
{
|
||||
return overrule;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async ValueTask CheckForEndVotingAsync()
|
||||
{
|
||||
if (_playerStates.All(ps => ps.IsDead || ps.DidVote))
|
||||
@@ -287,13 +406,35 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
var max = MaxPair(self, out var tie);
|
||||
var exiled = tie ? null : Game.GameNet.GameData!.GetPlayerById(max.Key)?.Controller;
|
||||
|
||||
// exile or others?
|
||||
var wasOverruled = false;
|
||||
ushort overrideId = 0;
|
||||
|
||||
var winningOverrule = GetWinningOverrule();
|
||||
if (winningOverrule != null)
|
||||
{
|
||||
wasOverruled = true;
|
||||
overrideId = winningOverrule.OverruleNonce;
|
||||
|
||||
var overruled = Game.GameNet.GameData!.GetPlayerById(winningOverrule.OverruledPlayerId);
|
||||
if (overruled != null && overruled.IsImpostor)
|
||||
{
|
||||
exiled = overruled.Controller;
|
||||
}
|
||||
else
|
||||
{
|
||||
var judge = Game.GameNet.GameData!.GetPlayerById(winningOverrule.JudgePlayerId);
|
||||
exiled = judge?.Controller;
|
||||
}
|
||||
}
|
||||
|
||||
if (exiled != null && exiled.PlayerInfo != null)
|
||||
{
|
||||
exiled.PlayerInfo.LastDeathReason = DeathReason.Exile;
|
||||
await _eventManager.CallAsync(new PlayerExileEvent(Game, Game.GetClientPlayer(exiled!.OwnerId)!, exiled));
|
||||
}
|
||||
|
||||
await _eventManager.CallAsync(new MeetingEndedEvent(Game, this, exiled, tie));
|
||||
await _eventManager.CallAsync(new MeetingEndedEvent(Game, this, exiled, tie, wasOverruled, overrideId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,7 +392,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
else
|
||||
{
|
||||
if (role is RoleTypes.ImpostorGhost or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel)
|
||||
if (role is RoleTypes.ImpostorGhost or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel or RoleTypes.SpiritGuide)
|
||||
{
|
||||
PlayerInfo.RoleWhenAlive = PlayerInfo.RoleType;
|
||||
PlayerInfo.IsDead = true;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
@@ -51,14 +52,18 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public bool Disconnected { get; internal set; }
|
||||
|
||||
public bool IsImpostor => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.ImpostorGhost or RoleTypes.Phantom;
|
||||
public bool IsImpostor => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.ImpostorGhost or RoleTypes.Phantom or RoleTypes.Viper;
|
||||
|
||||
public bool CanVent => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Phantom or RoleTypes.Engineer;
|
||||
public bool CanVent => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Phantom or RoleTypes.Viper or RoleTypes.Engineer;
|
||||
|
||||
public bool IsDead { get; internal set; }
|
||||
|
||||
public DeathReason LastDeathReason { get; internal set; }
|
||||
|
||||
public string? FriendCode { get; internal set; }
|
||||
|
||||
public string? Puid { get; internal set; }
|
||||
|
||||
public List<TaskInfo> Tasks { get; internal set; } = new List<TaskInfo>(0);
|
||||
|
||||
public DateTimeOffset LastMurder { get; set; }
|
||||
@@ -128,12 +133,13 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
Tasks[i].Serialize(writer);
|
||||
}
|
||||
|
||||
writer.Write(string.Empty); // FriendCode
|
||||
writer.Write(string.Empty); // PUID
|
||||
// Resolved by the matchmaker and pushed onto the player by a plugin
|
||||
writer.Write(FriendCode ?? string.Empty);
|
||||
writer.Write(Puid ?? string.Empty);
|
||||
return new ValueTask<bool>(true);
|
||||
}
|
||||
|
||||
public override ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer? target, IMessageReader reader, bool initialState)
|
||||
public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer? target, IMessageReader reader, bool initialState)
|
||||
{
|
||||
PlayerId = reader.ReadByte();
|
||||
ClientId = reader.ReadPackedInt32();
|
||||
@@ -175,11 +181,29 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
Tasks[i].Deserialize(reader);
|
||||
}
|
||||
|
||||
// Impostor doesn't expose fields that aren't properly validated
|
||||
reader.ReadString(); // FriendCode
|
||||
reader.ReadString(); // PUID
|
||||
var friendCode = reader.ReadString();
|
||||
var puid = reader.ReadString();
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
if (Game.GetClientPlayer(ClientId) is { } owner)
|
||||
{
|
||||
FriendCode ??= owner.Client.FriendCode;
|
||||
Puid ??= owner.Client.Puid;
|
||||
}
|
||||
|
||||
await ValidateIdentifiersAsync(sender, friendCode, puid);
|
||||
}
|
||||
|
||||
private async ValueTask ValidateIdentifiersAsync(IClientPlayer sender, string? friendCode, string? puid)
|
||||
{
|
||||
var mismatch = (!string.IsNullOrEmpty(friendCode) && FriendCode != null && FriendCode != friendCode)
|
||||
|| (!string.IsNullOrEmpty(puid) && Puid != null && Puid != puid);
|
||||
|
||||
if (!mismatch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await sender.Client.ReportCheatAsync(CheatContext.Deserialize, CheatCategory.Identifiers, $"Claimed identifiers for player {ClientId} do not match the values known for that player");
|
||||
}
|
||||
|
||||
public override async ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Impostor.Server.Net.Inner.Objects.ShipStatus
|
||||
case RpcCalls.CloseDoorsOfType:
|
||||
{
|
||||
if (!await ValidateCmd(call, sender, target) ||
|
||||
!await ValidateImpostor(call, sender, sender.Character!.PlayerInfo))
|
||||
!await ValidateImpostor(call, sender, sender.Character?.PlayerInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -100,6 +100,30 @@ namespace Impostor.Server.Net.Inner.Objects.ShipStatus
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.SpiritGuideMessage:
|
||||
{
|
||||
if (!await ValidateTarget(call, sender, target) ||
|
||||
!await ValidateRole(call, sender, sender.Character?.PlayerInfo, RoleTypes.SpiritGuide))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc67SpiritGuideMessage.Deserialize(reader, out var images);
|
||||
|
||||
if (images.Length == 0 || images.Length > 3)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(
|
||||
RpcCalls.SpiritGuideMessage,
|
||||
CheatCategory.ItemLimits,
|
||||
$"Client sent {images.Length} images (allowed: 1, 2 or 3)"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return await base.HandleRpcAsync(sender, target, call, reader);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,18 @@ 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)
|
||||
},
|
||||
new[]
|
||||
{
|
||||
new GameVersion(2026, 7, 15), // 18.0 (2026-08-18, build 7238, pc only)
|
||||
new GameVersion(2026, 7, 16), // 18.0 (2026-08-18, build 7238, mobile only)
|
||||
},
|
||||
new[]
|
||||
{
|
||||
new GameVersion(2026, 7, 20), // 19.0 (2026-09-29, build 7489, pc only)
|
||||
new GameVersion(2026, 7, 21), // 19.0 (2026-09-29, build 7492, mobile only)
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -496,6 +496,8 @@ namespace Impostor.Server.Net.State
|
||||
playerInfo.NetId = _nextNetId++;
|
||||
playerInfo.OwnerId = ServerOwned;
|
||||
playerInfo.ClientId = sender.Client.Id;
|
||||
playerInfo.FriendCode = sender.Client.FriendCode;
|
||||
playerInfo.Puid = sender.Client.Puid;
|
||||
playerInfo.PlayerId = GameNet.GameData.GetNextAvailablePlayerId();
|
||||
|
||||
// If player played a previous game, restore their color
|
||||
|
||||
@@ -210,12 +210,6 @@ namespace Impostor.Server.Net.State
|
||||
return GameJoinResult.FromError(GameJoinError.InvalidLimbo);
|
||||
}
|
||||
|
||||
if (GameState == GameStates.Ended)
|
||||
{
|
||||
await HandleJoinGameNext(player, isNew);
|
||||
return GameJoinResult.CreateSuccess(player);
|
||||
}
|
||||
|
||||
var @event = new GamePlayerJoiningEvent(this, player);
|
||||
await _eventManager.CallAsync(@event);
|
||||
|
||||
@@ -224,6 +218,12 @@ namespace Impostor.Server.Net.State
|
||||
return @event.JoinResult.Value;
|
||||
}
|
||||
|
||||
if (GameState == GameStates.Ended)
|
||||
{
|
||||
await HandleJoinGameNext(player, isNew);
|
||||
return GameJoinResult.CreateSuccess(player);
|
||||
}
|
||||
|
||||
await HandleJoinGameNew(player, isNew);
|
||||
return GameJoinResult.CreateSuccess(player);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
"EnableOwnershipChecks": true,
|
||||
"EnableRoleChecks": true,
|
||||
"EnableTargetChecks": true,
|
||||
"ForbidProtocolExtensions": true
|
||||
"EnableIdentifierChecks": 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"
|
||||
}
|
||||
},
|
||||
@@ -56,6 +56,12 @@
|
||||
"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, )",
|
||||
@@ -97,8 +103,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 +420,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