EnhancedNetwork / TownofHost-Enhanced

TOHE is the best Host-Only mod for anyone who wants to change their Among Us Experience!
https://tohe.weareten.ca
GNU General Public License v3.0
83 stars 77 forks source link

[Bug] for RoleBase Branch, ReportDeadBodyPatch returns null references. #848

Closed D1GQ closed 6 months ago

D1GQ commented 6 months ago

Overview

You get an error when calling a meeting caused by null reference in TOHE.ReportDeadBodyPatch.AfterReportTasks and TOHE.ReportDeadBodyPatch.Prefix

Error

Here is the error message and dump.

Error Message In Meeting

image

Error In Dump

[Info   :      TOHE] [00:47:39][ReportDeadBody]D1GQ => null
[Error  :      TOHE] [00:47:39][ReportDeadBodyPatch]System.NullReferenceException: Object reference not set to an instance of an object.
   at TOHE.ReportDeadBodyPatch.AfterReportTasks(PlayerControl player, PlayerInfo target)
   at TOHE.ReportDeadBodyPatch.Prefix(PlayerControl __instance, PlayerInfo target)

Possible Fix

I just added a few null checks to fix the errors, but I don't know if I broke anything process so y'all will have to overview it.

Fix 1 For TOHE.ReportDeadBodyPatch.AfterReportTasks

public static void AfterReportTasks(PlayerControl player, GameData.PlayerInfo target)
{
    //=============================================
    // Hereinafter, it is assumed that the button is confirmed to be pressed
    //=============================================

    Main.LastVotedPlayerInfo = null;
    Main.GuesserGuessed.Clear();
    Main.AllKillers.Clear();

    foreach (var playerStates in Main.PlayerStates.Values.ToArray())
    {
        if (target == null) return;
        playerStates.RoleClass?.OnReportDeadBody(player, target?.Object);
    }

    // Alchemist & Bloodlust
    Alchemist.OnReportDeadBodyGlobal();

    if (Aware.IsEnable) Aware.OnReportDeadBody();

    if (target != null) Sleuth.OnReportDeadBody(player, target?.Object);

    foreach (var pc in Main.AllPlayerControls)
    {
        if (target == null) return;
        if (!Doppelganger.CheckDoppelVictim(pc.PlayerId) && !Murderer.CheckMurdererVictim(target.PlayerId))
        {
            // Update skins again, since players have different skins
            // And can be easily distinguished from each other
            if (Camouflage.IsCamouflage && Options.KPDCamouflageMode.GetValue() is 2 or 3)
            {
                Camouflage.RpcSetSkin(pc);
            }

            // Check shapeshift and revert skin to default
            if (Main.CheckShapeshift.ContainsKey(pc.PlayerId))
            {
                Camouflage.RpcSetSkin(pc, RevertToDefault: true);
            }
        }

        Logger.Info($"Player {pc?.Data?.PlayerName}: Id {pc.PlayerId} - is alive: {pc.IsAlive()}", "CheckIsAlive");
    }

    // Set meeting time
    MeetingTimeManager.OnReportDeadBody();

    // Clear all Notice players
    NameNotifyManager.Reset();

    // Update Notify Roles for Meeting
    Utils.DoNotifyRoles(isForMeeting: true, NoCache: true, CamouflageIsForMeeting: true);

    // Sync all settings on meeting start
    _ = new LateTask(Utils.SyncAllSettings, 3f, "Sync all settings after report");
}

Fix 2 For TOHE.ReportDeadBodyPatch.Prefix

