Merge pull request #442 from Impostor/2021.11.9

2021.11.9 and 2021.12.14 support
This commit is contained in:
miniduikboot authored and GitHub committed 2021-12-21 23:27:38 +01:00
commit d7d7547182
77 files changed
+819 -872

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<VersionPrefix>1.6.0</VersionPrefix>
<VersionPrefix>1.7.0</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
</PropertyGroup>
@@ -1,38 +0,0 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Events.Announcements
{
/// <summary>
/// Event fired after client requests a announcement.
/// </summary>
public interface IAnnouncementRequestEvent : IEvent
{
public interface IResponse
{
/// <summary>
/// Gets or sets a value indicating whether announcement should be loaded from client's cache, can save some bytes.
/// </summary>
public bool UseCached { get; set; }
/// <summary>
/// Gets or sets announcement, should be null when <see cref="UseCached" /> is set to true.
/// </summary>
public Announcement? Announcement { get; set; }
}
/// <summary>
/// Gets client's last announcement id.
/// </summary>
public int Id { get; }
/// <summary>
/// Gets client's language.
/// </summary>
public Language Language { get; }
/// <summary>
/// Gets or sets plugin made response.
/// </summary>
public IResponse Response { get; set; }
}
}
@@ -16,6 +16,13 @@ namespace Impostor.Api
return original[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short ReadInt16(this ref ReadOnlySpan<byte> input)
{
var original = Advance<short>(ref input);
return BinaryPrimitives.ReadInt16LittleEndian(original);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReadInt32(this ref ReadOnlySpan<byte> input)
{
@@ -1,14 +0,0 @@
namespace Impostor.Api.Innersloth
{
public readonly struct Announcement
{
public readonly int Id;
public readonly string Message;
public Announcement(int id, string message)
{
Id = id;
Message = message;
}
}
}
@@ -1,121 +0,0 @@
namespace Impostor.Api.Innersloth.Customization
{
public enum HatType : uint
{
NoHat = 0,
Astronaut = 1,
BaseballCap = 2,
BrainSlug = 3,
BushHat = 4,
CaptainsHat = 5,
DoubleTopHat = 6,
Flowerpot = 7,
Goggles = 8,
HardHat = 9,
Military = 10,
PaperHat = 11,
PartyHat = 12,
Police = 13,
Stethescope = 14,
TopHat = 15,
TowelWizard = 16,
Ushanka = 17,
Viking = 18,
WallCap = 19,
Snowman = 20,
Reindeer = 21,
Lights = 22,
Santa = 23,
Tree = 24,
Present = 25,
Candycanes = 26,
ElfHat = 27,
NewYears2018 = 28,
WhiteHat = 29,
Crown = 30,
Eyebrows = 31,
HaloHat = 32,
HeroCap = 33,
PipCap = 34,
PlungerHat = 35,
ScubaHat = 36,
StickminHat = 37,
StrawHat = 38,
TenGallonHat = 39,
ThirdEyeHat = 40,
ToiletPaperHat = 41,
Toppat = 42,
BlackFedora = 43,
SkiGoggles = 44,
Headphones = 45,
MaskHat = 46,
PaperMask = 47,
Security = 48,
StrapHat = 49,
Banana = 50,
Beanie = 51,
Bear = 52,
Cheese = 53,
Cherry = 54,
Egg = 55,
GreenFedora = 56,
Flamingo = 57,
FlowerPin = 58,
Helmet = 59,
Plant = 60,
BatEyes = 61,
BatWings = 62,
Horns = 63,
Mohawk = 64,
Pumpkin = 65,
ScaryBag = 66,
Witch = 67,
Wolf = 68,
Pirate = 69,
Plague = 70,
Machete = 71,
Fred = 72,
MinerCap = 73,
WinterHat = 74,
Archae = 75,
Antenna = 76,
Balloon = 77,
BirdNest = 78,
BlackBelt = 79,
Caution = 80,
Chef = 81,
CopHat = 82,
DoRag = 83,
DumSticker = 84,
Fez = 85,
GeneralHat = 86,
GreyThing = 87,
HunterCap = 88,
JungleHat = 89,
MiniCrewmate = 90,
NinjaMask = 91,
RamHorns = 92,
SnowCrewmate = 93,
GeoffHat = 94,
DaveCap = 95,
EllieRose = 96,
SvenHat = 97,
BurtHat = 98,
EllryMohawk = 99,
ThomasMonoc = 100,
Wizard = 101,
Fredrick = 102,
MrMacbeth = 103,
ToppatHenry = 104,
ToppatEllie = 105,
GeoffreyPlumb = 106,
RightHandMan = 107,
AngryEyebrows = 108,
ChocolateIce = 109,
HeartPin = 110,
Ponytail = 111,
RubberGlove = 112,
UnicornHorn = 113,
Zipper = 114,
}
}
@@ -1,18 +0,0 @@
namespace Impostor.Api.Innersloth.Customization
{
public enum PetType : uint
{
NoPet = 0,
Alien = 1,
Crewmate = 2,
Doggy = 3,
Stickmin = 4,
Hamster = 5,
Robot = 6,
Ufo = 7,
Ellie = 8,
Squig = 9,
Bedcrab = 10,
Glitch = 11,
}
}
@@ -0,0 +1,56 @@
using Impostor.Api.Net.Messages;
namespace Impostor.Api.Innersloth.Customization
{
public class PlayerOutfit
{
public ColorType Color { get; internal set; } = (ColorType)(-1);
public string HatId { get; internal set; } = "missing";
public string PetId { get; internal set; } = "missing";
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
{
get
{
if (!string.IsNullOrEmpty(PlayerName) && Color != (ColorType)(-1) && HatId != "missing" && PetId != "missing" && SkinId != "missing" && VisorId != "missing")
{
return NamePlateId == "missing";
}
return true;
}
}
public void Serialize(IMessageWriter writer)
{
writer.Write(PlayerName);
writer.WritePacked((int)Color);
writer.Write(HatId);
writer.Write(PetId);
writer.Write(SkinId);
writer.Write(VisorId);
writer.Write(NamePlateId);
}
public void Deserialize(IMessageReader reader)
{
PlayerName = reader.ReadString();
Color = (ColorType)reader.ReadPackedInt32();
HatId = reader.ReadString();
PetId = reader.ReadString();
SkinId = reader.ReadString();
VisorId = reader.ReadString();
NamePlateId = reader.ReadString();
}
}
}
@@ -0,0 +1,8 @@
namespace Impostor.Api.Innersloth.Customization
{
public enum PlayerOutfitType
{
Default,
Shapeshifted,
}
}
@@ -1,25 +0,0 @@
namespace Impostor.Api.Innersloth.Customization
{
public enum SkinType : uint
{
None = 0,
Astro = 1,
Capt = 2,
Mech = 3,
Military = 4,
Police = 5,
Science = 6,
SuitB = 7,
SuitW = 8,
Wall = 9,
Hazmat = 10,
Security = 11,
Tarmac = 12,
Miner = 13,
Winter = 14,
Archae = 15,
Prisoner = 16,
CCC = 17,
RightHandMan = 18,
}
}
+16 -1
View File
@@ -9,7 +9,7 @@ namespace Impostor.Api.Innersloth
/// <summary>
/// The latest major version of the game client.
/// </summary>
public const int LatestVersion = 4;
public const int LatestVersion = 5;
/// <summary>
/// Gets or sets host's version of the game.
@@ -124,6 +124,11 @@ namespace Impostor.Api.Innersloth
/// </summary>
public TaskBarUpdate TaskBarUpdate { get; set; } = TaskBarUpdate.Always;
/// <summary>
/// Gets or sets role options.
/// </summary>
public RoleOptionsData RoleOptions { get; set; } = new RoleOptionsData();
/// <summary>
/// Gets or sets a value indicating whether the GameOptions are the default ones.
/// </summary>
@@ -184,6 +189,11 @@ namespace Impostor.Api.Innersloth
}
if (version > 4)
{
RoleOptions.Serialize(writer);
}
if (version > 5)
{
throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
}
@@ -246,6 +256,11 @@ namespace Impostor.Api.Innersloth
}
if (Version > 4)
{
RoleOptions = RoleOptionsData.Deserialize(bytes);
}
if (Version > 5)
{
throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
}
@@ -0,0 +1,50 @@
using System;
using Impostor.Api.Net.Messages;
namespace Impostor.Api.Innersloth
{
public class PlatformSpecificData
{
public PlatformSpecificData(Platforms platform, string platformName, ulong? xboxPlatformId = null, ulong? psnPlatformId = null)
{
Platform = platform;
PlatformName = platformName;
XboxPlatformId = xboxPlatformId;
PsnPlatformId = psnPlatformId;
}
public PlatformSpecificData(IMessageReader reader)
{
Platform = (Platforms)reader.Tag;
PlatformName = reader.ReadString();
XboxPlatformId = Platform == Platforms.Xbox ? reader.ReadUInt64() : null;
PsnPlatformId = Platform == Platforms.Playstation ? reader.ReadUInt64() : null;
}
public Platforms Platform { get; }
public string PlatformName { get; }
public ulong? XboxPlatformId { get; }
public ulong? PsnPlatformId { get; }
public void Serialize(IMessageWriter writer)
{
writer.StartMessage((byte)Platform);
writer.Write(PlatformName);
switch (Platform)
{
case Platforms.Xbox:
writer.Write(XboxPlatformId ?? throw new NullReferenceException($"{nameof(XboxPlatformId)} shouldn't be null when {nameof(Platform)} is Xbox"));
break;
case Platforms.Playstation:
writer.Write(PsnPlatformId ?? throw new NullReferenceException($"{nameof(PsnPlatformId)} shouldn't be null when {nameof(Platform)} is Playstation"));
break;
}
writer.EndMessage();
}
}
}
+2
View File
@@ -11,5 +11,7 @@ namespace Impostor.Api.Innersloth
IPhone = 6,
Android = 7,
Switch = 8,
Xbox = 9,
Playstation = 10,
}
}
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.IO;
using Impostor.Api.Net.Messages;
namespace Impostor.Api.Innersloth
{
public class RoleOptionsData
{
public bool ShapeshifterLeaveSkin { get; set; }
public byte ShapeshifterCooldown { get; set; } = 10;
public byte ShapeshifterDuration { get; set; } = 30;
public byte ScientistCooldown { get; set; } = 15;
public byte ScientistBatteryCharge { get; set; } = 5;
public byte GuardianAngelCooldown { get; set; } = 60;
public bool ImpostorsCanSeeProtect { get; set; }
public byte ProtectionDurationSeconds { get; set; } = 10;
public byte EngineerCooldown { get; set; } = 30;
public byte EngineerInVentMaxTime { get; set; } = 15;
public Dictionary<RoleTypes, RoleRate> RoleRates { get; } = new Dictionary<RoleTypes, RoleRate>();
public static RoleOptionsData Deserialize(ReadOnlySpan<byte> span)
{
var roleOptionsData = new RoleOptionsData();
var num = span.ReadInt32();
for (var i = 0; i < num; i++)
{
var key = (RoleTypes)span.ReadInt16();
var roleRate = new RoleRate(span.ReadByte(), span.ReadByte());
roleOptionsData.RoleRates[key] = roleRate;
}
roleOptionsData.ShapeshifterLeaveSkin = span.ReadBoolean();
roleOptionsData.ShapeshifterCooldown = span.ReadByte();
roleOptionsData.ShapeshifterDuration = span.ReadByte();
roleOptionsData.ScientistCooldown = span.ReadByte();
roleOptionsData.GuardianAngelCooldown = span.ReadByte();
roleOptionsData.EngineerCooldown = span.ReadByte();
roleOptionsData.EngineerInVentMaxTime = span.ReadByte();
roleOptionsData.ScientistBatteryCharge = span.ReadByte();
roleOptionsData.ProtectionDurationSeconds = span.ReadByte();
roleOptionsData.ImpostorsCanSeeProtect = span.ReadBoolean();
return roleOptionsData;
}
public static RoleOptionsData Deserialize(IMessageReader reader)
{
var roleOptionsData = new RoleOptionsData();
var num = reader.ReadPackedInt32();
for (var i = 0; i < num; i++)
{
var key = (RoleTypes)reader.ReadInt16();
var roleRate = new RoleRate(reader.ReadByte(), reader.ReadByte());
roleOptionsData.RoleRates[key] = roleRate;
}
roleOptionsData.ShapeshifterLeaveSkin = reader.ReadBoolean();
roleOptionsData.ShapeshifterCooldown = reader.ReadByte();
roleOptionsData.ShapeshifterDuration = reader.ReadByte();
roleOptionsData.ScientistCooldown = reader.ReadByte();
roleOptionsData.GuardianAngelCooldown = reader.ReadByte();
roleOptionsData.EngineerCooldown = reader.ReadByte();
roleOptionsData.EngineerInVentMaxTime = reader.ReadByte();
roleOptionsData.ScientistBatteryCharge = reader.ReadByte();
roleOptionsData.ProtectionDurationSeconds = reader.ReadByte();
roleOptionsData.ImpostorsCanSeeProtect = reader.ReadBoolean();
return roleOptionsData;
}
public void Serialize(IMessageWriter writer)
{
writer.WritePacked(RoleRates.Count);
foreach (var roleRate in RoleRates)
{
writer.Write((ushort)roleRate.Key);
writer.Write((byte)roleRate.Value.MaxCount);
writer.Write((byte)roleRate.Value.Chance);
}
writer.Write(ShapeshifterLeaveSkin);
writer.Write(ShapeshifterCooldown);
writer.Write(ShapeshifterDuration);
writer.Write(ScientistCooldown);
writer.Write(GuardianAngelCooldown);
writer.Write(EngineerCooldown);
writer.Write(EngineerInVentMaxTime);
writer.Write(ScientistBatteryCharge);
writer.Write(ProtectionDurationSeconds);
writer.Write(ImpostorsCanSeeProtect);
}
public void Serialize(BinaryWriter writer)
{
writer.Write(RoleRates.Count);
foreach (var roleRate in RoleRates)
{
writer.Write((ushort)roleRate.Key);
writer.Write((byte)roleRate.Value.MaxCount);
writer.Write((byte)roleRate.Value.Chance);
}
writer.Write(ShapeshifterLeaveSkin);
writer.Write(ShapeshifterCooldown);
writer.Write(ShapeshifterDuration);
writer.Write(ScientistCooldown);
writer.Write(GuardianAngelCooldown);
writer.Write(EngineerCooldown);
writer.Write(EngineerInVentMaxTime);
writer.Write(ScientistBatteryCharge);
writer.Write(ProtectionDurationSeconds);
writer.Write(ImpostorsCanSeeProtect);
}
public readonly struct RoleRate
{
public readonly int MaxCount;
public readonly int Chance;
public RoleRate(int maxCount, int chance)
{
MaxCount = maxCount;
Chance = chance;
}
}
}
}
+12
View File
@@ -0,0 +1,12 @@
namespace Impostor.Api.Innersloth
{
public enum RoleTypes : ushort
{
Crewmate,
Impostor,
Scientist,
Engineer,
GuardianAngel,
Shapeshifter,
}
}
+6 -1
View File
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using Impostor.Api.Innersloth;
using Impostor.Api.Net.Messages;
@@ -65,6 +65,11 @@ namespace Impostor.Api.Net
/// </summary>
IClientPlayer? Player { get; }
/// <summary>
/// Gets platform specific data of the <see cref="IClient" />.
/// </summary>
PlatformSpecificData PlatformSpecificData { get; }
ValueTask<bool> ReportCheatAsync(CheatContext context, string message);
ValueTask HandleMessageAsync(IMessageReader message, MessageType messageType);
@@ -49,25 +49,25 @@ namespace Impostor.Api.Net.Inner.Objects
/// Sets the hat of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="hatType">An hat for the player.</param>
/// <param name="hatId">An hat for the player.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask SetHatAsync(HatType hatType);
ValueTask SetHatAsync(string hatId);
/// <summary>
/// Sets the pet of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="petType">A pet for the player.</param>
/// <param name="petId">A pet for the player.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask SetPetAsync(PetType petType);
ValueTask SetPetAsync(string petId);
/// <summary>
/// Sets the skin of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="skinType">A skin for the player.</param>
/// <param name="skinId">A skin for the player.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask SetSkinAsync(SkinType skinType);
ValueTask SetSkinAsync(string skinId);
/// <summary>
/// Send a chat message as the current <see cref="IInnerPlayerControl" />.
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Customization;
@@ -12,26 +12,6 @@ namespace Impostor.Api.Net.Inner.Objects
/// </summary>
string PlayerName { get; }
/// <summary>
/// Gets the color of the player.
/// </summary>
ColorType Color { get; }
/// <summary>
/// Gets the hat of the player.
/// </summary>
HatType Hat { get; }
/// <summary>
/// Gets the pet of the player.
/// </summary>
PetType Pet { get; }
/// <summary>
/// Gets the skin of the player.
/// </summary>
SkinType Skin { get; }
/// <summary>
/// Gets a value indicating whether the player is an impostor.
/// </summary>
@@ -42,6 +22,12 @@ namespace Impostor.Api.Net.Inner.Objects
/// </summary>
bool IsDead { get; }
Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; }
PlayerOutfitType CurrentOutfitType { get; set; }
PlayerOutfit CurrentOutfit { get; }
/// <summary>
/// Gets the reason why the player is dead in the current game.
/// </summary>
@@ -50,5 +36,7 @@ namespace Impostor.Api.Net.Inner.Objects
IEnumerable<ITaskInfo> Tasks { get; }
DateTimeOffset LastMurder { get; }
uint PlayerLevel { get; }
}
}
+11 -3
View File
@@ -11,15 +11,12 @@
SetName = 6,
CheckColor = 7,
SetColor = 8,
SetHat = 9,
SetSkin = 10,
ReportDeadBody = 11,
MurderPlayer = 12,
SendChat = 13,
StartMeeting = 14,
SetScanner = 15,
SendChatNote = 16,
SetPet = 17,
SetStartCounter = 18,
EnterVent = 19,
ExitVent = 20,
@@ -37,5 +34,16 @@
SendQuickChat = 33,
BootFromVent = 34,
UpdateSystem = 35,
SetLevel = 38,
SetHat = 39,
SetSkin = 40,
SetPet = 41,
SetVisor = 42,
SetNamePlate = 43,
SetRole = 44,
ProtectPlayer = 45,
Shapeshift = 46,
CheckMurder = 47,
CheckProtect = 48,
}
}
@@ -1,15 +0,0 @@
namespace Impostor.Api.Net.Messages.Announcements
{
public static class Message00UseCache
{
public static void Serialize(IMessageWriter writer)
{
writer.StartMessage(AnnouncementsMessageFlags.UseCache);
writer.EndMessage();
}
public static void Deserialize(IMessageReader reader)
{
}
}
}
@@ -1,19 +0,0 @@
namespace Impostor.Api.Net.Messages.Announcements
{
public static class Message01Update
{
public static void Serialize(IMessageWriter writer, int id, string message)
{
writer.StartMessage(AnnouncementsMessageFlags.SetUpdate);
writer.WritePacked(id);
writer.Write(message);
writer.EndMessage();
}
public static void Deserialize(IMessageReader reader, out int id, out string message)
{
id = reader.ReadPackedInt32();
message = reader.ReadString();
}
}
}
@@ -1,32 +0,0 @@
using System.Collections.Generic;
namespace Impostor.Api.Net.Messages.Announcements
{
public static class Message04AddLanguages
{
public static void Serialize(IMessageWriter writer, Dictionary<string, uint> languages)
{
writer.StartMessage(AnnouncementsMessageFlags.AddLanguages);
writer.Write((byte)languages.Count);
foreach (var (name, id) in languages)
{
writer.Write(name);
writer.Write(id);
}
writer.EndMessage();
}
public static void Deserialize(IMessageReader reader, out Dictionary<string, uint> languages)
{
var length = reader.ReadByte();
languages = new Dictionary<string, uint>(length);
for (var i = 0; i < length; i++)
{
languages[reader.ReadString()] = reader.ReadUInt32();
}
}
}
}
@@ -1,15 +0,0 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.Announcements
{
public class MessageHello
{
public static void Deserialize(IMessageReader reader, out int announcementVersion, out int id, out Language language)
{
reader.ReadByte(); // SendOption header, probably added by accident
announcementVersion = reader.ReadPackedInt32();
id = reader.ReadPackedInt32();
language = (Language)reader.ReadPackedInt32();
}
}
}
@@ -1,9 +0,0 @@
namespace Impostor.Api.Net.Messages
{
public class AnnouncementsMessageFlags
{
public const byte UseCache = 0;
public const byte SetUpdate = 1;
public const byte AddLanguages = 4;
}
}
@@ -1,15 +0,0 @@
namespace Impostor.Api.Net.Messages.Auth
{
public static class Message01Complete
{
public static void Serialize(IMessageWriter writer, uint nonce)
{
writer.WritePacked(nonce);
}
public static void Deserialize(IMessageReader reader, out uint nonce)
{
nonce = reader.ReadUInt32();
}
}
}
@@ -1,14 +0,0 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.Auth
{
public static class MessageHandshake
{
public static void Deserialize(IMessageReader reader, out int clientVersion, out Platforms platform, out string clientId)
{
clientVersion = reader.ReadInt32();
platform = (Platforms)reader.ReadByte();
clientId = reader.ReadString();
}
}
}
@@ -4,18 +4,14 @@ namespace Impostor.Api.Net.Messages.C2S
{
public static class HandshakeC2S
{
public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out uint? lastNonceReceived, out Language language, out QuickChatModes chatMode)
public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out Language language, out QuickChatModes chatMode, out PlatformSpecificData? platformSpecificData)
{
clientVersion = reader.ReadInt32();
name = reader.ReadString();
if (clientVersion >= Version.V1)
{
lastNonceReceived = reader.ReadUInt32();
}
else
{
lastNonceReceived = null;
reader.ReadUInt32(); // lastNonceReceived, always 0 since 2021.11.9
}
if (clientVersion >= Version.V2)
@@ -28,6 +24,22 @@ namespace Impostor.Api.Net.Messages.C2S
language = Language.English;
chatMode = QuickChatModes.FreeChatOrQuickChat;
}
if (clientVersion >= Version.V3)
{
using var platformReader = reader.ReadMessage();
platformSpecificData = new PlatformSpecificData(platformReader);
reader.ReadInt32(); // crossplayFlags, not used yet
}
else
{
platformSpecificData = null;
}
if (clientVersion >= Version.V4)
{
reader.ReadByte(); // purpose unknown, seems hardcoded to 0
}
}
private static class Version
@@ -35,6 +47,10 @@ namespace Impostor.Api.Net.Messages.C2S
public static readonly int V1 = GameVersion.GetVersion(2021, 4, 25);
public static readonly int V2 = GameVersion.GetVersion(2021, 6, 30);
public static readonly int V3 = GameVersion.GetVersion(2021, 11, 9);
public static readonly int V4 = GameVersion.GetVersion(2021, 12, 14);
}
}
}
@@ -8,6 +8,7 @@ namespace Impostor.Api.Net.Messages.C2S
{
writer.StartMessage(MessageFlags.HostGame);
gameOptionsData.Serialize(writer);
writer.Write(int.MaxValue); // crossplayFlags
writer.EndMessage();
}
@@ -15,12 +16,11 @@ namespace Impostor.Api.Net.Messages.C2S
/// Deserialize a packet.
/// </summary>
/// <param name="reader"><see cref="IMessageReader" /> with <see cref="IMessageReader.Tag" /> 0.</param>
/// <param name="chatMode">The chat type selected in the client of the player.</param>
/// <returns>Deserialized <see cref="GameOptionsData" />.</returns>
public static GameOptionsData Deserialize(IMessageReader reader, out QuickChatModes chatMode)
public static GameOptionsData Deserialize(IMessageReader reader)
{
var gameOptionsData = GameOptionsData.DeserializeCreate(reader);
chatMode = (QuickChatModes)reader.ReadByte();
reader.ReadInt32(); // crossplayFlags, not used yet
return gameOptionsData;
}
@@ -13,6 +13,7 @@ namespace Impostor.Api.Net.Messages.C2S
public static void Deserialize(IMessageReader reader, out GameCode gameCode)
{
gameCode = reader.ReadInt32();
reader.ReadBoolean(); // no crossplay
}
}
}
@@ -12,9 +12,15 @@ namespace Impostor.Api.Net.Messages.C2S
public static void Deserialize(IMessageReader reader, out GameOptionsData options, out QuickChatModes chatMode)
{
reader.ReadPackedInt32(); // Hardcoded 0.
var version = reader.ReadPackedInt32();
if (version != 2)
{
throw new NotSupportedException($"Version {version} of {nameof(Message16GetGameListC2S)} is not supported");
}
options = GameOptionsData.DeserializeCreate(reader);
chatMode = (QuickChatModes)reader.ReadByte();
reader.ReadInt32(); // crossplayFlags, not used yet
}
}
}
@@ -0,0 +1,17 @@
using System;
namespace Impostor.Api.Net.Messages.C2S
{
public class Message21SetActivePodType
{
public static void Serialize(IMessageWriter writer)
{
throw new NotImplementedException();
}
public static void Deserialize(IMessageReader reader, out string podType)
{
podType = reader.ReadString();
}
}
}
@@ -48,6 +48,10 @@ namespace Impostor.Api.Net.Messages
int ReadInt32();
ulong ReadUInt64();
long ReadInt64();
float ReadSingle();
string ReadString(int length);
@@ -68,6 +68,18 @@ namespace Impostor.Api.Net.Messages
/// <param name="value">Value to write.</param>
void Write(int value);
/// <summary>
/// Writes an ulong to the message.
/// </summary>
/// <param name="value">Value to write.</param>
void Write(ulong value);
/// <summary>
/// Writes an ulong to the message.
/// </summary>
/// <param name="value">Value to write.</param>
void Write(long value);
/// <summary>
/// Writes a float to the message.
/// </summary>
@@ -16,7 +16,13 @@
public const byte WaitForHost = 12;
public const byte Redirect = 13;
public const byte ReselectServer = 14;
public const byte GetGameList = 9;
public const byte GetGameListV2 = 16;
public const byte ReportPlayer = 17;
public const byte QuickMatch = 18;
public const byte QuickMatchHost = 19;
public const byte SetGameSession = 20;
public const byte SetActivePodType = 21;
public const byte QueryPlatformIds = 22;
public const byte QueryLobbyInfo = 23;
}
}
@@ -1,17 +0,0 @@
using Impostor.Api.Innersloth.Customization;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc09SetHat
{
public static void Serialize(IMessageWriter writer, HatType hat)
{
writer.WritePacked((uint)hat);
}
public static void Deserialize(IMessageReader reader, out HatType hat)
{
hat = (HatType)reader.ReadPackedUInt32();
}
}
}
@@ -1,17 +0,0 @@
using Impostor.Api.Innersloth.Customization;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc10SetSkin
{
public static void Serialize(IMessageWriter writer, SkinType skin)
{
writer.WritePacked((uint)skin);
}
public static void Deserialize(IMessageReader reader, out SkinType skin)
{
skin = (SkinType)reader.ReadPackedUInt32();
}
}
}
@@ -1,17 +0,0 @@
using Impostor.Api.Innersloth.Customization;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc17SetPet
{
public static void Serialize(IMessageWriter writer, PetType pet)
{
writer.WritePacked((uint)pet);
}
public static void Deserialize(IMessageReader reader, out PetType pet)
{
pet = (PetType)reader.ReadPackedUInt32();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc39SetHat
{
public static void Serialize(IMessageWriter writer, string hat)
{
writer.Write(hat);
}
public static void Deserialize(IMessageReader reader, out string hat)
{
hat = reader.ReadString();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc40SetSkin
{
public static void Serialize(IMessageWriter writer, string skin)
{
writer.Write(skin);
}
public static void Deserialize(IMessageReader reader, out string skin)
{
skin = reader.ReadString();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc41SetPet
{
public static void Serialize(IMessageWriter writer, string pet)
{
writer.Write(pet);
}
public static void Deserialize(IMessageReader reader, out string pet)
{
pet = reader.ReadString();
}
}
}
@@ -0,0 +1,17 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc44SetRole
{
public static void Serialize(IMessageWriter writer, RoleTypes role)
{
writer.Write((ushort)role);
}
public static void Deserialize(IMessageReader reader, out RoleTypes role)
{
role = (RoleTypes)reader.ReadUInt16();
}
}
}
@@ -0,0 +1,21 @@
using Impostor.Api.Games;
using Impostor.Api.Innersloth.Customization;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc45ProtectPlayer
{
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl, ColorType color)
{
writer.Write(playerControl.NetId);
writer.Write((byte)color);
}
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl, out ColorType color)
{
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
color = (ColorType)reader.ReadByte();
}
}
}
@@ -0,0 +1,20 @@
using Impostor.Api.Games;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc46Shapeshift
{
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl, bool shouldAnimate)
{
writer.Write(playerControl.NetId);
writer.Write(shouldAnimate);
}
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl, out bool shouldAnimate)
{
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
shouldAnimate = reader.ReadBoolean();
}
}
}
@@ -0,0 +1,18 @@
using Impostor.Api.Games;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc47CheckMurder
{
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl)
{
writer.Write(playerControl.NetId);
}
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl)
{
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
}
}
}
@@ -0,0 +1,18 @@
using Impostor.Api.Games;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc48CheckProtect
{
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl)
{
writer.Write(playerControl.NetId);
}
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl)
{
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
}
}
}
@@ -1,11 +1,11 @@
using System;
using System;
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.S2C
{
public class Message01JoinGameS2C
{
public static void SerializeJoin(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId)
public static void SerializeJoin(IMessageWriter writer, bool clear, int gameCode, IClientPlayer player, int hostId)
{
if (clear)
{
@@ -14,8 +14,11 @@ namespace Impostor.Api.Net.Messages.S2C
writer.StartMessage(MessageFlags.JoinGame);
writer.Write(gameCode);
writer.Write(playerId);
writer.Write(player.Client.Id);
writer.Write(hostId);
writer.Write(player.Client.Name);
player.Client.PlatformSpecificData.Serialize(writer);
writer.WritePacked(player.Character?.PlayerInfo.PlayerLevel ?? 1);
writer.EndMessage();
}
@@ -1,10 +1,10 @@
using System;
using System;
namespace Impostor.Api.Net.Messages.S2C
{
public static class Message07JoinedGameS2C
{
public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId, int[] otherPlayerIds)
public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId, IClientPlayer[] otherPlayers)
{
if (clear)
{
@@ -15,11 +15,14 @@ namespace Impostor.Api.Net.Messages.S2C
writer.Write(gameCode);
writer.Write(playerId);
writer.Write(hostId);
writer.WritePacked(otherPlayerIds.Length);
writer.WritePacked(otherPlayers.Length);
foreach (var id in otherPlayerIds)
foreach (var ply in otherPlayers)
{
writer.WritePacked(id);
writer.WritePacked(ply.Client.Id);
writer.Write(ply.Client.Name);
ply.Client.PlatformSpecificData.Serialize(writer);
writer.WritePacked(ply.Character?.PlayerInfo.PlayerLevel ?? 1);
}
writer.EndMessage();
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Impostor.Api.Games;
@@ -25,6 +25,9 @@ namespace Impostor.Api.Net.Messages.S2C
writer.Write((byte)game.Options.Map);
writer.Write((byte)game.Options.NumImpostors);
writer.Write((byte)game.Options.MaxPlayers);
var platform = game.Host?.Client.PlatformSpecificData;
writer.Write((byte)(platform?.Platform ?? 0));
writer.Write(platform?.PlatformName ?? string.Empty);
writer.EndMessage();
}
@@ -39,7 +39,7 @@ namespace Impostor.Api.Net.Messages.S2C
if (hasReason)
{
var inner = reader.ReadMessage();
using var inner = reader.ReadMessage();
reason = (DisconnectReason)inner.ReadByte();
message = reason == DisconnectReason.Custom ? inner.ReadString() : null;
}
@@ -18,7 +18,6 @@ namespace Impostor.Plugins.Example
services.AddSingleton<IEventListener, ClientEventListener>();
services.AddSingleton<IEventListener, PlayerEventListener>();
services.AddSingleton<IEventListener, MeetingEventListener>();
services.AddSingleton<IEventListener, AnnouncementsListener>();
}
}
}
@@ -1,26 +0,0 @@
using Impostor.Api.Events;
using Impostor.Api.Events.Announcements;
using Impostor.Api.Innersloth;
namespace Impostor.Plugins.Example.Handlers
{
public class AnnouncementsListener : IEventListener
{
private const int Id = 50;
[EventListener]
public void OnAnnouncementRequestEvent(IAnnouncementRequestEvent e)
{
if (e.Id == Id)
{
// Client already has announcement cached, lets just use that
e.Response.UseCached = true;
}
else
{
// Client is receiving this announcement for the first time, window will popup
e.Response.Announcement = new Announcement(Id, "Hello!");
}
}
}
}
@@ -40,9 +40,10 @@ namespace Impostor.Plugins.Example.Handlers
{
// Modify player properties.
await playerControl.SetColorAsync((ColorType)_random.Next(1, 9));
await playerControl.SetHatAsync((HatType)_random.Next(1, 9));
await playerControl.SetSkinAsync((SkinType)_random.Next(1, 9));
await playerControl.SetPetAsync((PetType)_random.Next(1, 9));
// TODO Rewrite using cosmetics source generator
// await playerControl.SetHatAsync((HatType)_random.Next(1, 9));
// await playerControl.SetSkinAsync((SkinType)_random.Next(1, 9));
// await playerControl.SetPetAsync((PetType)_random.Next(1, 9));
await Task.Delay(TimeSpan.FromMilliseconds(5000));
}
@@ -74,9 +75,9 @@ namespace Impostor.Plugins.Example.Handlers
if (e.Message == "look")
{
await e.PlayerControl.SetColorAsync(ColorType.Pink);
await e.PlayerControl.SetHatAsync(HatType.Cheese);
await e.PlayerControl.SetSkinAsync(SkinType.Police);
await e.PlayerControl.SetPetAsync(PetType.Ufo);
await e.PlayerControl.SetHatAsync("hat_pk05_Cheese");
await e.PlayerControl.SetSkinAsync("skin_Police");
await e.PlayerControl.SetPetAsync("pet_alien1");
}
if (e.Message == "snap")
@@ -1,22 +0,0 @@
using Impostor.Server.Utils;
namespace Impostor.Server.Config
{
internal class AnnouncementsServerConfig
{
public const string Section = "AnnouncementsServer";
private string? _resolvedListenIp;
public bool Enabled { get; set; } = true;
public string ListenIp { get; set; } = "0.0.0.0";
public ushort ListenPort { get; set; } = 22024;
public string ResolveListenIp()
{
return _resolvedListenIp ??= IpUtils.ResolveIp(ListenIp);
}
}
}
@@ -1,27 +0,0 @@
using System.IO;
using Impostor.Server.Utils;
namespace Impostor.Server.Config
{
internal class AuthServerConfig
{
public const string Section = "AuthServer";
private string? _resolvedListenIp;
public bool Enabled { get; set; } = false;
public string ListenIp { get; set; } = "0.0.0.0";
public ushort ListenPort { get; set; } = 22025;
public string Certificate { get; set; } = Path.Combine("dtls", "certificate.pem");
public string PrivateKey { get; set; } = Path.Combine("dtls", "key.pem");
public string ResolveListenIp()
{
return _resolvedListenIp ??= IpUtils.ResolveIp(ListenIp);
}
}
}
@@ -1,27 +0,0 @@
using Impostor.Api.Events.Announcements;
using Impostor.Api.Innersloth;
namespace Impostor.Server.Events.Announcements
{
public class AnnouncementRequestEvent : IAnnouncementRequestEvent
{
public AnnouncementRequestEvent(int id, Language language)
{
Id = id;
Language = language;
}
public int Id { get; }
public Language Language { get; }
public IAnnouncementRequestEvent.IResponse Response { get; set; } = new AnnouncementResponse();
public class AnnouncementResponse : IAnnouncementRequestEvent.IResponse
{
public bool UseCached { get; set; } = false;
public Announcement? Announcement { get; set; } = null;
}
}
}
@@ -1,106 +0,0 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Impostor.Api.Events.Managers;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.Announcements;
using Impostor.Hazel;
using Impostor.Hazel.Udp;
using Impostor.Server.Config;
using Impostor.Server.Events.Announcements;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
namespace Impostor.Server.Net
{
internal class AnnouncementsService : IHostedService
{
private readonly ILogger<AnnouncementsService> _logger;
private readonly AnnouncementsServerConfig _config;
private readonly ObjectPool<MessageReader> _readerPool;
private readonly IEventManager _eventManager;
private UdpConnectionListener? _connection;
public AnnouncementsService(ILogger<AnnouncementsService> logger, IOptions<AnnouncementsServerConfig> config, ObjectPool<MessageReader> readerPool, IEventManager eventManager)
{
_logger = logger;
_config = config.Value;
_readerPool = readerPool;
_eventManager = eventManager;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var endpoint = new IPEndPoint(IPAddress.Parse(_config.ResolveListenIp()), _config.ListenPort);
var mode = endpoint.AddressFamily switch
{
AddressFamily.InterNetwork => IPMode.IPv4,
AddressFamily.InterNetworkV6 => IPMode.IPv6,
_ => throw new InvalidOperationException(),
};
_connection = new UdpConnectionListener(endpoint, _readerPool, mode)
{
NewConnection = OnNewConnection,
};
await _connection.StartAsync();
_logger.LogInformation("Announcements server is listening on {Address}:{Port}", endpoint.Address, endpoint.Port);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogWarning("Announcements server is shutting down!");
if (_connection != null)
{
await _connection.DisposeAsync();
}
}
private async ValueTask OnNewConnection(NewConnectionEventArgs e)
{
MessageHello.Deserialize(e.HandshakeData, out var announcementVersion, out var id, out var language);
_logger.LogDebug("Client requested announcement (version: {Version}, id: {Id}, language: {Language})", announcementVersion, id, language);
if (announcementVersion != 2)
{
await e.Connection.Disconnect("Unsupported announcement version");
return;
}
var @event = new AnnouncementRequestEvent(id, language);
await _eventManager.CallAsync(@event);
var response = @event.Response;
if (response.UseCached)
{
using var writer = MessageWriter.Get(MessageType.Reliable);
Message00UseCache.Serialize(writer);
await e.Connection.SendAsync(writer);
_logger.LogDebug("Sent UseCache response");
}
if (response.Announcement != null)
{
using var writer = MessageWriter.Get(MessageType.Reliable);
var announcement = response.Announcement.Value;
Message01Update.Serialize(writer, announcement.Id, announcement.Message);
await e.Connection.SendAsync(writer);
_logger.LogDebug("Sent ({Id}) {Message}", announcement.Id, announcement.Message);
}
await e.Connection.Disconnect(null);
}
}
}
-88
View File
@@ -1,88 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Impostor.Api.Events.Managers;
using Impostor.Api.Net.Messages;
using Impostor.Api.Net.Messages.Auth;
using Impostor.Hazel;
using Impostor.Hazel.Dtls;
using Impostor.Server.Config;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
namespace Impostor.Server.Net
{
internal class AuthService : IHostedService
{
private readonly ILogger<AuthService> _logger;
private readonly AuthServerConfig _config;
private readonly ObjectPool<MessageReader> _readerPool;
private readonly IEventManager _eventManager;
private DtlsConnectionListener? _connection;
public AuthService(ILogger<AuthService> logger, IOptions<AuthServerConfig> config, ObjectPool<MessageReader> readerPool, IEventManager eventManager)
{
_logger = logger;
_config = config.Value;
_readerPool = readerPool;
_eventManager = eventManager;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var endpoint = new IPEndPoint(IPAddress.Parse(_config.ResolveListenIp()), _config.ListenPort);
var mode = endpoint.AddressFamily switch
{
AddressFamily.InterNetwork => IPMode.IPv4,
AddressFamily.InterNetworkV6 => IPMode.IPv6,
_ => throw new InvalidOperationException(),
};
var rsa = RSA.Create();
rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(string.Join(string.Empty, (await File.ReadAllLinesAsync(_config.PrivateKey, cancellationToken)).Where(x => !x.StartsWith("-----")))), out _);
var cert = new X509Certificate2(_config.Certificate).CopyWithPrivateKey(rsa);
_connection = new DtlsConnectionListener(endpoint, _readerPool, mode);
_connection.SetCertificate(cert);
_connection.NewConnection = ConnectionOnNewConnection;
await _connection.StartAsync();
_logger.LogInformation("Auth server is listening on {Address}:{Port}", endpoint.Address, endpoint.Port);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogWarning("Auth server is shutting down!");
if (_connection != null)
{
await _connection.DisposeAsync();
}
}
private ValueTask ConnectionOnNewConnection(NewConnectionEventArgs e)
{
MessageHandshake.Deserialize(e.HandshakeData, out var clientVersion, out var platform, out var clientId);
_logger.LogTrace("New authentication request: {clientVersion}, {platform}, {clientId}", clientVersion, platform, clientId);
using var writer = MessageWriter.Get(MessageType.Reliable);
writer.StartMessage(1);
Message01Complete.Serialize(writer, (uint)RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue));
writer.EndMessage();
return e.Connection.SendAsync(writer);
}
}
}
+9 -3
View File
@@ -24,8 +24,8 @@ namespace Impostor.Server.Net
private readonly GameManager _gameManager;
private readonly ICustomMessageManager<ICustomRootMessage> _customMessageManager;
public Client(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, string name, int gameVersion, Language language, QuickChatModes chatMode, IHazelConnection connection)
: base(name, gameVersion, language, chatMode, connection)
public Client(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, IHazelConnection connection)
: base(name, gameVersion, language, chatMode, platformSpecificData, connection)
{
_logger = logger;
_antiCheatConfig = antiCheatOptions.Value;
@@ -64,7 +64,7 @@ namespace Impostor.Server.Net
case MessageFlags.HostGame:
{
// Read game settings.
var gameInfo = Message00HostGameC2S.Deserialize(reader, out _);
var gameInfo = Message00HostGameC2S.Deserialize(reader);
// Create game.
var game = await _gameManager.CreateAsync(this, gameInfo);
@@ -257,6 +257,12 @@ namespace Impostor.Server.Net
break;
}
case MessageFlags.SetActivePodType:
{
Message21SetActivePodType.Deserialize(reader, out _);
break;
}
default:
if (_customMessageManager.TryGet(flag, out var customRootMessage))
{
+4 -1
View File
@@ -13,12 +13,13 @@ namespace Impostor.Server.Net
{
internal abstract class ClientBase : IClient
{
protected ClientBase(string name, int gameVersion, Language language, QuickChatModes chatMode, IHazelConnection connection)
protected ClientBase(string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, IHazelConnection connection)
{
Name = name;
GameVersion = gameVersion;
Language = language;
ChatMode = chatMode;
PlatformSpecificData = platformSpecificData;
Connection = connection;
Items = new ConcurrentDictionary<object, object>();
}
@@ -31,6 +32,8 @@ namespace Impostor.Server.Net
public QuickChatModes ChatMode { get; }
public PlatformSpecificData PlatformSpecificData { get; }
public int GameVersion { get; }
public IHazelConnection Connection { get; }
@@ -15,9 +15,9 @@ namespace Impostor.Server.Net.Factories
_serviceProvider = serviceProvider;
}
public ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode)
public ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData)
{
var client = ActivatorUtilities.CreateInstance<TClient>(_serviceProvider, name, clientVersion, language, chatMode, connection);
var client = ActivatorUtilities.CreateInstance<TClient>(_serviceProvider, name, clientVersion, language, chatMode, platformSpecificData, connection);
connection.Client = client;
return client;
}
@@ -5,6 +5,6 @@ namespace Impostor.Server.Net.Factories
{
internal interface IClientFactory
{
ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode);
ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData);
}
}
@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Impostor.Api;
using Impostor.Api.Innersloth;
using Impostor.Api.Net;
using Impostor.Server.Net.Inner.Objects;
@@ -85,6 +86,32 @@ namespace Impostor.Server.Net.Inner
return true;
}
protected async ValueTask<bool> ValidateCanVent(CheatContext context, IClientPlayer sender, InnerPlayerInfo playerInfo, bool value = true)
{
if (playerInfo.CanVent != value)
{
if (await sender.Client.ReportCheatAsync(context, "Failed can vent check"))
{
return false;
}
}
return true;
}
protected async ValueTask<bool> ValidateRole(CheatContext context, IClientPlayer sender, InnerPlayerInfo playerInfo, RoleTypes role)
{
if (playerInfo.RoleType != role)
{
if (await sender.Client.ReportCheatAsync(context, $"Failed role = {role} check"))
{
return false;
}
}
return true;
}
protected async ValueTask<bool> UnregisteredCall(CheatContext context, IClientPlayer sender)
{
if (await sender.Client.ReportCheatAsync(context, "Client sent unregistered call"))
@@ -47,7 +47,7 @@ namespace Impostor.Server.Net.Inner.Objects.Components
case RpcCalls.EnterVent:
case RpcCalls.ExitVent:
{
if (!await ValidateImpostor(call, sender, _playerControl.PlayerInfo))
if (!await ValidateCanVent(call, sender, _playerControl.PlayerInfo))
{
return false;
}
@@ -59,16 +59,18 @@ namespace Impostor.Server.Net.Inner.Objects
return;
}
if (initialState)
while (reader.Position < reader.Length)
{
var num = reader.ReadPackedInt32();
for (var i = 0; i < num; i++)
var inner = reader.ReadMessage();
var playerInfo = this.GetPlayerById(inner.Tag);
if (playerInfo != null)
{
var playerId = reader.ReadByte();
var playerInfo = new InnerPlayerInfo(playerId);
playerInfo.Deserialize(reader);
playerInfo.Deserialize(inner);
}
else
{
playerInfo = new InnerPlayerInfo(inner.Tag);
playerInfo.Deserialize(inner);
if (!_allPlayers.TryAdd(playerInfo.PlayerId, playerInfo))
{
@@ -76,28 +78,6 @@ namespace Impostor.Server.Net.Inner.Objects
}
}
}
else
{
while (reader.Position < reader.Length)
{
var inner = reader.ReadMessage();
var playerInfo = this.GetPlayerById(inner.Tag);
if (playerInfo != null)
{
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.");
}
}
}
}
}
public override async ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
@@ -125,6 +125,10 @@ namespace Impostor.Server.Net.Inner.Objects
}
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.
// But for whatever reason host sends VotingComplete before sending his vote.
@@ -29,37 +29,37 @@ namespace Impostor.Server.Net.Inner.Objects
public async ValueTask SetColorAsync(ColorType color)
{
PlayerInfo.Color = color;
PlayerInfo.CurrentOutfit.Color = color;
using var writer = Game.StartRpc(NetId, RpcCalls.SetColor);
Rpc08SetColor.Serialize(writer, color);
await Game.FinishRpcAsync(writer);
}
public async ValueTask SetHatAsync(HatType hat)
public async ValueTask SetHatAsync(string hatId)
{
PlayerInfo.Hat = hat;
PlayerInfo.CurrentOutfit.HatId = hatId;
using var writer = Game.StartRpc(NetId, RpcCalls.SetHat);
Rpc09SetHat.Serialize(writer, hat);
Rpc39SetHat.Serialize(writer, hatId);
await Game.FinishRpcAsync(writer);
}
public async ValueTask SetPetAsync(PetType pet)
public async ValueTask SetPetAsync(string petId)
{
PlayerInfo.Pet = pet;
PlayerInfo.CurrentOutfit.PetId = petId;
using var writer = Game.StartRpc(NetId, RpcCalls.SetPet);
Rpc17SetPet.Serialize(writer, pet);
Rpc41SetPet.Serialize(writer, petId);
await Game.FinishRpcAsync(writer);
}
public async ValueTask SetSkinAsync(SkinType skin)
public async ValueTask SetSkinAsync(string skinId)
{
PlayerInfo.Skin = skin;
PlayerInfo.CurrentOutfit.SkinId = skinId;
using var writer = Game.StartRpc(NetId, RpcCalls.SetSkin);
Rpc10SetSkin.Serialize(writer, skin);
Rpc40SetSkin.Serialize(writer, skinId);
await Game.FinishRpcAsync(writer);
}
@@ -61,6 +61,9 @@ namespace Impostor.Server.Net.Inner.Objects
internal Queue<ColorType> RequestedColorId { get; } = new Queue<ColorType>();
/// <summary> Gets or sets target that was set by the last CheckMurder RPC. </summary>
internal IInnerPlayerControl? IsMurdering { get; set; } = null;
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
@@ -182,8 +185,8 @@ namespace Impostor.Server.Net.Inner.Objects
return false;
}
Rpc09SetHat.Deserialize(reader, out var hat);
return await HandleSetHat(sender, hat);
Rpc39SetHat.Deserialize(reader, out var hat);
return true;
}
case RpcCalls.SetSkin:
@@ -193,8 +196,41 @@ namespace Impostor.Server.Net.Inner.Objects
return false;
}
Rpc10SetSkin.Deserialize(reader, out var skin);
return await HandleSetSkin(sender, skin);
Rpc40SetSkin.Deserialize(reader, out var skin);
return true;
}
case RpcCalls.SetVisor:
{
if (!await ValidateOwnership(call, sender))
{
return false;
}
// Rpc42SetVistor.Deserialize(reader, out var visor);
return true;
}
case RpcCalls.SetNamePlate:
{
if (!await ValidateOwnership(call, sender))
{
return false;
}
// Rpc43SetNamePlate.Deserialize(reader, out var namePlate);
return true;
}
case RpcCalls.SetLevel:
{
if (!await ValidateOwnership(call, sender))
{
return false;
}
// Rpc38SetLevel.Deserialize(reader, out var level);
return true;
}
case RpcCalls.ReportDeadBody:
@@ -210,7 +246,7 @@ namespace Impostor.Server.Net.Inner.Objects
case RpcCalls.MurderPlayer:
{
if (!await ValidateOwnership(call, sender) || !await ValidateImpostor(call, sender, PlayerInfo))
if (!await ValidateHost(call, sender))
{
return false;
}
@@ -271,7 +307,7 @@ namespace Impostor.Server.Net.Inner.Objects
return false;
}
Rpc17SetPet.Deserialize(reader, out var pet);
Rpc41SetPet.Deserialize(reader, out var pet);
return await HandleSetPet(sender, pet);
}
@@ -308,6 +344,65 @@ namespace Impostor.Server.Net.Inner.Objects
break;
}
case RpcCalls.SetRole:
{
if (!await ValidateHost(call, sender))
{
return false;
}
Rpc44SetRole.Deserialize(reader, out var role);
PlayerInfo.RoleType = role;
break;
}
case RpcCalls.ProtectPlayer:
{
if (!await ValidateHost(call, sender))
{
return false;
}
Rpc45ProtectPlayer.Deserialize(reader, Game, out _, out _);
break;
}
case RpcCalls.Shapeshift:
{
if (!await ValidateRole(call, sender, PlayerInfo, RoleTypes.Shapeshifter))
{
return false;
}
Rpc46Shapeshift.Deserialize(reader, Game, out _, out _);
break;
}
case RpcCalls.CheckMurder:
{
if (!await ValidateImpostor(call, sender, PlayerInfo))
{
return false;
}
Rpc47CheckMurder.Deserialize(reader, Game, out var murdered);
return await HandleCheckMurder(sender, murdered);
}
case RpcCalls.CheckProtect:
{
if (!await ValidateRole(call, sender, PlayerInfo, RoleTypes.GuardianAngel))
{
return false;
}
Rpc48CheckProtect.Deserialize(reader, Game, out _);
break;
}
default:
return await base.HandleRpcAsync(sender, target, call, reader);
}
@@ -343,7 +438,7 @@ namespace Impostor.Server.Net.Inner.Objects
var player = Game.GameNet.GameData!.GetPlayerById(infectedIds.Span[i]);
if (player != null)
{
player.IsImpostor = true;
// player.IsImpostor = true;
}
}
@@ -479,7 +574,7 @@ namespace Impostor.Server.Net.Inner.Objects
if (sender.IsOwner(this))
{
if (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.Color == color))
if (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.CurrentOutfit.Color == color))
{
if (await sender.Client.ReportCheatAsync(RpcCalls.SetColor, "Client sent a color that is already used"))
{
@@ -497,7 +592,7 @@ namespace Impostor.Server.Net.Inner.Objects
var expected = RequestedColorId.Dequeue();
while (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.Color == expected))
while (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.CurrentOutfit.Color == expected))
{
expected = (ColorType)(((byte)expected + 1) % ColorsCount);
}
@@ -510,45 +605,62 @@ namespace Impostor.Server.Net.Inner.Objects
}
}
PlayerInfo.Color = color;
PlayerInfo.CurrentOutfit.Color = color;
return true;
}
private async ValueTask<bool> HandleSetHat(ClientPlayer sender, HatType hat)
private async ValueTask<bool> HandleSetHat(ClientPlayer sender, string hat)
{
if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetHat, "Client tried to change hat while not in lobby"))
{
return false;
}
PlayerInfo.Hat = hat;
PlayerInfo.CurrentOutfit.HatId = hat;
return true;
}
private async ValueTask<bool> HandleSetSkin(ClientPlayer sender, SkinType skin)
private async ValueTask<bool> HandleSetSkin(ClientPlayer sender, string skin)
{
if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetSkin, "Client tried to change skin while not in lobby"))
{
return false;
}
PlayerInfo.Skin = skin;
PlayerInfo.CurrentOutfit.SkinId = skin;
return true;
}
private async ValueTask<bool> HandleCheckMurder(ClientPlayer sender, IInnerPlayerControl? target)
{
if (!PlayerInfo.CanMurder(Game, _dateTimeProvider))
{
if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, "Client tried to murder too fast"))
{
return false;
}
}
PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing);
if (target == null || target.PlayerInfo.IsImpostor)
{
if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, "Client tried to murder invalid target"))
{
return false;
}
}
IsMurdering = target;
return true;
}
private async ValueTask<bool> HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target)
{
if (!PlayerInfo.CanMurder(Game, _dateTimeProvider))
{
if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder too fast"))
{
return false;
}
}
if (target == null || target.PlayerInfo.IsImpostor)
{
if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder invalid target"))
@@ -557,7 +669,14 @@ namespace Impostor.Server.Net.Inner.Objects
}
}
PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing);
// If the host is also the impostor that committed the murder, CheckMurder is actually sent *after* the MurderPlayer RPC
if (sender.Character != this && target != IsMurdering)
{
if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Host tried to murder incorrect target"))
{
return false;
}
}
if (target != null && !target.PlayerInfo.IsDead)
{
@@ -565,6 +684,8 @@ namespace Impostor.Server.Net.Inner.Objects
await _eventManager.CallAsync(new PlayerMurderEvent(Game, sender, this, target));
}
IsMurdering = null;
return true;
}
@@ -582,14 +703,14 @@ namespace Impostor.Server.Net.Inner.Objects
await _eventManager.CallAsync(new PlayerStartMeetingEvent(Game, Game.GetClientPlayer(this.OwnerId)!, this, deadPlayer));
}
private async ValueTask<bool> HandleSetPet(ClientPlayer sender, PetType pet)
private async ValueTask<bool> HandleSetPet(ClientPlayer sender, string pet)
{
if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetPet, "Client tried to change pet while not in lobby"))
{
return false;
}
PlayerInfo.Pet = pet;
PlayerInfo.CurrentOutfit.PetId = pet;
return true;
}
@@ -21,17 +21,22 @@ namespace Impostor.Server.Net.Inner.Objects
public string PlayerName { get; internal set; } = string.Empty;
public ColorType Color { get; internal set; } = (ColorType)(-1);
public Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; } = new()
{
[PlayerOutfitType.Default] = new PlayerOutfit(),
};
public HatType Hat { get; internal set; }
public PlayerOutfitType CurrentOutfitType { get; set; } = PlayerOutfitType.Default;
public PetType Pet { get; internal set; }
public PlayerOutfit CurrentOutfit => Outfits[CurrentOutfitType];
public SkinType Skin { get; internal set; }
public RoleTypes RoleType { get; internal set; }
public bool Disconnected { get; internal set; }
public bool IsImpostor { get; internal set; }
public bool IsImpostor => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter;
public bool CanVent => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Engineer;
public bool IsDead { get; internal set; }
@@ -41,6 +46,8 @@ namespace Impostor.Server.Net.Inner.Objects
public DateTimeOffset LastMurder { get; set; }
public uint PlayerLevel { get; internal set; }
public bool CanMurder(IGame game, IDateTimeProvider dateTimeProvider)
{
if (!IsImpostor)
@@ -58,16 +65,23 @@ namespace Impostor.Server.Net.Inner.Objects
public void Deserialize(IMessageReader reader)
{
PlayerName = reader.ReadString();
Color = (ColorType)reader.ReadPackedInt32();
Hat = (HatType)reader.ReadPackedUInt32();
Pet = (PetType)reader.ReadPackedUInt32();
Skin = (SkinType)reader.ReadPackedUInt32();
Outfits.Clear();
var b = reader.ReadByte();
for (var i = 0; i < b; i++)
{
var key = (PlayerOutfitType)reader.ReadByte();
Outfits[key] = new PlayerOutfit();
Outfits[key].Deserialize(reader);
}
PlayerLevel = reader.ReadPackedUInt32();
var flag = reader.ReadByte();
Disconnected = (flag & 1) > 0;
IsImpostor = (flag & 2) > 0;
IsDead = (flag & 4) > 0;
Disconnected = (flag & 1) != 0;
IsDead = (flag & 4) != 0;
var roleType = (RoleTypes)reader.ReadUInt16();
RoleType = roleType;
var taskCount = reader.ReadByte();
@@ -22,7 +22,8 @@ namespace Impostor.Server.Net.Manager
// NOTE: when updating this array, keep the versions ordered from old to new, otherwise the version compare logic doesn't work properly
private static readonly int[] SupportedVersions =
{
GameVersion.GetVersion(2021, 6, 30), // 2021.6.30
GameVersion.GetVersion(2021, 11, 9), // 2021.11.9
GameVersion.GetVersion(2021, 12, 14), // 2021.12.14
};
private readonly ILogger<ClientManager> _logger;
@@ -65,10 +66,10 @@ namespace Impostor.Server.Net.Manager
return clientId;
}
public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode)
public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData? platformSpecificData)
{
var versionCompare = CompareVersion(clientVersion);
if (versionCompare != VersionCompareResult.Compatible)
if (versionCompare != VersionCompareResult.Compatible || platformSpecificData == null)
{
GameVersion.ParseVersion(clientVersion, out var year, out var month, out var day, out var revision);
_logger.LogTrace("Client connected using unsupported version: {clientVersion} ({version})", clientVersion, $"{year}.{month}.{day}{(revision == 0 ? string.Empty : "." + revision)}");
@@ -105,7 +106,7 @@ namespace Impostor.Server.Net.Manager
return;
}
var client = _clientFactory.Create(connection, name, clientVersion, language, chatMode);
var client = _clientFactory.Create(connection, name, clientVersion, language, chatMode, platformSpecificData);
var id = NextId();
client.Id = id;
+2 -2
View File
@@ -62,14 +62,14 @@ namespace Impostor.Server.Net
private async ValueTask OnNewConnection(NewConnectionEventArgs e)
{
// Handshake.
HandshakeC2S.Deserialize(e.HandshakeData, out var clientVersion, out var name, out _, out var language, out var chatMode);
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);
await _eventManager.CallAsync(new ClientConnectionEvent(connection, e.HandshakeData));
// Register client
await _clientManager.RegisterConnectionAsync(connection, name, clientVersion, language, chatMode);
await _clientManager.RegisterConnectionAsync(connection, name, clientVersion, language, chatMode, platformSpecificData);
}
}
}
@@ -24,11 +24,12 @@ namespace Impostor.Server.Net.Redirector
int gameVersion,
Language language,
QuickChatModes chatMode,
PlatformSpecificData platformSpecificData,
HazelConnection connection,
ClientManager clientManager,
INodeProvider nodeProvider,
INodeLocator nodeLocator)
: base(name, gameVersion, language, chatMode, connection)
: base(name, gameVersion, language, chatMode, platformSpecificData, connection)
{
_clientManager = clientManager;
_nodeProvider = nodeProvider;
@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Threading.Tasks;
using Impostor.Api.Innersloth;
using Impostor.Api.Net;
@@ -78,12 +78,12 @@ namespace Impostor.Server.Net.State
private void WriteJoinedGameMessage(IMessageWriter message, bool clear, IClientPlayer player)
{
var playerIds = _players
var players = _players
.Where(x => x.Value != player)
.Select(x => x.Key)
.Select(x => x.Value)
.ToArray();
Message07JoinedGameS2C.Serialize(message, clear, Code, player.Client.Id, HostId, playerIds);
Message07JoinedGameS2C.Serialize(message, clear, Code, player.Client.Id, HostId, players);
}
private void WriteAlterGameMessage(IMessageWriter message, bool clear, bool isPublic)
+2 -2
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -114,7 +114,7 @@ namespace Impostor.Server.Net.State
private ValueTask BroadcastJoinMessage(IMessageWriter message, bool clear, ClientPlayer player)
{
Message01JoinGameS2C.SerializeJoin(message, clear, Code, player.Client.Id, HostId);
Message01JoinGameS2C.SerializeJoin(message, clear, Code, player, HostId);
return SendToAllExceptAsync(message, player.Client.Id);
}
-20
View File
@@ -116,14 +116,6 @@ namespace Impostor.Server
.GetSection(ServerRedirectorConfig.Section)
.Get<ServerRedirectorConfig>() ?? new ServerRedirectorConfig();
var announcementsServer = host.Configuration
.GetSection(AnnouncementsServerConfig.Section)
.Get<AnnouncementsServerConfig>() ?? new AnnouncementsServerConfig();
var authServer = host.Configuration
.GetSection(AuthServerConfig.Section)
.Get<AuthServerConfig>() ?? new AuthServerConfig();
services.AddSingleton<ServerEnvironment>();
services.AddSingleton<IServerEnvironment>(p => p.GetRequiredService<ServerEnvironment>());
services.AddSingleton<IDateTimeProvider, RealDateTimeProvider>();
@@ -131,8 +123,6 @@ namespace Impostor.Server
services.Configure<DebugConfig>(host.Configuration.GetSection(DebugConfig.Section));
services.Configure<AntiCheatConfig>(host.Configuration.GetSection(AntiCheatConfig.Section));
services.Configure<ServerConfig>(host.Configuration.GetSection(ServerConfig.Section));
services.Configure<AnnouncementsServerConfig>(host.Configuration.GetSection(AnnouncementsServerConfig.Section));
services.Configure<AuthServerConfig>(host.Configuration.GetSection(AuthServerConfig.Section));
services.Configure<ServerRedirectorConfig>(host.Configuration.GetSection(ServerRedirectorConfig.Section));
if (redirector.Enabled)
@@ -218,16 +208,6 @@ namespace Impostor.Server
services.AddSingleton<IEventManager, EventManager>();
services.AddSingleton<Matchmaker>();
services.AddHostedService<MatchmakerService>();
if (announcementsServer.Enabled)
{
services.AddHostedService<AnnouncementsService>();
}
if (authServer.Enabled)
{
services.AddHostedService<AuthService>();
}
})
.UseSerilog()
.UseConsoleLifetime()
@@ -18,8 +18,8 @@ namespace Impostor.Server.Recorder
private bool _createdGame;
private bool _recordAfter;
public ClientRecorder(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, GameManager gameManager, string name, int gameVersion, Language language, QuickChatModes chatMode, HazelConnection connection, PacketRecorder recorder)
: base(logger, antiCheatOptions, clientManager, gameManager, customMessageManager, name, gameVersion, language, chatMode, connection)
public ClientRecorder(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, GameManager gameManager, string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, HazelConnection connection, PacketRecorder recorder)
: base(logger, antiCheatOptions, clientManager, gameManager, customMessageManager, name, gameVersion, language, chatMode, platformSpecificData, connection)
{
_recorder = recorder;
_isFirst = true;
-5
View File
@@ -5,11 +5,6 @@
"ListenIp": "0.0.0.0",
"ListenPort": 22023
},
"AnnouncementsServer": {
"Enabled": true,
"ListenIp": "0.0.0.0",
"ListenPort": 22024
},
"AntiCheat": {
"Enabled": true,
"BanIpFromGame": true
+2 -2
View File
@@ -170,7 +170,7 @@ namespace Impostor.Tools.ServerReplay
// Create and register connection.
var connection = new MockHazelConnection(address);
await _clientManager.RegisterConnectionAsync(connection, name, gameVersion, Language.English, QuickChatModes.FreeChatOrQuickChat);
await _clientManager.RegisterConnectionAsync(connection, name, gameVersion, Language.English, QuickChatModes.FreeChatOrQuickChat, new PlatformSpecificData(Platforms.Unknown, "ServerReplay"));
// Store reference for ourselfs.
Connections.Add(clientId, connection);
@@ -200,7 +200,7 @@ namespace Impostor.Tools.ServerReplay
if (tag == MessageFlags.HostGame)
{
GameOptions.Add(clientId, Message00HostGameC2S.Deserialize(message, out _));
GameOptions.Add(clientId, Message00HostGameC2S.Deserialize(message));
}
else if (Connections.TryGetValue(clientId, out var client))
{