1010101110-serverpermadeath icon

serverpermadeath

Server side only. Records deaths and bans characters when they die.

Last updated a year ago
Total downloads 654
Total rating 0 
Categories Server-side
Dependency string 1010101110-serverpermadeath-1.0.0
Dependants 2 other packages depend on this package

This mod requires the following mods to function

denikson-BepInExPack_Valheim-5.4.2105 icon
denikson-BepInExPack_Valheim

BepInEx pack for Valheim. Preconfigured and includes unstripped Unity DLLs.

Preferred version: 5.4.2105

README

serverpermadeath

this mod runs on the server only. The goal of this mod is to give server authority / no client mod solution to permadeath. We don't want to use the originally bannedlist.txt because that will ban a player all together. We want players to continue playing, they just can't use a character that has died.

new server textfile permadeath.txt

Server checks for dead players. if a player dies:

  • adds steamid|charactername to permadeath.txt
  • shout that player has died
  • start a 30 sec timer giving them time to say goodbye
  • kicks them, shows Banned popup

Server checks permadeath.txt and all players connected. if player|charactername is on list:

  • kicks them, shows Banned popup

Combine this with servercharacters https://valheim.thunderstore.io/package/Smoothbrain/ServerCharacters/ to have a hardcore server

install

prerequisite: have bepinex installed on server

drag serverpermadeath.dll into bepinex/plugins folder

restart server

code

 public static SyncedList characterbans;
public static Dictionary<string, int> timers = new Dictionary<string, int>();

//initialize the synced list used for tracking bans
[HarmonyPatch(typeof(ZNet), "Awake")]
public static class awakepatch{
    private static void Postfix()
    {
        characterbans = new SyncedList(Utils.GetSaveDataPath(FileHelpers.FileSource.Local) + "/permadeath.txt",
        "List of dead players by steamid|charname - ONE per line - serverpermadeath mod");
    }
}



[HarmonyPatch(typeof(ZNet), "CheckWhiteList")]
public static class CheckCharWhitelist
{
    private static bool Prefix(ZNet __instance)
    {
        //runs every 5s
        //check whitelist
        if (__instance.m_peers != null)
        {
            if (__instance.m_peers.Count > 0)
            {
                foreach (ZNetPeer znetPeer in __instance.m_peers.ToList<ZNetPeer>())
                {
                    if (znetPeer.IsReady() && !znetPeer.m_characterID.IsNone())
                    {
                        string charName = znetPeer.m_playerName;
                        string id = znetPeer.m_socket.GetHostName();
                        string idandname = id + "|" + charName;
                        bool bannedalready = characterbans.Contains(idandname);

                        //they are banned, gtfo
                        if (bannedalready)
                        {
                            ZNet.instance.InternalKick(znetPeer);
                        }
                        else
                        {
                            //check if they're dead
                            ZDO zdo = __instance.m_zdoMan.GetZDO(znetPeer.m_characterID);
                            if (zdo != null)
                            {
                                bool dead = zdo.GetBool("dead", false);

                                if (dead && !TrippingPatches.timers.ContainsKey(idandname))
                                {
                                    //tell everyone they are dead
                                    ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "ChatMessage", new object[]
                                    {
                                    znetPeer.m_refPos,
                                    2,
                                    znetPeer.m_playerName,
                                    znetPeer.m_playerName + " has been slain"
                                    });
                                    ZLog.Log(znetPeer.m_playerName + " DIED " + znetPeer.m_refPos.ToString());

                                    //start a timer to kick them
                                    TrippingPatches.timers.Add(idandname, 5);
                                }
                            }
                        }
                    }
                }
            }
        }

        //clean up the timers
        foreach (string key in TrippingPatches.timers.Keys.ToList<string>())
        {
            TrippingPatches.timers[key] = TrippingPatches.timers[key] - 1;
            if (TrippingPatches.timers[key] < 0)
            {
                //add them to the banned list
                characterbans.Add(key);

                //remove the timer
                TrippingPatches.timers.Remove(key);
            }
        }

        return true;
    }
}