{"id":18057267,"url":"https://github.com/m4cs/valheim-server-rpc-guide","last_synced_at":"2025-07-10T08:36:38.983Z","repository":{"id":96218459,"uuid":"346084828","full_name":"M4cs/Valheim-Server-RPC-Guide","owner":"M4cs","description":"A Guide on Creating Advanced RPC Systems in Valheim","archived":false,"fork":false,"pushed_at":"2021-03-09T18:32:50.000Z","size":18,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-17T21:13:29.513Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/M4cs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-09T17:15:25.000Z","updated_at":"2024-02-03T12:21:37.000Z","dependencies_parsed_at":"2023-05-30T15:15:54.910Z","dependency_job_id":null,"html_url":"https://github.com/M4cs/Valheim-Server-RPC-Guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M4cs%2FValheim-Server-RPC-Guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M4cs%2FValheim-Server-RPC-Guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M4cs%2FValheim-Server-RPC-Guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M4cs%2FValheim-Server-RPC-Guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/M4cs","download_url":"https://codeload.github.com/M4cs/Valheim-Server-RPC-Guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247320216,"owners_count":20919734,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-31T02:07:14.174Z","updated_at":"2025-04-05T10:22:47.492Z","avatar_url":"https://github.com/M4cs.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Valheim Server Validated RPC Guide\nA Guide for making a powerful RPC request/message system with Valheim's ZRoutedRPC\n\n# Introduction\nValheim comes packed with a robust RPC messaging system through the ZRoutedRPC class. You can patch the RPC_* methods provided but you can also create your own RPC methods. Before you start, it may help to break your project up into a server side project and a client side project. This will make your RPC methods a lot easier to work with because you know where you will be handling them, and you can create server specific, and client specific RPC handlers. \n\nThe RPC messaging system Valheim uses thrives off ZPackages. These are serializable data packets that can be sent over the RPC system. You can write almost all standard types of data to them including `bool, byte, byte[], char, double, int, long, Quaternion, sbyte, float, string, uint, ulong, Vector2i, Vector3, and ZDOID`. While `Quaternion, Vector2i, and Vector3` are Unity types, you can still de/serialize them with ZPackages. \n\n# Requirements\n\n* BepInEx \u003e=5.4\n* HarmonyLib (Ships w/ BepInEx)\n* Publicized Assemblies (For our example)\n\n# Creating Server Validated RPC Requests\n\nWhen creating your custom RPC methods, you can name them whatever you want, however it's recommended you prefix them with `RPC_` as that is what the rest of the codebase uses and it's common practice. It also helps to distinguish your RPC methods from others. It's recommended that you create either an RPC class or an RPC namespace inside of your Mod's namespace. You can do this by creating a folder called `RPC` or simply creating a new class with the name you desire. For the examples, our RPC methods will be in the `Mod.RPC` namespace, inside of the `RPC` class.\n\n## Where can this be used?\n\nThis guide was written with the idea that you are hosting a **dedicated** server and providing a Server and Client mod. One to run on the server, one to run client side. **I simply don't know how this will work P2P, you will probably need to add the server functions to the Client mod and check for `ZNet.instance.IsServer()` if you are the host or not.**\n\n## The Messaging Flow\n\n\u003cp align=\"center\"\u003e\n  \u003ca\u003e\u003cimg width=\"450\" height=\"700\" src=\"https://mbcdn.sfo2.digitaloceanspaces.com/RPCDiagram.png\"/\u003e\u003c/a\u003e\n\u003cp\u003e\n  \n1. The client makes a request to the server with the information necessary for the request.\n2. The server reads the ZPackage sent by the client. It reads the ZPackage and validates the request.\n\n**If Client Request Is Valid:**\n1. Sends a new package, or the package sent by the client, to the correct clients.\n2. Client will handle this locally.\n\n**If Client Request Is Invalid:**\n1. Sends an error event to the client.\n2. Client handles the error event.\n\nbut what does this look like in code?\n\n## Our Custom Handlers\n\n**The usecase:** I want to create a function, that allows clients to run a chat command as admin, and send a server announcement. It will display in the client's chat without using the RPC chat (you could replace this with logic to do something else, this is purely an example).\n\nFirst, we need to create an RPC function for our server to handle our Client request. In the `RPC` class of our **Server** project, we create a function called `RPC_RequestServerAnnouncement`. This will take a sender ID and a ZPackage. We assume that the ZPackage holds a single string with the message the Client wants to send. You will see us build this out later.\n\n```cs\npublic static void RPC_RequestServerAnnouncement(long sender, ZPackage pkg)\n{\n    if (pkg != null \u0026\u0026 pkg.Size() \u003e 0)\n    { // Check that our Package is not null, and if it isn't check that it isn't empty.\n        ZNetPeer peer = ZNet.instance.GetPeer(sender); // Get the Peer from the sender, to later check the SteamID against our Adminlist.\n        if (peer != null)\n        { // Confirm the peer exists\n            string peerSteamID = ((ZSteamSocket)peer.m_socket).GetPeerID().m_SteamID.ToString(); // Get the SteamID from peer.\n            if (\n                ZNet.instance.m_adminList != null \u0026\u0026\n                ZNet.instance.m_adminList.Contains(peerSteamID)\n            )\n            { // Check that the SteamID is in our Admin List.\n                string msg = pkg.ReadString(); // Read the message from the user.\n                if (!msg.Equals(\"I hate you\"))\n                { // Example of validating the string.\n                    pkg.SetPos(0); // Reset the position of our cursor so the client's can re-read the package.\n                    ZRoutedRpc.instance.InvokeRoutedRPC(0L, \"EventServerAnnouncement\", new object[] { pkg }); // Send our Event to all Clients. 0L specifies that it will be sent to everybody\n                }\n                else\n                {\n                    ZPackage newPkg = new ZPackage(); // Create a new ZPackage.\n                    newPkg.Write(\"That's not nice!\"); // Shame them.\n                    ZRoutedRpc.instance.InvokeRoutedRPC(sender, \"BadRequestMsg\", new object[] { newPkg }); // Send the error message.\n                }\n            }\n        } else {\n            ZPackage newPkg = new ZPackage(); // Create a new ZPackage.\n            newPkg.Write(\"You aren't an Admin!\"); // Tell them what's going on.\n            ZRoutedRpc.instance.InvokeRoutedRPC(sender, \"BadRequestMsg\", new object[] { newPkg }); // Send the error message.\n        }\n    }\n}\n```\n\nThis will give us a basic server validator and allow us to send the events to the client whether it's completing the request, or sending them an error. `0L` as our target makes our RPC message send to all clients. If we don't want to send to all players, it should be the sender ID or a players RPC id. However! Since we call `0L` for the target from our server, we need to create a Mock client handler function for the server, but we will just make that an empty return. For consistency we will name this the same as our Client RPC function that we will make next! Add a function in the same **Server** `RPC` class and name it `RPC_EventServerAnnouncement`.\n\n```cs\npublic static void RPC_EventServerAnnouncement(long sender, ZPackage pkg) {\n    return;\n}\n```\n\nNow we can create our Client side handler. This will be the same name as the function above, except this is where we put our Client logic! Make a new function in the `RPC` class of your **Client** mod.\n\n```cs\npublic static void RPC_EventServerAnnouncement(long sender, ZPackage pkg) {\n    if (sender == ZRoutedRpc.instance.GetServerPeerID() \u0026\u0026 pkg != null \u0026\u0026 pkg.Size() \u003e 0) { // Confirm our Server is sending the RPC\n        string announcement = pkg.ReadString();\n        if (announcement != \"\") { // Make sure it isn't empty\n            Chat.instance.AddString(\"Server\", announcement, Talker.Type.Shout); // Add our server announcement to the Client's chat instance\n        }\n    }\n}\n```\n\nWe now have to create a Client handler for error messages. I like to display an error in chat, but you can do whatever you want to! Add a new function called `RPC_BadRequestMsg`.\n```cs\npublic static void RPC_BadRequestMsg(long sender, ZPackage pkg) {\n    if (sender == ZRoutedRpc.instance.GetServerPeerID() \u0026\u0026 pkg != null \u0026\u0026 pkg.Size() \u003e 0) { // Confirm our Server is sending the RPC\n        string msg = pkg.ReadString(); // Get Our Msg\n        if (msg != \"\") { // Make sure it isn't empty\n            Chat.instance.AddString(\"Server\", \"\u003ccolor=\\\"red\\\"\u003e\" + msg + \"\u003c/color\u003e\", Talker.Type.Normal); // Add to chat with red color because it's an error\n        }\n    }\n}\n```\n\nNow we have to create a Mock server handler for the request. Add an empty function with the same name as your Server's request handler.\n\n```cs\npublic static void RPC_RequestServerAnnouncement(long sender, ZPackage pkg) {\n    return;\n}\n```\n\nAs far as creating the logic for our custom RPC system, we are finished! We just have to patch the `Game` class, to register our new RPC functions. In **both the Client and Server projects**, create a patch for Game.Start() and register your functions.\n\nFor the **Server** project, we can patch all the functions we created.\n\n```cs\n[HarmonyPatch(typeof(Game), \"Start\")]\npublic static class GameStartPatch {\n    private static void Prefix() {\n        ZRoutedRpc.instance.Register(\"RequestServerAnnouncement\", new Action\u003clong, ZPackage\u003e(RPC.RPC_RequestServerAnnouncement); // Our Server Handler\n        ZRoutedRpc.instance.Register(\"EventServerAnnouncement\", new Action\u003clong, ZPackage\u003e(RPC.RPC_EventServerAnnouncement); // Our Mock Client Function\n    }\n}\n```\n\nFor the **Client** project, we can patch all the functions we created.\n\n```cs\n[HarmonyPatch(typeof(Game), \"Start\")]\npublic static class GameStartPatch {\n    private static void Prefix() {\n        ZRoutedRpc.instance.Register(\"RequestServerAnnouncement\", new Action\u003clong, ZPackage\u003e(RPC.RPC_RequestServerAnnouncement); // Our Mock Server Handler\n        ZRoutedRpc.instance.Register(\"EventServerAnnouncement\", new Action\u003clong, ZPackage\u003e(RPC.RPC_EventServerAnnouncement); // Our Client Function\n        ZRoutedRpc.instance.Register(\"BadRequestMsg\", new Action\u003clong, ZPackage\u003e(RPC.RPC_BadRequestMsg); // Our Error Handler\n    }\n}\n```\n\nThat's it! We are all setup to use our new custom RPC methods with server validation! If you'd like to initiate the request, we'd patch the Chat class and handle a custom command on our **Client** project.\n\n```cs\n\n[HarmonyPatch(typeof(Chat), \"InputText\")]\npublic static class ChatHandler\n{\n    private static bool Prefix(ref Chat __instance)\n    {\n        string text = __instance.m_input.text; // Get the chat text\n        string[] textSplit = text.Split(' '); // Split up args\n        if (textSplit.Length \u003e 1) { // Make sure it's more than 1 word\n            if (textSplit[0] == \"/announce\") { // Check if it's our command\n                ZPackage pkg = new ZPackage(); // Create ZPackage\n                string msg = \"\"; // Make msg\n                for (int i = 1; i \u003c textSplit.Length; i++) {\n                    msg += textSplit[i] + \" \";\n                }\n                pkg.Write(msg); // Write msg to ZPackage\n                \n                // Send msg over RPC to server\n                ZRoutedRpc.instance.InvokeRoutedRpc(ZRoutedRpc.instance.GetServerPeerID(), \"RequestServerAnnouncement\", new object[] { pkg });\n                return false; // Skip original operation\n            }\n        }\n        return true;\n    }\n}\n```\n\nThis will start our RPC flow with the server and if the validation works, you will see the announcement, or you will get an error because you aren't an admin or said you hate everybody 😜\n\n### Need help?\n\nCome check out the Valheim Modding [discord](https://discord.gg/KEHFbeyv52). We have a great community that is always willing to help out! You can find me in there with the username @macs \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm4cs%2Fvalheim-server-rpc-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fm4cs%2Fvalheim-server-rpc-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm4cs%2Fvalheim-server-rpc-guide/lists"}