attempt fortegreen disconnect fix & some improvements

This commit is contained in:
Gurge44 committed 2024-12-01 14:12:26 +01:00
1 parent 4aaae6d20b
commit 3a388706f7
44 files changed
+1142 -381

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ dotnet_diagnostic.ide0060.severity = none
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_namespace_declarations = file_scoped:none
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
+13 -1
View File
@@ -107,7 +107,19 @@ namespace EHR
IsDeadCache.Clear();
IsCached = false;
if (doSend) SendGameData();
if (doSend)
{
SendGameData();
LateTask.New(RestoreIsDeadByExile, 0.3f, "AntiBlackOut.RestoreIsDeadByExile");
}
}
private static void RestoreIsDeadByExile()
{
var sender = CustomRpcSender.Create("AntiBlackout.RestoreIsDeadByExile", SendOption.Reliable);
Main.AllPlayerControls.DoIf(x => x.Data.IsDead && !x.Data.Disconnected, x => sender.AutoStartRpc(x.NetId, (byte)RpcCalls.Exiled).EndRpc());
sender.SendMessage();
}
public static void SendGameData([CallerMemberName] string callerMethodName = "")
+1 -1
View File
@@ -1485,7 +1485,7 @@ namespace EHR
public static void RpcExileV2(this PlayerControl player)
{
player.Exiled();
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.Exiled, SendOption.None);
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.Exiled, SendOption.Reliable);
AmongUsClient.Instance.FinishRpcImmediately(writer);
FixedUpdatePatch.LoversSuicide(player.PlayerId);
}
+1 -1
View File
@@ -533,7 +533,7 @@ namespace EHR
{
get
{
if (!IsOnlineGame) return false;
if (IsLocalGame && !IsNotJoined) return true;
const string domain = "among.us";
+84 -74
View File
@@ -148,10 +148,14 @@ other: ∟ ⌠ ⌡ ╬ ╨ ▓ ▒ ░ « » █ ▄ ▌▀▐│ ┤ ╡ ╢
}
}
if (AmongUsClient.Instance.AmHost)
switch (AmongUsClient.Instance.AmHost)
{
nt.SnapTo(location, (ushort)(nt.lastSequenceId + 328));
nt.SetDirtyBit(uint.MaxValue);
case true:
nt.SnapTo(location, (ushort)(nt.lastSequenceId + 328));
nt.SetDirtyBit(uint.MaxValue);
break;
case false when !nt.AmOwner:
return false;
}
var newSid = (ushort)(nt.lastSequenceId + 8);
@@ -3090,88 +3094,94 @@ other: ∟ ⌠ ⌡ ╬ ╨ ▓ ▒ ░ « » █ ▄ ▌▀▐│ ┤ ╡ ╢
public static string SummaryTexts(byte id, bool disableColor = true, bool check = false)
{
//var RolePos = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.English or SupportedLangs.Russian ? 37 : 34;
//var KillsPos = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.English or SupportedLangs.Russian ? 14 : 12;
string name = Main.AllPlayerNames[id].RemoveHtmlTags().Replace("\r\n", string.Empty);
if (id == PlayerControl.LocalPlayer.PlayerId)
name = DataManager.player.Customization.Name;
else
name = GetPlayerById(id)?.Data.PlayerName ?? name;
TaskState taskState = Main.PlayerStates[id].TaskState;
string TaskCount;
if (taskState.HasTasks)
try
{
NetworkedPlayerInfo info = GetPlayerInfoById(id);
Color TaskCompleteColor = HasTasks(info) ? Color.green : Color.cyan;
Color NonCompleteColor = HasTasks(info) ? Color.yellow : Color.white;
string name = Main.AllPlayerNames[id].RemoveHtmlTags().Replace("\r\n", string.Empty);
if (Workhorse.IsThisRole(id)) NonCompleteColor = Workhorse.RoleColor;
if (id == PlayerControl.LocalPlayer.PlayerId)
name = DataManager.player.Customization.Name;
else
name = GetPlayerById(id)?.Data.PlayerName ?? name;
Color NormalColor = taskState.IsTaskFinished ? TaskCompleteColor : NonCompleteColor;
TaskState taskState = Main.PlayerStates[id].TaskState;
string TaskCount;
if (Main.PlayerStates.TryGetValue(id, out PlayerState ps))
if (taskState.HasTasks)
{
NormalColor = ps.MainRole switch
NetworkedPlayerInfo info = GetPlayerInfoById(id);
Color TaskCompleteColor = HasTasks(info) ? Color.green : Color.cyan;
Color NonCompleteColor = HasTasks(info) ? Color.yellow : Color.white;
if (Workhorse.IsThisRole(id)) NonCompleteColor = Workhorse.RoleColor;
Color NormalColor = taskState.IsTaskFinished ? TaskCompleteColor : NonCompleteColor;
if (Main.PlayerStates.TryGetValue(id, out PlayerState ps))
{
CustomRoles.Crewpostor => Color.red,
CustomRoles.Cherokious => GetRoleColor(CustomRoles.Cherokious),
_ => NormalColor
};
NormalColor = ps.MainRole switch
{
CustomRoles.Crewpostor => Color.red,
CustomRoles.Cherokious => GetRoleColor(CustomRoles.Cherokious),
_ => NormalColor
};
}
Color TextColor = NormalColor;
var Completed = $"{taskState.CompletedTasksCount}";
TaskCount = ColorString(TextColor, $" ({Completed}/{taskState.AllTasksCount})");
}
else
TaskCount = string.Empty;
var summary = $"{ColorString(Main.PlayerColors[id], name)} - {GetDisplayRoleName(id, true)}{TaskCount}{GetKillCountText(id)} ({GetVitalText(id, true)})";
switch (Options.CurrentGameMode)
{
case CustomGameMode.SoloKombat:
summary = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.SChinese or SupportedLangs.TChinese ? $"{GetProgressText(id)}\t<pos=22%>{ColorString(Main.PlayerColors[id], name)}</pos>" : $"{ColorString(Main.PlayerColors[id], name)}<pos=30%>{GetProgressText(id)}</pos>";
if (GetProgressText(id).Trim() == string.Empty) return string.Empty;
break;
case CustomGameMode.FFA:
summary = $"{ColorString(Main.PlayerColors[id], name)} {GetKillCountText(id, true)}";
break;
case CustomGameMode.Speedrun:
case CustomGameMode.MoveAndStop:
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount.Replace("(", string.Empty).Replace(")", string.Empty)} ({GetVitalText(id, true)})";
break;
case CustomGameMode.HotPotato:
int time = HotPotato.GetSurvivalTime(id);
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time}</color>s")}</color> ({GetVitalText(id, true)})";
break;
case CustomGameMode.NaturalDisasters:
int time2 = NaturalDisasters.SurvivalTime(id);
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time2 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time2}</color>s")}</color> ({GetVitalText(id, true)})";
break;
case CustomGameMode.RoomRush:
int time3 = RoomRush.GetSurvivalTime(id);
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time3 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time3}</color>s")}</color> ({GetVitalText(id, true)})";
break;
case CustomGameMode.CaptureTheFlag:
summary = $"{ColorString(Main.PlayerColors[id], name)}: {CTFManager.GetStatistics(id)}";
if (CTFManager.IsDeathPossible) summary += $" ({GetVitalText(id, true)})";
break;
case CustomGameMode.AllInOne:
string survivalTimeText = !Main.PlayerStates[id].IsDead ? string.Empty : $" ({GetString("SurvivedTimePrefix")}: <#f542ad>{RoomRush.GetSurvivalTime(id)}s</color>)";
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount}{GetKillCountText(id, true)} ({GetVitalText(id, true)}){survivalTimeText}";
break;
}
Color TextColor = NormalColor;
var Completed = $"{taskState.CompletedTasksCount}";
TaskCount = ColorString(TextColor, $" ({Completed}/{taskState.AllTasksCount})");
return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned")
? "INVALID"
: disableColor
? summary.RemoveHtmlTags()
: summary;
}
else
TaskCount = string.Empty;
var summary = $"{ColorString(Main.PlayerColors[id], name)} - {GetDisplayRoleName(id, true)}{TaskCount}{GetKillCountText(id)} ({GetVitalText(id, true)})";
switch (Options.CurrentGameMode)
catch (Exception e)
{
case CustomGameMode.SoloKombat:
summary = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.SChinese or SupportedLangs.TChinese ? $"{GetProgressText(id)}\t<pos=22%>{ColorString(Main.PlayerColors[id], name)}</pos>" : $"{ColorString(Main.PlayerColors[id], name)}<pos=30%>{GetProgressText(id)}</pos>";
if (GetProgressText(id).Trim() == string.Empty) return string.Empty;
break;
case CustomGameMode.FFA:
summary = $"{ColorString(Main.PlayerColors[id], name)} {GetKillCountText(id, true)}";
break;
case CustomGameMode.Speedrun:
case CustomGameMode.MoveAndStop:
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount.Replace("(", string.Empty).Replace(")", string.Empty)} ({GetVitalText(id, true)})";
break;
case CustomGameMode.HotPotato:
int time = HotPotato.GetSurvivalTime(id);
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time}</color>s")}</color> ({GetVitalText(id, true)})";
break;
case CustomGameMode.NaturalDisasters:
int time2 = NaturalDisasters.SurvivalTime(id);
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time2 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time2}</color>s")}</color> ({GetVitalText(id, true)})";
break;
case CustomGameMode.RoomRush:
int time3 = RoomRush.GetSurvivalTime(id);
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time3 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time3}</color>s")}</color> ({GetVitalText(id, true)})";
break;
case CustomGameMode.CaptureTheFlag:
summary = $"{ColorString(Main.PlayerColors[id], name)}: {CTFManager.GetStatistics(id)}";
if (CTFManager.IsDeathPossible) summary += $" ({GetVitalText(id, true)})";
break;
case CustomGameMode.AllInOne:
string survivalTimeText = !Main.PlayerStates[id].IsDead ? string.Empty : $" ({GetString("SurvivedTimePrefix")}: <#f542ad>{RoomRush.GetSurvivalTime(id)}s</color>)";
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount}{GetKillCountText(id, true)} ({GetVitalText(id, true)}){survivalTimeText}";
break;
ThrowException(e);
return $"{id.ColoredPlayerName()} - ERROR";
}
return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned")
? "INVALID"
: disableColor
? summary.RemoveHtmlTags()
: summary;
}
public static string GetRemainingKillers(bool notify = false, bool president = false)
+365 -277
View File
@@ -1,355 +1,443 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using EHR.Modules;
using HarmonyLib;
using Hazel;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using InnerNet;
using TMPro;
using UnityEngine;
using static EHR.Translator;
namespace EHR
namespace EHR;
[HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.MakePublic))]
internal static class MakePublicPatch
{
[HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.MakePublic))]
internal static class MakePublicPatch
public static bool Prefix()
{
public static bool Prefix()
if (ModUpdater.IsBroken || (ModUpdater.HasUpdate && ModUpdater.ForceUpdate) || !VersionChecker.IsSupported)
{
if (ModUpdater.IsBroken || (ModUpdater.HasUpdate && ModUpdater.ForceUpdate) || !VersionChecker.IsSupported)
{
var message = string.Empty;
if (!VersionChecker.IsSupported) message = GetString("UnsupportedVersion");
var message = string.Empty;
if (!VersionChecker.IsSupported) message = GetString("UnsupportedVersion");
if (ModUpdater.IsBroken) message = GetString("ModBrokenMessage");
if (ModUpdater.IsBroken) message = GetString("ModBrokenMessage");
if (ModUpdater.HasUpdate) message = GetString("CanNotJoinPublicRoomNoLatest");
if (ModUpdater.HasUpdate) message = GetString("CanNotJoinPublicRoomNoLatest");
Logger.Info(message, "MakePublicPatch");
Logger.SendInGame(message);
return false;
}
return true;
Logger.Info(message, "MakePublicPatch");
Logger.SendInGame(message);
return false;
}
}
[HarmonyPatch(typeof(MMOnlineManager), nameof(MMOnlineManager.Start))]
return true;
}
}
[HarmonyPatch(typeof(MMOnlineManager), nameof(MMOnlineManager.Start))]
// ReSharper disable once InconsistentNaming
internal static class MMOnlineManagerStartPatch
internal static class MMOnlineManagerStartPatch
{
public static void Postfix()
{
public static void Postfix()
if (!((ModUpdater.HasUpdate && ModUpdater.ForceUpdate) || ModUpdater.IsBroken)) return;
GameObject obj = GameObject.Find("FindGameButton");
if (obj)
{
if (!((ModUpdater.HasUpdate && ModUpdater.ForceUpdate) || ModUpdater.IsBroken)) return;
obj.SetActive(false);
TextMeshPro textObj = Object.Instantiate(obj.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>());
textObj.transform.position = new(1f, -0.3f, 0);
textObj.name = "CanNotJoinPublic";
GameObject obj = GameObject.Find("FindGameButton");
string message = ModUpdater.IsBroken
? $"<size=2>{Utils.ColorString(Color.red, GetString("ModBrokenMessage"))}</size>"
: $"<size=2>{Utils.ColorString(Color.red, GetString("CanNotJoinPublicRoomNoLatest"))}</size>";
if (obj)
{
obj.SetActive(false);
TextMeshPro textObj = Object.Instantiate(obj.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>());
textObj.transform.position = new(1f, -0.3f, 0);
textObj.name = "CanNotJoinPublic";
string message = ModUpdater.IsBroken
? $"<size=2>{Utils.ColorString(Color.red, GetString("ModBrokenMessage"))}</size>"
: $"<size=2>{Utils.ColorString(Color.red, GetString("CanNotJoinPublicRoomNoLatest"))}</size>";
LateTask.New(() => { textObj.text = message; }, 0.01f, "CanNotJoinPublic");
}
LateTask.New(() => { textObj.text = message; }, 0.01f, "CanNotJoinPublic");
}
}
}
[HarmonyPatch(typeof(SplashManager), nameof(SplashManager.Update))]
internal static class SplashLogoAnimatorPatch
[HarmonyPatch(typeof(SplashManager), nameof(SplashManager.Update))]
internal static class SplashLogoAnimatorPatch
{
public static void Prefix(SplashManager __instance)
{
public static void Prefix(SplashManager __instance)
{
__instance.sceneChanger.AllowFinishLoadingScene();
__instance.startedSceneLoad = true;
}
__instance.sceneChanger.AllowFinishLoadingScene();
__instance.startedSceneLoad = true;
}
}
[HarmonyPatch(typeof(EOSManager), nameof(EOSManager.IsAllowedOnline))]
internal static class RunLoginPatch
[HarmonyPatch(typeof(EOSManager), nameof(EOSManager.IsAllowedOnline))]
internal static class RunLoginPatch
{
public const int ClickCount = 0;
public static void Prefix(ref bool canOnline)
{
public const int ClickCount = 0;
public static void Prefix(ref bool canOnline)
{
if (DebugModeManager.AmDebugger) canOnline = true;
}
if (DebugModeManager.AmDebugger) canOnline = true;
}
}
[HarmonyPatch(typeof(BanMenu), nameof(BanMenu.SetVisible))]
internal static class BanMenuSetVisiblePatch
[HarmonyPatch(typeof(BanMenu), nameof(BanMenu.SetVisible))]
internal static class BanMenuSetVisiblePatch
{
public static bool Prefix(BanMenu __instance, bool show)
{
public static bool Prefix(BanMenu __instance, bool show)
{
if (!AmongUsClient.Instance.AmHost) return true;
if (!AmongUsClient.Instance.AmHost) return true;
show &= PlayerControl.LocalPlayer && PlayerControl.LocalPlayer.Data != null;
__instance.BanButton.gameObject.SetActive(AmongUsClient.Instance.CanBan());
__instance.KickButton.gameObject.SetActive(AmongUsClient.Instance.CanKick());
__instance.MenuButton.gameObject.SetActive(show);
show &= PlayerControl.LocalPlayer && PlayerControl.LocalPlayer.Data != null;
__instance.BanButton.gameObject.SetActive(AmongUsClient.Instance.CanBan());
__instance.KickButton.gameObject.SetActive(AmongUsClient.Instance.CanKick());
__instance.MenuButton.gameObject.SetActive(show);
return false;
}
}
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.CanBan))]
internal static class InnerNetClientCanBanPatch
{
public static bool Prefix(InnerNetClient __instance, ref bool __result)
{
__result = __instance.AmHost;
return false;
}
}
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.KickPlayer))]
internal static class KickPlayerPatch
{
public static bool Prefix( /*InnerNetClient __instance,*/ int clientId, bool ban)
{
if (!AmongUsClient.Instance.AmHost && !OnGameJoinedPatch.JoiningGame) return true;
if (AmongUsClient.Instance.ClientId == clientId)
{
Logger.SendInGame($"Game Attempting to {(ban ? "Ban" : "Kick")} Host, Blocked the attempt.");
Logger.Info("Game attempted to kick/ban host....", "KickPlayerPatch");
return false;
}
if (ban) BanManager.AddBanPlayer(AmongUsClient.Instance.GetRecentClient(clientId));
return true;
}
}
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.CanBan))]
internal static class InnerNetClientCanBanPatch
[HarmonyPatch(typeof(ResolutionManager), nameof(ResolutionManager.SetResolution))]
internal static class SetResolutionManager
{
public static void Postfix()
{
public static bool Prefix(InnerNetClient __instance, ref bool __result)
{
__result = __instance.AmHost;
return false;
}
if (MainMenuManagerPatch.UpdateButton != null) MainMenuManagerPatch.UpdateButton.transform.localPosition = MainMenuManagerPatch.Template.transform.localPosition + new Vector3(0.25f, 0.75f);
}
}
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.KickPlayer))]
internal static class KickPlayerPatch
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendAllStreamedObjects))]
internal static class InnerNetObjectSerializePatch
{
public static void Prefix()
{
public static bool Prefix( /*InnerNetClient __instance,*/ int clientId, bool ban)
{
if (!AmongUsClient.Instance.AmHost && !OnGameJoinedPatch.JoiningGame) return true;
if (AmongUsClient.Instance.AmHost) Main.Instance.StartCoroutine(GameOptionsSender.SendAllGameOptionsAsync());
}
}
if (AmongUsClient.Instance.ClientId == clientId)
[HarmonyPatch(typeof(InnerNetClient))]
public class InnerNetClientPatch
{
private static byte Timer;
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendInitialData))]
[HarmonyPrefix]
public static bool SendInitialDataPrefix(InnerNetClient __instance, int clientId)
{
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return true;
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
messageWriter.StartMessage(6);
messageWriter.Write(__instance.GameId);
messageWriter.WritePacked(clientId);
List<InnerNetObject> obj = __instance.allObjects;
lock (obj)
{
System.Collections.Generic.HashSet<GameObject> hashSet = [];
for (int i = 0; i < __instance.allObjects.Count; i++)
{
Logger.SendInGame($"Game Attempting to {(ban ? "Ban" : "Kick")} Host, Blocked the attempt.");
Logger.Info("Game attempted to kick/ban host....", "KickPlayerPatch");
return false;
InnerNetObject innerNetObject = __instance.allObjects[i]; // False error
if (innerNetObject && (innerNetObject.OwnerId != -4 || __instance.AmModdedHost) && hashSet.Add(innerNetObject.gameObject))
{
GameManager gameManager = innerNetObject as GameManager;
if (gameManager != null)
{
__instance.SendGameManager(clientId, gameManager);
}
else
{
if (innerNetObject is not NetworkedPlayerInfo)
__instance.WriteSpawnMessage(innerNetObject, innerNetObject.OwnerId, innerNetObject.SpawnFlags, messageWriter);
}
}
}
if (ban) BanManager.AddBanPlayer(AmongUsClient.Instance.GetRecentClient(clientId));
return true;
messageWriter.EndMessage();
__instance.SendOrDisconnect(messageWriter);
messageWriter.Recycle();
}
DelaySpawnPlayerInfo(__instance, clientId);
return false;
}
[HarmonyPatch(typeof(ResolutionManager), nameof(ResolutionManager.SetResolution))]
internal static class SetResolutionManager
private static void DelaySpawnPlayerInfo(InnerNetClient __instance, int clientId)
{
public static void Postfix()
var players = GameData.Instance.AllPlayers.ToArray().ToList();
while (players.Count > 0)
{
if (MainMenuManagerPatch.UpdateButton != null) MainMenuManagerPatch.UpdateButton.transform.localPosition = MainMenuManagerPatch.Template.transform.localPosition + new Vector3(0.25f, 0.75f);
}
}
var batch = players.Take(5).ToList();
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendAllStreamedObjects))]
internal static class InnerNetObjectSerializePatch
{
public static void Prefix()
{
if (AmongUsClient.Instance.AmHost) Main.Instance.StartCoroutine(GameOptionsSender.SendAllGameOptionsAsync());
}
}
internal static class InnerNetClientPatch
{
private static byte Timer;
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendInitialData))]
[HarmonyPrefix]
public static bool SendInitialDataPrefix(InnerNetClient __instance, int clientId)
{
if (!Constants.IsVersionModded() || GameStates.IsInGame || __instance.NetworkMode != NetworkModes.OnlineGame) return true;
// We make sure other stuff like PlayerControl and LobbyBehavior is spawned properly
// Then we spawn the networked data for new clients
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
messageWriter.StartMessage(6);
messageWriter.Write(__instance.GameId);
messageWriter.WritePacked(clientId);
List<InnerNetObject> obj = __instance.allObjects;
lock (obj)
foreach (var player in batch)
{
System.Collections.Generic.HashSet<GameObject> hashSet = [];
if (messageWriter.Length > 500) break;
for (var i = 0; i < __instance.allObjects.Count; i++)
if (player != null && player.ClientId != clientId && !player.Disconnected)
{
InnerNetObject innerNetObject = __instance.allObjects[i]; // False error
if (innerNetObject && (innerNetObject.OwnerId != -4 || __instance.AmModdedHost) && hashSet.Add(innerNetObject.gameObject))
{
var gameManager = innerNetObject as GameManager;
if (gameManager != null)
__instance.SendGameManager(clientId, gameManager);
else
{
if (innerNetObject is not NetworkedPlayerInfo) __instance.WriteSpawnMessage(innerNetObject, innerNetObject.OwnerId, innerNetObject.SpawnFlags, messageWriter);
}
}
__instance.WriteSpawnMessage(player, player.OwnerId, player.SpawnFlags, messageWriter);
}
messageWriter.EndMessage();
__instance.SendOrDisconnect(messageWriter);
messageWriter.Recycle();
players.Remove(player);
}
DelaySpawnPlayerInfo(__instance, clientId);
return false;
}
private static void DelaySpawnPlayerInfo(InnerNetClient __instance, int clientId)
{
Il2CppArrayBase<NetworkedPlayerInfo> players = GameData.Instance.AllPlayers.ToArray();
// We send 5 players at a time to prevent too large packets
foreach (NetworkedPlayerInfo[] batch in players.Chunk(5))
{
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
messageWriter.StartMessage(6);
messageWriter.Write(__instance.GameId);
messageWriter.WritePacked(clientId);
batch.DoIf(p => p != null && p.ClientId != clientId && !p.Disconnected, p => __instance.WriteSpawnMessage(p, p.OwnerId, p.SpawnFlags, messageWriter));
messageWriter.EndMessage();
__instance.SendOrDisconnect(messageWriter);
messageWriter.Recycle();
}
}
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendAllStreamedObjects))]
[HarmonyPrefix]
public static bool SendAllStreamedObjectsPrefix(InnerNetClient __instance, ref bool __result)
{
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return true;
// Bypass all NetworkedData here.
__result = false;
List<InnerNetObject> obj = __instance.allObjects;
lock (obj)
{
for (var i = 0; i < __instance.allObjects.Count; i++)
{
InnerNetObject innerNetObject = __instance.allObjects[i]; // False error
if (innerNetObject && innerNetObject is not NetworkedPlayerInfo && innerNetObject.IsDirty && (innerNetObject.AmOwner || (innerNetObject.OwnerId == -2 && __instance.AmHost)))
{
MessageWriter messageWriter = __instance.Streams[(int)innerNetObject.sendMode];
messageWriter.StartMessage(1);
messageWriter.WritePacked(innerNetObject.NetId);
try
{
if (innerNetObject.Serialize(messageWriter, false))
messageWriter.EndMessage();
else
messageWriter.CancelMessage();
if (innerNetObject.Chunked && innerNetObject.IsDirty) __result = true;
}
catch (Exception ex)
{
Logger.Exception(ex, "SendAllStreamedObjectsPrefix");
messageWriter.CancelMessage();
}
}
}
}
for (var j = 0; j < __instance.Streams.Length; j++)
{
MessageWriter messageWriter2 = __instance.Streams[j];
if (messageWriter2.HasBytes(7))
{
messageWriter2.EndMessage();
__instance.SendOrDisconnect(messageWriter2);
messageWriter2.Clear((SendOption)j);
messageWriter2.StartMessage(5);
messageWriter2.Write(__instance.GameId);
}
}
return false;
}
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.FixedUpdate))]
[HarmonyPostfix]
public static void FixedUpdatePostfix(InnerNetClient __instance)
{
if (!Constants.IsVersionModded() || GameStates.IsInGame || !__instance.AmHost || __instance.Streams == null || __instance.NetworkMode != NetworkModes.OnlineGame) return;
if (Timer == 0)
{
Timer = 1;
return;
}
NetworkedPlayerInfo player = GameData.Instance.AllPlayers.ToArray().FirstOrDefault(x => x.IsDirty);
if (player != null)
{
Timer = 0;
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
messageWriter.StartMessage(5);
messageWriter.Write(__instance.GameId);
messageWriter.StartMessage(1);
messageWriter.WritePacked(player.NetId);
try
{
if (player.Serialize(messageWriter, false))
messageWriter.EndMessage();
else
{
messageWriter.CancelMessage();
player.ClearDirtyBits();
return;
}
messageWriter.EndMessage();
__instance.SendOrDisconnect(messageWriter);
messageWriter.Recycle();
}
catch (Exception ex)
{
Logger.Exception(ex, "FixedUpdatePostfix");
messageWriter.CancelMessage();
player.ClearDirtyBits();
}
}
messageWriter.EndMessage();
__instance.SendOrDisconnect(messageWriter);
messageWriter.Recycle();
}
}
[HarmonyPatch]
internal static class AuthTimeoutPatch
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendAllStreamedObjects))]
[HarmonyPrefix]
public static bool SendAllStreamedObjectsPrefix(InnerNetClient __instance, ref bool __result)
{
[HarmonyPatch(typeof(AuthManager._CoConnect_d__4), nameof(AuthManager._CoConnect_d__4.MoveNext))]
[HarmonyPatch(typeof(AuthManager._CoWaitForNonce_d__6), nameof(AuthManager._CoWaitForNonce_d__6.MoveNext))]
[HarmonyPrefix]
// From Reactor.gg
// https://github.com/NuclearPowered/Reactor/blob/master/Reactor/Patches/Miscellaneous/CustomServersPatch.cs
public static bool CoWaitforNoncePrefix(ref bool __result)
{
if (GameStates.IsVanillaServer) return true;
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return true;
__result = false;
List<InnerNetObject> obj = __instance.allObjects;
__result = false;
return false;
lock (obj)
{
for (int i = 0; i < __instance.allObjects.Count; i++)
{
InnerNetObject innerNetObject = __instance.allObjects[i]; // False error
if (innerNetObject && innerNetObject is not NetworkedPlayerInfo && innerNetObject.IsDirty && (innerNetObject.AmOwner || (innerNetObject.OwnerId == -2 && __instance.AmHost)))
{
MessageWriter messageWriter = __instance.Streams[(int)innerNetObject.sendMode];
messageWriter.StartMessage(1);
messageWriter.WritePacked(innerNetObject.NetId);
try
{
if (innerNetObject.Serialize(messageWriter, false))
{
messageWriter.EndMessage();
}
else
{
messageWriter.CancelMessage();
}
if (innerNetObject.Chunked && innerNetObject.IsDirty)
{
__result = true;
}
}
catch (Exception ex)
{
Logger.Exception(ex, "SendAllStreamedObjectsPrefix");
messageWriter.CancelMessage();
}
}
}
}
// If you don't patch this, you still need to wait for 5 s.
// I have no idea why this is happening
[HarmonyPatch(typeof(AmongUsClient._CoJoinOnlinePublicGame_d__1), nameof(AmongUsClient._CoJoinOnlinePublicGame_d__1.MoveNext))]
[HarmonyPrefix]
public static void EnableUdpMatchmakingPrefix(AmongUsClient._CoJoinOnlinePublicGame_d__1 __instance)
for (int j = 0; j < __instance.Streams.Length; j++)
{
// Skip to state 1, which just calls CoJoinOnlineGameDirect
if (__instance.__1__state == 0 && !ServerManager.Instance.IsHttp)
{
__instance.__1__state = 1;
MessageWriter messageWriter2 = __instance.Streams[j];
__instance.__8__1 = new()
{
matchmakerToken = string.Empty
};
if (messageWriter2.HasBytes(7))
{
messageWriter2.EndMessage();
__instance.SendOrDisconnect(messageWriter2);
messageWriter2.Clear((SendOption)j);
messageWriter2.StartMessage(5);
messageWriter2.Write(__instance.GameId);
}
}
return false;
}
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.Spawn))]
[HarmonyPostfix]
public static void Spawn_Postfix(InnerNetClient __instance, InnerNetObject netObjParent, int ownerId = -2, SpawnFlags flags = SpawnFlags.None)
{
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return;
if (__instance.AmHost)
{
switch (netObjParent)
{
case NetworkedPlayerInfo playerinfo:
LateTask.New(() =>
{
if (playerinfo != null && AmongUsClient.Instance.AmConnected)
{
var client = AmongUsClient.Instance.GetClient(playerinfo.ClientId);
if (client != null && !client.IsDisconnected())
{
if (playerinfo.IsIncomplete)
{
Logger.Info($"Disconnecting Client [{client.Id}]{client.PlayerName} {client.FriendCode} for playerinfo timeout", "DelayedNetworkedData");
AmongUsClient.Instance.SendLateRejection(client.Id, DisconnectReasons.ClientTimeout);
__instance.OnPlayerLeft(client, DisconnectReasons.ClientTimeout);
}
}
}
}, 5f, "PlayerInfo Green Bean Kick", false);
break;
case PlayerControl player:
LateTask.New(() =>
{
if (player != null && !player.notRealPlayer && !player.isDummy && AmongUsClient.Instance.AmConnected)
{
var client = AmongUsClient.Instance.GetClient(player.OwnerId);
if (client != null && !client.IsDisconnected())
{
if (player.Data == null || player.Data.IsIncomplete)
{
Logger.Info($"Disconnecting Client [{client.Id}]{client.PlayerName} {client.FriendCode} for playercontrol timeout", "DelayedNetworkedData");
AmongUsClient.Instance.SendLateRejection(client.Id, DisconnectReasons.ClientTimeout);
__instance.OnPlayerLeft(client, DisconnectReasons.ClientTimeout);
}
}
}
}, 5.5f, "PlayerControl Green Bean Kick", false);
break;
}
}
if (!__instance.AmHost)
{
Debug.LogError("Tried to spawn while not host:" + (netObjParent?.ToString()));
}
}
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.FixedUpdate))]
[HarmonyPostfix]
public static void FixedUpdatePostfix(InnerNetClient __instance)
{
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return;
if (!__instance.AmHost || __instance.Streams == null) return;
if (Timer == 0)
{
Timer = 1;
return;
}
var player = GameData.Instance.AllPlayers.ToArray().FirstOrDefault(x => x.IsDirty);
if (player != null)
{
Timer = 0;
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
messageWriter.StartMessage(5);
messageWriter.Write(__instance.GameId);
messageWriter.StartMessage(1);
messageWriter.WritePacked(player.NetId);
try
{
if (player.Serialize(messageWriter, false))
{
messageWriter.EndMessage();
}
else
{
messageWriter.CancelMessage();
player.ClearDirtyBits();
return;
}
messageWriter.EndMessage();
__instance.SendOrDisconnect(messageWriter);
messageWriter.Recycle();
}
catch (Exception ex)
{
Logger.Exception(ex, "FixedUpdatePostfix");
messageWriter.CancelMessage();
player.ClearDirtyBits();
}
}
}
}
[HarmonyPatch(typeof(GameData), nameof(GameData.DirtyAllData))]
internal class DirtyAllDataPatch
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static bool Prefix()
{
return false;
}
}
[HarmonyPatch]
internal static class AuthTimeoutPatch
{
[HarmonyPatch(typeof(AuthManager._CoConnect_d__4), nameof(AuthManager._CoConnect_d__4.MoveNext))]
[HarmonyPatch(typeof(AuthManager._CoWaitForNonce_d__6), nameof(AuthManager._CoWaitForNonce_d__6.MoveNext))]
[HarmonyPrefix]
// From Reactor.gg
// https://github.com/NuclearPowered/Reactor/blob/master/Reactor/Patches/Miscellaneous/CustomServersPatch.cs
public static bool CoWaitforNoncePrefix(ref bool __result)
{
if (GameStates.IsVanillaServer) return true;
__result = false;
return false;
}
// If you don't patch this, you still need to wait for 5 s.
// I have no idea why this is happening
[HarmonyPatch(typeof(AmongUsClient._CoJoinOnlinePublicGame_d__1), nameof(AmongUsClient._CoJoinOnlinePublicGame_d__1.MoveNext))]
[HarmonyPrefix]
public static void EnableUdpMatchmakingPrefix(AmongUsClient._CoJoinOnlinePublicGame_d__1 __instance)
{
// Skip to state 1, which just calls CoJoinOnlineGameDirect
if (__instance.__1__state == 0 && !ServerManager.Instance.IsHttp)
{
__instance.__1__state = 1;
__instance.__8__1 = new()
{
matchmakerToken = string.Empty
};
}
}
}
+491 -18
View File
@@ -1,33 +1,506 @@
using AmongUs.GameOptions;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using AmongUs.GameOptions;
using HarmonyLib;
using Il2CppInterop.Runtime.Attributes;
using TMPro;
using UnityEngine;
namespace EHR.Patches;
// https://github.com/CrowdedMods/CrowdedMod/blob/master/src/CrowdedMod
namespace EHR.Patches
{
internal static class Crowded
{
public const int MaxImpostors = 127 / 2;
// Niko adjusted mono behavior patches to fit into non-reactor mods
[HarmonyPatch(typeof(GameOptionsData), nameof(GameOptionsData.AreInvalid))]
public static class InvalidOptionsPatches
internal static class Crowded
{
private static CreateOptionsPicker Instance;
public static readonly int MaxImpostors = GameOptionsManager.Instance.currentHostOptions.MaxPlayers / 2;
private static int MaxPlayers => GameStates.IsVanillaServer ? 15 : 127;
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.Awake))]
public static class CreateOptionsPickerAwake
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static void Prefix(CreateOptionsPicker __instance)
{
public static bool Prefix(GameOptionsData __instance, [HarmonyArgument(0)] int maxExpectedPlayers)
Instance = __instance;
if (GameStates.IsVanillaServer)
{
return __instance.MaxPlayers > maxExpectedPlayers ||
__instance.NumImpostors < 1 ||
__instance.NumImpostors + 1 > maxExpectedPlayers / 2 ||
__instance.KillDistance is < 0 or > 2 ||
__instance.PlayerSpeedMod is <= 0f or > 3f;
if (GameOptionsManager.Instance.GameHostOptions != null)
{
if (GameOptionsManager.Instance.GameHostOptions.MaxPlayers > 15)
{
GameOptionsManager.Instance.GameHostOptions.SetInt(Int32OptionNames.MaxPlayers, 15);
}
}
}
}
[HarmonyPatch(typeof(SecurityLogger), nameof(SecurityLogger.Awake))]
public static class SecurityLoggerPatch
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static void Postfix(CreateOptionsPicker __instance)
{
public static void Postfix(ref SecurityLogger __instance)
if (__instance.mode != SettingsMode.Host) return;
{
__instance.Timers = new float[127];
var firstButtonRenderer = __instance.MaxPlayerButtons[0];
firstButtonRenderer.GetComponentInChildren<TextMeshPro>().text = "-";
firstButtonRenderer.enabled = false;
var firstButtonButton = firstButtonRenderer.GetComponent<PassiveButton>();
firstButtonButton.OnClick.RemoveAllListeners();
firstButtonButton.OnClick.AddListener((Action)(() =>
{
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i];
var tmp = playerButton.GetComponentInChildren<TextMeshPro>();
var newValue = Mathf.Max(byte.Parse(tmp.text) - 10, byte.Parse(playerButton.name) - 2);
tmp.text = newValue.ToString();
}
__instance.UpdateMaxPlayersButtons(__instance.GetTargetOptions());
}));
Object.Destroy(firstButtonRenderer);
var lastButtonRenderer = __instance.MaxPlayerButtons[^1];
lastButtonRenderer.GetComponentInChildren<TextMeshPro>().text = "+"; // False error
lastButtonRenderer.enabled = false; // False error
var lastButtonButton = lastButtonRenderer.GetComponent<PassiveButton>(); // False error
lastButtonButton.OnClick.RemoveAllListeners();
lastButtonButton.OnClick.AddListener((Action)(() =>
{
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i];
var tmp = playerButton.GetComponentInChildren<TextMeshPro>();
var newValue = Mathf.Min(byte.Parse(tmp.text) + 10,
MaxPlayers - 14 + byte.Parse(playerButton.name));
tmp.text = newValue.ToString();
}
__instance.UpdateMaxPlayersButtons(__instance.GetTargetOptions());
}));
Object.Destroy(lastButtonRenderer); // False error
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i].GetComponent<PassiveButton>();
var text = playerButton.GetComponentInChildren<TextMeshPro>();
playerButton.OnClick.RemoveAllListeners();
playerButton.OnClick.AddListener((Action)(() =>
{
var maxPlayers = byte.Parse(text.text);
var maxImp = Mathf.Min(__instance.GetTargetOptions().NumImpostors, maxPlayers / 2);
__instance.GetTargetOptions().SetInt(Int32OptionNames.NumImpostors, maxImp);
__instance.ImpostorButtons[1].TextMesh.text = maxImp.ToString();
__instance.SetMaxPlayersButtons(maxPlayers);
}));
}
foreach (var button in __instance.MaxPlayerButtons)
{
button.enabled = button.GetComponentInChildren<TextMeshPro>().text == __instance.GetTargetOptions().MaxPlayers.ToString();
}
}
{
var secondButton = __instance.ImpostorButtons[1];
secondButton.SpriteRenderer.enabled = false;
Object.Destroy(secondButton.transform.FindChild("ConsoleHighlight").gameObject);
Object.Destroy(secondButton.PassiveButton);
Object.Destroy(secondButton.BoxCollider);
var secondButtonText = secondButton.TextMesh;
secondButtonText.text = __instance.GetTargetOptions().NumImpostors.ToString();
var firstButton = __instance.ImpostorButtons[0];
firstButton.SpriteRenderer.enabled = false;
firstButton.TextMesh.text = "-";
var firstPassiveButton = firstButton.PassiveButton;
firstPassiveButton.OnClick.RemoveAllListeners();
firstPassiveButton.OnClick.AddListener((Action)(() =>
{
var newVal = Mathf.Clamp(
byte.Parse(secondButtonText.text) - 1,
1,
__instance.GetTargetOptions().MaxPlayers / 2
);
__instance.SetImpostorButtons(newVal);
secondButtonText.text = newVal.ToString();
}));
var thirdButton = __instance.ImpostorButtons[2];
thirdButton.SpriteRenderer.enabled = false;
thirdButton.TextMesh.text = "+";
var thirdPassiveButton = thirdButton.PassiveButton;
thirdPassiveButton.OnClick.RemoveAllListeners();
thirdPassiveButton.OnClick.AddListener((Action)(() =>
{
var newVal = Mathf.Clamp(
byte.Parse(secondButtonText.text) + 1,
1,
__instance.GetTargetOptions().MaxPlayers / 2
);
__instance.SetImpostorButtons(newVal);
secondButtonText.text = newVal.ToString();
}));
}
}
}
[HarmonyPatch(typeof(ServerManager), nameof(ServerManager.SetRegion))]
public static class ServerManager_SetRegion
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static void Postfix()
{
if (GameStates.IsVanillaServer)
{
if (GameOptionsManager.Instance.GameHostOptions != null && GameOptionsManager.Instance.GameHostOptions.MaxPlayers > 15)
GameOptionsManager.Instance.GameHostOptions.SetInt(Int32OptionNames.MaxPlayers, 15);
if (Instance)
{
for (var i = 1; i < 11; i++)
{
var playerButton = Instance.MaxPlayerButtons[i];
var tmp = playerButton.GetComponentInChildren<TextMeshPro>();
var newValue = Mathf.Min(byte.Parse(tmp.text) + 10,
MaxPlayers - 14 + byte.Parse(playerButton.name));
tmp.text = newValue.ToString();
}
Instance.UpdateMaxPlayersButtons(Instance.GetTargetOptions());
}
}
}
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateMaxPlayersButtons))]
public static class CreateOptionsPickerUpdateMaxPlayersButtons
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static bool Prefix(CreateOptionsPicker __instance, [HarmonyArgument(0)] IGameOptions opts)
{
if (__instance.mode != SettingsMode.Host) return true;
if (__instance.CrewArea)
{
__instance.CrewArea.SetCrewSize(opts.MaxPlayers, opts.NumImpostors);
}
var selectedAsString = opts.MaxPlayers.ToString();
for (var i = 1; i < __instance.MaxPlayerButtons.Count - 1; i++)
{
__instance.MaxPlayerButtons[i].enabled = __instance.MaxPlayerButtons[i].GetComponentInChildren<TextMeshPro>().text == selectedAsString; // False errors
}
return false;
}
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateImpostorsButtons))]
public static class CreateOptionsPickerUpdateImpostorsButtons
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static bool Prefix(CreateOptionsPicker __instance)
{
return __instance.mode != SettingsMode.Host;
}
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.SetImpostorButtons))]
public static class CreateOptionsPickerSetImpostorButtons
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static bool Prefix(CreateOptionsPicker __instance, int numImpostors)
{
if (__instance.mode != SettingsMode.Host) return true;
IGameOptions targetOptions = __instance.GetTargetOptions();
targetOptions.SetInt(Int32OptionNames.NumImpostors, numImpostors);
__instance.SetTargetOptions(targetOptions);
__instance.UpdateImpostorsButtons(numImpostors);
return false;
}
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.SetMaxPlayersButtons))]
public static class CreateOptionsPickerSetMaxPlayersButtons
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static bool Prefix(CreateOptionsPicker __instance, int maxPlayers)
{
if (DestroyableSingleton<FindAGameManager>.InstanceExists || __instance.mode != SettingsMode.Host)
{
return true;
}
IGameOptions targetOptions = __instance.GetTargetOptions();
targetOptions.SetInt(Int32OptionNames.MaxPlayers, maxPlayers);
__instance.SetTargetOptions(targetOptions);
__instance.UpdateMaxPlayersButtons(targetOptions);
return false;
}
}
[HarmonyPatch(typeof(GameOptionsData), nameof(GameOptionsData.AreInvalid))]
public static class InvalidOptionsPatches
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static bool Prefix(GameOptionsData __instance, [HarmonyArgument(0)] int maxExpectedPlayers)
{
return __instance.MaxPlayers > maxExpectedPlayers ||
__instance.NumImpostors < 1 ||
__instance.NumImpostors + 1 > maxExpectedPlayers / 2 ||
__instance.KillDistance is < 0 or > 2 ||
__instance.PlayerSpeedMod is <= 0f or > 3f;
}
}
[HarmonyPatch(typeof(SecurityLogger), nameof(SecurityLogger.Awake))]
public static class SecurityLoggerPatch
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static void Postfix(ref SecurityLogger __instance)
{
__instance.Timers = new float[127];
}
}
[HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.Update))]
public static class PlayerTabIsSelectedItemEquippedPatch
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static void Postfix(PlayerTab __instance)
{
if (GameOptionsManager.Instance.CurrentGameOptions.MaxPlayers > 15)
{
__instance.currentColorIsEquipped = false;
}
}
}
[HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.UpdateAvailableColors))]
public static class PlayerTabUpdateAvailableColorsPatch
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static bool Prefix(PlayerTab __instance)
{
if (GameOptionsManager.Instance.CurrentGameOptions.MaxPlayers <= 15)
{
return true;
}
__instance.AvailableColors.Clear();
for (var i = 0; i < Palette.PlayerColors.Count; i++)
{
if (!PlayerControl.LocalPlayer || PlayerControl.LocalPlayer.CurrentOutfit.ColorId != i)
{
__instance.AvailableColors.Add(i);
}
}
return false;
}
}
}
public class AbstractPagingBehaviour(IntPtr ptr) : MonoBehaviour(ptr)
{
protected const string PageIndexGameObjectName = "CrowdedMod_PageIndex";
// ReSharper disable once ReplaceWithFieldKeyword
private int _page;
protected static int MaxPerPage => 15;
public virtual int PageIndex
{
get => _page;
set
{
_page = value;
OnPageChanged();
}
}
protected virtual int MaxPageIndex => throw new("MaxPageIndex must be overridden");
public virtual void Start() => OnPageChanged();
public virtual void Update()
{
if (Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown(KeyCode.LeftArrow) || Input.mouseScrollDelta.y > 0f)
Cycle(false);
else if (Input.GetKeyDown(KeyCode.DownArrow) || Input.GetKeyDown(KeyCode.RightArrow) || Input.mouseScrollDelta.y < 0f)
Cycle(true);
}
public virtual void OnPageChanged() => throw new("OnPageChanged must be overridden");
/// <summary>
/// Loops around if you go over the limits.<br/>
/// Attempting to go up a page while on the first page will take you to the last page and vice versa.
/// </summary>
public virtual void Cycle(bool increment)
{
var change = increment ? 1 : -1;
PageIndex = Mathf.Clamp(PageIndex + change, 0, MaxPageIndex);
}
}
public class MeetingHudPagingBehaviour(IntPtr ptr) : AbstractPagingBehaviour(ptr)
{
private readonly MeetingHud meetingHud = null!;
[HideFromIl2Cpp] private IEnumerable<PlayerVoteArea> Targets => meetingHud.playerStates.OrderBy(p => p.AmDead);
protected override int MaxPageIndex => (Targets.Count() - 1) / MaxPerPage;
public override void Start() => OnPageChanged();
public override void Update()
{
base.Update();
// Sometimes the timer text is spammed with the page counter for some eccentric reason, so this is just a Band-Aid fix for it
if (meetingHud.state is MeetingHud.VoteStates.Animating or MeetingHud.VoteStates.Proceeding || meetingHud.TimerText.text.Contains($" ({PageIndex + 1}/{MaxPageIndex + 1})")) return; // TimerText does not update there
meetingHud.TimerText.text += $" ({PageIndex + 1}/{MaxPageIndex + 1})";
}
public override void OnPageChanged()
{
var i = 0;
foreach (var button in Targets)
{
if (i >= PageIndex * MaxPerPage && i < (PageIndex + 1) * MaxPerPage)
{
button.gameObject.SetActive(true);
var relativeIndex = i % MaxPerPage;
var row = relativeIndex / 3;
var col = relativeIndex % 3;
var buttonTransform = button.transform;
buttonTransform.localPosition = meetingHud.VoteOrigin +
new Vector3(
meetingHud.VoteButtonOffsets.x * col,
meetingHud.VoteButtonOffsets.y * row,
buttonTransform.localPosition.z
);
}
else button.gameObject.SetActive(false);
i++;
}
}
}
public class ShapeShifterPagingBehaviour(IntPtr ptr) : AbstractPagingBehaviour(ptr)
{
public ShapeshifterMinigame shapeshifterMinigame = null!;
private TextMeshPro PageText = null!;
[HideFromIl2Cpp] private IEnumerable<ShapeshifterPanel> Targets => shapeshifterMinigame.potentialVictims.ToArray();
protected override int MaxPageIndex => (Targets.Count() - 1) / MaxPerPage;
public override void Start()
{
PageText = Instantiate(HudManager.Instance.KillButton.cooldownTimerText, shapeshifterMinigame.transform);
PageText.name = PageIndexGameObjectName;
PageText.enableWordWrapping = false;
PageText.gameObject.SetActive(true);
PageText.transform.localPosition = new(4.1f, -2.36f, -1f);
PageText.transform.localScale *= 0.5f;
OnPageChanged();
}
public override void OnPageChanged()
{
PageText.text = $"({PageIndex + 1}/{MaxPageIndex + 1})";
var i = 0;
foreach (var panel in Targets)
{
if (i >= PageIndex * MaxPerPage && i < (PageIndex + 1) * MaxPerPage)
{
panel.gameObject.SetActive(true);
var relativeIndex = i % MaxPerPage;
var row = relativeIndex / 3;
var col = relativeIndex % 3;
var buttonTransform = panel.transform;
buttonTransform.localPosition =
new(
shapeshifterMinigame.XStart + shapeshifterMinigame.XOffset * col,
shapeshifterMinigame.YStart + shapeshifterMinigame.YOffset * row,
buttonTransform.localPosition.z
);
}
else panel.gameObject.SetActive(false);
i++;
}
}
}
public class VitalsPagingBehaviour(IntPtr ptr) : AbstractPagingBehaviour(ptr)
{
public VitalsMinigame vitalsMinigame = null!;
private TextMeshPro PageText = null!;
[HideFromIl2Cpp] private IEnumerable<VitalsPanel> Targets => vitalsMinigame.vitals.ToArray();
protected override int MaxPageIndex => (Targets.Count() - 1) / MaxPerPage;
public override void Start()
{
PageText = Instantiate(HudManager.Instance.KillButton.cooldownTimerText, vitalsMinigame.transform);
PageText.name = PageIndexGameObjectName;
PageText.enableWordWrapping = false;
PageText.gameObject.SetActive(true);
PageText.transform.localPosition = new(2.7f, -2f, -1f);
PageText.transform.localScale *= 0.5f;
OnPageChanged();
}
public override void OnPageChanged()
{
if (PlayerTask.PlayerHasTaskOfType<HudOverrideTask>(PlayerControl.LocalPlayer))
return;
PageText.text = $"({PageIndex + 1}/{MaxPageIndex + 1})";
var i = 0;
foreach (var panel in Targets)
{
if (i >= PageIndex * MaxPerPage && i < (PageIndex + 1) * MaxPerPage)
{
panel.gameObject.SetActive(true);
var relativeIndex = i % MaxPerPage;
var row = relativeIndex / 3;
var col = relativeIndex % 3;
var panelTransform = panel.transform;
panelTransform.localPosition =
new(
vitalsMinigame.XStart + vitalsMinigame.XOffset * col,
vitalsMinigame.YStart + vitalsMinigame.YOffset * row,
panelTransform.localPosition.z
);
}
else panel.gameObject.SetActive(false);
i++;
}
}
}
+1 -2
View File
@@ -511,10 +511,9 @@ namespace EHR
__instance.Value = (float)Math.Round(__instance.Value, 2);
break;
case StringNames.GameNumImpostors:
__instance.ValidRange = new(1, Crowded.MaxImpostors);
__instance.ValidRange = new(0, Crowded.MaxImpostors);
__instance.Value = (float)Math.Round(__instance.Value, 2);
if (DebugModeManager.AmDebugger) __instance.ValidRange.min = 0;
break;
}
+6 -4
View File
@@ -506,10 +506,7 @@ namespace EHR
CustomRoles.NiceGuesser
=> PlayerControl.LocalPlayer.KillSfx,
CustomRoles.Swooper or
CustomRoles.Wraith or
CustomRoles.Chameleon or
CustomRoles.Drainer
CustomRoles.Drainer
=> PlayerControl.LocalPlayer.MyPhysics.ImpostorDiscoveredSound,
CustomRoles.Addict or
@@ -534,10 +531,15 @@ namespace EHR
CustomRoles.Noisemaker
or CustomRoles.NoisemakerEHR
or CustomRoles.DarkHide
or CustomRoles.Specter
=> GetIntroSound(RoleTypes.Noisemaker),
CustomRoles.Phantom
or CustomRoles.PhantomEHR
or CustomRoles.Swooper
or CustomRoles.Wraith
or CustomRoles.Chameleon
=> GetIntroSound(RoleTypes.Phantom),
CustomRoles.Shapeshifter
+1 -1
View File
@@ -109,7 +109,7 @@ namespace EHR
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnPlayerJoined))]
internal static class OnPlayerJoinedPatch
{
private static bool IsDisconnected(this ClientData client)
public static bool IsDisconnected(this ClientData client)
{
var __instance = AmongUsClient.Instance;
+5
View File
@@ -379,6 +379,11 @@ Source language: English
> - Reference: Custom Region Installer (adding modded regions)
>
### :star: [Crowded](https://github.com/CrowdedMods/CrowdedMod)
>
> - Crowded is integrated into EHR
### Endless Host Roles (EHR), with Endless Possibilities, brought to you by the Endless Network.
##### Developers:
+5
View File
@@ -147,5 +147,10 @@ namespace EHR.Crewmate
{
hud.AbilityButton?.OverrideText(Translator.GetString("AddictVentButtonText"));
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -459,5 +459,10 @@ namespace EHR.Crewmate
else
hud.AbilityButton?.OverrideText(GetString("AlchemistVentButtonText"));
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -160,5 +160,10 @@ namespace EHR.Crewmate
if (ReviveStartTS != 0) return string.Format(Translator.GetString("AltruistSuffixRevive"), ReviveTime.GetInt() - (Utils.TimeStamp - ReviveStartTS));
return string.Format(Translator.GetString("AltruistSuffix"), Translator.GetString(RevivingMode ? "AltruistReviveMode" : "AltruistReportMode"));
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -121,5 +121,10 @@ namespace EHR.Crewmate
{
IsTeleported = false;
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -180,5 +180,10 @@ namespace EHR.Crewmate
return string.Format(Translator.GetString("Catcher.Suffix"), TrapPlaceDelay.GetInt() - (Utils.TimeStamp - DelayStartTS));
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -58,5 +58,10 @@ namespace EHR.Crewmate
{
UseAbility(pc);
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -74,5 +74,10 @@ namespace EHR.Crewmate
pc.RpcRemoveAbilityUse();
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -459,5 +459,10 @@ namespace EHR.Crewmate
return sb.ToString();
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -80,5 +80,10 @@ namespace EHR.Crewmate
else
pc.Notify(Translator.GetString("OutOfAbilityUsesDoMoreTasks"));
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -96,5 +96,10 @@ namespace EHR.Crewmate
pc.RPCPlayCustomSound("Dove");
pc.Notify(string.Format(Translator.GetString("DovesOfNeaceOnGuard"), pc.GetAbilityUseLimit()));
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -113,5 +113,10 @@ namespace EHR.Crewmate
PlayersInVents.Clear();
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -213,5 +213,10 @@ namespace EHR.Crewmate
return sb.ToString();
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -144,5 +144,10 @@ namespace EHR.Crewmate
else
pc.Notify(Translator.GetString("OutOfAbilityUsesDoMoreTasks"));
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -140,5 +140,10 @@ namespace EHR.Crewmate
player.MarkDirtySettings();
}
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -95,5 +95,10 @@ namespace EHR.Crewmate
OverrideTasksData.Create(9516, TabGroup.CrewmateRoles, CustomRoles.Mayor);
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -50,5 +50,10 @@ namespace EHR.Crewmate
{
pc.TPToRandomVent();
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -208,5 +208,10 @@ namespace EHR.Crewmate
return sb.ToString();
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -187,6 +187,11 @@ namespace EHR.Crewmate
return $"<#ff0000>O<sub>2</sub>:</color> {Utils.ColorString(GetLevelColor(), OxygenLevel.ToString())}%";
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
private enum Level
{
Blind,
+5
View File
@@ -76,5 +76,10 @@ namespace EHR.Crewmate
pc.NoCheckStartMeeting(pc.Data);
}
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -77,5 +77,10 @@ namespace EHR.Crewmate
pc.RpcRemoveAbilityUse();
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -131,5 +131,10 @@ namespace EHR.Crewmate
return $"\u25b6 ({AbilityDuration.GetInt() - (Utils.TimeStamp - ActivateTimeStamp)}s)";
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -109,5 +109,10 @@ namespace EHR.Crewmate
player.Notify(Translator.GetString("SecurityGuardSkillStop"));
}
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -201,5 +201,10 @@ namespace EHR.Crewmate
state.FinishPatrolling();
}, 0.1f, "SentinelFinishPatrol");
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ namespace EHR.Crewmate
public static void OnAnyoneDeath(PlayerControl killer, PlayerControl target)
{
Instances.DoIf(x => x.Target == target.PlayerId, _ => Main.AllAlivePlayerControls.Do(p => p.Notify(string.Format(Translator.GetString("SoothsayerDiedNotify"), Utils.ColorString(Main.PlayerColors.GetValueOrDefault(killer.PlayerId), killer.GetRealName())), 10f)));
Instances.DoIf(x => x.Target == killer.PlayerId, _ => Main.AllAlivePlayerControls.Do(p => p.Notify(string.Format(Translator.GetString("SoothsayerDiedNotify"), Utils.ColorString(Main.PlayerColors.GetValueOrDefault(killer.PlayerId), killer.GetRealName())), 10f)));
}
}
}
+5
View File
@@ -211,6 +211,11 @@ namespace EHR.Crewmate
return $"<#ffffff>{Timer}</color>{base.GetProgressText(playerId, comms)}";
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
private enum Mode
{
TeleportTarget,
+5
View File
@@ -145,5 +145,10 @@ namespace EHR.Crewmate
{
return Target != byte.MaxValue && seer.PlayerId == target.PlayerId && seer.PlayerId == TetherId ? $"<color=#00ffa5>Target:</color> <color=#ffffff>{Utils.GetPlayerById(Target).GetRealName()}</color>" : string.Empty;
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -141,5 +141,10 @@ namespace EHR.Crewmate
player.Notify(Translator.GetString("TimeMasterSkillStop"), (int)player.GetAbilityUseLimit());
}
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -304,6 +304,11 @@ namespace EHR.Crewmate
return true;
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
private enum ToiletVisibilityOptions
{
Instant,
+5
View File
@@ -139,5 +139,10 @@ namespace EHR.Crewmate
player.Notify(string.Format(Translator.GetString("VeteranOffGuard"), (int)player.GetAbilityUseLimit()));
}
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -315,5 +315,10 @@ namespace EHR.Impostor
return aa == null ? string.Empty : $"<#ffff00>\u26a0 {Delay.GetInt() - (Utils.TimeStamp - aa.ExtraAbilityStartTimeStamp):N0}</color>";
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return !IsMonitor || pc.GetClosestVent()?.Id == ventId;
}
}
}
+5
View File
@@ -291,5 +291,10 @@ namespace EHR.Impostor
target.RpcCheckAndMurder(target);
return false;
}
public override bool CanUseVent(PlayerControl pc, int ventId)
{
return UsedRole != CustomRoles.Chameleon || pc.GetClosestVent()?.Id == ventId;
}
}
}
+12
View File
@@ -167,6 +167,17 @@ namespace EHR
return seerRole.IsNK() && seerRole == target.GetCustomRole() && seer.GetTeam() == target.GetTeam();
}
protected bool IsThisRole(PlayerControl pc)
{
return pc.GetCustomRole() == Enum.Parse<CustomRoles>(GetType().Name, true);
}
protected bool IsThisRole(byte id)
{
var role = Main.PlayerStates.TryGetValue(id, out var state) ? state.MainRole : CustomRoles.NotAssigned;
return role == Enum.Parse<CustomRoles>(GetType().Name, true);
}
// Option setup simplifier
protected OptionSetupHandler StartSetup(int id, bool single = false)
{
@@ -179,6 +190,7 @@ namespace EHR
if (single) Options.SetupSingleRoleOptions(id++, tab, role, hideMaxSetting: true);
else Options.SetupRoleOptions(id++, tab, role);
return new(id, tab, role);
}
}
+10
View File
@@ -12,6 +12,7 @@ using BepInEx.Unity.IL2CPP.Utils.Collections;
using EHR;
using EHR.Modules;
using EHR.Neutral;
using EHR.Patches;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using UnityEngine;
@@ -25,6 +26,8 @@ namespace EHR
[BepInPlugin(PluginGuid, "EHR", PluginVersion)]
[BepInIncompatibility("jp.ykundesu.supernewroles")]
[BepInIncompatibility("MalumMenu")]
[BepInIncompatibility("com.ten.thebetterroles")]
[BepInIncompatibility("xyz.crowdedmods.crowdedmod")]
[BepInProcess("Among Us.exe")]
public class Main : BasePlugin
{
@@ -709,6 +712,13 @@ namespace EHR
handler.Info($"{nameof(ThisAssembly.Git.Tag)}: {ThisAssembly.Git.Tag}");
ClassInjector.RegisterTypeInIl2Cpp<ErrorText>();
ClassInjector.RegisterTypeInIl2Cpp<MeetingHudPagingBehaviour>();
ClassInjector.RegisterTypeInIl2Cpp<ShapeShifterPagingBehaviour>();
ClassInjector.RegisterTypeInIl2Cpp<VitalsPagingBehaviour>();
NormalGameOptionsV08.RecommendedImpostors = NormalGameOptionsV08.MaxImpostors = Enumerable.Repeat(127, 127).ToArray();
NormalGameOptionsV08.MinPlayers = Enumerable.Repeat(4, 127).ToArray();
HideNSeekGameOptionsV08.MinPlayers = Enumerable.Repeat(4, 127).ToArray();
CustomLogger.ClearLog();
LogMonitor.StartMonitoring();