mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-20 01:46:11 +00:00
Merge pull request #501 from Impostor/feature/2022.12.8
This commit is contained in:
75 files changed
+1707
-525
No files matched your search
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
<VersionPrefix>1.7.3</VersionPrefix>
|
||||
<VersionPrefix>1.8.0</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Inner;
|
||||
|
||||
@@ -9,7 +10,9 @@ namespace Impostor.Api.Games
|
||||
{
|
||||
public interface IGame
|
||||
{
|
||||
GameOptionsData Options { get; }
|
||||
IGameOptions Options { get; }
|
||||
|
||||
GameFilterOptions FilterOptions { get; }
|
||||
|
||||
GameCode Code { get; }
|
||||
|
||||
@@ -52,7 +55,7 @@ namespace Impostor.Api.Games
|
||||
void BanIp(IPAddress ipAddress);
|
||||
|
||||
/// <summary>
|
||||
/// Syncs the internal <see cref="GameOptionsData" /> to all players.
|
||||
/// Syncs the internal <see cref="IGameOptions" /> to all players.
|
||||
/// Necessary to do if you modified it, otherwise it won't be used.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ValueTask" /> representing the asynchronous operation.</returns>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
namespace Impostor.Api.Games.Managers
|
||||
{
|
||||
@@ -14,8 +15,9 @@ namespace Impostor.Api.Games.Managers
|
||||
/// Creates a new game.
|
||||
/// </summary>
|
||||
/// <param name="options">Game options.</param>
|
||||
/// <param name="filterOptions">Filter options.</param>
|
||||
/// <returns>Created game or null if creation was cancelled by a plugin.</returns>
|
||||
/// <exception cref="ImpostorException">Thrown when game creation failed.</exception>
|
||||
ValueTask<IGame?> CreateAsync(GameOptionsData options);
|
||||
ValueTask<IGame?> CreateAsync(IGameOptions options, GameFilterOptions filterOptions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Impostor.Api.Innersloth;
|
||||
|
||||
public enum CrossplayFlags
|
||||
{
|
||||
Unknown = 0,
|
||||
StandaloneEpicPC = 1 << 1,
|
||||
StandaloneSteamPC = 1 << 2,
|
||||
StandaloneMac = 1 << 3,
|
||||
StandaloneWin10 = 1 << 4,
|
||||
StandaloneItch = 1 << 5,
|
||||
IPhone = 1 << 6,
|
||||
Android = 1 << 7,
|
||||
Switch = 1 << 8,
|
||||
Xbox = 1 << 9,
|
||||
Playstation = 1 << 10,
|
||||
All = int.MaxValue,
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Api.Innersloth;
|
||||
|
||||
public class GameFilterOptions
|
||||
{
|
||||
public HashSet<string> FilterTags { get; } = new HashSet<string>();
|
||||
|
||||
public static GameFilterOptions CreateDefault()
|
||||
{
|
||||
return new GameFilterOptions
|
||||
{
|
||||
FilterTags =
|
||||
{
|
||||
"Beginner",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public static GameFilterOptions Deserialize(IMessageReader reader)
|
||||
{
|
||||
var options = new GameFilterOptions();
|
||||
|
||||
var count = reader.ReadPackedInt32();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
options.FilterTags.Add(reader.ReadString());
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.WritePacked(FilterTags.Count);
|
||||
foreach (var filterTag in FilterTags)
|
||||
{
|
||||
writer.Write(filterTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public enum GameModes : byte
|
||||
{
|
||||
None,
|
||||
Normal,
|
||||
HideNSeek,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public static class GameOptionsFactory
|
||||
{
|
||||
public const byte ModularOptionsDataVersion = 7;
|
||||
|
||||
public static void Serialize(IMessageWriter writer, IGameOptions gameOptions)
|
||||
{
|
||||
if (gameOptions.Version < ModularOptionsDataVersion)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
// In theory we should use WriteBytesAndSize here, but it's costly and requires computing the size
|
||||
// Base game does similar hacks and completely ignores this value for modular game options so we can just write 0 here
|
||||
writer.WritePacked(0);
|
||||
|
||||
writer.Write(gameOptions.Version);
|
||||
writer.StartMessage(0);
|
||||
writer.Write((byte)gameOptions.GameMode);
|
||||
gameOptions.Serialize(writer);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
public static IGameOptions Deserialize(IMessageReader reader)
|
||||
{
|
||||
reader.ReadPackedInt32();
|
||||
var version = reader.ReadByte();
|
||||
|
||||
if (version < ModularOptionsDataVersion)
|
||||
{
|
||||
return LegacyGameOptionsData.Deserialize(reader, version);
|
||||
}
|
||||
|
||||
var optionsReader = reader.ReadMessage();
|
||||
var gameMode = (GameModes)optionsReader.ReadByte();
|
||||
|
||||
return gameMode switch
|
||||
{
|
||||
GameModes.Normal => NormalGameOptions.Deserialize(optionsReader, version),
|
||||
GameModes.HideNSeek => HideNSeekGameOptions.Deserialize(optionsReader, version),
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
public static void DeserializeInto(IMessageReader reader, IGameOptions gameOptions)
|
||||
{
|
||||
reader.ReadPackedInt32();
|
||||
var version = reader.ReadByte();
|
||||
|
||||
if (version < ModularOptionsDataVersion)
|
||||
{
|
||||
((LegacyGameOptionsData)gameOptions).Deserialize(reader);
|
||||
return;
|
||||
}
|
||||
|
||||
var optionsReader = reader.ReadMessage();
|
||||
var gameMode = (GameModes)optionsReader.ReadByte();
|
||||
|
||||
switch (gameMode)
|
||||
{
|
||||
case GameModes.Normal:
|
||||
((NormalGameOptions)gameOptions).Deserialize(optionsReader);
|
||||
break;
|
||||
case GameModes.HideNSeek:
|
||||
((HideNSeekGameOptions)gameOptions).Deserialize(optionsReader);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(gameMode), gameMode, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public class HideNSeekGameOptions : IGameOptions
|
||||
{
|
||||
public const int LatestVersion = 7;
|
||||
|
||||
public HideNSeekGameOptions(byte version = LatestVersion)
|
||||
{
|
||||
Version = version;
|
||||
IGameOptions.EnsureVersionIsModular<HideNSeekGameOptions>(version);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte Version { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameModes GameMode => GameModes.HideNSeek;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte MaxPlayers { get; set; } = 15;
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameKeywords Keywords { get; set; } = GameKeywords.English;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MapTypes Map { get; set; } = MapTypes.Skeld;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int NumImpostors { get; set; } = 1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsDefaults { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Player speed modifier.
|
||||
/// </summary>
|
||||
public float PlayerSpeedMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Light modifier for the players that are members of the crew as a multiplier value.
|
||||
/// </summary>
|
||||
public float CrewLightMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Light modifier for the players that are Impostors as a multiplier value.
|
||||
/// </summary>
|
||||
public float ImpostorLightMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of common tasks.
|
||||
/// </summary>
|
||||
public int NumCommonTasks { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of long tasks.
|
||||
/// </summary>
|
||||
public int NumLongTasks { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of short tasks.
|
||||
/// </summary>
|
||||
public int NumShortTasks { get; set; } = 2;
|
||||
|
||||
public int CrewmateVentUses { get; set; } = 1;
|
||||
|
||||
public float CrewmateTimeInVent { get; set; } = 3f;
|
||||
|
||||
public float HidingTime { get; set; } = 200f;
|
||||
|
||||
public float CrewmateFlashlightSize { get; set; } = 0.35f;
|
||||
|
||||
public float ImpostorFlashlightSize { get; set; } = 0.25f;
|
||||
|
||||
public bool UseFlashlight { get; set; } = true;
|
||||
|
||||
public bool FinalHideSeekMap { get; set; } = true;
|
||||
|
||||
public float FinalHideTime { get; set; } = 50f;
|
||||
|
||||
public float FinalSeekerSpeed { get; set; } = 1.2f;
|
||||
|
||||
public bool FinalHidePings { get; set; } = true;
|
||||
|
||||
public bool ShowNames { get; set; } = true;
|
||||
|
||||
public uint SeekerPlayerId { get; set; } = 0xFFFFFFFF;
|
||||
|
||||
public float MaxPingTime { get; set; } = 6f;
|
||||
|
||||
public static HideNSeekGameOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new HideNSeekGameOptions(version);
|
||||
options.Deserialize(reader);
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
MaxPlayers = reader.ReadByte();
|
||||
Keywords = (GameKeywords)reader.ReadInt32();
|
||||
Map = (MapTypes)reader.ReadByte();
|
||||
PlayerSpeedMod = reader.ReadSingle();
|
||||
CrewLightMod = reader.ReadSingle();
|
||||
ImpostorLightMod = reader.ReadSingle();
|
||||
NumCommonTasks = reader.ReadByte();
|
||||
NumLongTasks = reader.ReadByte();
|
||||
NumShortTasks = reader.ReadByte();
|
||||
IsDefaults = reader.ReadBoolean();
|
||||
|
||||
CrewmateVentUses = reader.ReadInt32();
|
||||
HidingTime = reader.ReadSingle();
|
||||
CrewmateFlashlightSize = reader.ReadSingle();
|
||||
ImpostorFlashlightSize = reader.ReadSingle();
|
||||
UseFlashlight = reader.ReadBoolean();
|
||||
FinalHideSeekMap = reader.ReadBoolean();
|
||||
FinalHideTime = reader.ReadSingle();
|
||||
FinalSeekerSpeed = reader.ReadSingle();
|
||||
FinalHidePings = reader.ReadBoolean();
|
||||
ShowNames = reader.ReadBoolean();
|
||||
SeekerPlayerId = reader.ReadUInt32();
|
||||
MaxPingTime = reader.ReadSingle();
|
||||
CrewmateTimeInVent = reader.ReadSingle();
|
||||
|
||||
if (Version > 7)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(MaxPlayers);
|
||||
writer.Write((uint)Keywords);
|
||||
writer.Write((byte)Map);
|
||||
writer.Write(PlayerSpeedMod);
|
||||
writer.Write(CrewLightMod);
|
||||
writer.Write(ImpostorLightMod);
|
||||
writer.Write((byte)NumCommonTasks);
|
||||
writer.Write((byte)NumLongTasks);
|
||||
writer.Write((byte)NumShortTasks);
|
||||
writer.Write(IsDefaults);
|
||||
|
||||
writer.Write(CrewmateVentUses);
|
||||
writer.Write(HidingTime);
|
||||
writer.Write(CrewmateFlashlightSize);
|
||||
writer.Write(ImpostorFlashlightSize);
|
||||
writer.Write(UseFlashlight);
|
||||
writer.Write(FinalHideSeekMap);
|
||||
writer.Write(FinalHideTime);
|
||||
writer.Write(FinalSeekerSpeed);
|
||||
writer.Write(FinalHidePings);
|
||||
writer.Write(ShowNames);
|
||||
writer.Write(SeekerPlayerId);
|
||||
writer.Write(MaxPingTime);
|
||||
writer.Write(CrewmateTimeInVent);
|
||||
|
||||
if (Version > 7)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public interface IGameOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the version.
|
||||
/// </summary>
|
||||
public byte Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently active gamemode.
|
||||
/// </summary>
|
||||
public GameModes GameMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of players for this lobby.
|
||||
/// </summary>
|
||||
public byte MaxPlayers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the language of the lobby as per <see cref="GameKeywords" /> enum.
|
||||
/// </summary>
|
||||
public GameKeywords Keywords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Map selected for this lobby.
|
||||
/// </summary>
|
||||
public MapTypes Map { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of impostors for this lobby.
|
||||
/// </summary>
|
||||
public int NumImpostors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the GameOptions are the default ones.
|
||||
/// </summary>
|
||||
public bool IsDefaults { get; set; }
|
||||
|
||||
public void Serialize(IMessageWriter writer);
|
||||
|
||||
public static void EnsureVersionIsModular<TCaller>(byte version)
|
||||
{
|
||||
if (version < GameOptionsFactory.ModularOptionsDataVersion)
|
||||
{
|
||||
throw new ImpostorException($"{typeof(TCaller).Name} didn't exist before version 7, did you mean {nameof(LegacyGameOptionsData)}?");
|
||||
}
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
public static void ThrowUnknownVersion<TCaller>(byte version)
|
||||
{
|
||||
throw new ImpostorException($"Unknown {typeof(TCaller).Name} version {version}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
using Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public class LegacyGameOptionsData : IGameOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The latest major version of the game client.
|
||||
/// </summary>
|
||||
public const int LatestVersion = 5;
|
||||
|
||||
public LegacyGameOptionsData(byte version = LatestVersion)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets host's version of the game.
|
||||
/// </summary>
|
||||
public byte Version { get; set; }
|
||||
|
||||
public GameModes GameMode => GameModes.Normal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of players for this lobby.
|
||||
/// </summary>
|
||||
public byte MaxPlayers { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the language of the lobby as per <see cref="GameKeywords" /> enum.
|
||||
/// </summary>
|
||||
public GameKeywords Keywords { get; set; } = GameKeywords.English;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Map selected for this lobby.
|
||||
/// </summary>
|
||||
public MapTypes Map { get; set; } = MapTypes.Skeld;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Player speed modifier.
|
||||
/// </summary>
|
||||
public float PlayerSpeedMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Light modifier for the players that are members of the crew as a multiplier value.
|
||||
/// </summary>
|
||||
public float CrewLightMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Light modifier for the players that are Impostors as a multiplier value.
|
||||
/// </summary>
|
||||
public float ImpostorLightMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Impostor cooldown to kill in seconds.
|
||||
/// </summary>
|
||||
public float KillCooldown { get; set; } = 15f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of common tasks.
|
||||
/// </summary>
|
||||
public int NumCommonTasks { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of long tasks.
|
||||
/// </summary>
|
||||
public int NumLongTasks { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of short tasks.
|
||||
/// </summary>
|
||||
public int NumShortTasks { get; set; } = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of emergency meetings each player can call during the game in seconds.
|
||||
/// </summary>
|
||||
public int NumEmergencyMeetings { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cooldown between each time any player can call an emergency meeting in seconds.
|
||||
/// </summary>
|
||||
public int EmergencyCooldown { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of impostors for this lobby.
|
||||
/// </summary>
|
||||
public int NumImpostors { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether ghosts (dead crew members) can do tasks.
|
||||
/// </summary>
|
||||
public bool GhostsDoTasks { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Kill as per values in <see cref="KillDistances" />.
|
||||
/// </summary>
|
||||
public KillDistances KillDistance { get; set; } = KillDistances.Normal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time for discussion before voting time in seconds.
|
||||
/// </summary>
|
||||
public int DiscussionTime { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time for voting in seconds.
|
||||
/// </summary>
|
||||
public int VotingTime { get; set; } = 120;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether an ejected player is an impostor or not.
|
||||
/// </summary>
|
||||
public bool ConfirmImpostor { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether players are able to see tasks being performed by other players.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By being set to true, tasks such as Empty Garbage, Submit Scan, Clear asteroids, Prime shields execution will be visible to other players.
|
||||
/// </remarks>
|
||||
public bool VisualTasks { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the vote is anonymous.
|
||||
/// </summary>
|
||||
public bool AnonymousVotes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the task bar update mode as per values in <see cref="Innersloth.TaskBarUpdate" />.
|
||||
/// </summary>
|
||||
public TaskBarUpdate TaskBarUpdate { get; set; } = TaskBarUpdate.Always;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets role options.
|
||||
/// </summary>
|
||||
public LegacyRoleOptionsData RoleOptions { get; set; } = new LegacyRoleOptionsData();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the GameOptions are the default ones.
|
||||
/// </summary>
|
||||
public bool IsDefaults { get; set; } = true;
|
||||
|
||||
public static LegacyGameOptionsData Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new LegacyGameOptionsData(version);
|
||||
options.Deserialize(reader);
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
Version = reader.ReadByte();
|
||||
MaxPlayers = reader.ReadByte();
|
||||
Keywords = (GameKeywords)reader.ReadUInt32();
|
||||
Map = (MapTypes)reader.ReadByte();
|
||||
PlayerSpeedMod = reader.ReadSingle();
|
||||
|
||||
CrewLightMod = reader.ReadSingle();
|
||||
ImpostorLightMod = reader.ReadSingle();
|
||||
KillCooldown = reader.ReadSingle();
|
||||
|
||||
NumCommonTasks = reader.ReadByte();
|
||||
NumLongTasks = reader.ReadByte();
|
||||
NumShortTasks = reader.ReadByte();
|
||||
|
||||
NumEmergencyMeetings = reader.ReadInt32();
|
||||
|
||||
NumImpostors = reader.ReadByte();
|
||||
KillDistance = (KillDistances)reader.ReadByte();
|
||||
DiscussionTime = reader.ReadInt32();
|
||||
VotingTime = reader.ReadInt32();
|
||||
|
||||
IsDefaults = reader.ReadBoolean();
|
||||
|
||||
if (Version >= 2)
|
||||
{
|
||||
EmergencyCooldown = reader.ReadByte();
|
||||
}
|
||||
|
||||
if (Version >= 3)
|
||||
{
|
||||
ConfirmImpostor = reader.ReadBoolean();
|
||||
VisualTasks = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
if (Version >= 4)
|
||||
{
|
||||
AnonymousVotes = reader.ReadBoolean();
|
||||
TaskBarUpdate = (TaskBarUpdate)reader.ReadByte();
|
||||
}
|
||||
|
||||
if (Version >= 5)
|
||||
{
|
||||
RoleOptions = LegacyRoleOptionsData.Deserialize(reader);
|
||||
}
|
||||
|
||||
if (Version >= 6)
|
||||
{
|
||||
// Nothing was changed in V6
|
||||
}
|
||||
|
||||
if (Version > 6)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<LegacyGameOptionsData>(Version);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes this instance of GameOptionsData object to a specified BinaryWriter.
|
||||
/// </summary>
|
||||
/// <param name="writer">The stream to write the message to.</param>
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write((byte)Version);
|
||||
writer.Write((byte)MaxPlayers);
|
||||
writer.Write((uint)Keywords);
|
||||
writer.Write((byte)Map);
|
||||
writer.Write((float)PlayerSpeedMod);
|
||||
writer.Write((float)CrewLightMod);
|
||||
writer.Write((float)ImpostorLightMod);
|
||||
writer.Write((float)KillCooldown);
|
||||
writer.Write((byte)NumCommonTasks);
|
||||
writer.Write((byte)NumLongTasks);
|
||||
writer.Write((byte)NumShortTasks);
|
||||
writer.Write((int)NumEmergencyMeetings);
|
||||
writer.Write((byte)NumImpostors);
|
||||
writer.Write((byte)KillDistance);
|
||||
writer.Write((uint)DiscussionTime);
|
||||
writer.Write((uint)VotingTime);
|
||||
writer.Write((bool)IsDefaults);
|
||||
|
||||
if (Version >= 2)
|
||||
{
|
||||
writer.Write((byte)EmergencyCooldown);
|
||||
}
|
||||
|
||||
if (Version >= 3)
|
||||
{
|
||||
writer.Write((bool)ConfirmImpostor);
|
||||
writer.Write((bool)VisualTasks);
|
||||
}
|
||||
|
||||
if (Version >= 4)
|
||||
{
|
||||
writer.Write((bool)AnonymousVotes);
|
||||
writer.Write((byte)TaskBarUpdate);
|
||||
}
|
||||
|
||||
if (Version >= 5)
|
||||
{
|
||||
RoleOptions.Serialize(writer);
|
||||
}
|
||||
|
||||
if (Version >= 6)
|
||||
{
|
||||
// Nothing was changed in V6
|
||||
}
|
||||
|
||||
if (Version > 6)
|
||||
{
|
||||
throw new ImpostorException($"Unknown {nameof(LegacyGameOptionsData)} version {Version}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
using Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
public class NormalGameOptions : IGameOptions
|
||||
{
|
||||
public const int LatestVersion = 7;
|
||||
|
||||
public NormalGameOptions(byte version = LatestVersion)
|
||||
{
|
||||
Version = version;
|
||||
IGameOptions.EnsureVersionIsModular<NormalGameOptions>(version);
|
||||
RoleOptions = new RoleOptionsCollection(version);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte Version { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameModes GameMode => GameModes.Normal;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte MaxPlayers { get; set; } = 10;
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameKeywords Keywords { get; set; } = GameKeywords.English;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MapTypes Map { get; set; } = MapTypes.Skeld;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int NumImpostors { get; set; } = 1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsDefaults { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Player speed modifier.
|
||||
/// </summary>
|
||||
public float PlayerSpeedMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Light modifier for the players that are members of the crew as a multiplier value.
|
||||
/// </summary>
|
||||
public float CrewLightMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Light modifier for the players that are Impostors as a multiplier value.
|
||||
/// </summary>
|
||||
public float ImpostorLightMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Impostor cooldown to kill in seconds.
|
||||
/// </summary>
|
||||
public float KillCooldown { get; set; } = 15f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of common tasks.
|
||||
/// </summary>
|
||||
public int NumCommonTasks { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of long tasks.
|
||||
/// </summary>
|
||||
public int NumLongTasks { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of short tasks.
|
||||
/// </summary>
|
||||
public int NumShortTasks { get; set; } = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of emergency meetings each player can call during the game in seconds.
|
||||
/// </summary>
|
||||
public int NumEmergencyMeetings { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cooldown between each time any player can call an emergency meeting in seconds.
|
||||
/// </summary>
|
||||
public int EmergencyCooldown { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether ghosts (dead crew members) can do tasks.
|
||||
/// </summary>
|
||||
public bool GhostsDoTasks { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Kill as per values in <see cref="KillDistances" />.
|
||||
/// </summary>
|
||||
public KillDistances KillDistance { get; set; } = KillDistances.Normal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time for discussion before voting time in seconds.
|
||||
/// </summary>
|
||||
public int DiscussionTime { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time for voting in seconds.
|
||||
/// </summary>
|
||||
public int VotingTime { get; set; } = 120;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether an ejected player is an impostor or not.
|
||||
/// </summary>
|
||||
public bool ConfirmImpostor { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether players are able to see tasks being performed by other players.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By being set to true, tasks such as Empty Garbage, Submit Scan, Clear asteroids, Prime shields execution will be visible to other players.
|
||||
/// </remarks>
|
||||
public bool VisualTasks { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the vote is anonymous.
|
||||
/// </summary>
|
||||
public bool AnonymousVotes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the task bar update mode as per values in <see cref="Innersloth.TaskBarUpdate" />.
|
||||
/// </summary>
|
||||
public TaskBarUpdate TaskBarUpdate { get; set; } = TaskBarUpdate.Always;
|
||||
|
||||
public RoleOptionsCollection RoleOptions { get; set; }
|
||||
|
||||
public static NormalGameOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new NormalGameOptions(version);
|
||||
options.Deserialize(reader);
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
MaxPlayers = reader.ReadByte();
|
||||
Keywords = (GameKeywords)reader.ReadUInt32();
|
||||
Map = (MapTypes)reader.ReadByte();
|
||||
PlayerSpeedMod = reader.ReadSingle();
|
||||
|
||||
CrewLightMod = reader.ReadSingle();
|
||||
ImpostorLightMod = reader.ReadSingle();
|
||||
KillCooldown = reader.ReadSingle();
|
||||
|
||||
NumCommonTasks = reader.ReadByte();
|
||||
NumLongTasks = reader.ReadByte();
|
||||
NumShortTasks = reader.ReadByte();
|
||||
|
||||
NumEmergencyMeetings = reader.ReadInt32();
|
||||
|
||||
NumImpostors = reader.ReadByte();
|
||||
KillDistance = (KillDistances)reader.ReadByte();
|
||||
DiscussionTime = reader.ReadInt32();
|
||||
VotingTime = reader.ReadInt32();
|
||||
|
||||
IsDefaults = reader.ReadBoolean();
|
||||
|
||||
EmergencyCooldown = reader.ReadByte();
|
||||
ConfirmImpostor = reader.ReadBoolean();
|
||||
VisualTasks = reader.ReadBoolean();
|
||||
AnonymousVotes = reader.ReadBoolean();
|
||||
TaskBarUpdate = (TaskBarUpdate)reader.ReadByte();
|
||||
|
||||
RoleOptions.Deserialize(reader);
|
||||
|
||||
if (Version > 7)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(MaxPlayers);
|
||||
writer.Write((uint)Keywords);
|
||||
writer.Write((byte)Map);
|
||||
writer.Write(PlayerSpeedMod);
|
||||
|
||||
writer.Write(CrewLightMod);
|
||||
writer.Write(ImpostorLightMod);
|
||||
writer.Write(KillCooldown);
|
||||
|
||||
writer.Write((byte)NumCommonTasks);
|
||||
writer.Write((byte)NumLongTasks);
|
||||
writer.Write((byte)NumShortTasks);
|
||||
|
||||
writer.Write(NumEmergencyMeetings);
|
||||
|
||||
writer.Write((byte)NumImpostors);
|
||||
writer.Write((byte)KillDistance);
|
||||
writer.Write(DiscussionTime);
|
||||
writer.Write(VotingTime);
|
||||
|
||||
writer.Write(IsDefaults);
|
||||
|
||||
writer.Write((byte)EmergencyCooldown);
|
||||
writer.Write(ConfirmImpostor);
|
||||
writer.Write(VisualTasks);
|
||||
writer.Write(AnonymousVotes);
|
||||
writer.Write((byte)TaskBarUpdate);
|
||||
|
||||
RoleOptions.Serialize(writer);
|
||||
|
||||
if (Version > 7)
|
||||
{
|
||||
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class EngineerRoleOptions : IRoleOptions
|
||||
{
|
||||
public EngineerRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.Engineer;
|
||||
|
||||
public byte Cooldown { get; set; } = 30;
|
||||
|
||||
public byte InVentMaxTime { get; set; } = 15;
|
||||
|
||||
public static EngineerRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new EngineerRoleOptions(version);
|
||||
|
||||
options.Cooldown = reader.ReadByte();
|
||||
options.InVentMaxTime = reader.ReadByte();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(Cooldown);
|
||||
writer.Write(InVentMaxTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class GuardianAngelRoleOptions : IRoleOptions
|
||||
{
|
||||
public GuardianAngelRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.GuardianAngel;
|
||||
|
||||
public byte Cooldown { get; set; } = 60;
|
||||
|
||||
public byte ProtectionDurationSeconds { get; set; } = 10;
|
||||
|
||||
public bool ImpostorsCanSeeProtect { get; set; }
|
||||
|
||||
public static GuardianAngelRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new GuardianAngelRoleOptions(version);
|
||||
|
||||
options.Cooldown = reader.ReadByte();
|
||||
options.ProtectionDurationSeconds = reader.ReadByte();
|
||||
options.ImpostorsCanSeeProtect = reader.ReadBoolean();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(Cooldown);
|
||||
writer.Write(ProtectionDurationSeconds);
|
||||
writer.Write(ImpostorsCanSeeProtect);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public interface IRoleOptions
|
||||
{
|
||||
RoleTypes Type { get; }
|
||||
|
||||
void Serialize(IMessageWriter writer);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class LegacyRoleOptionsData
|
||||
{
|
||||
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 LegacyRoleOptionsData Deserialize(IMessageReader reader)
|
||||
{
|
||||
var roleOptionsData = new LegacyRoleOptionsData();
|
||||
var num = reader.ReadPackedInt32();
|
||||
for (var i = 0; i < num; i++)
|
||||
{
|
||||
var key = (RoleTypes)reader.ReadInt16();
|
||||
var roleRate = RoleRate.Deserialize(reader);
|
||||
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);
|
||||
roleRate.Value.Serialize(writer);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class RoleOptionsCollection
|
||||
{
|
||||
public RoleOptionsCollection(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public Dictionary<RoleTypes, RoleData> Roles { get; } = new Dictionary<RoleTypes, RoleData>();
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
Roles.Clear();
|
||||
|
||||
var count = reader.ReadPackedInt32();
|
||||
Roles.EnsureCapacity(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var roleType = (RoleTypes)reader.ReadInt16();
|
||||
var roleRate = RoleRate.Deserialize(reader);
|
||||
var roleOptionsReader = reader.ReadMessage();
|
||||
IRoleOptions roleOptions = roleType switch
|
||||
{
|
||||
RoleTypes.Scientist => ScientistRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Engineer => EngineerRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.GuardianAngel => GuardianAngelRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
RoleTypes.Shapeshifter => ShapeshifterRoleOptions.Deserialize(roleOptionsReader, Version),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(roleType), roleType, null),
|
||||
};
|
||||
|
||||
Roles.Add(roleType, new RoleData(roleType, roleOptions, roleRate));
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.WritePacked(Roles.Count);
|
||||
foreach (var (key, roleData) in Roles)
|
||||
{
|
||||
writer.Write((ushort)key);
|
||||
roleData.Rate.Serialize(writer);
|
||||
writer.StartMessage(0);
|
||||
roleData.RoleOptions.Serialize(writer);
|
||||
writer.EndMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly record struct RoleData(RoleTypes Type, IRoleOptions RoleOptions, RoleRate Rate);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public readonly record struct RoleRate(byte MaxCount, byte Chance)
|
||||
{
|
||||
public static RoleRate Deserialize(IMessageReader reader)
|
||||
{
|
||||
return new RoleRate(reader.ReadByte(), reader.ReadByte());
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(MaxCount);
|
||||
writer.Write(Chance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class ScientistRoleOptions : IRoleOptions
|
||||
{
|
||||
public ScientistRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.Scientist;
|
||||
|
||||
public byte Cooldown { get; set; } = 15;
|
||||
|
||||
public byte BatteryCharge { get; set; } = 5;
|
||||
|
||||
public static ScientistRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new ScientistRoleOptions(version);
|
||||
|
||||
options.Cooldown = reader.ReadByte();
|
||||
options.BatteryCharge = reader.ReadByte();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(Cooldown);
|
||||
writer.Write(BatteryCharge);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
|
||||
|
||||
public class ShapeshifterRoleOptions : IRoleOptions
|
||||
{
|
||||
public ShapeshifterRoleOptions(byte version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public byte Version { get; }
|
||||
|
||||
public RoleTypes Type => RoleTypes.Shapeshifter;
|
||||
|
||||
public bool LeaveSkin { get; set; }
|
||||
|
||||
public byte Cooldown { get; set; } = 10;
|
||||
|
||||
public byte Duration { get; set; } = 30;
|
||||
|
||||
public static ShapeshifterRoleOptions Deserialize(IMessageReader reader, byte version)
|
||||
{
|
||||
var options = new ShapeshifterRoleOptions(version);
|
||||
|
||||
options.LeaveSkin = reader.ReadBoolean();
|
||||
options.Cooldown = reader.ReadByte();
|
||||
options.Duration = reader.ReadByte();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(LeaveSkin);
|
||||
writer.Write(Cooldown);
|
||||
writer.Write(Duration);
|
||||
}
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public class GameOptionsData
|
||||
{
|
||||
/// <summary>
|
||||
/// The latest major version of the game client.
|
||||
/// </summary>
|
||||
public const int LatestVersion = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets host's version of the game.
|
||||
/// </summary>
|
||||
public byte Version { get; set; } = LatestVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of players for this lobby.
|
||||
/// </summary>
|
||||
public byte MaxPlayers { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the language of the lobby as per <see cref="GameKeywords" /> enum.
|
||||
/// </summary>
|
||||
public GameKeywords Keywords { get; set; } = GameKeywords.English;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Map selected for this lobby.
|
||||
/// </summary>
|
||||
public MapTypes Map { get; set; } = MapTypes.Skeld;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Player speed modifier.
|
||||
/// </summary>
|
||||
public float PlayerSpeedMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Light modifier for the players that are members of the crew as a multiplier value.
|
||||
/// </summary>
|
||||
public float CrewLightMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Light modifier for the players that are Impostors as a multiplier value.
|
||||
/// </summary>
|
||||
public float ImpostorLightMod { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Impostor cooldown to kill in seconds.
|
||||
/// </summary>
|
||||
public float KillCooldown { get; set; } = 15f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of common tasks.
|
||||
/// </summary>
|
||||
public int NumCommonTasks { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of long tasks.
|
||||
/// </summary>
|
||||
public int NumLongTasks { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of short tasks.
|
||||
/// </summary>
|
||||
public int NumShortTasks { get; set; } = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of emergency meetings each player can call during the game in seconds.
|
||||
/// </summary>
|
||||
public int NumEmergencyMeetings { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cooldown between each time any player can call an emergency meeting in seconds.
|
||||
/// </summary>
|
||||
public int EmergencyCooldown { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of impostors for this lobby.
|
||||
/// </summary>
|
||||
public int NumImpostors { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether ghosts (dead crew members) can do tasks.
|
||||
/// </summary>
|
||||
public bool GhostsDoTasks { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Kill as per values in <see cref="KillDistances" />.
|
||||
/// </summary>
|
||||
public KillDistances KillDistance { get; set; } = KillDistances.Normal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time for discussion before voting time in seconds.
|
||||
/// </summary>
|
||||
public int DiscussionTime { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time for voting in seconds.
|
||||
/// </summary>
|
||||
public int VotingTime { get; set; } = 120;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether an ejected player is an impostor or not.
|
||||
/// </summary>
|
||||
public bool ConfirmImpostor { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether players are able to see tasks being performed by other players.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By being set to true, tasks such as Empty Garbage, Submit Scan, Clear asteroids, Prime shields execution will be visible to other players.
|
||||
/// </remarks>
|
||||
public bool VisualTasks { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the vote is anonymous.
|
||||
/// </summary>
|
||||
public bool AnonymousVotes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the task bar update mode as per values in <see cref="Innersloth.TaskBarUpdate" />.
|
||||
/// </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>
|
||||
public bool IsDefaults { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize a packet/message to a new GameOptionsData object.
|
||||
/// </summary>
|
||||
/// <param name="reader">Message reader object containing the raw message.</param>
|
||||
/// <returns>GameOptionsData object.</returns>
|
||||
public static GameOptionsData DeserializeCreate(IMessageReader reader)
|
||||
{
|
||||
var options = new GameOptionsData();
|
||||
options.Deserialize(reader.ReadBytesAndSize());
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes this instance of GameOptionsData object to a specified BinaryWriter.
|
||||
/// </summary>
|
||||
/// <param name="writer">The stream to write the message to.</param>
|
||||
/// <param name="version">The version of the game.</param>
|
||||
public void Serialize(BinaryWriter writer, byte version = LatestVersion)
|
||||
{
|
||||
writer.Write((byte)version);
|
||||
writer.Write((byte)MaxPlayers);
|
||||
writer.Write((uint)Keywords);
|
||||
writer.Write((byte)Map);
|
||||
writer.Write((float)PlayerSpeedMod);
|
||||
writer.Write((float)CrewLightMod);
|
||||
writer.Write((float)ImpostorLightMod);
|
||||
writer.Write((float)KillCooldown);
|
||||
writer.Write((byte)NumCommonTasks);
|
||||
writer.Write((byte)NumLongTasks);
|
||||
writer.Write((byte)NumShortTasks);
|
||||
writer.Write((int)NumEmergencyMeetings);
|
||||
writer.Write((byte)NumImpostors);
|
||||
writer.Write((byte)KillDistance);
|
||||
writer.Write((uint)DiscussionTime);
|
||||
writer.Write((uint)VotingTime);
|
||||
writer.Write((bool)IsDefaults);
|
||||
|
||||
if (version >= 2)
|
||||
{
|
||||
writer.Write((byte)EmergencyCooldown);
|
||||
}
|
||||
|
||||
if (version >= 3)
|
||||
{
|
||||
writer.Write((bool)ConfirmImpostor);
|
||||
writer.Write((bool)VisualTasks);
|
||||
}
|
||||
|
||||
if (version >= 4)
|
||||
{
|
||||
writer.Write((bool)AnonymousVotes);
|
||||
writer.Write((byte)TaskBarUpdate);
|
||||
}
|
||||
|
||||
if (version >= 5)
|
||||
{
|
||||
RoleOptions.Serialize(writer);
|
||||
}
|
||||
|
||||
if (version >= 6)
|
||||
{
|
||||
// Nothing was changed in V6
|
||||
}
|
||||
|
||||
if (version > 6)
|
||||
{
|
||||
throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
using var memory = new MemoryStream();
|
||||
using var writerBin = new BinaryWriter(memory);
|
||||
Serialize(writerBin);
|
||||
writer.WriteBytesAndSize(memory.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize a ReadOnlyMemory object to this instance of the GameOptionsData object.
|
||||
/// </summary>
|
||||
/// <param name="memory">Memory containing the message/packet.</param>
|
||||
public void Deserialize(ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
var bytes = memory.Span;
|
||||
|
||||
Version = bytes.ReadByte();
|
||||
MaxPlayers = bytes.ReadByte();
|
||||
Keywords = (GameKeywords)bytes.ReadUInt32();
|
||||
Map = (MapTypes)bytes.ReadByte();
|
||||
PlayerSpeedMod = bytes.ReadSingle();
|
||||
|
||||
CrewLightMod = bytes.ReadSingle();
|
||||
ImpostorLightMod = bytes.ReadSingle();
|
||||
KillCooldown = bytes.ReadSingle();
|
||||
|
||||
NumCommonTasks = bytes.ReadByte();
|
||||
NumLongTasks = bytes.ReadByte();
|
||||
NumShortTasks = bytes.ReadByte();
|
||||
|
||||
NumEmergencyMeetings = bytes.ReadInt32();
|
||||
|
||||
NumImpostors = bytes.ReadByte();
|
||||
KillDistance = (KillDistances)bytes.ReadByte();
|
||||
DiscussionTime = bytes.ReadInt32();
|
||||
VotingTime = bytes.ReadInt32();
|
||||
|
||||
IsDefaults = bytes.ReadBoolean();
|
||||
|
||||
if (Version >= 2)
|
||||
{
|
||||
EmergencyCooldown = bytes.ReadByte();
|
||||
}
|
||||
|
||||
if (Version >= 3)
|
||||
{
|
||||
ConfirmImpostor = bytes.ReadBoolean();
|
||||
VisualTasks = bytes.ReadBoolean();
|
||||
}
|
||||
|
||||
if (Version >= 4)
|
||||
{
|
||||
AnonymousVotes = bytes.ReadBoolean();
|
||||
TaskBarUpdate = (TaskBarUpdate)bytes.ReadByte();
|
||||
}
|
||||
|
||||
if (Version >= 5)
|
||||
{
|
||||
RoleOptions = RoleOptionsData.Deserialize(bytes);
|
||||
}
|
||||
|
||||
if (Version >= 6)
|
||||
{
|
||||
// Nothing was changed in V6
|
||||
}
|
||||
|
||||
if (Version > 6)
|
||||
{
|
||||
throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,7 @@
|
||||
|
||||
Sabotage = 17,
|
||||
|
||||
/// <summary>
|
||||
/// Decontam on Mira and bottom decontam on Polus
|
||||
/// </summary>
|
||||
/// <remarks>The only decontamination on Mira and bottom decontamination on Polus.</remarks>
|
||||
Decontamination = 18,
|
||||
|
||||
Launchpad = 19,
|
||||
@@ -57,9 +55,7 @@
|
||||
|
||||
Dropship = 25,
|
||||
|
||||
/// <summary>
|
||||
/// Top decontam on Polus
|
||||
/// </summary>
|
||||
/// <remarks>Top decontamination on Polus.</remarks>
|
||||
Decontamination2 = 26,
|
||||
|
||||
Outside = 27,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
using Impostor.Api.Net.Inner.Objects.ShipStatus;
|
||||
|
||||
namespace Impostor.Api.Net.Inner
|
||||
@@ -8,6 +9,8 @@ namespace Impostor.Api.Net.Inner
|
||||
/// </summary>
|
||||
public interface IGameNet
|
||||
{
|
||||
IInnerGameManager? GameManager { get; }
|
||||
|
||||
IInnerLobbyBehaviour? LobbyBehaviour { get; }
|
||||
|
||||
IInnerGameData? GameData { get; }
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
|
||||
public interface IInnerGameManager
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
namespace Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
|
||||
public interface IInnerHideAndSeekManager : IInnerGameManager
|
||||
{
|
||||
ILogicGameFlowHnS LogicFlowHnS { get; }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
|
||||
public interface IInnerNormalGameManager : IInnerGameManager
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Impostor.Api.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
public interface ILogicGameFlowHnS
|
||||
{
|
||||
float CurrentFinalHideTime { get; }
|
||||
|
||||
float CurrentHideTime { get; }
|
||||
}
|
||||
@@ -45,5 +45,7 @@
|
||||
Shapeshift = 46,
|
||||
CheckMurder = 47,
|
||||
CheckProtect = 48,
|
||||
Pet = 49,
|
||||
CancelPet = 50,
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,24 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.C2S
|
||||
{
|
||||
public static class Message00HostGameC2S
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, GameOptionsData gameOptionsData)
|
||||
public static void Serialize(IMessageWriter writer, IGameOptions gameOptions, CrossplayFlags crossplayFlags, GameFilterOptions gameFilterOptions)
|
||||
{
|
||||
writer.StartMessage(MessageFlags.HostGame);
|
||||
gameOptionsData.Serialize(writer);
|
||||
writer.Write(int.MaxValue); // crossplayFlags
|
||||
GameOptionsFactory.Serialize(writer, gameOptions);
|
||||
writer.Write((int)crossplayFlags);
|
||||
gameFilterOptions.Serialize(writer);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize a packet.
|
||||
/// </summary>
|
||||
/// <param name="reader"><see cref="IMessageReader" /> with <see cref="IMessageReader.Tag" /> 0.</param>
|
||||
/// <returns>Deserialized <see cref="GameOptionsData" />.</returns>
|
||||
public static GameOptionsData Deserialize(IMessageReader reader)
|
||||
public static void Deserialize(IMessageReader reader, out IGameOptions gameOptions, out CrossplayFlags crossplayFlags, out GameFilterOptions gameFilterOptions)
|
||||
{
|
||||
var gameOptionsData = GameOptionsData.DeserializeCreate(reader);
|
||||
reader.ReadInt32(); // crossplayFlags, not used yet
|
||||
|
||||
return gameOptionsData;
|
||||
gameOptions = GameOptionsFactory.Deserialize(reader);
|
||||
crossplayFlags = (CrossplayFlags)reader.ReadInt32();
|
||||
gameFilterOptions = GameFilterOptions.Deserialize(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.C2S
|
||||
{
|
||||
@@ -10,7 +11,7 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out GameOptionsData options, out QuickChatModes chatMode)
|
||||
public static void Deserialize(IMessageReader reader, out IGameOptions options, out QuickChatModes chatMode, out CrossplayFlags crossplayFlags, out GameFilterOptions gameFilterOptions)
|
||||
{
|
||||
var version = reader.ReadPackedInt32();
|
||||
if (version != 2)
|
||||
@@ -18,9 +19,10 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
throw new NotSupportedException($"Version {version} of {nameof(Message16GetGameListC2S)} is not supported");
|
||||
}
|
||||
|
||||
options = GameOptionsData.DeserializeCreate(reader);
|
||||
options = GameOptionsFactory.Deserialize(reader);
|
||||
chatMode = (QuickChatModes)reader.ReadByte();
|
||||
reader.ReadInt32(); // crossplayFlags, not used yet
|
||||
crossplayFlags = (CrossplayFlags)reader.ReadInt32();
|
||||
gameFilterOptions = GameFilterOptions.Deserialize(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace Impostor.Api.Net.Messages
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Impostor.Api.Net.Messages
|
||||
{
|
||||
public static class MessageFlags
|
||||
{
|
||||
@@ -24,5 +28,20 @@
|
||||
public const byte SetActivePodType = 21;
|
||||
public const byte QueryPlatformIds = 22;
|
||||
public const byte QueryLobbyInfo = 23;
|
||||
|
||||
private static readonly Dictionary<byte, string> FlagCache;
|
||||
|
||||
static MessageFlags()
|
||||
{
|
||||
FlagCache = typeof(MessageFlags)
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(fi => fi.IsLiteral && !fi.IsInitOnly)
|
||||
.ToDictionary(x => (byte)x.GetValue(null)!, y => y.Name);
|
||||
}
|
||||
|
||||
public static string FlagToString(byte flag)
|
||||
{
|
||||
return FlagCache[flag];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,22 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc02SyncSettings
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, GameOptionsData gameOptionsData)
|
||||
public static void Serialize(IMessageWriter writer, IGameOptions gameOptionsData)
|
||||
{
|
||||
gameOptionsData.Serialize(writer);
|
||||
GameOptionsFactory.Serialize(writer, gameOptionsData);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, GameOptionsData gameOptionsData)
|
||||
public static void Deserialize(IMessageReader reader, out IGameOptions gameOptionsData)
|
||||
{
|
||||
gameOptionsData.Deserialize(reader.ReadBytesAndSize());
|
||||
gameOptionsData = GameOptionsFactory.Deserialize(reader);
|
||||
}
|
||||
|
||||
public static void DeserializeInto(IMessageReader reader, IGameOptions gameOptionsData)
|
||||
{
|
||||
GameOptionsFactory.DeserializeInto(reader, gameOptionsData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc49Pet
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, Vector2 position, Vector2 petPosition)
|
||||
{
|
||||
writer.Write(position);
|
||||
writer.Write(petPosition);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out Vector2 position, out Vector2 petPosition)
|
||||
{
|
||||
position = reader.ReadVector2();
|
||||
petPosition = reader.ReadVector2();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc50CancelPet
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer)
|
||||
{
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,19 @@
|
||||
using System;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Games;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.S2C
|
||||
{
|
||||
public static class Message00HostGameS2C
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, int gameCode)
|
||||
public static void Serialize(IMessageWriter writer, GameCode gameCode)
|
||||
{
|
||||
writer.StartMessage(MessageFlags.HostGame);
|
||||
writer.Write(gameCode);
|
||||
gameCode.Serialize(writer);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
public static GameOptionsData Deserialize(IMessageReader reader)
|
||||
public static void Deserialize(IMessageReader reader, out GameCode gameCode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
gameCode = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net.Messages.C2S;
|
||||
using Impostor.Hazel;
|
||||
using Impostor.Hazel.Abstractions;
|
||||
@@ -27,11 +28,11 @@ namespace Impostor.Client.App
|
||||
|
||||
var writeGameCreate = MessageWriter.Get(MessageType.Reliable);
|
||||
|
||||
Message00HostGameC2S.Serialize(writeGameCreate, new GameOptionsData
|
||||
Message00HostGameC2S.Serialize(writeGameCreate, new LegacyGameOptionsData
|
||||
{
|
||||
MaxPlayers = 4,
|
||||
NumImpostors = 2,
|
||||
});
|
||||
}, CrossplayFlags.All, GameFilterOptions.CreateDefault());
|
||||
|
||||
// TODO: ObjectPool for MessageReaders
|
||||
using (var connection = new UdpClientConnection(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 22023), null))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Plugins;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -22,7 +23,7 @@ namespace Impostor.Plugins.Example
|
||||
{
|
||||
_logger.LogInformation("Example is being enabled.");
|
||||
|
||||
var game = await _gameManager.CreateAsync(new GameOptionsData());
|
||||
var game = await _gameManager.CreateAsync(new NormalGameOptions(), GameFilterOptions.CreateDefault());
|
||||
if (game == null)
|
||||
{
|
||||
_logger.LogWarning("Example game creation was cancelled");
|
||||
|
||||
@@ -19,21 +19,16 @@ namespace Impostor.Plugins.Example.Handlers
|
||||
{
|
||||
_logger.LogInformation("Game creation requested by {client}", e.Client == null ? "a plugin" : e.Client.Name);
|
||||
|
||||
if (e.Client != null)
|
||||
{
|
||||
var gameCode = GameCode.From(e.Client.Name);
|
||||
|
||||
if (!gameCode.IsInvalid)
|
||||
{
|
||||
e.GameCode = gameCode;
|
||||
}
|
||||
|
||||
if (e.Client.Name == "dima")
|
||||
{
|
||||
e.IsCancelled = true;
|
||||
e.Client.DisconnectAsync(DisconnectReason.Custom, "No you dont >:(");
|
||||
}
|
||||
}
|
||||
// TODO: Code below causes the lobby to stop loading and close after 5 secs.
|
||||
// if (e.Client != null)
|
||||
// {
|
||||
// var gameCode = GameCode.From(e.Client.Name);
|
||||
//
|
||||
// if (!gameCode.IsInvalid)
|
||||
// {
|
||||
// e.GameCode = gameCode;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
[EventListener]
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
||||
using Impostor.Api.Events;
|
||||
using Impostor.Api.Events.Player;
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Plugins.Example.Handlers
|
||||
@@ -65,9 +66,13 @@ namespace Impostor.Plugins.Example.Handlers
|
||||
|
||||
if (e.Message == "test")
|
||||
{
|
||||
e.Game.Options.KillCooldown = 0;
|
||||
e.Game.Options.NumImpostors = 2;
|
||||
e.Game.Options.PlayerSpeedMod = 5;
|
||||
|
||||
if (e.Game.Options is NormalGameOptions normalGameOptions)
|
||||
{
|
||||
normalGameOptions.KillCooldown = 0;
|
||||
normalGameOptions.PlayerSpeedMod = 5;
|
||||
}
|
||||
|
||||
await e.Game.SyncSettingsAsync();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Impostor.Api;
|
||||
using Impostor.Api.Config;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Messages;
|
||||
@@ -58,17 +59,17 @@ namespace Impostor.Server.Net
|
||||
{
|
||||
var flag = reader.Tag;
|
||||
|
||||
_logger.LogTrace("[{0}] Server got {1}.", Id, flag);
|
||||
_logger.LogTrace("[{0}] Server got {1}.", Id, MessageFlags.FlagToString(flag));
|
||||
|
||||
switch (flag)
|
||||
{
|
||||
case MessageFlags.HostGame:
|
||||
{
|
||||
// Read game settings.
|
||||
var gameInfo = Message00HostGameC2S.Deserialize(reader);
|
||||
Message00HostGameC2S.Deserialize(reader, out var gameOptions, out _, out var gameFilterOptions);
|
||||
|
||||
// Create game.
|
||||
var game = await _gameManager.CreateAsync(this, gameInfo);
|
||||
var game = await _gameManager.CreateAsync(this, gameOptions, gameFilterOptions);
|
||||
|
||||
if (game == null)
|
||||
{
|
||||
@@ -259,8 +260,8 @@ namespace Impostor.Server.Net
|
||||
|
||||
case MessageFlags.GetGameListV2:
|
||||
{
|
||||
Message16GetGameListC2S.Deserialize(reader, out var options, out _);
|
||||
await OnRequestGameListAsync(options);
|
||||
Message16GetGameListC2S.Deserialize(reader, out var options, out _, out _, out var filterOptions);
|
||||
await OnRequestGameListAsync(options, filterOptions);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -354,15 +355,13 @@ namespace Impostor.Server.Net
|
||||
/// <summary>
|
||||
/// Triggered when the connected client requests the game listing.
|
||||
/// </summary>
|
||||
/// <param name="options">
|
||||
/// All options given.
|
||||
/// At this moment, the client can only specify the map, impostor count and chat language.
|
||||
/// </param>
|
||||
private ValueTask OnRequestGameListAsync(GameOptionsData options)
|
||||
/// <param name="options">Options specific to the game mode. At this moment, the client can only specify the map, impostor count and chat language.</param>
|
||||
/// <param name="filterOptions">Filter options not specific to the game mode.</param>
|
||||
private ValueTask OnRequestGameListAsync(IGameOptions options, GameFilterOptions filterOptions)
|
||||
{
|
||||
using var message = MessageWriter.Get(MessageType.Reliable);
|
||||
|
||||
var games = _gameManager.FindListings((MapFlags)options.Map, options.NumImpostors, options.Keywords, this.GameVersion);
|
||||
var games = _gameManager.FindListings((MapFlags)options.Map, options.NumImpostors, options.Keywords, this.GameVersion, filterOptions.FilterTags);
|
||||
|
||||
Message16GetGameListS2C.Serialize(message, games);
|
||||
|
||||
|
||||
@@ -108,6 +108,18 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
Rpc31ClimbLadder.Deserialize(reader, out var ladderId, out var lastClimbLadderSid);
|
||||
break;
|
||||
|
||||
case RpcCalls.Pet:
|
||||
{
|
||||
Rpc49Pet.Deserialize(reader, out var position, out var petPosition);
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.CancelPet:
|
||||
{
|
||||
Rpc50CancelPet.Deserialize(reader);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return await base.HandleRpcAsync(sender, target, call, reader);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Inner;
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
using Impostor.Server.Net.Inner.Objects.GameManager.Logic;
|
||||
using Impostor.Server.Net.State;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager;
|
||||
|
||||
internal abstract class InnerGameManager : InnerNetObject, IInnerGameManager
|
||||
{
|
||||
private readonly ILogger<InnerGameManager> _logger;
|
||||
private readonly List<GameLogicComponent> _logicComponents = new();
|
||||
|
||||
public InnerGameManager(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerGameManager> logger) : base(customMessageManager, game)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
Components.Add(this);
|
||||
}
|
||||
|
||||
public LogicGameFlow LogicFlow { get; protected init; } = null!;
|
||||
|
||||
public LogicMinigame LogicMinigame { get; protected init; } = null!;
|
||||
|
||||
public LogicRoleSelection LogicRoleSelection { get; protected init; } = null!;
|
||||
|
||||
public LogicUsables LogicUsables { get; protected init; } = null!;
|
||||
|
||||
public LogicOptions LogicOptions { get; protected init; } = null!;
|
||||
|
||||
protected T AddGameLogic<T>(T logic)
|
||||
where T : GameLogicComponent
|
||||
{
|
||||
_logicComponents.Add(logic);
|
||||
return logic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the tag of the registered <see cref="GameLogicComponent"/>.
|
||||
/// </summary>
|
||||
/// <param name="logic">Instance to search for.</param>
|
||||
/// <typeparam name="T">Intance type to search for.</typeparam>
|
||||
/// <returns>Tag of the registered <see cref="GameLogicComponent"/>, or -1 if not found.</returns>
|
||||
internal int GetGameLogicTag<T>(T logic)
|
||||
where T : GameLogicComponent
|
||||
{
|
||||
return _logicComponents.IndexOf(logic);
|
||||
}
|
||||
|
||||
public override ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
|
||||
{
|
||||
foreach (var logicComponent in _logicComponents)
|
||||
{
|
||||
logicComponent.HandleRpc(call, reader);
|
||||
}
|
||||
|
||||
return ValueTask.FromResult(true);
|
||||
}
|
||||
|
||||
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer? target, IMessageReader reader, bool initialState)
|
||||
{
|
||||
while (reader.Position < reader.Length)
|
||||
{
|
||||
var innerReader = reader.ReadMessage();
|
||||
var tag = (int)innerReader.Tag;
|
||||
if (tag < 0 || tag > this._logicComponents.Count)
|
||||
{
|
||||
_logger.LogError("Out of bounds in DeserializeAsync of InnerGameManager");
|
||||
continue;
|
||||
}
|
||||
|
||||
var component = this._logicComponents[tag];
|
||||
|
||||
component.Deserialize(innerReader, initialState);
|
||||
|
||||
if (innerReader.Position < innerReader.Length)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Server did not consume all bytes from {0} component {1} ({2} < {3}).",
|
||||
nameof(InnerGameManager),
|
||||
component.GetType().FullName,
|
||||
innerReader.Position,
|
||||
innerReader.Length);
|
||||
}
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager;
|
||||
|
||||
internal partial class InnerHideAndSeekManager
|
||||
{
|
||||
ILogicGameFlowHnS IInnerHideAndSeekManager.LogicFlowHnS => LogicFlowHnS;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
using Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
using Impostor.Server.Net.State;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager;
|
||||
|
||||
internal partial class InnerHideAndSeekManager : InnerGameManager, IInnerHideAndSeekManager
|
||||
{
|
||||
public InnerHideAndSeekManager(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerGameManager> logger) : base(customMessageManager, game, logger)
|
||||
{
|
||||
LogicMusic = AddGameLogic(new LogicHnSMusic());
|
||||
LogicMinigame = AddGameLogic(new LogicMinigameHnS());
|
||||
LogicFlowHnS = new LogicGameFlowHnS();
|
||||
LogicFlow = AddGameLogic(LogicFlowHnS);
|
||||
LogicUsables = AddGameLogic(new LogicUsablesHnS());
|
||||
LogicRoleSelection = AddGameLogic(new LogicRoleSelectionHnS());
|
||||
LogicOptionsHnS = new LogicOptionsHnS(game);
|
||||
LogicOptions = AddGameLogic(this.LogicOptionsHnS);
|
||||
LogicDangerLevel = AddGameLogic(new LogicHnSDangerLevel());
|
||||
LogicPing = AddGameLogic(new LogicPingsHnS());
|
||||
LogicDeathPopup = AddGameLogic(new LogicHnSDeathPopup());
|
||||
}
|
||||
|
||||
public LogicHnSMusic LogicMusic { get; private set; }
|
||||
|
||||
public LogicGameFlowHnS LogicFlowHnS { get; private set; }
|
||||
|
||||
public LogicOptionsHnS LogicOptionsHnS { get; private set; }
|
||||
|
||||
public LogicHnSDangerLevel LogicDangerLevel { get; private set; }
|
||||
|
||||
public LogicPingsHnS LogicPing { get; private set; }
|
||||
|
||||
public LogicHnSDeathPopup LogicDeathPopup { get; private set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
using Impostor.Server.Net.Inner.Objects.GameManager.Logic;
|
||||
using Impostor.Server.Net.Inner.Objects.GameManager.Logic.Normal;
|
||||
using Impostor.Server.Net.State;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager;
|
||||
|
||||
internal class InnerNormalGameManager : InnerGameManager, IInnerNormalGameManager
|
||||
{
|
||||
public InnerNormalGameManager(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerGameManager> logger) : base(customMessageManager, game, logger)
|
||||
{
|
||||
LogicFlow = AddGameLogic(new LogicGameFlowNormal());
|
||||
LogicMinigame = AddGameLogic(new LogicMinigame());
|
||||
LogicRoleSelection = AddGameLogic(new LogicRoleSelectionNormal());
|
||||
LogicUsables = AddGameLogic(new LogicUsablesBasic());
|
||||
LogicOptions = AddGameLogic(new LogicOptionsNormal(game));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using Impostor.Api.Net.Inner;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic;
|
||||
|
||||
internal abstract class GameLogicComponent
|
||||
{
|
||||
public virtual void HandleRpc(RpcCalls callId, IMessageReader reader)
|
||||
{
|
||||
throw new NotImplementedException($"Unhandled RpcCall {callId}");
|
||||
}
|
||||
|
||||
public virtual bool Serialize(IMessageWriter writer, bool initialState)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual void Deserialize(IMessageReader reader, bool initialState)
|
||||
{
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicGameFlowHnS : LogicGameFlow, ILogicGameFlowHnS
|
||||
{
|
||||
public float CurrentFinalHideTime { get; private set; }
|
||||
|
||||
public float CurrentHideTime { get; private set; }
|
||||
|
||||
public override void Deserialize(IMessageReader reader, bool initialState)
|
||||
{
|
||||
var num = reader.ReadSingle();
|
||||
|
||||
CurrentFinalHideTime = reader.ReadSingle();
|
||||
CurrentHideTime = num;
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicHnSDangerLevel : GameLogicComponent
|
||||
{
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicHnSDeathPopup : GameLogicComponent
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicHnSMusic : GameLogicComponent
|
||||
{
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicMinigameHnS : LogicMinigame
|
||||
{
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
using Impostor.Server.Net.State;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicOptionsHnS : LogicOptions
|
||||
{
|
||||
public LogicOptionsHnS(Game game) : base(game)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicPingsHnS : GameLogicComponent
|
||||
{
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicRoleSelectionHnS : LogicRoleSelection
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.HideAndSeek;
|
||||
|
||||
internal class LogicUsablesHnS : LogicUsables
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic;
|
||||
|
||||
internal abstract class LogicGameFlow : GameLogicComponent
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic;
|
||||
|
||||
internal class LogicMinigame : GameLogicComponent
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Server.Net.State;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic;
|
||||
|
||||
internal abstract class LogicOptions : GameLogicComponent
|
||||
{
|
||||
private readonly Game _game;
|
||||
|
||||
protected LogicOptions(Game game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
public override bool Serialize(IMessageWriter writer, bool initialState)
|
||||
{
|
||||
GameOptionsFactory.Serialize(writer, _game.Options);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Deserialize(IMessageReader reader, bool initialState)
|
||||
{
|
||||
GameOptionsFactory.DeserializeInto(reader, _game.Options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic;
|
||||
|
||||
internal abstract class LogicRoleSelection : GameLogicComponent
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic;
|
||||
|
||||
internal abstract class LogicUsables : GameLogicComponent
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.Normal;
|
||||
|
||||
internal class LogicGameFlowNormal : LogicGameFlow
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Impostor.Server.Net.State;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.Normal;
|
||||
|
||||
internal class LogicOptionsNormal : LogicOptions
|
||||
{
|
||||
public LogicOptionsNormal(Game game) : base(game)
|
||||
{
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.Normal;
|
||||
|
||||
internal class LogicRoleSelectionNormal : LogicRoleSelection
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Impostor.Server.Net.Inner.Objects.GameManager.Logic.Normal;
|
||||
|
||||
internal class LogicUsablesBasic : LogicUsables
|
||||
{
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using Impostor.Api;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Events.Player;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Inner;
|
||||
@@ -43,7 +44,15 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
try
|
||||
{
|
||||
const float AnimationTime = 0.25f + 0.5f + 0.4f + 3f + 0.75f + 5f;
|
||||
await Task.Delay(TimeSpan.FromSeconds(AnimationTime + Game.Options.DiscussionTime + Game.Options.VotingTime), _timerToken.Token);
|
||||
if (Game.Options.GameMode == GameModes.Normal)
|
||||
{
|
||||
var options = (NormalGameOptions)Game.Options;
|
||||
await Task.Delay(TimeSpan.FromSeconds(AnimationTime + options.DiscussionTime + options.VotingTime), _timerToken.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc02SyncSettings.Deserialize(reader, Game.Options);
|
||||
Rpc02SyncSettings.DeserializeInto(reader, Game.Options);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Utils;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects
|
||||
@@ -59,7 +60,15 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
// the impostor has a cooldown of half the usual duration. As a workaround we always assume the kill was
|
||||
// prevented and the impostor only has half of its cooldown.
|
||||
// FIXME when the base game improved their implementation.
|
||||
return dateTimeProvider.UtcNow.Subtract(LastMurder).TotalSeconds >= game.Options.KillCooldown / 2;
|
||||
if (game.Options.GameMode == GameModes.Normal)
|
||||
{
|
||||
var options = (NormalGameOptions)game.Options;
|
||||
return dateTimeProvider.UtcNow.Subtract(LastMurder).TotalSeconds >= options.KillCooldown / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
|
||||
@@ -21,14 +21,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, 11, 9), // 2021.11.9
|
||||
GameVersion.GetVersion(2021, 12, 14), // 2021.12.14
|
||||
GameVersion.GetVersion(2022, 1, 10), // 2021.2.23
|
||||
GameVersion.GetVersion(2022, 2, 2), // 2022.3.29 and 2022.4.19
|
||||
GameVersion.GetVersion(2022, 4, 20), // 2022.6.21 and 2022.7.12
|
||||
GameVersion.GetVersion(2022, 5, 12), // 2022.8.23
|
||||
GameVersion.GetVersion(2022, 7, 25), // 2022.10.18
|
||||
GameVersion.GetVersion(2022, 9, 2), // 2022.10.25
|
||||
GameVersion.GetVersion(2022, 11, 1), // 2022.12.8
|
||||
GameVersion.GetVersion(2022, 11, 9), // 2022.12.14
|
||||
};
|
||||
|
||||
private readonly ILogger<ClientManager> _logger;
|
||||
|
||||
@@ -10,6 +10,7 @@ using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Server.Events;
|
||||
using Impostor.Server.Net.State;
|
||||
@@ -50,7 +51,7 @@ namespace Impostor.Server.Net.Manager
|
||||
return game;
|
||||
}
|
||||
|
||||
public IEnumerable<Game> FindListings(MapFlags map, int impostorCount, GameKeywords language, int gameVersion, int count = 10)
|
||||
public IEnumerable<Game> FindListings(MapFlags map, int impostorCount, GameKeywords language, int gameVersion, HashSet<string> filterTags, int count = 10)
|
||||
{
|
||||
var results = 0;
|
||||
|
||||
@@ -59,7 +60,7 @@ namespace Impostor.Server.Net.Manager
|
||||
x.Value.IsPublic &&
|
||||
x.Value.GameState == GameStates.NotStarted &&
|
||||
x.Value.PlayerCount < x.Value.Options.MaxPlayers &&
|
||||
(_compatibilityConfig.AllowVersionMixing == true || x.Value.Host?.Client.GameVersion == gameVersion)))
|
||||
(_compatibilityConfig.AllowVersionMixing || x.Value.Host == null || x.Value.Host.Client.GameVersion == gameVersion)))
|
||||
{
|
||||
// Check for options.
|
||||
if (!map.HasFlag((MapFlags)(1 << (byte)game.Options.Map)))
|
||||
@@ -77,6 +78,11 @@ namespace Impostor.Server.Net.Manager
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!game.FilterOptions.FilterTags.SetEquals(filterTags))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add to result.
|
||||
yield return game;
|
||||
|
||||
@@ -110,7 +116,7 @@ namespace Impostor.Server.Net.Manager
|
||||
await _eventManager.CallAsync(new GameDestroyedEvent(game));
|
||||
}
|
||||
|
||||
public async ValueTask<IGame?> CreateAsync(IClient? owner, GameOptionsData options)
|
||||
public async ValueTask<IGame?> CreateAsync(IClient? owner, IGameOptions options, GameFilterOptions filterOptions)
|
||||
{
|
||||
var @event = new GameCreationEvent(this, owner);
|
||||
await _eventManager.CallAsync(@event);
|
||||
@@ -120,11 +126,11 @@ namespace Impostor.Server.Net.Manager
|
||||
return null;
|
||||
}
|
||||
|
||||
var (success, game) = await TryCreateAsync(options, @event.GameCode);
|
||||
var (success, game) = await TryCreateAsync(options, filterOptions, @event.GameCode);
|
||||
|
||||
for (var i = 0; i < 10 && !success; i++)
|
||||
{
|
||||
(success, game) = await TryCreateAsync(options);
|
||||
(success, game) = await TryCreateAsync(options, filterOptions);
|
||||
}
|
||||
|
||||
if (!success || game == null)
|
||||
@@ -135,15 +141,15 @@ namespace Impostor.Server.Net.Manager
|
||||
return game;
|
||||
}
|
||||
|
||||
public ValueTask<IGame?> CreateAsync(GameOptionsData options)
|
||||
public ValueTask<IGame?> CreateAsync(IGameOptions options, GameFilterOptions filterOptions)
|
||||
{
|
||||
return CreateAsync(null, options);
|
||||
return CreateAsync(null, options, filterOptions);
|
||||
}
|
||||
|
||||
private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(GameOptionsData options, GameCode? desiredGameCode = null)
|
||||
private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(IGameOptions options, GameFilterOptions filterOptions, GameCode? desiredGameCode = null)
|
||||
{
|
||||
var gameCode = desiredGameCode ?? _gameCodeFactory.Create();
|
||||
var game = ActivatorUtilities.CreateInstance<Game>(_serviceProvider, _publicIp, gameCode, options);
|
||||
var game = ActivatorUtilities.CreateInstance<Game>(_serviceProvider, _publicIp, gameCode, options, filterOptions);
|
||||
|
||||
if (!_games.TryAdd(gameCode, game))
|
||||
{
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Inner;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Hazel;
|
||||
using Impostor.Server.Net.Inner;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
{
|
||||
@@ -25,24 +25,40 @@ namespace Impostor.Server.Net.State
|
||||
{
|
||||
if (Host?.Character == null)
|
||||
{
|
||||
throw new ImpostorException("Attempted to set infected when the host was not spawned.");
|
||||
throw new ImpostorException("Attempted to change settings when the host was not spawned.");
|
||||
}
|
||||
|
||||
using (var writer = StartRpc(Host.Character.NetId, RpcCalls.SyncSettings))
|
||||
if (GameNet.GameManager == null)
|
||||
{
|
||||
// Someone will probably forget to do this, so we include it here.
|
||||
// If this is not done, the host will overwrite changes later with the defaults.
|
||||
Options.IsDefaults = false;
|
||||
|
||||
await using (var memory = new MemoryStream())
|
||||
await using (var writerBin = new BinaryWriter(memory))
|
||||
{
|
||||
Options.Serialize(writerBin, GameOptionsData.LatestVersion);
|
||||
writer.WriteBytesAndSize(memory.ToArray());
|
||||
}
|
||||
|
||||
await FinishRpcAsync(writer);
|
||||
throw new ImpostorException("Attempted to change options when the game manager was not spawned.");
|
||||
}
|
||||
|
||||
var gameOptionsTag = GameNet.GameManager.GetGameLogicTag(GameNet.GameManager.LogicOptions);
|
||||
if (gameOptionsTag == -1)
|
||||
{
|
||||
throw new ImpostorException("Attempted to change options when the LogicOptions was not spawned.");
|
||||
}
|
||||
|
||||
// Someone will probably forget to do this, so we include it here.
|
||||
// If this is not done, the host will overwrite changes later with the defaults.
|
||||
Options.IsDefaults = false;
|
||||
|
||||
using var writer = MessageWriter.Get(MessageType.Reliable);
|
||||
|
||||
writer.StartMessage(MessageFlags.GameData);
|
||||
Code.Serialize(writer);
|
||||
|
||||
writer.StartMessage(GameDataTag.DataFlag);
|
||||
writer.WritePacked(GameNet.GameManager.NetId);
|
||||
|
||||
writer.StartMessage((byte)gameOptionsTag);
|
||||
GameNet.GameManager.LogicOptions.Serialize(writer, false);
|
||||
|
||||
writer.EndMessage();
|
||||
writer.EndMessage();
|
||||
writer.EndMessage();
|
||||
|
||||
await SendToAllAsync(writer);
|
||||
}
|
||||
|
||||
public async ValueTask SetPrivacyAsync(bool isPublic)
|
||||
|
||||
@@ -10,6 +10,7 @@ using Impostor.Server.Events.Player;
|
||||
using Impostor.Server.Net.Inner;
|
||||
using Impostor.Server.Net.Inner.Objects;
|
||||
using Impostor.Server.Net.Inner.Objects.Components;
|
||||
using Impostor.Server.Net.Inner.Objects.GameManager;
|
||||
using Impostor.Server.Net.Inner.Objects.ShipStatus;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -40,6 +41,8 @@ namespace Impostor.Server.Net.State
|
||||
typeof(InnerPolusShipStatus),
|
||||
typeof(InnerSkeldShipStatus), // April fools skeld
|
||||
typeof(InnerAirshipStatus),
|
||||
typeof(InnerHideAndSeekManager),
|
||||
typeof(InnerNormalGameManager),
|
||||
};
|
||||
|
||||
private readonly List<InnerNetObject> _allObjects = new List<InnerNetObject>();
|
||||
@@ -287,6 +290,12 @@ namespace Impostor.Server.Net.State
|
||||
{
|
||||
switch (netObj)
|
||||
{
|
||||
case InnerGameManager innerGameManager:
|
||||
{
|
||||
GameNet.GameManager = innerGameManager;
|
||||
break;
|
||||
}
|
||||
|
||||
case InnerLobbyBehaviour lobby:
|
||||
{
|
||||
GameNet.LobbyBehaviour = lobby;
|
||||
|
||||
@@ -10,6 +10,7 @@ using Impostor.Api.Config;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Messages.S2C;
|
||||
using Impostor.Server.Events;
|
||||
@@ -36,7 +37,8 @@ namespace Impostor.Server.Net.State
|
||||
GameManager gameManager,
|
||||
IPEndPoint publicIp,
|
||||
GameCode code,
|
||||
GameOptionsData options,
|
||||
IGameOptions options,
|
||||
GameFilterOptions filterOptions,
|
||||
ClientManager clientManager,
|
||||
IEventManager eventManager,
|
||||
IOptions<CompatibilityConfig> compatibilityConfig)
|
||||
@@ -53,6 +55,7 @@ namespace Impostor.Server.Net.State
|
||||
GameState = GameStates.NotStarted;
|
||||
GameNet = new GameNet();
|
||||
Options = options;
|
||||
FilterOptions = filterOptions;
|
||||
_clientManager = clientManager;
|
||||
_eventManager = eventManager;
|
||||
_compatibilityConfig = compatibilityConfig.Value;
|
||||
@@ -71,7 +74,9 @@ namespace Impostor.Server.Net.State
|
||||
|
||||
public GameStates GameState { get; private set; }
|
||||
|
||||
public GameOptionsData Options { get; }
|
||||
public IGameOptions Options { get; }
|
||||
|
||||
public GameFilterOptions FilterOptions { get; }
|
||||
|
||||
public IDictionary<object, object> Items { get; }
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Impostor.Api.Net.Inner;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
using Impostor.Api.Net.Inner.Objects.GameManager;
|
||||
using Impostor.Api.Net.Inner.Objects.ShipStatus;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
@@ -7,6 +8,8 @@ namespace Impostor.Server.Net.State
|
||||
/// <inheritdoc />
|
||||
internal partial class GameNet : IGameNet
|
||||
{
|
||||
IInnerGameManager? IGameNet.GameManager => GameManager;
|
||||
|
||||
IInnerLobbyBehaviour? IGameNet.LobbyBehaviour => LobbyBehaviour;
|
||||
|
||||
IInnerGameData? IGameNet.GameData => GameData;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using Impostor.Server.Net.Inner.Objects;
|
||||
using Impostor.Server.Net.Inner.Objects.Components;
|
||||
using Impostor.Server.Net.Inner.Objects.GameManager;
|
||||
using Impostor.Server.Net.Inner.Objects.ShipStatus;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
{
|
||||
internal partial class GameNet
|
||||
{
|
||||
public InnerGameManager? GameManager { get; internal set; }
|
||||
|
||||
public InnerLobbyBehaviour? LobbyBehaviour { get; internal set; }
|
||||
|
||||
public InnerGameData? GameData { get; internal set; }
|
||||
|
||||
@@ -8,13 +8,14 @@ using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Hazel.Abstractions;
|
||||
using Impostor.Api.Net.Messages.C2S;
|
||||
using Impostor.Api.Utils;
|
||||
using Impostor.Hazel;
|
||||
using Impostor.Hazel.Abstractions;
|
||||
using Impostor.Hazel.Extensions;
|
||||
using Impostor.Server;
|
||||
using Impostor.Server.Events;
|
||||
@@ -37,7 +38,7 @@ namespace Impostor.Tools.ServerReplay
|
||||
{
|
||||
private static readonly ILogger Logger = Log.ForContext(typeof(Program));
|
||||
private static readonly Dictionary<int, IHazelConnection> Connections = new Dictionary<int, IHazelConnection>();
|
||||
private static readonly Dictionary<int, GameOptionsData> GameOptions = new Dictionary<int, GameOptionsData>();
|
||||
private static readonly Dictionary<int, IGameOptions> GameOptions = new Dictionary<int, IGameOptions>();
|
||||
|
||||
private static ServiceProvider _serviceProvider;
|
||||
|
||||
@@ -199,7 +200,8 @@ namespace Impostor.Tools.ServerReplay
|
||||
|
||||
if (tag == MessageFlags.HostGame)
|
||||
{
|
||||
GameOptions.Add(clientId, Message00HostGameC2S.Deserialize(message));
|
||||
Message00HostGameC2S.Deserialize(message, out var gameOptions, out _, out _);
|
||||
GameOptions.Add(clientId, gameOptions);
|
||||
}
|
||||
else if (Connections.TryGetValue(clientId, out var client))
|
||||
{
|
||||
@@ -212,7 +214,7 @@ namespace Impostor.Tools.ServerReplay
|
||||
case RecordedPacketType.GameCreated:
|
||||
_gameCodeFactory.Result = GameCode.From(reader.ReadString());
|
||||
|
||||
await _gameManager.CreateAsync(Connections[clientId].Client, GameOptions[clientId]);
|
||||
await _gameManager.CreateAsync(Connections[clientId].Client, GameOptions[clientId], GameFilterOptions.CreateDefault());
|
||||
|
||||
GameOptions.Remove(clientId);
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user