mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-20 01:46:11 +00:00
Implement new GameOptions system
Co-authored-by: miniduikboot <mini@duikbo.at>
This commit is contained in:
1 parent
d1ac9301be
commit
8dfa38b4b5
36 files changed
+1189
-471
No files matched your search
@@ -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,7 @@ namespace Impostor.Api.Games
|
||||
{
|
||||
public interface IGame
|
||||
{
|
||||
GameOptionsData Options { get; }
|
||||
IGameOptions Options { get; }
|
||||
|
||||
GameCode Code { get; }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
namespace Impostor.Api.Games.Managers
|
||||
{
|
||||
@@ -16,6 +16,6 @@ namespace Impostor.Api.Games.Managers
|
||||
/// <param name="options">Game 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Impostor.Api.Innersloth;
|
||||
|
||||
public enum CrossplayFlags
|
||||
{
|
||||
Default = 4,
|
||||
All = int.MaxValue,
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Api.Innersloth;
|
||||
|
||||
public class GameFilterOptions
|
||||
{
|
||||
public HashSet<string> FilterTags { get; } = new HashSet<string>();
|
||||
|
||||
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(NumCommonTasks);
|
||||
writer.Write(NumLongTasks);
|
||||
writer.Write(NumShortTasks);
|
||||
|
||||
writer.Write(NumEmergencyMeetings);
|
||||
|
||||
writer.Write(NumImpostors);
|
||||
writer.Write((byte)KillDistance);
|
||||
writer.Write(DiscussionTime);
|
||||
writer.Write(VotingTime);
|
||||
|
||||
writer.Write(IsDefaults);
|
||||
|
||||
writer.Write(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.GameOptions;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.S2C
|
||||
{
|
||||
@@ -12,7 +12,7 @@ namespace Impostor.Api.Net.Messages.S2C
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
public static GameOptionsData Deserialize(IMessageReader reader)
|
||||
public static LegacyGameOptionsData Deserialize(IMessageReader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"resolved": "1.2.0.435",
|
||||
"contentHash": "ouwPWZxbOV3SmCZxIRqHvljkSzkCyi1tDoMzQtDb/bRP8ctASV/iRJr+A2Gdj0QLaLmWnqTWDrH82/iP+X80Lg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETStandard,Version=v2.1/linux-x64": {}
|
||||
}
|
||||
}
|
||||
@@ -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, new GameFilterOptions());
|
||||
|
||||
// TODO: ObjectPool for MessageReaders
|
||||
using (var connection = new UdpClientConnection(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 22023), null))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 +22,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());
|
||||
if (game == null)
|
||||
{
|
||||
_logger.LogWarning("Example game creation was cancelled");
|
||||
|
||||
@@ -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;
|
||||
@@ -65,10 +66,10 @@ namespace Impostor.Server.Net
|
||||
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);
|
||||
|
||||
if (game == null)
|
||||
{
|
||||
@@ -259,7 +260,7 @@ namespace Impostor.Server.Net
|
||||
|
||||
case MessageFlags.GetGameListV2:
|
||||
{
|
||||
Message16GetGameListC2S.Deserialize(reader, out var options, out _);
|
||||
Message16GetGameListC2S.Deserialize(reader, out var options, out _, out _, out _);
|
||||
await OnRequestGameListAsync(options);
|
||||
break;
|
||||
}
|
||||
@@ -358,7 +359,7 @@ namespace Impostor.Server.Net
|
||||
/// All options given.
|
||||
/// At this moment, the client can only specify the map, impostor count and chat language.
|
||||
/// </param>
|
||||
private ValueTask OnRequestGameListAsync(GameOptionsData options)
|
||||
private ValueTask OnRequestGameListAsync(IGameOptions options)
|
||||
{
|
||||
using var message = MessageWriter.Get(MessageType.Reliable);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
@@ -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)))
|
||||
@@ -110,7 +111,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)
|
||||
{
|
||||
var @event = new GameCreationEvent(this, owner);
|
||||
await _eventManager.CallAsync(@event);
|
||||
@@ -135,12 +136,12 @@ namespace Impostor.Server.Net.Manager
|
||||
return game;
|
||||
}
|
||||
|
||||
public ValueTask<IGame?> CreateAsync(GameOptionsData options)
|
||||
public ValueTask<IGame?> CreateAsync(IGameOptions options)
|
||||
{
|
||||
return CreateAsync(null, options);
|
||||
}
|
||||
|
||||
private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(GameOptionsData options, GameCode? desiredGameCode = null)
|
||||
private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(IGameOptions options, GameCode? desiredGameCode = null)
|
||||
{
|
||||
var gameCode = desiredGameCode ?? _gameCodeFactory.Create();
|
||||
var game = ActivatorUtilities.CreateInstance<Game>(_serviceProvider, _publicIp, gameCode, options);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
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.Rpcs;
|
||||
using Impostor.Hazel;
|
||||
|
||||
namespace Impostor.Server.Net.State
|
||||
@@ -21,6 +20,7 @@ namespace Impostor.Server.Net.State
|
||||
_bannedIps.Add(ipAddress);
|
||||
}
|
||||
|
||||
// TODO This no longer does anything, it was replaced by LogicOptions
|
||||
public async ValueTask SyncSettingsAsync()
|
||||
{
|
||||
if (Host?.Character == null)
|
||||
@@ -34,12 +34,7 @@ namespace Impostor.Server.Net.State
|
||||
// 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());
|
||||
}
|
||||
Rpc02SyncSettings.Serialize(writer, Options);
|
||||
|
||||
await FinishRpcAsync(writer);
|
||||
}
|
||||
|
||||
@@ -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,7 @@ namespace Impostor.Server.Net.State
|
||||
GameManager gameManager,
|
||||
IPEndPoint publicIp,
|
||||
GameCode code,
|
||||
GameOptionsData options,
|
||||
IGameOptions options,
|
||||
ClientManager clientManager,
|
||||
IEventManager eventManager,
|
||||
IOptions<CompatibilityConfig> compatibilityConfig)
|
||||
@@ -71,7 +72,7 @@ namespace Impostor.Server.Net.State
|
||||
|
||||
public GameStates GameState { get; private set; }
|
||||
|
||||
public GameOptionsData Options { get; }
|
||||
public IGameOptions Options { get; }
|
||||
|
||||
public IDictionary<object, object> Items { get; }
|
||||
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user