Decompiled source of FiresSteamworksPatcher v1.0.0

FiresSteamworksPatcher.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("FiresSteamworksPatcher")]
[assembly: AssemblyDescription("BepInEx preloader patcher. (1) Adds the missing Steam SDK 1.51+ recv-buffer enum members to Valheim's bundled com.rlabrecque.steamworks.net.dll so existing SetConfigValue paths can target them. (2) Bumps the ZDOMan.SendZDOs queue cap from vanilla 10240 to 102400 (10x) so per-peer ZDO flushes have more headroom on busy dedicated servers.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FiresSteamworksPatcher")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("ee0f80de-6bd2-4a52-8997-bd60dce71b00")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FiresSteamworksPatcher
{
	public static class FiresSteamworksPatcher
	{
		private const int ZdoSendQueueCapBytes = 102400;

		private const int VanillaZdoSendQueueCapBytes = 10240;

		private const int QueueSizeCallSearchRadius = 15;

		private const string SteamworksConfigEnumFullName = "Steamworks.ESteamNetworkingConfigValue";

		private const string ZdoManTypeName = "ZDOMan";

		private const string SendZdosMethodName = "SendZDOs";

		private const string GetSendQueueSizeMethodName = "GetSendQueueSize";

		private const string GhettoNetworkingDllGlob = "*GhettoNetwork*.dll";

		private static readonly ManualLogSource Log = Logger.CreateLogSource("FiresSteamworksPatcher");

		private static readonly (string Name, int Value)[] RecvBufferEnumMembers = new(string, int)[4]
		{
			("k_ESteamNetworkingConfig_RecvBufferSize", 47),
			("k_ESteamNetworkingConfig_RecvBufferMessages", 48),
			("k_ESteamNetworkingConfig_RecvMaxMessageSize", 49),
			("k_ESteamNetworkingConfig_RecvMaxSegmentsPerPacket", 50)
		};

		private static bool? _ghettoNetworkingPresent;

		private static bool? _isDedicatedServer;

		public static IEnumerable<string> TargetDLLs
		{
			get
			{
				if (!IsDedicatedServer())
				{
					Log.LogInfo((object)"Not running as a dedicated server — FiresSteamworksPatcher will not patch any assemblies. Both patches (Steam recv-buffer enum members and ZDOMan.SendZDOs queue cap) only affect server-side network behavior, so the patcher is no-op on client installs even when FGN is present.");
					return Array.Empty<string>();
				}
				if (!IsGhettoNetworkingInstalled())
				{
					Log.LogInfo((object)"FiresGhettoNetworking not detected in BepInEx/plugins/ — FiresSteamworksPatcher will not patch any assemblies. This patcher exists to support FiresGhettoNetworking's send-buffer + queue-cap features and has no other effect; without FGN there's nothing to do.");
					return Array.Empty<string>();
				}
				return new string[2] { "com.rlabrecque.steamworks.net.dll", "assembly_valheim.dll" };
			}
		}

		public static void Patch(AssemblyDefinition assembly)
		{
			AssemblyNameDefinition name = assembly.Name;
			string text = ((name != null) ? ((AssemblyNameReference)name).Name : null) ?? "<unknown>";
			if (!IsDedicatedServer())
			{
				Log.LogInfo((object)(text + ": not a dedicated server, leaving assembly untouched."));
				return;
			}
			if (!IsGhettoNetworkingInstalled())
			{
				Log.LogInfo((object)(text + ": FGN not present, leaving assembly untouched."));
				return;
			}
			try
			{
				ModuleDefinition mainModule = assembly.MainModule;
				TypeDefinition type = mainModule.GetType("Steamworks.ESteamNetworkingConfigValue");
				if (type != null)
				{
					AddMissingRecvBufferEnumMembers(type, text);
				}
				TypeDefinition type2 = mainModule.GetType("ZDOMan");
				if (type2 != null)
				{
					RaiseSendZdosQueueCap(type2, text);
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"{text}: patch failed, server will run with vanilla behavior: {arg}");
			}
		}

		private static bool IsDedicatedServer()
		{
			if (_isDedicatedServer.HasValue)
			{
				return _isDedicatedServer.Value;
			}
			try
			{
				string processName = Process.GetCurrentProcess().ProcessName;
				_isDedicatedServer = !string.IsNullOrEmpty(processName) && processName.IndexOf("server", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			catch (Exception ex)
			{
				Log.LogInfo((object)("Could not read process name (" + ex.GetType().Name + ": " + ex.Message + "); treating as 'not a server'."));
				_isDedicatedServer = false;
			}
			return _isDedicatedServer.Value;
		}

		private static bool IsGhettoNetworkingInstalled()
		{
			if (_ghettoNetworkingPresent.HasValue)
			{
				return _ghettoNetworkingPresent.Value;
			}
			try
			{
				string pluginPath = Paths.PluginPath;
				if (string.IsNullOrEmpty(pluginPath) || !Directory.Exists(pluginPath))
				{
					_ghettoNetworkingPresent = false;
					return false;
				}
				string[] files = Directory.GetFiles(pluginPath, "*GhettoNetwork*.dll", SearchOption.AllDirectories);
				_ghettoNetworkingPresent = files.Length != 0;
			}
			catch (Exception ex)
			{
				Log.LogInfo((object)("Could not scan plugins directory (" + ex.GetType().Name + ": " + ex.Message + "); treating as 'FGN not present'."));
				_ghettoNetworkingPresent = false;
			}
			return _ghettoNetworkingPresent.Value;
		}

		private static void AddMissingRecvBufferEnumMembers(TypeDefinition enumType, string assemblyName)
		{
			int num = 0;
			int num2 = 0;
			(string, int)[] recvBufferEnumMembers = RecvBufferEnumMembers;
			for (int i = 0; i < recvBufferEnumMembers.Length; i++)
			{
				(string, int) tuple = recvBufferEnumMembers[i];
				if (EnumHasMember(enumType, tuple.Item1))
				{
					num2++;
					continue;
				}
				enumType.Fields.Add(BuildEnumLiteral(enumType, tuple.Item1, tuple.Item2));
				num++;
			}
			Log.LogInfo((object)$"{assemblyName}: Steamworks enum — added {num}, {num2} already present.");
		}

		private static FieldDefinition BuildEnumLiteral(TypeDefinition enumType, string name, int value)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			return new FieldDefinition(name, (FieldAttributes)32854, (TypeReference)(object)enumType)
			{
				Constant = value
			};
		}

		private static bool EnumHasMember(TypeDefinition enumType, string name)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			Enumerator<FieldDefinition> enumerator = enumType.Fields.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					FieldDefinition current = enumerator.Current;
					if (((MemberReference)current).Name == name)
					{
						return true;
					}
				}
			}
			finally
			{
				((IDisposable)enumerator).Dispose();
			}
			return false;
		}

		private static void RaiseSendZdosQueueCap(TypeDefinition zdoManType, string assemblyName)
		{
			MethodDefinition val = FindMethodByName(zdoManType, "SendZDOs");
			if (val == null)
			{
				Log.LogWarning((object)(assemblyName + ": ZDOMan.SendZDOs not found; queue cap patch skipped."));
				return;
			}
			Collection<Instruction> instructions = val.Body.Instructions;
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < instructions.Count; i++)
			{
				if (IsQueueCapConstantAt(instructions, i, out var value))
				{
					if (value >= 102400)
					{
						num2++;
						continue;
					}
					instructions[i].Operand = 102400;
					num++;
				}
			}
			if (num == 0 && num2 == 0)
			{
				Log.LogWarning((object)(assemblyName + ": no queue cap constant matched in ZDOMan.SendZDOs; vanilla behavior in effect."));
				return;
			}
			Log.LogInfo((object)$"{assemblyName}: ZDOMan.SendZDOs queue cap — raised {num} to {102400}, {num2} already at target.");
		}

		private static MethodDefinition FindMethodByName(TypeDefinition type, string name)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			Enumerator<MethodDefinition> enumerator = type.Methods.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					MethodDefinition current = enumerator.Current;
					if (((MemberReference)current).Name == name)
					{
						return current;
					}
				}
			}
			finally
			{
				((IDisposable)enumerator).Dispose();
			}
			return null;
		}

		private static bool IsQueueCapConstantAt(Collection<Instruction> instructions, int index, out int value)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			value = 0;
			Instruction val = instructions[index];
			if (val.OpCode != OpCodes.Ldc_I4)
			{
				return false;
			}
			value = (int)val.Operand;
			if (value < 10240)
			{
				return false;
			}
			return IsNearGetSendQueueSizeCall(instructions, index);
		}

		private static bool IsNearGetSendQueueSizeCall(Collection<Instruction> instructions, int center)
		{
			int num = Math.Max(0, center - 15);
			int num2 = Math.Min(instructions.Count, center + 15 + 1);
			for (int i = num; i < num2; i++)
			{
				if (IsCallTo(instructions[i], "GetSendQueueSize"))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsCallTo(Instruction instruction, string methodName)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt)
			{
				return false;
			}
			object operand = instruction.Operand;
			MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null);
			return val != null && ((MemberReference)val).Name == methodName;
		}
	}
}