#define DEBUG
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Extensions;
using Microsoft.CodeAnalysis;
using Steamworks;
using UnityEngine;
using VBNetTweaks.Utils;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("VBNetTweaks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("VBNetTweaks")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d9c87954-ce20-459d-81ec-96599caed427")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 VBNetTweaks
{
public static class AdaptiveThrottler
{
private class PeerStats
{
public float lastPing;
public float lastPingTime;
public int missedPings;
}
private static float _lastEvalTime;
private static float _currentInterval = 0.05f;
private const float LowPingThreshold = 0.15f;
private const float HighPingThreshold = 0.6f;
private const float MinInterval = 0.03f;
private const float MaxInterval = 0.1f;
private const float EvalInterval = 1f;
private static readonly Dictionary<ZNetPeer, PeerStats> _peerStats = new Dictionary<ZNetPeer, PeerStats>();
public static void Update(ZNet znet, float dt)
{
if (!Object.op_Implicit((Object)(object)znet) || !Helper.IsServer())
{
return;
}
_lastEvalTime += dt;
if (_lastEvalTime < 1f)
{
return;
}
_lastEvalTime = 0f;
List<ZNetPeer> peers = znet.m_peers;
if (peers == null || peers.Count == 0)
{
_currentInterval = VBNetTweaks.SendInterval?.Value ?? 0.05f;
return;
}
float time = Time.time;
float num = 0f;
float num2 = 0f;
int num3 = 0;
for (int i = 0; i < peers.Count; i++)
{
ZNetPeer val = peers[i];
if (val?.m_rpc == null)
{
continue;
}
if (!_peerStats.TryGetValue(val, out var value))
{
value = new PeerStats();
_peerStats[val] = value;
}
float timeSinceLastPing = val.m_rpc.GetTimeSinceLastPing();
if (timeSinceLastPing < 10f)
{
if (timeSinceLastPing > value.lastPing * 2f)
{
value.missedPings++;
}
else
{
value.missedPings = Mathf.Max(0, value.missedPings - 1);
}
value.lastPing = timeSinceLastPing;
value.lastPingTime = time;
num3++;
}
else if (time - value.lastPingTime > 5f)
{
value.missedPings += 2;
}
if (time - value.lastPingTime < 3f)
{
if (timeSinceLastPing > num)
{
num = timeSinceLastPing;
}
num2 += (float)value.missedPings;
}
}
if (num3 == 0)
{
_currentInterval = VBNetTweaks.SendInterval?.Value ?? 0.05f;
return;
}
num2 /= (float)num3;
float num4 = VBNetTweaks.SendInterval?.Value ?? 0.05f;
float num5 = num4;
if (num < 0.15f)
{
num5 = Mathf.Max(0.03f, num4 * 0.7f);
}
else if (num > 0.6f)
{
num5 = Mathf.Min(0.1f, num4 * 1.5f);
}
if (num2 > 2f)
{
num5 = Mathf.Min(0.1f, num5 * 1.3f);
}
else if (num2 < 0.5f && num < 0.6f)
{
num5 = Mathf.Max(0.03f, num5 * 0.9f);
}
_currentInterval = num5;
if (VBNetTweaks.DebugEnabled.Value)
{
VBNetTweaks.LogDebug($"AdaptiveThrottler: ping={num:0.000}s loss={num2:F1} " + $"base={num4:0.000}s -> interval={_currentInterval:0.000}s");
}
}
public static void OnPeerDisconnected(ZNetPeer peer)
{
_peerStats.Remove(peer);
}
public static float GetInterval(float fallback)
{
return (_currentInterval > 0f) ? _currentInterval : fallback;
}
}
public interface ICompressor
{
byte[] Compress(byte[] data);
byte[] Decompress(byte[] data);
}
public class DeflateCompressor : ICompressor
{
public byte[] Compress(byte[] data)
{
if (data.Length < 32)
{
return data;
}
using MemoryStream memoryStream = new MemoryStream();
using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Fastest))
{
deflateStream.Write(data, 0, data.Length);
}
byte[] array = memoryStream.ToArray();
return (array.Length < data.Length) ? array : data;
}
public byte[] Decompress(byte[] data)
{
using MemoryStream stream = new MemoryStream(data);
using DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress);
using MemoryStream memoryStream = new MemoryStream();
deflateStream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
public class ZstdCompressor : ICompressor
{
private dynamic _compressor;
private dynamic _decompressor;
public ZstdCompressor()
{
try
{
Assembly assembly = Assembly.Load("ZstdSharp");
Type type = assembly.GetType("ZstdSharp.Compressor");
Type type2 = assembly.GetType("ZstdSharp.Decompressor");
_compressor = Activator.CreateInstance(type, 1);
_decompressor = Activator.CreateInstance(type2);
}
catch
{
throw new Exception("ZstdSharp not available");
}
}
public byte[] Compress(byte[] data)
{
if (data.Length < 32)
{
return data;
}
dynamic val = _compressor.Wrap(data);
return (val is byte[] array) ? array : val.ToArray();
}
public byte[] Decompress(byte[] data)
{
dynamic val = _decompressor.Unwrap(data);
return (val is byte[] array) ? array : val.ToArray();
}
}
[HarmonyPatch]
public static class PlayerCache
{
private static List<Player> _cachedPlayers = new List<Player>();
private static Dictionary<long, Player> _playersById = new Dictionary<long, Player>();
private static Dictionary<long, bool> _playerAttachedState = new Dictionary<long, bool>();
private static Dictionary<long, ZDOID> _playerShipMap = new Dictionary<long, ZDOID>();
private static int _cachedFrame = -1;
private static float _cachedTime = -1f;
public static List<Player> GetAll()
{
return GetCached();
}
public static List<Player> GetCurrentFrame()
{
return GetCached(0f);
}
public static List<Player> GetCached(float maxAgeSeconds = 0.5f)
{
if (Time.time - _cachedTime > maxAgeSeconds || _cachedFrame != Time.frameCount)
{
RefreshCache();
_cachedTime = Time.time;
_cachedFrame = Time.frameCount;
}
return _cachedPlayers;
}
public static Player GetById(long id)
{
if (_cachedFrame != Time.frameCount)
{
RefreshCache();
}
Player value;
return _playersById.TryGetValue(id, out value) ? value : null;
}
public static bool IsPlayerOnShip(long playerId)
{
return _playerShipMap.ContainsKey(playerId);
}
public static bool IsPlayerAttached(long playerId)
{
bool value;
return _playerAttachedState.TryGetValue(playerId, out value) && value;
}
public static ZDOID GetPlayerShip(long playerId)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
ZDOID value;
return (ZDOID)(_playerShipMap.TryGetValue(playerId, out value) ? value : default(ZDOID));
}
public static void UpdatePlayerState(long playerId, bool attached, ZDOID shipId)
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
_playerAttachedState[playerId] = attached;
if (((ZDOID)(ref shipId)).IsNone())
{
_playerShipMap.Remove(playerId);
}
else
{
_playerShipMap[playerId] = shipId;
}
}
public static void RemovePlayer(long playerId)
{
_playersById.Remove(playerId);
_playerAttachedState.Remove(playerId);
_playerShipMap.Remove(playerId);
}
private static void RefreshCache()
{
_cachedPlayers.Clear();
_playersById.Clear();
List<Player> allPlayers = Player.GetAllPlayers();
_cachedPlayers.AddRange(allPlayers);
foreach (Player item in allPlayers)
{
if (Object.op_Implicit((Object)(object)item))
{
long playerID = item.GetPlayerID();
_playersById[playerID] = item;
}
}
if (VBNetTweaks.DebugEnabled.Value && VBNetTweaks.VerboseLogging.Value)
{
VBNetTweaks.LogVerbose($"PlayerCache refreshed: {_cachedPlayers.Count} players");
}
}
public static void Invalidate()
{
_cachedFrame = -1;
_cachedTime = -1f;
}
[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
[HarmonyPostfix]
private static void OnNewConnection()
{
Invalidate();
}
[HarmonyPatch(typeof(ZNet), "Disconnect")]
[HarmonyPostfix]
private static void OnDisconnect(ZNetPeer peer)
{
Invalidate();
if (peer != null)
{
RemovePlayer(peer.m_uid);
}
}
}
[HarmonyPatch]
public static class PlayerSyncSystem
{
private class PD
{
public Vector3 pos;
public Quaternion rot;
public Vector3 vel;
public float t;
public bool ok;
}
private static readonly Dictionary<long, PD> _playerData = new Dictionary<long, PD>();
private static readonly Stack<PD> _pdPool = new Stack<PD>();
private static PD GetOrCreatePD()
{
if (_pdPool.Count > 0)
{
return _pdPool.Pop();
}
return new PD();
}
private static void ReturnPD(PD pd)
{
pd.ok = false;
_pdPool.Push(pd);
}
public static void CleanupPeer(long uid)
{
if (_playerData.TryGetValue(uid, out var value))
{
ReturnPD(value);
}
_playerData.Remove(uid);
PlayerCache.RemovePlayer(uid);
}
[HarmonyPatch(typeof(ZNetView), "Deserialize")]
[HarmonyPostfix]
public static void CapturePlayerState(ZNetView __instance)
{
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_0118: Unknown result type (might be due to invalid IL or missing references)
//IL_0119: Unknown result type (might be due to invalid IL or missing references)
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
if (Helper.IsServer() || !Object.op_Implicit((Object)(object)__instance))
{
return;
}
Player component = ((Component)__instance).GetComponent<Player>();
if (!Object.op_Implicit((Object)(object)component))
{
return;
}
ZDO zDO = __instance.GetZDO();
if (zDO == null || !zDO.IsValid())
{
return;
}
long owner = zDO.GetOwner();
if (owner == 0L || owner == ZNet.GetUID())
{
return;
}
Vector3 position = zDO.GetPosition();
Quaternion rotation = zDO.GetRotation();
if (!_playerData.TryGetValue(owner, out var value))
{
value = GetOrCreatePD();
value.pos = position;
value.rot = rotation;
value.t = Time.time;
value.ok = true;
_playerData[owner] = value;
return;
}
float num = Time.time - value.t;
if (num > 0f)
{
value.vel = (position - value.pos) / num;
}
value.pos = position;
value.rot = rotation;
value.t = Time.time;
value.ok = true;
}
[HarmonyPatch(typeof(Player), "LateUpdate")]
[HarmonyPostfix]
public static void SmoothRemotePlayers(Player __instance)
{
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
//IL_0104: Unknown result type (might be due to invalid IL or missing references)
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_0142: Unknown result type (might be due to invalid IL or missing references)
//IL_0149: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer || Helper.IsServer())
{
return;
}
long playerID = __instance.GetPlayerID();
if (!_playerData.TryGetValue(playerID, out var value) || !value.ok)
{
return;
}
bool flag = PlayerCache.IsPlayerAttached(playerID);
bool flag2 = PlayerCache.IsPlayerOnShip(playerID);
if (!(flag || flag2))
{
if (VBNetTweaks.EnablePlayerPrediction.Value)
{
float num = Time.deltaTime * 1.5f;
Vector3 val = value.pos + value.vel * num;
((Component)__instance).transform.position = Vector3.Lerp(((Component)__instance).transform.position, val, 0.8f);
}
if (VBNetTweaks.EnableClientInterpolation.Value)
{
float num2 = Mathf.Clamp01(Time.deltaTime * 12f);
Vector3 val2 = value.pos + value.vel * Time.deltaTime * 0.5f;
((Component)__instance).transform.position = Vector3.Lerp(((Component)__instance).transform.position, val2, num2);
((Component)__instance).transform.rotation = Quaternion.Slerp(((Component)__instance).transform.rotation, value.rot, num2);
}
}
}
[HarmonyPatch(typeof(ZNet), "Disconnect")]
[HarmonyPostfix]
public static void OnPeerDisconnect(ZNetPeer peer)
{
if (peer != null)
{
long uid = peer.m_uid;
CleanupPeer(uid);
AdaptiveThrottler.OnPeerDisconnected(peer);
}
}
}
public static class RpcBatcher
{
private class RpcEntry
{
public string Name;
public object[] Args;
public int Priority;
}
private static readonly Dictionary<ZNetView, List<RpcEntry>> rpcQueue = new Dictionary<ZNetView, List<RpcEntry>>();
private static readonly object _lock = new object();
private static float lastSendTime;
private const float SendInterval = 0.05f;
public static void Enqueue(ZNetView nview, string rpcName, int priority, params object[] args)
{
if (!Object.op_Implicit((Object)(object)nview) || !nview.IsValid())
{
return;
}
lock (_lock)
{
if (!rpcQueue.TryGetValue(nview, out var value))
{
value = new List<RpcEntry>();
rpcQueue[nview] = value;
}
value.Add(new RpcEntry
{
Name = rpcName,
Args = args,
Priority = priority
});
}
}
public static void Update()
{
float time = Time.time;
if (time - lastSendTime < 0.05f)
{
return;
}
lastSendTime = time;
Dictionary<ZNetView, List<RpcEntry>> dictionary;
lock (_lock)
{
if (rpcQueue.Count == 0)
{
return;
}
dictionary = new Dictionary<ZNetView, List<RpcEntry>>(rpcQueue.Count);
foreach (KeyValuePair<ZNetView, List<RpcEntry>> item in rpcQueue)
{
dictionary[item.Key] = new List<RpcEntry>(item.Value);
item.Value.Clear();
}
}
foreach (KeyValuePair<ZNetView, List<RpcEntry>> item2 in dictionary)
{
ZNetView key = item2.Key;
List<RpcEntry> value = item2.Value;
if (!Object.op_Implicit((Object)(object)key) || !key.IsValid() || value.Count == 0)
{
continue;
}
value.Sort((RpcEntry a, RpcEntry b) => b.Priority.CompareTo(a.Priority));
ZPackage val = ObjectPool.RentPackage();
try
{
val.Write(value.Count);
foreach (RpcEntry item3 in value)
{
val.Write(item3.Name);
val.Write(item3.Args.Length);
for (int i = 0; i < item3.Args.Length; i++)
{
RpcSerializer.WriteArg(val, item3.Args[i]);
}
}
key.InvokeRPC("VBNT_RPCBatch", new object[1] { val });
}
finally
{
ObjectPool.ReturnPackage(val);
}
}
}
public static void HandleBatch(long sender, ZPackage pkg)
{
int num = pkg.ReadInt();
for (int i = 0; i < num; i++)
{
string text = pkg.ReadString();
int num2 = pkg.ReadInt();
object[] array = new object[num2];
for (int j = 0; j < num2; j++)
{
array[j] = RpcSerializer.ReadArg(pkg);
}
if (VBNetTweaks.DebugEnabled.Value && VBNetTweaks.VerboseLogging.Value)
{
VBNetTweaks.LogDebug($"RpcBatcher: executing {text} ({num2} args)");
}
ZRoutedRpc.instance.InvokeRoutedRPC(sender, text, array);
}
}
}
[HarmonyPatch]
public static class ShipSyncSystem
{
private class ShipData
{
public Vector3 pos;
public Quaternion rot;
public Vector3 vel;
public float t;
public bool ok;
}
private class PlayerShipState
{
public Ship ship;
public Vector3 localPos;
public Quaternion localRot;
public float lastUpdate;
public bool isAttached;
}
private static readonly Dictionary<long, ShipData> _shipData;
private static readonly Dictionary<long, PlayerShipState> _playerStates;
private static readonly Dictionary<ZDOID, int> _playersOnShip;
private static readonly Dictionary<long, ZDOID> _playerShipMap;
private const string RPC_SYNC_ATTACHMENT = "VBNT.SyncAttachment";
public static bool ShipHasPlayers(ZDOID shipId)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
return _playersOnShip.ContainsKey(shipId);
}
public static int GetPlayersOnShipCount(ZDOID shipId)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
int value;
return _playersOnShip.TryGetValue(shipId, out value) ? value : 0;
}
public static bool IsPlayerOnShip(long playerId)
{
return PlayerCache.IsPlayerOnShip(playerId);
}
public static bool IsPlayerAttached(long playerId)
{
return PlayerCache.IsPlayerAttached(playerId);
}
static ShipSyncSystem()
{
_shipData = new Dictionary<long, ShipData>();
_playerStates = new Dictionary<long, PlayerShipState>();
_playersOnShip = new Dictionary<ZDOID, int>();
_playerShipMap = new Dictionary<long, ZDOID>();
if (ZRoutedRpc.instance != null)
{
ZRoutedRpc.instance.Register<long, bool, ZDOID, string>("VBNT.SyncAttachment", (Method<long, bool, ZDOID, string>)RPC_SyncAttachment);
}
}
private static void SyncAttachment(long playerId, bool attached, ZDOID shipId, string attachPointName = "")
{
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
if (Object.op_Implicit((Object)(object)ZNet.instance))
{
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "VBNT.SyncAttachment", new object[4] { playerId, attached, shipId, attachPointName });
PlayerCache.UpdatePlayerState(playerId, attached, shipId);
}
}
private static void RPC_SyncAttachment(long sender, long playerId, bool attached, ZDOID shipId, string attachPointName)
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
if (sender == ZNet.GetUID())
{
return;
}
Player byId = PlayerCache.GetById(playerId);
if (!Object.op_Implicit((Object)(object)byId))
{
VBNetTweaks.LogDebug($"RPC_SyncAttachment: player {playerId} not found");
return;
}
PlayerCache.UpdatePlayerState(playerId, attached, shipId);
if (attached)
{
GameObject val = ZNetScene.instance.FindInstance(shipId);
if (!Object.op_Implicit((Object)(object)val))
{
return;
}
Ship component = val.GetComponent<Ship>();
if (Object.op_Implicit((Object)(object)component))
{
Transform val2 = FindAttachPoint(component, attachPointName);
if (Object.op_Implicit((Object)(object)val2) && !((Character)byId).IsAttached())
{
((Character)byId).AttachStart(val2, (GameObject)null, false, false, true, "attach_chair", Vector3.zero, (Transform)null);
}
}
}
else if (((Character)byId).IsAttached())
{
((Character)byId).AttachStop();
}
}
public static void CleanupPeer(long uid)
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
_shipData.Remove(uid);
if (_playerShipMap.TryGetValue(uid, out var value))
{
if (_playersOnShip.TryGetValue(value, out var value2))
{
if (value2 <= 1)
{
_playersOnShip.Remove(value);
}
else
{
_playersOnShip[value] = value2 - 1;
}
}
_playerShipMap.Remove(uid);
}
_playerStates.Remove(uid);
PlayerCache.RemovePlayer(uid);
}
private static Transform FindAttachPoint(Ship ship, string pointName)
{
if (!Object.op_Implicit((Object)(object)ship))
{
return null;
}
ShipControlls componentInChildren = ((Component)ship).GetComponentInChildren<ShipControlls>();
if (Object.op_Implicit((Object)(object)componentInChildren) && Object.op_Implicit((Object)(object)componentInChildren.m_attachPoint))
{
return componentInChildren.m_attachPoint;
}
Chair[] componentsInChildren = ((Component)ship).GetComponentsInChildren<Chair>();
Chair[] array = componentsInChildren;
foreach (Chair val in array)
{
if (Object.op_Implicit((Object)(object)val.m_attachPoint))
{
return val.m_attachPoint;
}
}
if (!string.IsNullOrEmpty(pointName))
{
Transform val2 = ((Component)ship).transform.Find(pointName);
if (Object.op_Implicit((Object)(object)val2))
{
return val2;
}
}
return null;
}
private static Ship GetShipUnderPlayer(Player p)
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
Vector3 val = ((Component)p).transform.position + Vector3.up * 0.2f;
RaycastHit val2 = default(RaycastHit);
if (Physics.Raycast(val, Vector3.down, ref val2, 2f))
{
return ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<Ship>();
}
return null;
}
private static bool IsPlayerAttached(Player p)
{
return Object.op_Implicit((Object)(object)p) && ((Character)p).IsAttached();
}
private static bool IsShipZDO(ZDO zdo)
{
int prefab = zdo.GetPrefab();
return prefab == StringExtensionMethods.GetStableHashCode("Karve") || prefab == StringExtensionMethods.GetStableHashCode("VikingShip") || prefab == StringExtensionMethods.GetStableHashCode("Raft") || prefab == StringExtensionMethods.GetStableHashCode("VikingShip_Ashlands");
}
[HarmonyPatch(typeof(ZNetView), "Deserialize")]
[HarmonyPostfix]
public static void CaptureShipState(ZNetView __instance)
{
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_012e: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_0115: Unknown result type (might be due to invalid IL or missing references)
//IL_011a: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
//IL_0126: Unknown result type (might be due to invalid IL or missing references)
if (Helper.IsServer() || !Object.op_Implicit((Object)(object)__instance))
{
return;
}
Ship component = ((Component)__instance).GetComponent<Ship>();
if (!Object.op_Implicit((Object)(object)component))
{
return;
}
ZDO zDO = __instance.GetZDO();
if (zDO == null || !zDO.IsValid())
{
return;
}
long owner = zDO.GetOwner();
if (owner == 0 || owner == ZNet.GetUID())
{
return;
}
Vector3 position = zDO.GetPosition();
Quaternion rotation = zDO.GetRotation();
if (!_shipData.TryGetValue(owner, out var value))
{
value = new ShipData
{
pos = position,
rot = rotation,
t = Time.time,
ok = true
};
_shipData[owner] = value;
return;
}
float num = Time.time - value.t;
if (num > 0f)
{
value.vel = (position - value.pos) / num;
}
value.pos = position;
value.rot = rotation;
value.t = Time.time;
value.ok = true;
}
[HarmonyPatch(typeof(Ship), "CustomFixedUpdate")]
[HarmonyPostfix]
public static void SmoothShip(Ship __instance)
{
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
if (Helper.IsServer())
{
return;
}
ZNetView nview = __instance.m_nview;
ZDO val = ((nview != null) ? nview.GetZDO() : null);
if (val != null && IsShipZDO(val))
{
long owner = val.GetOwner();
if (_shipData.TryGetValue(owner, out var value) && value.ok)
{
Transform transform = ((Component)__instance).transform;
Vector3 val2 = value.pos + value.vel * Time.deltaTime;
transform.position = Vector3.Lerp(transform.position, val2, 0.25f);
transform.rotation = Quaternion.Slerp(transform.rotation, value.rot, 0.15f);
}
}
}
[HarmonyPatch(typeof(Player), "Update")]
[HarmonyPostfix]
public static void TrackLocalPlayer(Player __instance)
{
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_0223: Unknown result type (might be due to invalid IL or missing references)
//IL_0228: Unknown result type (might be due to invalid IL or missing references)
//IL_022d: Unknown result type (might be due to invalid IL or missing references)
//IL_023a: Unknown result type (might be due to invalid IL or missing references)
//IL_023f: Unknown result type (might be due to invalid IL or missing references)
//IL_024a: Unknown result type (might be due to invalid IL or missing references)
//IL_024f: Unknown result type (might be due to invalid IL or missing references)
//IL_0254: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_010d: Unknown result type (might be due to invalid IL or missing references)
//IL_017d: Unknown result type (might be due to invalid IL or missing references)
//IL_0182: Unknown result type (might be due to invalid IL or missing references)
//IL_0189: Unknown result type (might be due to invalid IL or missing references)
//IL_0190: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0130: Unknown result type (might be due to invalid IL or missing references)
//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
{
return;
}
long playerID = __instance.GetPlayerID();
PlayerShipState value;
Ship val = (_playerStates.TryGetValue(playerID, out value) ? value.ship : null);
Ship shipUnderPlayer = GetShipUnderPlayer(__instance);
bool flag = IsPlayerAttached(__instance);
if ((Object)(object)val == (Object)(object)shipUnderPlayer && Object.op_Implicit((Object)(object)shipUnderPlayer))
{
if (_playerStates.TryGetValue(playerID, out var value2))
{
value2.localPos = ((Component)shipUnderPlayer).transform.InverseTransformPoint(((Component)__instance).transform.position);
value2.localRot = Quaternion.Inverse(((Component)shipUnderPlayer).transform.rotation) * ((Component)__instance).transform.rotation;
value2.isAttached = flag;
value2.lastUpdate = Time.time;
}
return;
}
if (Object.op_Implicit((Object)(object)val))
{
ZNetView nview = val.m_nview;
ZDO val2 = ((nview != null) ? nview.GetZDO() : null);
if (val2 != null)
{
ZDOID uid = val2.m_uid;
if (_playersOnShip.TryGetValue(uid, out var value3))
{
if (value3 <= 1)
{
_playersOnShip.Remove(uid);
}
else
{
_playersOnShip[uid] = value3 - 1;
}
}
}
}
if (Object.op_Implicit((Object)(object)shipUnderPlayer))
{
ZNetView nview2 = shipUnderPlayer.m_nview;
ZDO val3 = ((nview2 != null) ? nview2.GetZDO() : null);
if (val3 != null)
{
ZDOID uid2 = val3.m_uid;
_playersOnShip[uid2] = ((!_playersOnShip.TryGetValue(uid2, out var value4)) ? 1 : (value4 + 1));
_playerShipMap[playerID] = uid2;
}
}
if (!_playerStates.TryGetValue(playerID, out var value5))
{
value5 = new PlayerShipState();
_playerStates[playerID] = value5;
}
bool isAttached = value5.isAttached;
bool flag2 = (Object)(object)val != (Object)(object)shipUnderPlayer;
value5.ship = shipUnderPlayer;
value5.isAttached = flag;
if (Object.op_Implicit((Object)(object)shipUnderPlayer))
{
value5.localPos = ((Component)shipUnderPlayer).transform.InverseTransformPoint(((Component)__instance).transform.position);
value5.localRot = Quaternion.Inverse(((Component)shipUnderPlayer).transform.rotation) * ((Component)__instance).transform.rotation;
}
value5.lastUpdate = Time.time;
if (flag2 || isAttached != flag)
{
ZDOID? obj;
if (shipUnderPlayer == null)
{
obj = null;
}
else
{
ZNetView nview3 = shipUnderPlayer.m_nview;
obj = ((nview3 == null) ? null : nview3.GetZDO()?.m_uid);
}
ZDOID? val4 = obj;
ZDOID valueOrDefault = val4.GetValueOrDefault();
SyncAttachment(playerID, flag, valueOrDefault);
}
}
[HarmonyPatch(typeof(Player), "LateUpdate")]
[HarmonyPostfix]
public static void ApplyShipCompensation(Player __instance)
{
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)__instance == (Object)(object)Player.m_localPlayer))
{
long playerID = __instance.GetPlayerID();
if (_playerStates.TryGetValue(playerID, out var value) && Object.op_Implicit((Object)(object)value.ship))
{
float num = 15f;
Vector3 val = ((Component)value.ship).transform.TransformPoint(value.localPos);
Quaternion val2 = ((Component)value.ship).transform.rotation * value.localRot;
((Component)__instance).transform.position = Vector3.Lerp(((Component)__instance).transform.position, val, Time.deltaTime * num);
((Component)__instance).transform.rotation = Quaternion.Slerp(((Component)__instance).transform.rotation, val2, Time.deltaTime * num);
}
}
}
}
[HarmonyPatch]
public static class ZDONetworkOptimizer
{
private class PeerCompressionStatus
{
public int Version { get; set; }
public bool PeerEnabled { get; set; }
public bool SendingCompressed { get; set; }
public bool ReceivingCompressed { get; set; }
public bool IsCompatible => Version == 1;
}
private static readonly Dictionary<ZDO, float> _distanceCache = new Dictionary<ZDO, float>();
private static Vector3 _cachedRefPos;
private static int _cachedFrame = -1;
private const int MaxZDOsPerTick = 350;
private const float MobNearDistance = 40f;
private const float MobMediumDistance = 100f;
private const float ImportantObjectDistance = 200f;
private static readonly int PlayerPrefab = StringExtensionMethods.GetStableHashCode("Player");
private static readonly HashSet<int> ShipPrefabs = new HashSet<int>
{
StringExtensionMethods.GetStableHashCode("Karve"),
StringExtensionMethods.GetStableHashCode("VikingShip"),
StringExtensionMethods.GetStableHashCode("Raft"),
StringExtensionMethods.GetStableHashCode("VikingShip_Ashlands")
};
private static readonly HashSet<int> ImportantPrefabs = new HashSet<int>
{
StringExtensionMethods.GetStableHashCode("portal_wood"),
StringExtensionMethods.GetStableHashCode("portal_stone"),
StringExtensionMethods.GetStableHashCode("piece_workbench"),
StringExtensionMethods.GetStableHashCode("piece_bed"),
StringExtensionMethods.GetStableHashCode("piece_chest")
};
private static readonly int HashAI = StringExtensionMethods.GetStableHashCode("ai");
private const int COMPRESSION_VERSION = 1;
private const string RPC_VERSION = "VBNT.CompressionVersion";
private const string RPC_ENABLED = "VBNT.CompressionEnabled";
private const string RPC_STARTED = "VBNT.CompressionStarted";
private static ICompressor _compressor;
private static readonly Dictionary<ISocket, PeerCompressionStatus> _peerStatus = new Dictionary<ISocket, PeerCompressionStatus>();
public static void Initialize()
{
if (!VBNetTweaks.EnableNetworkCompression.Value)
{
return;
}
try
{
string value = VBNetTweaks.CompressionAlgorithm.Value;
if (value.Equals("Zstd", StringComparison.OrdinalIgnoreCase))
{
_compressor = new ZstdCompressor();
VBNetTweaks.LogDebug("Using Zstd compressor");
}
else
{
_compressor = new DeflateCompressor();
VBNetTweaks.LogDebug("Using Deflate compressor");
}
}
catch (Exception ex)
{
VBNetTweaks.LogDebug("Compression init failed: " + ex.Message + ", using Deflate fallback");
try
{
_compressor = new DeflateCompressor();
}
catch
{
}
}
}
public static void OptimizedSendZDOToPeers(ZDOMan zdoManager, float dt)
{
try
{
int count = zdoManager.m_peers.Count;
if (count <= 0)
{
return;
}
ZDOMan obj = zdoManager;
obj.m_sendTimer += dt;
float effectiveSendInterval = VBNetTweaks.GetEffectiveSendInterval();
if (zdoManager.m_sendTimer < effectiveSendInterval)
{
return;
}
zdoManager.m_sendTimer = 0f;
int num = Mathf.Max(zdoManager.m_nextSendPeer, 0);
int peersPerUpdate = VBNetTweaks.GetPeersPerUpdate();
int num2 = 0;
for (int i = 0; i < Mathf.Min(peersPerUpdate, count); i++)
{
int index = (num + i) % count;
ZDOPeer peer = zdoManager.m_peers[index];
ZDOPeer obj2 = peer;
if (obj2 == null)
{
continue;
}
ZNetPeer peer2 = obj2.m_peer;
bool? obj3;
if (peer2 == null)
{
obj3 = null;
}
else
{
ISocket socket = peer2.m_socket;
obj3 = ((socket != null) ? new bool?(socket.IsConnected()) : null);
}
if (obj3 == true)
{
if (Helper.IsServer() && VBNetTweaks.EnableZDOThrottling.Value)
{
ApplyZDOThrottle(zdoManager, peer);
}
PerformanceMonitor.Track("SendZDOs", delegate
{
zdoManager.SendZDOs(peer, false);
});
num2++;
}
}
zdoManager.m_nextSendPeer = (num + num2) % count;
}
catch (Exception ex)
{
VBNetTweaks.LogDebug("ERROR in OptimizedSendZDOToPeers: " + ex.Message);
zdoManager.SendZDOToPeers2(dt);
}
}
private static void ApplyZDOThrottle(ZDOMan zdoManager, ZDOPeer peer)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: 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)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
List<ZDO> list = null;
List<ZDO> list2 = null;
try
{
Vector3 refPos = peer.m_peer.GetRefPos();
Vector2i zone = ZoneSystem.GetZone(refPos);
list = ObjectPool.RentList<ZDO>();
list2 = ObjectPool.RentList<ZDO>();
int num = ZoneSystem.instance?.m_activeArea ?? 3;
int num2 = ZoneSystem.instance?.m_activeDistantArea ?? 5;
zdoManager.FindSectorObjects(zone, num, num2, list, list2);
float value = VBNetTweaks.ZDOThrottleDistance.Value;
foreach (ZDO item in list)
{
float num3 = Vector3.Distance(item.GetPosition(), refPos);
item.m_tempSortValue = num3 - 150f;
}
foreach (ZDO item2 in list2)
{
float num4 = Vector3.Distance(item2.GetPosition(), refPos);
item2.m_tempSortValue = num4 + 150f;
if (num4 > value * 2f)
{
item2.m_tempSortValue += 300f;
}
}
}
finally
{
if (list != null)
{
ObjectPool.ReturnList(list);
}
if (list2 != null)
{
ObjectPool.ReturnList(list2);
}
}
}
private static float GetDistance(ZDO zdo, Vector3 refPos)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
if (_distanceCache.TryGetValue(zdo, out var value))
{
return value;
}
value = Vector3.Distance(zdo.GetPosition(), refPos);
_distanceCache[zdo] = value;
return value;
}
private static bool IsMob(ZDO zdo)
{
return zdo.GetInt(HashAI, -1) != -1;
}
private static void OptimizedRemoveObjects(ZNetScene scene, List<ZDO> near, List<ZDO> distant)
{
byte b = (byte)((uint)Time.frameCount & 0xFFu);
foreach (ZDO item in near)
{
if (item != null)
{
item.TempRemoveEarmark = b;
}
}
foreach (ZDO item2 in distant)
{
if (item2 != null)
{
item2.TempRemoveEarmark = b;
}
}
Dictionary<ZDO, ZNetView> instances = scene.m_instances;
List<ZNetView> tempRemoved = scene.m_tempRemoved;
tempRemoved.Clear();
List<ZDO> list = new List<ZDO>(instances.Keys);
foreach (ZDO item3 in list)
{
if (item3 == null || !instances.TryGetValue(item3, out var value) || (Object)(object)value == (Object)null)
{
instances.Remove(item3);
}
else if (item3.TempRemoveEarmark != b)
{
tempRemoved.Add(value);
}
}
foreach (ZNetView item4 in tempRemoved)
{
if (Object.op_Implicit((Object)(object)item4))
{
ZDO zdo = item4.m_zdo;
if (zdo != null)
{
zdo.Created = false;
item4.m_zdo = null;
}
Object.Destroy((Object)(object)((Component)item4).gameObject);
instances.Remove(zdo);
}
}
}
private static void RegisterCompressionRPCs(ZNetPeer peer)
{
peer.m_rpc.Register<int>("VBNT.CompressionVersion", (Action<ZRpc, int>)delegate(ZRpc rpc, int version)
{
RPC_CompressionVersion(peer, version);
});
peer.m_rpc.Register<bool>("VBNT.CompressionEnabled", (Action<ZRpc, bool>)delegate(ZRpc rpc, bool enabled)
{
RPC_CompressionEnabled(peer, enabled);
});
peer.m_rpc.Register<bool>("VBNT.CompressionStarted", (Action<ZRpc, bool>)delegate(ZRpc rpc, bool started)
{
RPC_CompressionStarted(peer, started);
});
}
private static void SendCompressionVersion(ZNetPeer peer)
{
peer.m_rpc.Invoke("VBNT.CompressionVersion", new object[1] { 1 });
}
private static void RPC_CompressionVersion(ZNetPeer peer, int version)
{
if (_peerStatus.TryGetValue(peer.m_socket, out var value))
{
value.Version = version;
if (value.IsCompatible)
{
VBNetTweaks.LogDebug("Compression compatible with " + GetPeerName(peer));
SendCompressionEnabledStatus(peer);
}
}
}
private static void SendCompressionEnabledStatus(ZNetPeer peer)
{
bool value = VBNetTweaks.EnableNetworkCompression.Value;
peer.m_rpc.Invoke("VBNT.CompressionEnabled", new object[1] { value });
}
private static void RPC_CompressionEnabled(ZNetPeer peer, bool enabled)
{
if (_peerStatus.TryGetValue(peer.m_socket, out var value))
{
value.PeerEnabled = enabled;
bool started = VBNetTweaks.EnableNetworkCompression.Value && enabled && value.IsCompatible;
SendCompressionStarted(peer, started);
}
}
private static void SendCompressionStarted(ZNetPeer peer, bool started)
{
if (_peerStatus.TryGetValue(peer.m_socket, out var value) && value.SendingCompressed != started)
{
peer.m_rpc.Invoke("VBNT.CompressionStarted", new object[1] { started });
peer.m_socket.Flush();
value.SendingCompressed = started;
VBNetTweaks.LogDebug("Compression " + (started ? "started" : "stopped") + " with " + GetPeerName(peer));
}
}
private static void RPC_CompressionStarted(ZNetPeer peer, bool started)
{
if (_peerStatus.TryGetValue(peer.m_socket, out var value))
{
value.ReceivingCompressed = started;
VBNetTweaks.LogDebug("Receiving " + (started ? "compressed" : "uncompressed") + " from " + GetPeerName(peer));
}
}
public static bool ShouldCompressSend(ISocket socket)
{
PeerCompressionStatus value;
return _peerStatus.TryGetValue(socket, out value) && value.SendingCompressed && _compressor != null;
}
public static bool ShouldCompressReceive(ISocket socket)
{
PeerCompressionStatus value;
return _peerStatus.TryGetValue(socket, out value) && value.ReceivingCompressed && _compressor != null;
}
public static byte[] Compress(byte[] data)
{
return _compressor?.Compress(data) ?? data;
}
public static byte[] Decompress(byte[] data)
{
return _compressor?.Decompress(data) ?? data;
}
private static string GetPeerName(ZNetPeer peer)
{
try
{
ISocket socket = peer.m_socket;
return ((socket != null) ? socket.GetEndPointString() : null) ?? peer.m_uid.ToString();
}
catch
{
return peer.m_uid.ToString();
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(ZDOMan), "Update")]
private static IEnumerable<CodeInstruction> ZDOManUpdateTranspiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).Start();
val.MatchStartForward((CodeMatch[])(object)new CodeMatch[1]
{
new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(ZDOMan), "SendZDOToPeers2", (Type[])null, (Type[])null), (string)null)
});
if (val.IsInvalid)
{
VBNetTweaks.LogDebug("WARNING: SendZDOToPeers2 not found");
return instructions;
}
val.SetOperandAndAdvance((object)AccessTools.Method(typeof(ZDONetworkOptimizer), "OptimizedSendZDOToPeers", (Type[])null, (Type[])null));
return val.InstructionEnumeration();
}
[HarmonyPrefix]
[HarmonyPatch(typeof(ZDOMan), "ServerSortSendZDOS")]
public static void ApplyWeights(List<ZDO> objects, Vector3 refPos)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
if (!Helper.IsServer() || !VBNetTweaks.EnablePlayerPositionBoost.Value)
{
return;
}
if (_cachedFrame != Time.frameCount || _cachedRefPos != refPos)
{
_distanceCache.Clear();
_cachedRefPos = refPos;
_cachedFrame = Time.frameCount;
}
foreach (ZDO @object in objects)
{
if (@object == null)
{
continue;
}
int prefab = @object.GetPrefab();
if (prefab == PlayerPrefab)
{
@object.m_tempSortValue -= 500f;
continue;
}
if (ShipPrefabs.Contains(prefab))
{
bool flag = ShipSyncSystem.ShipHasPlayers(@object.m_uid);
@object.m_tempSortValue += (flag ? (-450f) : (-200f));
continue;
}
float distance = GetDistance(@object, refPos);
if (IsMob(@object))
{
if (distance < 40f)
{
@object.m_tempSortValue -= 300f;
}
else if (distance < 100f)
{
@object.m_tempSortValue -= 150f;
}
else
{
@object.m_tempSortValue += distance;
}
}
else if (ImportantPrefabs.Contains(prefab) && distance < 200f)
{
@object.m_tempSortValue -= 150f;
}
else
{
@object.m_tempSortValue += distance;
}
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(ZDOMan), "ServerSortSendZDOS")]
public static void LimitZDOs(List<ZDO> objects)
{
if (Helper.IsServer() && objects.Count > 350)
{
objects.Sort((ZDO a, ZDO b) => a.m_tempSortValue.CompareTo(b.m_tempSortValue));
int num = objects.Count - 350;
objects.RemoveRange(350, num);
if (VBNetTweaks.DebugEnabled.Value)
{
VBNetTweaks.LogVerbose($"ZDO limiter: removed {num} objects");
}
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(ZNetScene), "RemoveObjects")]
private static bool RemoveObjectsPrefix(ZNetScene __instance, List<ZDO> currentNearObjects, List<ZDO> currentDistantObjects)
{
try
{
PerformanceMonitor.Track("RemoveObjects", delegate
{
OptimizedRemoveObjects(__instance, currentNearObjects, currentDistantObjects);
});
return false;
}
catch
{
return true;
}
}
[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
[HarmonyPostfix]
private static void OnNewConnection(ZNetPeer peer)
{
if (_compressor != null)
{
_peerStatus[peer.m_socket] = new PeerCompressionStatus();
RegisterCompressionRPCs(peer);
SendCompressionVersion(peer);
}
}
[HarmonyPatch(typeof(ZNet), "Disconnect")]
[HarmonyPostfix]
private static void OnDisconnect(ZNetPeer peer)
{
_peerStatus.Remove(peer.m_socket);
}
}
[HarmonyPatch]
public static class AILODPatches
{
private static bool ShouldUpdateAI(Character c)
{
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
List<Player> cached = PlayerCache.GetCached();
float num = float.MaxValue;
for (int i = 0; i < cached.Count; i++)
{
Player val = cached[i];
if (Object.op_Implicit((Object)(object)val))
{
float num2 = Vector3.Distance(((Component)c).transform.position, ((Component)val).transform.position);
if (num2 < num)
{
num = num2;
}
}
}
if (num <= VBNetTweaks.AILODNearDistance.Value)
{
return true;
}
if (num > VBNetTweaks.AILODFarDistance.Value)
{
float num3 = Mathf.Clamp(VBNetTweaks.AILODThrottleFactor.Value, 0.25f, 0.75f);
if (Time.time % (1f / num3) > Time.fixedDeltaTime)
{
return false;
}
}
return true;
}
[HarmonyPrefix]
public static bool FixedUpdate_Prefix(Character __instance)
{
if (Helper.IsServer())
{
ConfigEntry<bool> enableAILOD = VBNetTweaks.EnableAILOD;
if (enableAILOD != null && enableAILOD.Value)
{
if (!__instance.IsPlayer())
{
Tameable component = ((Component)__instance).GetComponent<Tameable>();
if (component == null || !component.IsTamed())
{
bool result = true;
PerformanceMonitor.Track("AI.FixedUpdate", delegate
{
result = ShouldUpdateAI(__instance);
});
return result;
}
}
return true;
}
}
return true;
}
}
[HarmonyPatch]
public static class MonsterAiPatches
{
[HarmonyPatch(typeof(RandEventSystem), "FixedUpdate")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> RandEventSystem_FixedUpdate_Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Expected O, but got Unknown
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Expected O, but got Unknown
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Expected O, but got Unknown
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Expected O, but got Unknown
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Expected O, but got Unknown
CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[2]
{
new CodeMatch((OpCode?)OpCodes.Ldsfld, (object)AccessTools.Field(typeof(Player), "m_localPlayer"), (string)null),
new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(Object), "op_Implicit", (Type[])null, (Type[])null), (string)null)
});
if (val.IsInvalid)
{
return instructions;
}
val.RemoveInstructions(2);
val.Insert((CodeInstruction[])(object)new CodeInstruction[2]
{
new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Player), "GetAllPlayers", (Type[])null, (Type[])null)),
new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(MonsterAiPatches), "HasAnyPlayerNearby", (Type[])null, (Type[])null))
});
return val.InstructionEnumeration();
}
[HarmonyPatch(typeof(SpawnSystem), "UpdateSpawning")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> SpawnSystem_UpdateSpawning_Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Expected O, but got Unknown
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Expected O, but got Unknown
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Expected O, but got Unknown
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Expected O, but got Unknown
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Expected O, but got Unknown
//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Expected O, but got Unknown
CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[4]
{
new CodeMatch((OpCode?)OpCodes.Ldsfld, (object)AccessTools.Field(typeof(Player), "m_localPlayer"), (string)null),
new CodeMatch((OpCode?)OpCodes.Ldnull, (object)null, (string)null),
new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Object), "op_Equality", (Type[])null, (Type[])null), (string)null),
new CodeMatch((OpCode?)OpCodes.Brfalse, (object)null, (string)null)
});
if (val.IsInvalid)
{
return instructions;
}
val.RemoveInstructions(3);
val.SetInstructionAndAdvance(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Player), "GetAllPlayers", (Type[])null, (Type[])null)));
val.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1]
{
new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(MonsterAiPatches), "HasAnyPlayerNearby", (Type[])null, (Type[])null))
});
val.SetOpcodeAndAdvance(OpCodes.Brfalse);
return val.InstructionEnumeration();
}
private static bool HasAnyPlayerNearby(List<Player> all)
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
if (all == null || all.Count == 0)
{
return false;
}
int num = ZoneSystem.instance?.m_activeArea ?? 3;
for (int i = 0; i < all.Count; i++)
{
Player val = all[i];
if (Object.op_Implicit((Object)(object)val))
{
Vector2i zone = ZoneSystem.GetZone(((Component)val).transform.position);
if (!ZNetScene.OutsideActiveArea(((Component)val).transform.position, zone, num))
{
return true;
}
}
}
return false;
}
}
[HarmonyPatch]
public static class SteamOptimizations
{
[HarmonyTranspiler]
[HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")]
private static IEnumerable<CodeInstruction> ZSteamSocket_RegisterGlobalCallbacks_Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Expected O, but got Unknown
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Expected O, but got Unknown
if (!Helper.IsServer())
{
return instructions;
}
CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
{
new CodeMatch((OpCode?)OpCodes.Ldc_I4, (object)153600, (string)null)
});
if (val.IsInvalid)
{
VBNetTweaks.LogDebug("WARNING: Steam transfer rate limit not found");
return instructions;
}
int num = 50000000;
val.SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldc_I4, (object)num));
if (VBNetTweaks.DebugEnabled.Value)
{
VBNetTweaks.LogVerbose("Steam transfer rate patched in transpiler");
}
VBNetTweaks.LogVerbose($"Steam transfer rate patched: 153600 -> {num}");
return val.InstructionEnumeration();
}
[HarmonyPostfix]
[HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")]
private static void ZSteamSocket_RegisterGlobalCallbacks_Postfix(ZSteamSocket __instance)
{
try
{
ConfigEntry<bool> enableSteamSendRate = VBNetTweaks.EnableSteamSendRate;
if (enableSteamSendRate == null || !enableSteamSendRate.Value)
{
return;
}
Assembly assembly = typeof(ZSteamSocket).Assembly;
Type type = assembly.GetType("Steamworks.SteamNetworkingUtils");
if (type == null)
{
VBNetTweaks.LogDebug("SteamNetworkingUtils type not found");
return;
}
MethodInfo setCfg = type.GetMethod("SetConfigValue", new Type[5]
{
typeof(ESteamNetworkingConfigValue),
typeof(ESteamNetworkingConfigScope),
typeof(IntPtr),
typeof(ESteamNetworkingConfigDataType),
typeof(IntPtr)
});
if (setCfg == null)
{
VBNetTweaks.LogDebug("SetConfigValue method not found");
return;
}
int num = Math.Max(64, VBNetTweaks.SteamSendRateMinKB.Value) * 1024;
int num2 = Math.Max(num, VBNetTweaks.SteamSendRateMaxKB.Value) * 1024;
int num3 = Math.Max(8388608, VBNetTweaks.SteamSendBufferSize.Value);
SetInt((ESteamNetworkingConfigValue)10, num);
SetInt((ESteamNetworkingConfigValue)11, num2);
SetInt((ESteamNetworkingConfigValue)9, num3);
VBNetTweaks.LogVerbose($"Steam send rates applied: min={num / 1024}KB/s max={num2 / 1024}KB/s buffer={num3}");
void SetInt(ESteamNetworkingConfigValue key, int value)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
GCHandle gCHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
try
{
setCfg.Invoke(null, new object[5]
{
key,
(object)(ESteamNetworkingConfigScope)1,
IntPtr.Zero,
(object)(ESteamNetworkingConfigDataType)1,
gCHandle.AddrOfPinnedObject()
});
}
finally
{
gCHandle.Free();
}
}
}
catch (Exception ex)
{
VBNetTweaks.LogDebug("Error applying Steam send rates: " + ex.Message);
}
}
}
public enum CompressionAlgorithm
{
Deflate,
Zstd
}
[BepInPlugin("VitByr.VBNetTweaks", "VBNetTweaks", "0.1.5")]
public class VBNetTweaks : BaseUnityPlugin
{
private const string ModName = "VBNetTweaks";
private const string ModVersion = "0.1.5";
private const string ModGUID = "VitByr.VBNetTweaks";
public static ConfigEntry<bool> EnableAILOD;
public static ConfigEntry<float> AILODNearDistance;
public static ConfigEntry<float> AILODFarDistance;
public static ConfigEntry<float> AILODThrottleFactor;
public static ConfigEntry<bool> EnableZDOThrottling;
public static ConfigEntry<float> ZDOThrottleDistance;
public static ConfigEntry<bool> EnablePlayerPositionBoost;
public static ConfigEntry<float> PlayerPositionUpdateMultiplier;
public static ConfigEntry<bool> EnableClientInterpolation;
public static ConfigEntry<bool> EnablePlayerPrediction;
public static ConfigEntry<bool> EnableMonsterAiPatches;
public static ConfigEntry<bool> EnableSteamSendRate;
public static ConfigEntry<int> SteamSendRateMinKB;
public static ConfigEntry<int> SteamSendRateMaxKB;
public static ConfigEntry<int> SteamSendBufferSize;
public static ConfigEntry<bool> EnableNetworkCompression;
public static ConfigEntry<string> CompressionAlgorithm;
public static ConfigEntry<bool> DebugEnabled;
public static ConfigEntry<bool> VerboseLogging;
public static ConfigEntry<float> SendInterval;
public static ConfigEntry<int> PeersPerUpdate;
public static ConfigEntry<bool> SceneDebugEnabled;
public static ConfigEntry<bool> EnableNetSync;
public static double NetTime;
public static float DeltaTimeFixedPhysics = 0.02f;
public static float DeltaTimePhysics = 0.01f;
private Harmony _harmony;
private static bool _serverConfigsInitialized;
private void Awake()
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Expected O, but got Unknown
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Expected O, but got Unknown
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_010b: Expected O, but got Unknown
DebugEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - General", "DebugEnabled", false, new ConfigDescription("Включить отладочный вывод", (AcceptableValueBase)null, Array.Empty<object>()));
VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - General", "VerboseLogging", false, new ConfigDescription("Включить подробное логирование успешных операций", (AcceptableValueBase)null, Array.Empty<object>()));
EnableNetworkCompression = ((BaseUnityPlugin)this).Config.Bind<bool>("Network", "EnableCompression", true, "Enable network compression (safe, negotiated between peers)");
CompressionAlgorithm = ((BaseUnityPlugin)this).Config.Bind<string>("Network", "CompressionAlgorithm", "Deflate", "Deflate (built-in) or Zstd (requires ZstdSharp)");
if (ZRoutedRpc.instance != null)
{
ZRoutedRpc.instance.Register<ZPackage>("VBNT_RPCBatch", (Action<long, ZPackage>)RpcBatcher.HandleBatch);
((BaseUnityPlugin)this).Logger.LogInfo((object)"VBNetTweaks: VBNT_RPCBatch registered");
}
if (EnableNetworkCompression.Value)
{
ZDONetworkOptimizer.Initialize();
}
_harmony = new Harmony("VitByr.VBNetTweaks");
_harmony.PatchAll(typeof(ZDONetworkOptimizer));
_harmony.PatchAll(typeof(SteamOptimizations));
_harmony.PatchAll(typeof(ShipSyncSystem));
_harmony.PatchAll(typeof(PlayerSyncSystem));
_harmony.PatchAll(typeof(ObjectPool));
_harmony.PatchAll(typeof(PlayerCache));
((MonoBehaviour)this).StartCoroutine(DelayedServerPatchInit());
((MonoBehaviour)this).StartCoroutine(DelayedServerConfigInit());
((BaseUnityPlugin)this).Logger.LogInfo((object)"VBNetTweaks загружен!");
if (DebugEnabled.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"Режим отладки включен");
}
}
private IEnumerator DelayedServerPatchInit()
{
float timeout = 30f;
float elapsed = 0f;
while (!Object.op_Implicit((Object)(object)ZNet.instance) && elapsed < timeout)
{
elapsed += Time.deltaTime;
yield return null;
}
if (!Object.op_Implicit((Object)(object)ZNet.instance))
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"ZNet.instance не появился за 30 секунд — серверные патчи не применены.");
}
else if (Helper.IsServer())
{
if (EnableAILOD?.Value ?? false)
{
_harmony.PatchAll(typeof(AILODPatches));
}
if (EnableMonsterAiPatches?.Value ?? false)
{
_harmony.PatchAll(typeof(MonsterAiPatches));
}
((BaseUnityPlugin)this).Logger.LogInfo((object)"VBNetTweaks: серверные патчи успешно применены.");
}
}
private IEnumerator DelayedServerConfigInit()
{
int maxAttempts = 100;
for (int i = 0; i < maxAttempts; i++)
{
if (Object.op_Implicit((Object)(object)ZNet.instance))
{
break;
}
yield return (object)new WaitForSeconds(0.25f);
}
if (EnableNetworkCompression.Value)
{
ZDONetworkOptimizer.Initialize();
}
if (Helper.IsServer())
{
EnableNetSync = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "02 - Network", "EnableNetSync", true, "Включить новую систему синхронизации NetSync", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
SendInterval = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "02 - Network", "SendInterval", 0.05f, "Интервал отправки данных (секунды) - ТОЛЬКО СЕРВЕР", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
PeersPerUpdate = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "02 - Network", "PeersPerUpdate", 20, "Количество пиров для обработки за один апдейт - ТОЛЬКО СЕРВЕР", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
EnableZDOThrottling = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "02 - Network", "EnableZDOThrottling", true, "Снижать частоту обновления для дальних ZDO (только для сервера).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
ZDOThrottleDistance = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "02 - Network", "ZDOThrottleDistance", 500f, "Дистанция (в метрах), за пределами которой ZDO обновляются реже.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
EnableSteamSendRate = ((BaseUnityPlugin)this).Config.Bind<bool>("02 - Network", "EnableSteamSendRateOverride", true, "Применять настройки скорости отправки Steam при запуске.");
SteamSendRateMinKB = ((BaseUnityPlugin)this).Config.Bind<int>("02 - Network", "SteamSendRateMinKB", 256, "Минимальная скорость отправки (КБ/с).");
SteamSendRateMaxKB = ((BaseUnityPlugin)this).Config.Bind<int>("02 - Network", "SteamSendRateMaxKB", 1024, "Максимальная скорость отправки (КБ/с).");
SteamSendBufferSize = ((BaseUnityPlugin)this).Config.Bind<int>("02 - Network", "SteamSendBufferBytes", 100000000, "Размер буфера отправки Steam (в байтах).");
SceneDebugEnabled = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "03 - Scene Optimizations", "SceneDebugEnabled", false, "Включить отладочный вывод для сцены", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
EnableAILOD = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "04 - AI", "EnableAILOD", true, "Уменьшать частоту обновления AI для дальних существ (только для сервера).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
AILODNearDistance = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "04 - AI", "AILODNearDistance", 100f, "Дистанция (в метрах), в пределах которой AI работает на полной скорости.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
AILODFarDistance = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "04 - AI", "AILODFarDistance", 300f, "Дистанция (в метрах), за пределами которой AI замедляется.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
AILODThrottleFactor = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "04 - AI", "AILODThrottleFactor", 0.5f, "Коэффициент замедления для дальнего AI (0.5 = половинная скорость).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
EnablePlayerPositionBoost = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "05 - Player Sync", "EnableHighFrequencyPositionUpdates", true, "Повысить приоритет обновления позиций игроков на сервере.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
PlayerPositionUpdateMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "05 - Player Sync", "PositionUpdateMultiplier", 2.5f, "Множитель приоритета синхронизации игроков (1.0 = стандарт, 2.5 = рекомендовано).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
EnableClientInterpolation = ((BaseUnityPlugin)this).Config.Bind<bool>("05 - Player Sync", "EnableClientInterpolation", true, "Сглаживать движения других игроков на клиенте (убирает рывки).");
EnablePlayerPrediction = ((BaseUnityPlugin)this).Config.Bind<bool>("05 - Player Sync", "EnableClientPrediction", true, "Прогнозировать движения других игроков между сетевыми обновлениями (плавность в бою).");
EnableMonsterAiPatches = ((BaseUnityPlugin)this).Config.Bind<bool>("06 - Gameplay", "EnableMonsterAiPatches", true, "Использовать всех игроков вместо локального для событий и спавна монстров.");
_serverConfigsInitialized = true;
((BaseUnityPlugin)this).Logger.LogInfo((object)"Серверные настройки VBNetTweaks инициализированы");
}
else
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"VBNetTweaks работает в клиентском режиме");
}
}
private void OnDestroy()
{
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
public static void LogDebug(string message)
{
if (DebugEnabled.Value)
{
Debug.Log((object)("[VBNetTweaks] " + message));
}
}
public static void LogVerbose(string message)
{
if (DebugEnabled.Value && VerboseLogging.Value)
{
Debug.Log((object)("[VBNetTweaks] " + message));
}
}
public static bool GetSceneDebugEnabled()
{
try
{
return SceneDebugEnabled?.Value ?? false;
}
catch
{
return false;
}
}
public static float GetEffectiveSendInterval()
{
float fallback = ((!_serverConfigsInitialized) ? 0.05f : (SendInterval?.Value ?? 0.05f));
return AdaptiveThrottler.GetInterval(fallback);
}
public static int GetPeersPerUpdate()
{
return (!_serverConfigsInitialized) ? 20 : (PeersPerUpdate?.Value ?? 20);
}
}
}
namespace VBNetTweaks.Utils
{
[HarmonyPatch]
public static class ObjectPool
{
private static class Pool<T>
{
public static readonly Stack<T> Stack = new Stack<T>();
public static int MaxSize = 256;
}
private static readonly Stack<ZPackage> _pkgPool = new Stack<ZPackage>();
private const int MaxPkgPoolSize = 128;
public static List<T> RentList<T>()
{
if (Pool<List<T>>.Stack.Count > 0)
{
List<T> list = Pool<List<T>>.Stack.Pop();
list.Clear();
return list;
}
LogAlloc("List<" + typeof(T).Name + ">");
return new List<T>();
}
public static void ReturnList<T>(List<T> list)
{
if (list != null)
{
list.Clear();
if (Pool<List<T>>.Stack.Count < Pool<List<T>>.MaxSize)
{
Pool<List<T>>.Stack.Push(list);
}
}
}
public static ZPackage RentPackage()
{
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Expected O, but got Unknown
if (_pkgPool.Count > 0)
{
ZPackage val = _pkgPool.Pop();
val.Clear();
LogReuse("ZPackage");
return val;
}
LogAlloc("ZPackage");
return new ZPackage();
}
public static void ReturnPackage(ZPackage pkg)
{
if (pkg != null)
{
pkg.Clear();
if (_pkgPool.Count < 128)
{
_pkgPool.Push(pkg);
}
}
}
[Conditional("DEBUG")]
private static void LogAlloc(string type)
{
if (VBNetTweaks.DebugEnabled.Value && VBNetTweaks.VerboseLogging.Value)
{
VBNetTweaks.LogVerbose("ObjectPool: allocated new " + type);
}
}
[Conditional("DEBUG")]
private static void LogReuse(string type)
{
if (VBNetTweaks.DebugEnabled.Value && VBNetTweaks.VerboseLogging.Value)
{
VBNetTweaks.LogVerbose("ObjectPool: reused " + type);
}
}
[HarmonyPatch(typeof(ZNetScene), "Awake")]
[HarmonyPostfix]
public static void OnSceneAwake()
{
_pkgPool.Clear();
Pool<List<ZDO>>.Stack.Clear();
Pool<List<Player>>.Stack.Clear();
VBNetTweaks.LogVerbose("ObjectPool cleared on scene load");
}
}
public static class PerformanceMonitor
{
private struct Sample
{
public string Name;
public float TotalTime;
public int Count;
public float LastLogTime;
}
private static Dictionary<string, Sample> _samples = new Dictionary<string, Sample>();
private const float LOG_INTERVAL = 5f;
public static void Track(string name, Action action)
{
if (!VBNetTweaks.DebugEnabled.Value)
{
action();
return;
}
Stopwatch stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
if (!_samples.TryGetValue(name, out var value))
{
Sample sample = default(Sample);
sample.Name = name;
value = sample;
}
value.TotalTime += stopwatch.ElapsedMilliseconds;
value.Count++;
float time = Time.time;
if (time - value.LastLogTime > 5f)
{
float num = value.TotalTime / (float)value.Count;
VBNetTweaks.LogDebug($"{name}: avg={num:F2}ms over {value.Count} samples");
value.TotalTime = 0f;
value.Count = 0;
value.LastLogTime = time;
}
_samples[name] = value;
}
}
public static class RpcSerializer
{
private const byte Type_None = 0;
private const byte Type_Int = 1;
private const byte Type_Float = 2;
private const byte Type_String = 3;
private const byte Type_Bool = 4;
private const byte Type_Vector3 = 5;
private const byte Type_Quat = 6;
private const byte Type_Long = 7;
public static void WriteArg(ZPackage pkg, object arg)
{
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
if (arg is int)
{
pkg.Write((byte)1);
pkg.Write((int)arg);
return;
}
if (arg is float)
{
pkg.Write((byte)2);
pkg.Write((float)arg);
return;
}
if (arg is string)
{
pkg.Write((byte)3);
pkg.Write((string)arg);
return;
}
if (arg is bool)
{
pkg.Write((byte)4);
pkg.Write((bool)arg);
return;
}
if (arg is Vector3)
{
pkg.Write((byte)5);
pkg.Write((Vector3)arg);
return;
}
if (arg is Quaternion)
{
pkg.Write((byte)6);
pkg.Write((Quaternion)arg);
return;
}
if (arg is long)
{
pkg.Write((byte)7);
pkg.Write((long)arg);
return;
}
pkg.Write((byte)0);
if (VBNetTweaks.DebugEnabled.Value)
{
VBNetTweaks.LogDebug("RpcSerializer: unsupported arg type " + arg.GetType());
}
}
public static object ReadArg(ZPackage pkg)
{
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
return pkg.ReadByte() switch
{
1 => pkg.ReadInt(),
2 => pkg.ReadSingle(),
3 => pkg.ReadString(),
4 => pkg.ReadBool(),
5 => pkg.ReadVector3(),
6 => pkg.ReadQuaternion(),
7 => pkg.ReadLong(),
_ => null,
};
}
}
[HarmonyPatch]
[HarmonyPatch(typeof(ZNet), "Update")]
public static class ZNetOptimizations
{
private static float _lastThrottlerUpdate;
private const float THROTTLER_INTERVAL = 0.5f;
[HarmonyPostfix]
public static void Postfix(ZNet __instance)
{
if (Object.op_Implicit((Object)(object)__instance) && Object.op_Implicit((Object)(object)ZNet.instance))
{
RpcBatcher.Update();
float time = Time.time;
if (time - _lastThrottlerUpdate >= 0.5f)
{
AdaptiveThrottler.Update(__instance, Time.deltaTime);
_lastThrottlerUpdate = time;
}
PerformanceMonitor.Track("ZNet.Update", delegate
{
});
}
}
}
public static class Helper
{
private static int _lastFrame = -1;
private static bool _cachedIsServer;
private static bool _cachedIsClient;
public static bool IsServer()
{
if (_lastFrame == Time.frameCount)
{
return _cachedIsServer;
}
try
{
ZNet instance = ZNet.instance;
_cachedIsServer = Object.op_Implicit((Object)(object)instance) && instance.IsServer();
_cachedIsClient = Object.op_Implicit((Object)(object)instance) && !instance.IsServer();
_lastFrame = Time.frameCount;
return _cachedIsServer;
}
catch
{
return false;
}
}
public static bool IsClient()
{
if (_lastFrame == Time.frameCount)
{
return _cachedIsClient;
}
IsServer();
return _cachedIsClient;
}
}
}