mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-20 11:39:41 +00:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53209aa78f | ||
|
|
c729d26ff6 | ||
|
|
e2157b0a6f | ||
|
|
ad18eda32f | ||
|
|
efda5bcef2 | ||
|
|
6a947457a5 | ||
|
|
a3c6ca77c1 | ||
|
|
9f3f826b17 | ||
|
|
36671ba1ba | ||
|
|
ace97ddc1d | ||
|
|
4dc4c90e8b | ||
|
|
9379c05676 | ||
|
|
ef98f4d20e | ||
|
|
9f6947b484 | ||
|
|
76ae1a3385 | ||
|
|
3c8a002d6e |
No files matched your search
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<VersionPrefix>1.9.3</VersionPrefix>
|
||||
<VersionPrefix>1.10.0</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
|
||||
@@ -79,6 +79,8 @@ public sealed class SourceGenerator : IIncrementalGenerator
|
||||
enumGenerator.Generate("GameOverReason", underlyingType: CSharpEnumUnderlyingType.Byte);
|
||||
enumGenerator.Generate("Platforms");
|
||||
enumGenerator.Generate("RoleTypes", underlyingType: CSharpEnumUnderlyingType.UnsignedShort);
|
||||
enumGenerator.Generate("RulesPresets", underlyingType: CSharpEnumUnderlyingType.Byte);
|
||||
enumGenerator.Generate("SpecialGameModes", underlyingType: CSharpEnumUnderlyingType.Byte);
|
||||
enumGenerator.Generate("StringNames");
|
||||
enumGenerator.Generate("SystemTypes", underlyingType: CSharpEnumUnderlyingType.Byte);
|
||||
enumGenerator.Generate("Language", sourceName: "SupportedLangs");
|
||||
|
||||
@@ -2,42 +2,102 @@ namespace Impostor.Api.Innersloth.Customization
|
||||
{
|
||||
public class PlayerOutfit
|
||||
{
|
||||
public ColorType Color { get; internal set; } = (ColorType)(-1);
|
||||
// Magic value that is assigned by default when the outfit is initially created. This is not
|
||||
// `null` because existing API already exposed this magic value. It's also not 0 (which is
|
||||
// the value exposed when serializing) because SetColor anticheat checks exist.
|
||||
private const ColorType UnknownColor = (ColorType)(-1);
|
||||
|
||||
public string HatId { get; internal set; } = "missing";
|
||||
private string _playerName = string.Empty;
|
||||
private ColorType _color = UnknownColor;
|
||||
private string _hatId = "missing";
|
||||
private string _petId = "missing";
|
||||
private string _skinId = "missing";
|
||||
private string _visorId = "missing";
|
||||
private string _namePlateId = "missing";
|
||||
|
||||
public string PetId { get; internal set; } = "missing";
|
||||
public bool IsDirty { get; internal set; }
|
||||
|
||||
public string SkinId { get; internal set; } = "missing";
|
||||
|
||||
public string VisorId { get; internal set; } = "missing";
|
||||
|
||||
public string NamePlateId { get; internal set; } = "missing";
|
||||
|
||||
public string PlayerName { get; internal set; } = "missing";
|
||||
|
||||
public bool IsIncomplete
|
||||
public string PlayerName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PlayerName) && Color != (ColorType)(-1) && HatId != "missing" && PetId != "missing" && SkinId != "missing" && VisorId != "missing")
|
||||
{
|
||||
return NamePlateId == "missing";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
get => _playerName;
|
||||
internal set => SetField(ref _playerName, value);
|
||||
}
|
||||
|
||||
public ColorType Color
|
||||
{
|
||||
get => _color;
|
||||
internal set => SetField(ref _color, value);
|
||||
}
|
||||
|
||||
public string HatId
|
||||
{
|
||||
get => _hatId;
|
||||
internal set => SetField(ref _hatId, value);
|
||||
}
|
||||
|
||||
public string PetId
|
||||
{
|
||||
get => _petId;
|
||||
internal set => SetField(ref _petId, value);
|
||||
}
|
||||
|
||||
public string SkinId
|
||||
{
|
||||
get => _skinId;
|
||||
internal set => SetField(ref _skinId, value);
|
||||
}
|
||||
|
||||
public string VisorId
|
||||
{
|
||||
get => _visorId;
|
||||
internal set => SetField(ref _visorId, value);
|
||||
}
|
||||
|
||||
public string NamePlateId
|
||||
{
|
||||
get => _namePlateId;
|
||||
internal set => SetField(ref _namePlateId, value);
|
||||
}
|
||||
|
||||
private byte HatSequenceId { get; set; } = 0;
|
||||
|
||||
private byte PetSequenceId { get; set; } = 0;
|
||||
|
||||
private byte SkinSequenceId { get; set; } = 0;
|
||||
|
||||
private byte VisorSequenceId { get; set; } = 0;
|
||||
|
||||
private byte NamePlateSequenceId { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a player outfit is complete or whether it needs to receive additional cosmetics.
|
||||
/// </summary>
|
||||
/// <returns>true if the outfit is incomplete, false if the outfit is complete.</returns>
|
||||
public bool IsIncomplete => string.IsNullOrEmpty(PlayerName)
|
||||
|| Color == UnknownColor
|
||||
|| HatId == "missing"
|
||||
|| PetId == "missing"
|
||||
|| SkinId == "missing"
|
||||
|| VisorId == "missing"
|
||||
|| NamePlateId == "missing";
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(PlayerName);
|
||||
writer.WritePacked((int)Color);
|
||||
|
||||
// Follow officials by not sending a color value that is out of range. Sending -1 can cause exceptions on the client side.
|
||||
writer.WritePacked(Color == UnknownColor ? 0 : (int)Color);
|
||||
|
||||
writer.Write(HatId);
|
||||
writer.Write(PetId);
|
||||
writer.Write(SkinId);
|
||||
writer.Write(VisorId);
|
||||
writer.Write(NamePlateId);
|
||||
writer.Write(HatSequenceId);
|
||||
writer.Write(PetSequenceId);
|
||||
writer.Write(SkinSequenceId);
|
||||
writer.Write(VisorSequenceId);
|
||||
writer.Write(NamePlateSequenceId);
|
||||
}
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
@@ -49,6 +109,17 @@ namespace Impostor.Api.Innersloth.Customization
|
||||
SkinId = reader.ReadString();
|
||||
VisorId = reader.ReadString();
|
||||
NamePlateId = reader.ReadString();
|
||||
HatSequenceId = reader.ReadByte();
|
||||
PetSequenceId = reader.ReadByte();
|
||||
SkinSequenceId = reader.ReadByte();
|
||||
VisorSequenceId = reader.ReadByte();
|
||||
NamePlateSequenceId = reader.ReadByte();
|
||||
}
|
||||
|
||||
private void SetField<T>(ref T field, T value)
|
||||
{
|
||||
field = value;
|
||||
IsDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
"0": "SkeldShipStatus",
|
||||
"1": "MeetingHud",
|
||||
"2": "LobbyBehaviour",
|
||||
"3": "GameData",
|
||||
"4": "PlayerControl",
|
||||
"5": "MiraShipStatus",
|
||||
"6": "PolusShipStatus",
|
||||
@@ -10,5 +9,7 @@
|
||||
"8": "AirshipStatus",
|
||||
"9": "HideAndSeekManager",
|
||||
"10": "NormalGameManager",
|
||||
"11": "NetworkedPlayerInfo",
|
||||
"12": "VoteBanSystem",
|
||||
"13": "FungleShipStatus"
|
||||
}
|
||||
@@ -6,5 +6,8 @@
|
||||
"GuardianAngel": 4,
|
||||
"Shapeshifter": 5,
|
||||
"CrewmateGhost": 6,
|
||||
"ImpostorGhost": 7
|
||||
"ImpostorGhost": 7,
|
||||
"Noisemaker": 8,
|
||||
"Phantom": 9,
|
||||
"Tracker": 10
|
||||
}
|
||||
@@ -55,5 +55,9 @@
|
||||
"CheckShapeshift": 55,
|
||||
"RejectShapeshift": 56,
|
||||
"LobbyTimeExpiring": 60,
|
||||
"ExtendLobbyTimer": 61
|
||||
"ExtendLobbyTimer": 61,
|
||||
"CheckVanish": 62,
|
||||
"StartVanish": 63,
|
||||
"CheckAppear": 64,
|
||||
"StartAppear": 65
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"Standard": 0,
|
||||
"StandardRoles": 1,
|
||||
"Flashlight": 2,
|
||||
"Custom": 100
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"None": 0,
|
||||
"ImpersonationCeleb": 1,
|
||||
"Spamming": 2,
|
||||
"InappropriateNameUnsportsmanlike": 3,
|
||||
"UnsportsmanlikeConduct": 4,
|
||||
"ImpersonationDevelopers": 5,
|
||||
"InappropriateChatPersonalInfo": 6,
|
||||
"InappropriateNameDerogatory": 7,
|
||||
"InappropriateNameNsfw": 8,
|
||||
"Bullying": 9,
|
||||
"CheatingHacking": 10,
|
||||
"InappropriateChatDating": 11,
|
||||
"WeaponizingRules": 12,
|
||||
"RepeatOffender3": 13,
|
||||
"SexualMisconduct": 14,
|
||||
"Doxing": 15,
|
||||
"IllegalActivity": 16,
|
||||
"Harassment": 17,
|
||||
"SelfHarmPromotion": 18,
|
||||
"RepeatOffender10": 19,
|
||||
"InvalidUser": 20
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"None": 0,
|
||||
"AprilFools": 1
|
||||
}
|
||||
@@ -570,10 +570,39 @@
|
||||
"RolesHelp_Engineer_02": 1563,
|
||||
"RolesHelp_GuardianAngel_01": 1564,
|
||||
"RolesHelp_GuardianAngel_02": 1565,
|
||||
"RolesHelp_Noisemaker_01": 1566,
|
||||
"RolesHelp_Noisemaker_02": 1567,
|
||||
"RolesHelp_Tracker_01": 1568,
|
||||
"RolesHelp_Tracker_02": 1569,
|
||||
"RolesHelp_Shapeshifter_01": 1600,
|
||||
"RolesHelp_Shapeshifter_02": 1601,
|
||||
"RolesHelp_Phantom_01": 1602,
|
||||
"RolesHelp_Phantom_02": 1603,
|
||||
"RolesHelpOutro_01": 1650,
|
||||
"RolesHelpOutro_02": 1651,
|
||||
"NoisemakerRole": 1660,
|
||||
"NoisemakerBlurb": 1661,
|
||||
"NoisemakerBlurbMed": 1662,
|
||||
"NoisemakerBlurbLong": 1663,
|
||||
"NoisemakerImpostorAlert": 1664,
|
||||
"NoisemakerAlertDuration": 1665,
|
||||
"PhantomAbility": 1670,
|
||||
"PhantomRole": 1671,
|
||||
"PhantomBlurb": 1672,
|
||||
"PhantomBlurbMed": 1673,
|
||||
"PhantomBlurbLong": 1674,
|
||||
"PhantomCooldown": 1675,
|
||||
"PhantomDuration": 1676,
|
||||
"PhantomAbilityUndo": 1677,
|
||||
"TrackerAbility": 1680,
|
||||
"TrackerRole": 1681,
|
||||
"TrackerBlurb": 1682,
|
||||
"TrackerBlurbMed": 1683,
|
||||
"TrackerBlurbLong": 1684,
|
||||
"TrackerCooldown": 1685,
|
||||
"TrackerDuration": 1686,
|
||||
"TrackerAbilityUndo": 1687,
|
||||
"TrackerDelay": 1688,
|
||||
"SanctionDuration": 1700,
|
||||
"SanctionPermanent": 1701,
|
||||
"SanctionConduct": 1702,
|
||||
@@ -1304,6 +1333,24 @@
|
||||
"ConfirmDeleteAccountsEmpty": 3209,
|
||||
"AccountRequestDelete": 3210,
|
||||
"HasBeenKilled": 3211,
|
||||
"RoomSettingsLabel": 3280,
|
||||
"ViewLabel": 3281,
|
||||
"RoomCodeLabel": 3282,
|
||||
"ModeLabel": 3283,
|
||||
"PrivacyLabel": 3284,
|
||||
"CapacityLabel": 3285,
|
||||
"GameSettingsLabel": 3286,
|
||||
"RoleSettingsLabel": 3287,
|
||||
"GamePresetsLabel": 3288,
|
||||
"GamePresetsDescription": 3289,
|
||||
"StandardRulesPresetDescription": 3290,
|
||||
"GameModeStandard": 3291,
|
||||
"GameModeStandardRoles": 3292,
|
||||
"GameModeCustom": 3293,
|
||||
"ReturnToLobby": 3294,
|
||||
"GameModeFlashlight": 3295,
|
||||
"HostNounLabel": 3296,
|
||||
"RoleSettingsAll": 3297,
|
||||
"ScrollList": 3300,
|
||||
"ScrollNews": 3301,
|
||||
"AmongUsAnnouncements": 3302,
|
||||
@@ -1331,6 +1378,33 @@
|
||||
"ReportNotificationBody": 3324,
|
||||
"MissingGuardianEmailText": 3325,
|
||||
"AprilFoolsModeToggle": 3326,
|
||||
"LobbyChangeSettingNotification": 3327,
|
||||
"ImpostorsCategory": 3350,
|
||||
"RolesCategory": 3351,
|
||||
"MeetingsCategory": 3352,
|
||||
"PlayersCategory": 3353,
|
||||
"TasksCategory": 3354,
|
||||
"GameSettingsDescription": 3355,
|
||||
"RoleSettingsDescription": 3356,
|
||||
"InfoLabel": 3357,
|
||||
"RoleQuotaLabel": 3358,
|
||||
"OverviewCategory": 3359,
|
||||
"CrewmateRolesHeader": 3360,
|
||||
"ImpostorRolesHeader": 3361,
|
||||
"ViewingLabel": 3362,
|
||||
"LocalLobbyLabel": 3363,
|
||||
"NumberRoleHeader": 3364,
|
||||
"ChanceRoleHeader": 3365,
|
||||
"WaitingForPlayers": 3366,
|
||||
"HideAndSeekModeDescription": 3367,
|
||||
"RolesRulesPresetDescription": 3368,
|
||||
"FlashlightRulesPresetDescription": 3369,
|
||||
"RoomCodeCopied": 3370,
|
||||
"FinalHideCategory": 3371,
|
||||
"LobbyChangeSettingNotificationRole": 3372,
|
||||
"StandardRulesPresetDescriptionHideAndSeek": 3373,
|
||||
"GameModeStandardSeek": 3374,
|
||||
"HostYouLabel": 3375,
|
||||
"MiningPit": 3400,
|
||||
"Jungle": 3401,
|
||||
"BuildSandcastle": 3402,
|
||||
@@ -1399,5 +1473,11 @@
|
||||
"HttpErrorBadGateway": 3531,
|
||||
"HttpErrorServiceUnavailable": 3532,
|
||||
"HttpErrorGatewayTimeout": 3533,
|
||||
"UserVersionError": 3534
|
||||
"UserVersionError": 3534,
|
||||
"QCLobbyRoleNoisemakers": 3535,
|
||||
"QCLobbyRoleTrackers": 3536,
|
||||
"QCLobbyRolePhantoms": 3537,
|
||||
"QCSelfWasTracking": 3538,
|
||||
"QCSelfGoingToNoisemaker": 3539,
|
||||
"QCSelfSawRoleInLocation": 3540
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dumpostorVersion": "1.0.0",
|
||||
"gameVersion": "2024.3.5",
|
||||
"dumpostorVersion": "1.0.1",
|
||||
"gameVersion": "2024.6.18",
|
||||
"platformType": "StandaloneItch"
|
||||
}
|
||||
@@ -2,7 +2,7 @@ namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public class HideNSeekGameOptions : IGameOptions
|
||||
{
|
||||
public const int LatestVersion = 7;
|
||||
public const int LatestVersion = 8;
|
||||
|
||||
public HideNSeekGameOptions(byte version = LatestVersion)
|
||||
{
|
||||
@@ -16,6 +16,12 @@ public class HideNSeekGameOptions : IGameOptions
|
||||
/// <inheritdoc />
|
||||
public GameModes GameMode => GameModes.HideNSeek;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SpecialGameModes SpecialMode { get; set; } = SpecialGameModes.None;
|
||||
|
||||
/// <inheritdoc />
|
||||
public RulesPresets RulesPreset { get; set; } = RulesPresets.Custom;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte MaxPlayers { get; set; } = 15;
|
||||
|
||||
@@ -96,6 +102,12 @@ public class HideNSeekGameOptions : IGameOptions
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
if (Version >= 8)
|
||||
{
|
||||
SpecialMode = (SpecialGameModes)reader.ReadByte();
|
||||
RulesPreset = (RulesPresets)reader.ReadByte();
|
||||
}
|
||||
|
||||
MaxPlayers = reader.ReadByte();
|
||||
Keywords = (GameKeywords)reader.ReadInt32();
|
||||
Map = (MapTypes)reader.ReadByte();
|
||||
@@ -121,7 +133,7 @@ public class HideNSeekGameOptions : IGameOptions
|
||||
MaxPingTime = reader.ReadSingle();
|
||||
CrewmateTimeInVent = reader.ReadSingle();
|
||||
|
||||
if (Version > 7)
|
||||
if (Version > LatestVersion)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
|
||||
}
|
||||
@@ -129,6 +141,12 @@ public class HideNSeekGameOptions : IGameOptions
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
if (Version >= 8)
|
||||
{
|
||||
writer.Write((byte)SpecialMode);
|
||||
writer.Write((byte)RulesPreset);
|
||||
}
|
||||
|
||||
writer.Write(MaxPlayers);
|
||||
writer.Write((uint)Keywords);
|
||||
writer.Write((byte)Map);
|
||||
@@ -154,7 +172,7 @@ public class HideNSeekGameOptions : IGameOptions
|
||||
writer.Write(MaxPingTime);
|
||||
writer.Write(CrewmateTimeInVent);
|
||||
|
||||
if (Version > 7)
|
||||
if (Version > LatestVersion)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
|
||||
}
|
||||
|
||||
@@ -10,10 +10,20 @@ public interface IGameOptions
|
||||
public byte Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently active gamemode.
|
||||
/// Gets the currently active gamemode. This is currently used for the Normal and HideAndSeek gamemodes.
|
||||
/// </summary>
|
||||
public GameModes GameMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently active special gamemode. This is currently used for the AprilFools gamemode.
|
||||
/// </summary>
|
||||
public SpecialGameModes SpecialMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rule preset.
|
||||
/// </summary>
|
||||
public RulesPresets RulesPreset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of players for this lobby.
|
||||
/// </summary>
|
||||
|
||||
@@ -21,6 +21,12 @@ public class LegacyGameOptionsData : IGameOptions
|
||||
|
||||
public GameModes GameMode => GameModes.Normal;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SpecialGameModes SpecialMode { get; } = SpecialGameModes.None;
|
||||
|
||||
/// <inheritdoc />
|
||||
public RulesPresets RulesPreset { get; } = RulesPresets.Custom;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of players for this lobby.
|
||||
/// </summary>
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public class NormalGameOptions : IGameOptions
|
||||
{
|
||||
public const int LatestVersion = 7;
|
||||
public const int LatestVersion = 8;
|
||||
|
||||
public NormalGameOptions(byte version = LatestVersion)
|
||||
{
|
||||
@@ -19,6 +19,12 @@ public class NormalGameOptions : IGameOptions
|
||||
/// <inheritdoc />
|
||||
public GameModes GameMode => GameModes.Normal;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SpecialGameModes SpecialMode { get; set; } = SpecialGameModes.None;
|
||||
|
||||
/// <inheritdoc />
|
||||
public RulesPresets RulesPreset { get; set; } = RulesPresets.Custom;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte MaxPlayers { get; set; } = 10;
|
||||
|
||||
@@ -133,6 +139,12 @@ public class NormalGameOptions : IGameOptions
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
if (Version >= 8)
|
||||
{
|
||||
SpecialMode = (SpecialGameModes)reader.ReadByte();
|
||||
RulesPreset = (RulesPresets)reader.ReadByte();
|
||||
}
|
||||
|
||||
MaxPlayers = reader.ReadByte();
|
||||
Keywords = (GameKeywords)reader.ReadUInt32();
|
||||
Map = (MapTypes)reader.ReadByte();
|
||||
@@ -163,7 +175,7 @@ public class NormalGameOptions : IGameOptions
|
||||
|
||||
RoleOptions.Deserialize(reader);
|
||||
|
||||
if (Version > 7)
|
||||
if (Version > LatestVersion)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
|
||||
}
|
||||
@@ -171,6 +183,12 @@ public class NormalGameOptions : IGameOptions
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
if (Version >= 8)
|
||||
{
|
||||
writer.Write((byte)SpecialMode);
|
||||
writer.Write((byte)RulesPreset);
|
||||
}
|
||||
|
||||
writer.Write(MaxPlayers);
|
||||
writer.Write((uint)Keywords);
|
||||
writer.Write((byte)Map);
|
||||
@@ -201,7 +219,7 @@ public class NormalGameOptions : IGameOptions
|
||||
|
||||
RoleOptions.Serialize(writer);
|
||||
|
||||
if (Version > 7)
|
||||
if (Version > LatestVersion)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class NoisemakerRoleOptions : IRoleOptions
|
||||
{
|
||||
public NoisemakerRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.Noisemaker;
|
||||
|
||||
public bool ImpostorAlert { get; set; } = true;
|
||||
|
||||
public byte AlertDuration { get; set; } = 10;
|
||||
|
||||
public static NoisemakerRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new NoisemakerRoleOptions(version);
|
||||
|
||||
options.AlertDuration = reader.ReadByte();
|
||||
options.ImpostorAlert = reader.ReadBoolean();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(AlertDuration);
|
||||
writer.Write(ImpostorAlert);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class PhantomRoleOptions : IRoleOptions
|
||||
{
|
||||
public PhantomRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.Phantom;
|
||||
|
||||
public byte Cooldown { get; set; } = 15;
|
||||
|
||||
public byte Duration { get; set; } = 30;
|
||||
|
||||
public static PhantomRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new PhantomRoleOptions(version);
|
||||
|
||||
options.Cooldown = reader.ReadByte();
|
||||
options.Duration = reader.ReadByte();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(Cooldown);
|
||||
writer.Write(Duration);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,9 @@ public class RoleOptionsCollection
|
||||
RoleTypes.Engineer => EngineerRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.GuardianAngel => GuardianAngelRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Shapeshifter => ShapeshifterRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Noisemaker => NoisemakerRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Phantom => PhantomRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Tracker => TrackerRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(roleType), roleType, null),
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class TrackerRoleOptions : IRoleOptions
|
||||
{
|
||||
public TrackerRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.Tracker;
|
||||
|
||||
public byte Cooldown { get; set; } = 15;
|
||||
|
||||
public byte Duration { get; set; } = 30;
|
||||
|
||||
public byte Delay { get; set; } = 1;
|
||||
|
||||
public static TrackerRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new TrackerRoleOptions(version);
|
||||
|
||||
options.Cooldown = reader.ReadByte();
|
||||
options.Duration = reader.ReadByte();
|
||||
options.Delay = reader.ReadByte();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(Cooldown);
|
||||
writer.Write(Duration);
|
||||
writer.Write(Delay);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Impostor.Api.Net.Inner.Objects
|
||||
{
|
||||
public interface IInnerGameData : IInnerNetObject
|
||||
public interface IInnerGameData
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,15 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc06SetName
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, string name)
|
||||
public static void Serialize(IMessageWriter writer, uint netId, string name)
|
||||
{
|
||||
writer.Write(netId);
|
||||
writer.Write(name);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out string name)
|
||||
public static void Deserialize(IMessageReader reader, out uint netId, out string name)
|
||||
{
|
||||
netId = reader.ReadUInt32();
|
||||
name = reader.ReadString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc08SetColor
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, ColorType color)
|
||||
public static void Serialize(IMessageWriter writer, uint netId, ColorType color)
|
||||
{
|
||||
writer.Write(netId);
|
||||
writer.Write((byte)color);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out ColorType color)
|
||||
public static void Deserialize(IMessageReader reader, out uint netId, out ColorType color)
|
||||
{
|
||||
netId = reader.ReadUInt32();
|
||||
color = (ColorType)reader.ReadByte();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,16 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc44SetRole
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, RoleTypes role)
|
||||
public static void Serialize(IMessageWriter writer, RoleTypes role, bool canOverrideRole)
|
||||
{
|
||||
writer.Write((ushort)role);
|
||||
writer.Write(canOverrideRole);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out RoleTypes role)
|
||||
public static void Deserialize(IMessageReader reader, out RoleTypes role, out bool canOverrideRole)
|
||||
{
|
||||
role = (RoleTypes)reader.ReadUInt16();
|
||||
canOverrideRole = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,8 @@ namespace Impostor.Server.Net.Inner
|
||||
|
||||
public SpawnFlags SpawnFlags { get; internal set; }
|
||||
|
||||
public virtual bool IsDirty { get; internal set; }
|
||||
|
||||
public bool IsOwnedBy(IClientPlayer player)
|
||||
{
|
||||
return OwnerId == player.Client.Id ||
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
{
|
||||
_logger = logger;
|
||||
_votes = new Dictionary<int, int[]>();
|
||||
Components.Add(this);
|
||||
}
|
||||
|
||||
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
|
||||
|
||||
@@ -1,41 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Events.Managers;
|
||||
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.Net.Inner.Objects.Components;
|
||||
using Impostor.Server.Net.State;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
internal partial class InnerGameData : InnerNetObject, IInnerGameData
|
||||
internal partial class InnerGameData : IInnerGameData
|
||||
{
|
||||
private readonly ILogger<InnerGameData> _logger;
|
||||
private readonly IEventManager _eventManager;
|
||||
private readonly ConcurrentDictionary<byte, InnerPlayerInfo> _allPlayers;
|
||||
|
||||
public InnerGameData(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerGameData> logger, IEventManager eventManager, IServiceProvider serviceProvider) : base(customMessageManager, game)
|
||||
{
|
||||
_logger = logger;
|
||||
_eventManager = eventManager;
|
||||
_allPlayers = new ConcurrentDictionary<byte, InnerPlayerInfo>();
|
||||
|
||||
Components.Add(this);
|
||||
Components.Add(ActivatorUtilities.CreateInstance<InnerVoteBanSystem>(serviceProvider, game));
|
||||
}
|
||||
private readonly ConcurrentDictionary<byte, InnerPlayerInfo> _allPlayers = new();
|
||||
private readonly ConcurrentDictionary<int, InnerPlayerInfo> _allPlayersByClientId = new();
|
||||
|
||||
public int PlayerCount => _allPlayers.Count;
|
||||
|
||||
public IReadOnlyDictionary<byte, InnerPlayerInfo> Players => _allPlayers;
|
||||
|
||||
internal IReadOnlyDictionary<int, InnerPlayerInfo> PlayersByClientId => _allPlayersByClientId;
|
||||
|
||||
public InnerPlayerInfo? GetPlayerById(byte id)
|
||||
{
|
||||
if (id == byte.MaxValue)
|
||||
@@ -46,103 +25,33 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return _allPlayers.TryGetValue(id, out var player) ? player : null;
|
||||
}
|
||||
|
||||
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
|
||||
internal bool AddPlayer(InnerPlayerInfo playerInfo)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return _allPlayers.TryAdd(playerInfo.PlayerId, playerInfo) &&
|
||||
_allPlayersByClientId.TryAdd(playerInfo.ClientId, playerInfo);
|
||||
}
|
||||
|
||||
public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer? target, IMessageReader reader, bool initialState)
|
||||
internal void RemovePlayer(byte playerId)
|
||||
{
|
||||
if (!await ValidateHost(CheatContext.Deserialize, sender))
|
||||
_allPlayers.TryRemove(playerId, out var player);
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
return;
|
||||
_allPlayersByClientId.TryRemove(player.ClientId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
while (reader.Position < reader.Length)
|
||||
internal byte GetNextAvailablePlayerId()
|
||||
{
|
||||
for (byte i = 0; i < byte.MaxValue; i++)
|
||||
{
|
||||
var inner = reader.ReadMessage();
|
||||
var playerInfo = this.GetPlayerById(inner.Tag);
|
||||
if (playerInfo != null)
|
||||
if (!Players.ContainsKey(i))
|
||||
{
|
||||
playerInfo.Deserialize(inner);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerInfo = new InnerPlayerInfo(inner.Tag);
|
||||
playerInfo.Deserialize(inner);
|
||||
|
||||
if (!_allPlayers.TryAdd(playerInfo.PlayerId, playerInfo))
|
||||
{
|
||||
throw new ImpostorException("Failed to add player to InnerGameData.");
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
|
||||
{
|
||||
if (!await ValidateHost(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (call)
|
||||
{
|
||||
case RpcCalls.SetTasks:
|
||||
{
|
||||
Rpc29SetTasks.Deserialize(reader, out var playerId, out var taskTypeIds);
|
||||
SetTasks(playerId, taskTypeIds);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return await base.HandleRpcAsync(sender, target, call, reader);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal InnerPlayerInfo? AddPlayer(InnerPlayerControl control)
|
||||
{
|
||||
var playerId = control.PlayerId;
|
||||
var playerInfo = new InnerPlayerInfo(control.PlayerId);
|
||||
|
||||
if (_allPlayers.TryAdd(playerId, playerInfo))
|
||||
{
|
||||
return playerInfo;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal void RemovePlayer(InnerPlayerControl control)
|
||||
{
|
||||
_allPlayers.TryRemove(control.PlayerInfo.PlayerId, out _);
|
||||
}
|
||||
|
||||
private void SetTasks(byte playerId, ReadOnlyMemory<byte> taskTypeIds)
|
||||
{
|
||||
var player = GetPlayerById(playerId);
|
||||
if (player == null)
|
||||
{
|
||||
_logger.LogTrace("Could not set tasks for playerId {0}.", playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.Disconnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
player.Tasks = new List<TaskInfo>(taskTypeIds.Length);
|
||||
|
||||
var taskId = 0u;
|
||||
foreach (var taskTypeId in taskTypeIds.Span)
|
||||
{
|
||||
var mapTasks = Game.GameNet!.ShipStatus?.Data.Tasks;
|
||||
var taskType = (mapTasks != null && mapTasks.ContainsKey(taskTypeId)) ? mapTasks[taskTypeId] : null;
|
||||
player.Tasks.Add(new TaskInfo(player, _eventManager, taskId++, taskType));
|
||||
}
|
||||
return byte.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,9 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public async ValueTask SetNameAsync(string name)
|
||||
{
|
||||
PlayerInfo.PlayerName = name;
|
||||
PlayerInfo.CurrentOutfit.PlayerName = name;
|
||||
|
||||
// TODO check playerinfo ownership
|
||||
using var writer = Game.StartRpc(NetId, RpcCalls.SetName);
|
||||
writer.Write(name);
|
||||
await Game.FinishRpcAsync(writer);
|
||||
@@ -32,7 +33,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
PlayerInfo.CurrentOutfit.Color = color;
|
||||
|
||||
using var writer = Game.StartRpc(NetId, RpcCalls.SetColor);
|
||||
Rpc08SetColor.Serialize(writer, color);
|
||||
Rpc08SetColor.Serialize(writer, PlayerInfo.NetId, color);
|
||||
await Game.FinishRpcAsync(writer);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc06SetName.Deserialize(reader, out var name);
|
||||
Rpc06SetName.Deserialize(reader, out var _, out var name);
|
||||
return await HandleSetName(sender, name);
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc08SetColor.Deserialize(reader, out var color);
|
||||
Rpc08SetColor.Deserialize(reader, out var _, out var color);
|
||||
return await HandleSetColor(sender, color);
|
||||
}
|
||||
|
||||
@@ -383,8 +383,16 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc44SetRole.Deserialize(reader, out var role);
|
||||
Rpc44SetRole.Deserialize(reader, out var role, out var _);
|
||||
|
||||
if (role is RoleTypes.ImpostorGhost or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel)
|
||||
{
|
||||
PlayerInfo.RoleWhenAlive = PlayerInfo.RoleType;
|
||||
PlayerInfo.IsDead = true;
|
||||
}
|
||||
|
||||
PlayerInfo.RoleType = role;
|
||||
PlayerInfo.IsDirty = true;
|
||||
|
||||
if (Game.GameState == GameStates.Starting && Game.Players.All(clientPlayer => clientPlayer.Character?.PlayerInfo.RoleType != null))
|
||||
{
|
||||
@@ -530,6 +538,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
PlayerInfo.IsDead = true;
|
||||
PlayerInfo.LastDeathReason = reason;
|
||||
PlayerInfo.IsDirty = true;
|
||||
}
|
||||
|
||||
internal void Protect(InnerPlayerControl guardianAngel)
|
||||
@@ -679,7 +688,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfo.PlayerName = name;
|
||||
PlayerInfo.CurrentOutfit.PlayerName = name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
internal partial class InnerPlayerInfo : IInnerPlayerInfo
|
||||
internal partial class InnerPlayerInfo : InnerNetObject, IInnerPlayerInfo
|
||||
{
|
||||
IEnumerable<ITaskInfo> IInnerPlayerInfo.Tasks => Tasks;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Utils;
|
||||
using Impostor.Server.Net.State;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
internal partial class InnerPlayerInfo
|
||||
{
|
||||
public InnerPlayerInfo(byte playerId)
|
||||
private readonly ILogger<InnerPlayerInfo> _logger;
|
||||
|
||||
public InnerPlayerInfo(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerPlayerInfo> logger) : base(customMessageManager, game)
|
||||
{
|
||||
PlayerId = playerId;
|
||||
Components.Add(this);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public InnerPlayerControl? Controller { get; internal set; }
|
||||
|
||||
public byte PlayerId { get; }
|
||||
public byte PlayerId { get; internal set; }
|
||||
|
||||
public string PlayerName { get; internal set; } = string.Empty;
|
||||
public int ClientId { get; internal set; }
|
||||
|
||||
public string PlayerName => CurrentOutfit.PlayerName;
|
||||
|
||||
public Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; } = new()
|
||||
{
|
||||
@@ -32,6 +42,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public RoleTypes? RoleType { get; internal set; }
|
||||
|
||||
public RoleTypes? RoleWhenAlive { get; internal set; }
|
||||
|
||||
public bool Disconnected { get; internal set; }
|
||||
|
||||
public bool IsImpostor => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.ImpostorGhost;
|
||||
@@ -48,6 +60,12 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public uint PlayerLevel { get; internal set; }
|
||||
|
||||
public override bool IsDirty
|
||||
{
|
||||
get => CurrentOutfit.IsDirty;
|
||||
internal set => CurrentOutfit.IsDirty = value;
|
||||
}
|
||||
|
||||
public bool CanMurder(IGame game, IDateTimeProvider dateTimeProvider)
|
||||
{
|
||||
if (!IsImpostor)
|
||||
@@ -71,13 +89,56 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
writer.Write(PlayerId);
|
||||
writer.WritePacked(ClientId);
|
||||
|
||||
writer.Write((byte)Outfits.Count);
|
||||
foreach (var outfit in Outfits)
|
||||
{
|
||||
writer.Write((byte)outfit.Key);
|
||||
outfit.Value.Serialize(writer);
|
||||
}
|
||||
|
||||
writer.WritePacked(PlayerLevel);
|
||||
|
||||
var flag = 0;
|
||||
if (Disconnected)
|
||||
{
|
||||
flag = (byte)(flag | 1u);
|
||||
}
|
||||
|
||||
if (IsDead)
|
||||
{
|
||||
flag = (byte)(flag | 4u);
|
||||
}
|
||||
|
||||
writer.Write((byte)flag);
|
||||
|
||||
writer.Write((ushort)(RoleType ?? 0));
|
||||
writer.Write(RoleWhenAlive.HasValue);
|
||||
if (RoleWhenAlive.HasValue)
|
||||
{
|
||||
writer.Write((ushort)RoleWhenAlive.Value);
|
||||
}
|
||||
|
||||
writer.Write((byte)Tasks.Count);
|
||||
for (var i = 0; i < Tasks.Count; i++)
|
||||
{
|
||||
Tasks[i].Serialize(writer);
|
||||
}
|
||||
|
||||
writer.Write(string.Empty); // FriendCode
|
||||
writer.Write(string.Empty); // PUID
|
||||
return new ValueTask<bool>(true);
|
||||
}
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
public override ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer? target, IMessageReader reader, bool initialState)
|
||||
{
|
||||
PlayerId = reader.ReadByte();
|
||||
ClientId = reader.ReadPackedInt32();
|
||||
|
||||
Outfits.Clear();
|
||||
var b = reader.ReadByte();
|
||||
for (var i = 0; i < b; i++)
|
||||
@@ -93,7 +154,15 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
Disconnected = (flag & 1) != 0;
|
||||
IsDead = (flag & 4) != 0;
|
||||
|
||||
_ = (RoleTypes)reader.ReadUInt16(); // ignore the RoleType here and only trust the SetRole rpc
|
||||
// Ignore the RoleType here and only trust the SetRole RPC, as
|
||||
// RoleType is not nullable in vanilla, while Impostor checks game
|
||||
// starts based on assigned roles.
|
||||
_ = (RoleTypes)reader.ReadUInt16();
|
||||
|
||||
if (reader.ReadBoolean())
|
||||
{
|
||||
RoleWhenAlive = (RoleTypes)reader.ReadUInt16();
|
||||
}
|
||||
|
||||
var taskCount = reader.ReadByte();
|
||||
|
||||
@@ -106,6 +175,12 @@ 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
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,8 @@ internal class CompatibilityManager : ICompatibilityManager
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new GameVersion(2023, 10, 1), // 2023.10.24
|
||||
new GameVersion(2023, 11, 15), // 2023.11.28
|
||||
},
|
||||
new[]
|
||||
{
|
||||
new GameVersion(2024, 2, 1), // 2024.3.5
|
||||
new GameVersion(2024, 2, 3), // 2024.6.4
|
||||
new GameVersion(2024, 3, 1), // 2024.6.18
|
||||
new GameVersion(2024, 4, 1), // 2024.8.13
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Innersloth;
|
||||
@@ -30,12 +31,21 @@ namespace Impostor.Server.Net.State
|
||||
/// </summary>
|
||||
private const int CurrentClient = -3;
|
||||
|
||||
/// <summary>
|
||||
/// Used to list objects that are managed by the game server.
|
||||
/// </summary>
|
||||
private const int ServerOwned = -4;
|
||||
|
||||
/// <summary>
|
||||
/// The first NetId that is considered as a server owned Network ID that the client will not allocate by default.
|
||||
/// </summary>
|
||||
private const int MinServerNetId = 100000;
|
||||
|
||||
private static readonly Dictionary<uint, Type> SpawnableObjects = new()
|
||||
{
|
||||
[0] = typeof(InnerSkeldShipStatus),
|
||||
[1] = typeof(InnerMeetingHud),
|
||||
[2] = typeof(InnerLobbyBehaviour),
|
||||
[3] = typeof(InnerGameData),
|
||||
[4] = typeof(InnerPlayerControl),
|
||||
[5] = typeof(InnerMiraShipStatus),
|
||||
[6] = typeof(InnerPolusShipStatus),
|
||||
@@ -43,13 +53,19 @@ namespace Impostor.Server.Net.State
|
||||
[8] = typeof(InnerAirshipStatus),
|
||||
[9] = typeof(InnerHideAndSeekManager),
|
||||
[10] = typeof(InnerNormalGameManager),
|
||||
[11] = typeof(InnerPlayerInfo),
|
||||
[12] = typeof(InnerVoteBanSystem),
|
||||
[13] = typeof(InnerFungleShipStatus),
|
||||
};
|
||||
|
||||
private static readonly Dictionary<Type, uint> SpawnableObjectIds = SpawnableObjects.ToDictionary((i) => i.Value, (i) => i.Key);
|
||||
|
||||
private readonly List<InnerNetObject> _allObjects = new List<InnerNetObject>();
|
||||
|
||||
private readonly Dictionary<uint, InnerNetObject> _allObjectsFast = new Dictionary<uint, InnerNetObject>();
|
||||
|
||||
private uint _nextNetId = MinServerNetId;
|
||||
|
||||
public T? FindObjectByNetId<T>(uint netId)
|
||||
where T : IInnerNetObject
|
||||
{
|
||||
@@ -254,6 +270,10 @@ namespace Impostor.Server.Net.State
|
||||
sender.Scene = scene;
|
||||
|
||||
_logger.LogTrace("> Scene {0} to {1}", clientId, sender.Scene);
|
||||
|
||||
await SyncServerObjectsAsync(sender);
|
||||
await SpawnPlayerInfoAsync(sender);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -306,6 +326,17 @@ namespace Impostor.Server.Net.State
|
||||
}
|
||||
}
|
||||
|
||||
// Check for dirty netobjects
|
||||
foreach (var netObject in _allObjectsFast.Values)
|
||||
{
|
||||
if (netObject.IsDirty && netObject.OwnerId == ServerOwned)
|
||||
{
|
||||
_logger.LogTrace("Sending over {Type} {NetId}", netObject.GetType().Name, netObject.NetId);
|
||||
await SendObjectData(netObject);
|
||||
netObject.IsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -325,9 +356,17 @@ namespace Impostor.Server.Net.State
|
||||
break;
|
||||
}
|
||||
|
||||
case InnerGameData data:
|
||||
case InnerPlayerInfo playerInfo:
|
||||
{
|
||||
GameNet.GameData = data;
|
||||
if (!GameNet.GameData.AddPlayer(playerInfo))
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Could not add PlayerInfo for playerId {PlayerId} with NetId {newId}, already have NetId {oldNetId}",
|
||||
playerInfo.PlayerId,
|
||||
playerInfo.NetId,
|
||||
GameNet.GameData.GetPlayerById(playerInfo.PlayerId)?.NetId);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -357,7 +396,7 @@ namespace Impostor.Server.Net.State
|
||||
}
|
||||
|
||||
// Hook up InnerPlayerControl <-> InnerPlayerControl.PlayerInfo.
|
||||
var playerInfo = GameNet.GameData!.GetPlayerById(control.PlayerId) ?? GameNet.GameData.AddPlayer(control);
|
||||
var playerInfo = GameNet.GameData.GetPlayerById(control.PlayerId);
|
||||
|
||||
if (playerInfo != null)
|
||||
{
|
||||
@@ -401,12 +440,6 @@ namespace Impostor.Server.Net.State
|
||||
break;
|
||||
}
|
||||
|
||||
case InnerGameData:
|
||||
{
|
||||
GameNet.GameData = null;
|
||||
break;
|
||||
}
|
||||
|
||||
case InnerVoteBanSystem:
|
||||
{
|
||||
GameNet.VoteBan = null;
|
||||
@@ -419,13 +452,18 @@ namespace Impostor.Server.Net.State
|
||||
break;
|
||||
}
|
||||
|
||||
case InnerPlayerControl control:
|
||||
case InnerPlayerInfo playerInfo:
|
||||
{
|
||||
if (GameState != GameStates.Started && GameState != GameStates.Starting)
|
||||
{
|
||||
GameNet.GameData?.RemovePlayer(control);
|
||||
GameNet.GameData.RemovePlayer(playerInfo.PlayerId);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case InnerPlayerControl control:
|
||||
{
|
||||
// Remove InnerPlayerControl <-> IClientPlayer.
|
||||
if (TryGetPlayer(control.OwnerId, out var player))
|
||||
{
|
||||
@@ -438,6 +476,51 @@ namespace Impostor.Server.Net.State
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask SyncServerObjectsAsync(ClientPlayer sender)
|
||||
{
|
||||
foreach (var obj in _allObjectsFast.Values)
|
||||
{
|
||||
if (obj.OwnerId == ServerOwned)
|
||||
{
|
||||
_logger.LogTrace("Syncing {Type} {NetId}", obj.GetType(), obj.NetId);
|
||||
await SendObjectSpawnAsync(obj, sender.Client.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask SpawnPlayerInfoAsync(ClientPlayer sender)
|
||||
{
|
||||
// Hosts spawn PlayerInfo objects if they requested authority
|
||||
if (IsHostAuthoritive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Only spawn a new PlayerInfo if one has not yet been spawned
|
||||
if (GameNet.GameData.PlayersByClientId.ContainsKey(sender.Client.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var playerInfo = (InnerPlayerInfo)ActivatorUtilities.CreateInstance(_serviceProvider, typeof(InnerPlayerInfo), this);
|
||||
playerInfo.SpawnFlags = SpawnFlags.None;
|
||||
playerInfo.NetId = _nextNetId++;
|
||||
playerInfo.OwnerId = ServerOwned;
|
||||
playerInfo.ClientId = sender.Client.Id;
|
||||
playerInfo.PlayerId = GameNet.GameData.GetNextAvailablePlayerId();
|
||||
|
||||
if (!AddNetObject(playerInfo))
|
||||
{
|
||||
_logger.LogError("Couldn't spawn PlayerInfo for {Name} ({ClientId})", sender.Client.Name, sender.Client.Id);
|
||||
playerInfo.NetId = uint.MaxValue;
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogTrace("Spawning PlayerInfo (netId {Netid})", playerInfo.NetId);
|
||||
await OnSpawnAsync(sender, playerInfo);
|
||||
await SendObjectSpawnAsync(playerInfo);
|
||||
}
|
||||
|
||||
private bool AddNetObject(InnerNetObject obj)
|
||||
{
|
||||
if (_allObjectsFast.ContainsKey(obj.NetId))
|
||||
|
||||
@@ -112,5 +112,46 @@ namespace Impostor.Server.Net.State
|
||||
{
|
||||
Message12WaitForHostS2C.Serialize(message, clear, Code, player.Client.Id);
|
||||
}
|
||||
|
||||
private async ValueTask SendObjectSpawnAsync(InnerNetObject obj, int? targetClientId = null)
|
||||
{
|
||||
using var writer = StartGameData(targetClientId);
|
||||
writer.StartMessage(GameDataTag.SpawnFlag);
|
||||
writer.WritePacked(SpawnableObjectIds[obj.GetType()]);
|
||||
writer.WritePacked(obj.OwnerId);
|
||||
writer.Write((byte)obj.SpawnFlags);
|
||||
|
||||
var components = obj.GetComponentsInChildren<InnerNetObject>();
|
||||
writer.WritePacked(components.Count);
|
||||
foreach (var component in components)
|
||||
{
|
||||
writer.WritePacked(obj.NetId);
|
||||
writer.StartMessage(1);
|
||||
await component.SerializeAsync(writer, true);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
writer.EndMessage();
|
||||
await FinishGameDataAsync(writer, targetClientId);
|
||||
}
|
||||
|
||||
private ValueTask SendObjectDespawn(InnerNetObject obj, int? targetClientId = null)
|
||||
{
|
||||
using var writer = StartGameData(targetClientId);
|
||||
writer.StartMessage(GameDataTag.DespawnFlag);
|
||||
writer.WritePacked(obj.NetId);
|
||||
writer.EndMessage();
|
||||
return FinishGameDataAsync(writer, targetClientId);
|
||||
}
|
||||
|
||||
private async ValueTask SendObjectData(InnerNetObject obj, int? targetClientId = null)
|
||||
{
|
||||
using var writer = StartGameData(targetClientId);
|
||||
writer.StartMessage(GameDataTag.DataFlag);
|
||||
writer.WritePacked(obj.NetId);
|
||||
await obj.SerializeAsync(writer, false);
|
||||
writer.EndMessage();
|
||||
await FinishGameDataAsync(writer, targetClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,24 @@ namespace Impostor.Server.Net.State
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up the PlayerInfo if we own it
|
||||
foreach (var playerInfo in GameNet.GameData.Players.Values)
|
||||
{
|
||||
if (playerInfo.ClientId == playerId)
|
||||
{
|
||||
if (playerInfo.OwnerId == ServerOwned)
|
||||
{
|
||||
_logger.LogInformation("Destroying PlayerInfo {nid}", playerInfo.NetId);
|
||||
GameNet.GameData.RemovePlayer(playerInfo.PlayerId);
|
||||
RemoveNetObject(playerInfo);
|
||||
|
||||
await SendObjectDespawn(playerInfo);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Impostor.Server.Net.State
|
||||
|
||||
IInnerLobbyBehaviour? IGameNet.LobbyBehaviour => LobbyBehaviour;
|
||||
|
||||
IInnerGameData? IGameNet.GameData => GameData;
|
||||
IInnerGameData IGameNet.GameData => GameData;
|
||||
|
||||
IInnerVoteBanSystem? IGameNet.VoteBan => VoteBan;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Impostor.Server.Net.State
|
||||
|
||||
public InnerLobbyBehaviour? LobbyBehaviour { get; internal set; }
|
||||
|
||||
public InnerGameData? GameData { get; internal set; }
|
||||
public InnerGameData GameData { get; internal set; } = new();
|
||||
|
||||
public InnerVoteBanSystem? VoteBan { get; internal set; }
|
||||
|
||||
|
||||
Reference in new issue
Block a user