Version Handshaking (without messing with Game's version) - Valheim-Modding/Wiki GitHub Wiki
This will be primarily for those that have server side mod implementation. The code below will preform a version handshake between your mod and the client's installed mods. This will prevent people from joining a server for which your mod lives server side. The client will be disconnected when they have not sent the correct RPC call for your mod, join with a different version than what's installed on the server, or have some sort of error in connection.
[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
public static class RegisterAndCheckVersion
{
private static void Prefix(ZNetPeer peer, ref ZNet __instance)
{
// Register version check call
ZLog.Log("YOURMOD - Registering version RPC handler");
peer.m_rpc.Register<ZPackage>("YOURMOD_Version", new Action<ZRpc, ZPackage>(RpcHandlers.RPC_YOURMOD_Version));
// Make calls to check versions
ZLog.Log("YOURMOD - Invoking version check");
ZPackage zpackage = new ZPackage();
zpackage.Write(YOURPLUGIN.version);
peer.m_rpc.Invoke("YOURMOD_Version", (object)zpackage);
}
}
[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
public static class VerifyClient
{
private static bool Prefix(ZRpc rpc, ZPackage pkg, ref ZNet __instance)
{
if (__instance.IsServer() && !RpcHandlers._validatedPeers.Contains(rpc))
{
// Disconnect peer if they didn't send mod version at all
ZLog.LogWarning("Peer never sent version, disconnecting");
rpc.Invoke("Error", (object)3);
return false; // Prevent calling underlying method
}
return true;
}
}
[HarmonyPatch(typeof(ZNet), "Disconnect")]
public static class RemoveDisconnectedPeerFromVerified
{
private static void Prefix(ZNetPeer peer, ref ZNet __instance)
{
if (__instance.IsServer())
{
// Remove peer from validated list
ZLog.Log("Peer disconnected, removing from validated list");
RpcHandlers._validatedPeers.Remove(peer.m_rpc);
}
}
}
public static class RpcHandlers
{
public static List<ZRpc> _validatedPeers = new List<ZRpc>();
public static void RPC_YOURMOD_Version(ZRpc rpc, ZPackage pkg)
{
var version = pkg.ReadString();
ZLog.Log("YOURMOD - Version check, server: " + version + ", mine: " + YOURPLUGIN.version);
if (version != YOURPLUGIN.version)
{
if (ZNet.instance.IsServer())
{
// Different versions - force disconnect client from server
ZLog.LogWarning("YOURMOD - Peer has incompatible version, disconnecting");
rpc.Invoke("Error", (object)3);
}
}
else
{
if (!ZNet.instance.IsServer())
{
// Enable mod on client if versions match
ZLog.Log("YOURMOD - Recieved same version from server!");
}
else
{
// Add client to validated list
ZLog.Log("YOURMOD - Adding peer to validated list");
_validatedPeers.Add(rpc);
}
}
}
}
using HarmonyLib;
using System;
using System.Collections.Generic;
namespace YOURMOD.NAMESPACE
{
[HarmonyPatch(typeof(ZNet), "Awake")]
public static class EnableOnDedicatedServer
{
private static void Prefix(ref ZNet __instance)
{
// Enable on dedicated servers
if (__instance.IsDedicated())
{
ZLog.Log("YOURMOD - Enabling for dedicated server");
}
}
}
[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
public static class RegisterAndCheckVersion
{
private static void Prefix(ZNetPeer peer, ref ZNet __instance)
{
// Register version check call
ZLog.Log("YOURMOD - Registering version RPC handler");
peer.m_rpc.Register<ZPackage>("YOURMOD_Version", new Action<ZRpc, ZPackage>(RpcHandlers.RPC_YOURMOD_Version));
// Make calls to check versions
ZLog.Log("YOURMOD - Invoking version check");
ZPackage zpackage = new ZPackage();
zpackage.Write(YOURPLUGIN.version);
peer.m_rpc.Invoke("YOURMOD_Version", (object)zpackage);
}
}
[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
public static class VerifyClient
{
private static bool Prefix(ZRpc rpc, ZPackage pkg, ref ZNet __instance)
{
if (__instance.IsServer() && !RpcHandlers._validatedPeers.Contains(rpc))
{
// Disconnect peer if they didn't send mod version at all
ZLog.LogWarning("YOURMOD - Peer never sent version, disconnecting");
rpc.Invoke("Error", (object)3);
return false; // Prevent calling underlying method
}
return true;
}
}
[HarmonyPatch(typeof(ZNet), "Disconnect")]
public static class RemoveDisconnectedPeerFromVerified
{
private static void Prefix(ZNetPeer peer, ref ZNet __instance)
{
if (__instance.IsServer())
{
// Remove peer from validated list
ZLog.Log("YOURMOD - Peer disconnected, removing from validated list");
RpcHandlers._validatedPeers.Remove(peer.m_rpc);
}
}
}
public static class RpcHandlers
{
public static List<ZRpc> _validatedPeers = new List<ZRpc>();
public static void RPC_YOURMOD_Version(ZRpc rpc, ZPackage pkg)
{
var version = pkg.ReadString();
ZLog.Log("Better Wards - Version check, server: " + version + ", mine: " + YOURPLUGIN.version);
if (version != YOURPLUGIN.version)
{
if (ZNet.instance.IsServer())
{
// Different versions - force disconnect client from server
ZLog.LogWarning("YOURMOD - Peer has incompatible version, disconnecting");
rpc.Invoke("Error", (object)3);
}
}
else
{
if (!ZNet.instance.IsServer())
{
// Enable mod on client if versions match
ZLog.Log("YOURMOD - Recieved same version from server!");
}
else
{
// Add client to validated list
ZLog.Log("YOURMOD - Adding peer to validated list");
_validatedPeers.Add(rpc);
}
}
}
}
}