class ReportDeadBodyPatch
{
    public static Dictionary<byte, bool> CanReport;
    public static Dictionary<byte, List<GameData.PlayerInfo>> WaitReport = [];
    public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] GameData.PlayerInfo target)
    {
        if (GameStates.IsMeeting || GameStates.IsHideNSeek) return false;

        if (target != null && EAC.RpcReportDeadBodyCheck(__instance, target))
        {
            Logger.Fatal("Eac patched the report body rpc", "ReportDeadBodyPatch");
            return false;
        }
        if (Options.DisableMeeting.GetBool()) return false;
        if (Options.CurrentGameMode == CustomGameMode.FFA) return false;

        if (!CanReport[__instance.PlayerId])
        {
            if (target == null) return true;
            WaitReport[__instance.PlayerId].Add(target);
            Logger.Warn($"{__instance.GetNameWithRole().RemoveHtmlTags()} : Reporting is prohibited and will wait until it becomes possible", "ReportDeadBody");
            return false;
        }

        Logger.Info($"{__instance.GetNameWithRole().RemoveHtmlTags()} => {target?.Object?.GetNameWithRole().RemoveHtmlTags() ?? "null"}", "ReportDeadBody");

        foreach (var kvp in Main.PlayerStates)
        {
            var pc = Utils.GetPlayerById(kvp.Key);
            kvp.Value.LastRoom = pc.GetPlainShipRoom();
        }

        if (!AmongUsClient.Instance.AmHost) return true;

        try
        {
            // If the player is dead, the meeting is canceled
            if (__instance.Data.IsDead) return false;

            //=============================================
            //Below, check if this meeting is allowed
            //=============================================

            var killer = target?.Object?.GetRealKiller();
            var killerRole = killer?.GetCustomRole();

            if (target == null) //Meeting
            {
                var playerRoleClass = __instance.GetRoleClass();

                if (playerRoleClass.OnCheckStartMeeting(__instance) == false)
                {
                    Logger.Info($"Player has role class: {playerRoleClass} - the start of the meeting has been cancelled", "ReportDeadBody");
                    return false;
                }
            }
            if (target != null) // Report dead body
            {
                // Guessed player cannot report
                if (Main.PlayerStates[target.PlayerId].deathReason == PlayerState.DeathReason.Gambled) return false;

                // Check report bead body
                foreach (var player in Main.PlayerStates.Values.ToArray())
                {
                    var playerRoleClass = player.RoleClass;
                    if (player == null ||  playerRoleClass == null) continue;

                    if (playerRoleClass.OnCheckReportDeadBody(__instance, target, killer) == false)
                    {
                        Logger.Info($"Player has role class: {playerRoleClass} - is canceled the report", "ReportDeadBody");
                        return false;
                    }
                }

                // if Bait is killed, check the setting condition
                if (!(target.Object.Is(CustomRoles.Bait) && Bait.BaitCanBeReportedUnderAllConditions.GetBool()))
                {
                    // Comms Camouflage
                    if (Options.DisableReportWhenCC.GetBool() && Utils.IsActive(SystemTypes.Comms) && Camouflage.IsActive) return false;
                }

                //Check unreportable bodies
                if (Main.UnreportableBodies.Contains(target.PlayerId))
                {
                    __instance.Notify(Utils.ColorString(__instance.GetRoleColor(), GetString("BodyCannotBeReported")));
                    return false;
                }

                if (target.Object.Is(CustomRoles.Unreportable)) return false;

                // 胆小鬼不敢报告
                var tpc = Utils.GetPlayerById(target.PlayerId);
                if (__instance.Is(CustomRoles.Oblivious))
                {
                    if (!tpc.Is(CustomRoles.Bait) || (tpc.Is(CustomRoles.Bait) && Oblivious.ObliviousBaitImmune.GetBool())) /* && (target?.Object != null)*/
                    {
                        return false;
                    }
                }

                var tar = Utils.GetPlayerById(target.PlayerId);

                if (__instance.Is(CustomRoles.Unlucky) && (target?.Object == null || !target.Object.Is(CustomRoles.Bait)))
                {
                    Unlucky.SuicideRand(__instance, Unlucky.StateSuicide.ReportDeadBody);
                    if (Unlucky.UnluckCheck[__instance.PlayerId]) return false;

                }
            }

            if (Options.SyncButtonMode.GetBool() && target == null)
            {
                Logger.Info($"Option: {Options.SyncedButtonCount.GetInt()}, has button count: {Options.UsedButtonCount}", "ReportDeadBody");
                if (Options.SyncedButtonCount.GetFloat() <= Options.UsedButtonCount)
                {
                    Logger.Info("The button has been canceled because the maximum number of available buttons has been exceeded", "ReportDeadBody");
                    return false;
                }
                else Options.UsedButtonCount++;

                if (Options.SyncedButtonCount.GetFloat() == Options.UsedButtonCount)
                {
                    Logger.Info("The maximum number of meeting buttons has been reached", "ReportDeadBody");
                }
            }

            AfterReportTasks(__instance, target);

        }
        catch (Exception e)
        {
            Logger.Exception(e, "ReportDeadBodyPatch");
            Logger.SendInGame("Error: " + e.ToString());

            // If there is an error in ReportDeadBodyPatch, update the player nicknames anyway
            MeetingTimeManager.OnReportDeadBody();
            NameNotifyManager.Reset();
            Utils.DoNotifyRoles(isForMeeting: true, NoCache: true, CamouflageIsForMeeting: true);
            _ = new LateTask(Utils.SyncAllSettings, 3f, "Sync all settings after report");
        }

        return true;
    }
    public static void AfterReportTasks(PlayerControl player, GameData.PlayerInfo target)
    {
        //=============================================
        // Hereinafter, it is assumed that the button is confirmed to be pressed
        //=============================================

        Main.LastVotedPlayerInfo = null;
        Main.GuesserGuessed.Clear();
        Main.AllKillers.Clear();

        foreach (var playerStates in Main.PlayerStates.Values.ToArray())
        {
            if (target == null) return;
            playerStates.RoleClass?.OnReportDeadBody(player, target?.Object);
        }

        // Alchemist & Bloodlust
        Alchemist.OnReportDeadBodyGlobal();

        if (Aware.IsEnable) Aware.OnReportDeadBody();

        if (target != null) Sleuth.OnReportDeadBody(player, target?.Object);

        foreach (var pc in Main.AllPlayerControls)
        {
            if (target == null) return;
            if (!Doppelganger.CheckDoppelVictim(pc.PlayerId) && !Murderer.CheckMurdererVictim(target.PlayerId))
            {
                // Update skins again, since players have different skins
                // And can be easily distinguished from each other
                if (Camouflage.IsCamouflage && Options.KPDCamouflageMode.GetValue() is 2 or 3)
                {
                    Camouflage.RpcSetSkin(pc);
                }

                // Check shapeshift and revert skin to default
                if (Main.CheckShapeshift.ContainsKey(pc.PlayerId))
                {
                    Camouflage.RpcSetSkin(pc, RevertToDefault: true);
                }
            }

            Logger.Info($"Player {pc?.Data?.PlayerName}: Id {pc.PlayerId} - is alive: {pc.IsAlive()}", "CheckIsAlive");
        }

        // Set meeting time
        MeetingTimeManager.OnReportDeadBody();

        // Clear all Notice players
        NameNotifyManager.Reset();

        // Update Notify Roles for Meeting
        Utils.DoNotifyRoles(isForMeeting: true, NoCache: true, CamouflageIsForMeeting: true);

        // Sync all settings on meeting start
        _ = new LateTask(Utils.SyncAllSettings, 3f, "Sync all settings after report");
    }
    public static async void ChangeLocalNameAndRevert(string name, int time)
    {
        //async Taskじゃ警告出るから仕方ないよね。
        var revertName = PlayerControl.LocalPlayer.name;
        PlayerControl.LocalPlayer.RpcSetNameEx(name);
        await Task.Delay(time);
        PlayerControl.LocalPlayer.RpcSetNameEx(revertName);
    }
}
Tommy-XL commented 6 months ago

I was unable to reproduce the error in the last RoleBase commit

D1GQ commented 6 months ago

I was unable to reproduce the error in the last RoleBase commit

Interesting my build is based off of a RoleBase build from like 3-4days ago, and I got it every time I called a meeting until I added the null checks, I was in a Lobby alone or with 1 other player, so I don't know why it would be happening on my build.

Tommy-XL commented 6 months ago

Try this on different roles, both old roles and new roles you added

Tommy-XL commented 6 months ago

Error called here Screenshot_20240425_151453

Tommy-XL commented 6 months ago

You trying check target, not pc

D1GQ commented 6 months ago

Ahhhhhh ok, I'll go ahead and fix that.

D1GQ commented 6 months ago

I fixed it, thanks for the help! I'm going to go ahead and close this.