using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using AntiItemTeleport.Core; using AntiItemTeleport.Patches; using BepInEx; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RepoAntiItemTeleport")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.6.0.0")] [assembly: AssemblyInformationalVersion("2.6.0")] [assembly: AssemblyProduct("Anti Item Teleport")] [assembly: AssemblyTitle("RepoAntiItemTeleport")] [assembly: AssemblyVersion("2.6.0.0")] namespace AntiItemTeleport { [BepInPlugin("com.sunwu.anti_Item_teleport", "Anti Item Teleport", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PatchInstaller.InstallAll(new Harmony("com.sunwu.anti_Item_teleport")); SceneManager.sceneLoaded += delegate { PatchInstaller.TryInstallDispatchGuard(); }; Log.LogInfo((object)"Anti Item Teleport 1.0.0 ready - clients cannot change object positions (enforced on the host only)."); } private void Update() { PatchInstaller.TryInstallDispatchGuard(); } } internal static class PluginInfo { internal const string Guid = "com.sunwu.anti_Item_teleport"; internal const string Name = "Anti Item Teleport"; internal const string Version = "1.0.0"; } } namespace AntiItemTeleport.Patches { internal static class GumballRosterFix { private static bool _resolved; private static bool _ready; private static FieldRef> _hypnosisLines; private static FieldRef> _playerInSight; private static FieldRef> _previouslySeen; internal static bool Resolve() { if (_resolved) { return _ready; } _resolved = true; try { _hypnosisLines = AccessTools.FieldRefAccess>("hypnosisLines"); _playerInSight = AccessTools.FieldRefAccess>("playerInSight"); _previouslySeen = AccessTools.FieldRefAccess>("previouslySeenList"); _ready = true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("GumballValuable internal fields not found, disabling the gumball fix: " + ex.Message)); } return _ready; } internal static bool Prefix(GumballValuable __instance) { if ((Object)(object)__instance == (Object)null) { return true; } try { List list = SemiFunc.PlayerGetList(); List list2 = _hypnosisLines.Invoke(__instance); if (list == null || list2 == null) { return true; } int count = list.Count; if (count <= list2.Count) { return true; } Grow(_playerInSight.Invoke(__instance), count); Grow(_previouslySeen.Invoke(__instance), count); while (list2.Count < count) { GameObject val = CreateHypnosisLine(__instance, list[list2.Count]); if ((Object)(object)val == (Object)null) { GuardLog.Warn("gumball", $"[Gumball] Could not create a hypnosis line (need {count}, have {list2.Count})." + " Skipping the roster update."); return false; } list2.Add(val); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Gumball list repair failed, skipping this frame: " + ex.Message)); return false; } return true; } private static void Grow(List list, int need) { if (list != null) { while (list.Count < need) { list.Add(item: false); } } } private static GameObject CreateHypnosisLine(GumballValuable gumball, PlayerAvatar player) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gumball.hypnosisLine == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player.localCamera == (Object)null) { return null; } Transform overrideTransform = player.localCamera.GetOverrideTransform(); if ((Object)(object)overrideTransform == (Object)null) { return null; } GameObject val = Object.Instantiate(gumball.hypnosisLine, ((Component)gumball).transform.position, Quaternion.identity); if ((Object)(object)val == (Object)null) { return null; } val.transform.SetParent(overrideTransform); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; EyeLines component = val.GetComponent(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val); return null; } component.InitializeLine(player); val.transform.SetParent(gumball.HypnosisLinesParenTransform); val.SetActive(false); return val; } } internal static class RpcDispatchGuard { private const string TargetMethod = "SetPositionRPC"; private static readonly object KeyViewId = (byte)0; private static readonly object KeyMethodName = (byte)3; private static readonly object KeyParameters = (byte)4; private static readonly object KeyRpcIndex = (byte)5; internal static bool ShouldDrop(Hashtable rpcData, Player sender) { if (rpcData == null) { return false; } if (sender != null && sender == PhotonNetwork.LocalPlayer) { return false; } if (!RunGate.HostInGame) { return false; } try { if (ResolveMethodName(rpcData) != "SetPositionRPC") { return false; } PhotonView val = FindView(rpcData); string text = (((Object)(object)val != (Object)null) ? $"{((Object)val).name} (View {val.ViewID})" : "(unknown)"); GuardLog.Warn("drop", "[Blocked] Dropped a client object position request | target: " + text + " | position: " + DescribePosition(rpcData) + " | from: " + RpcOrigin.DescribeSender(sender)); if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent() != (Object)null) { CheatReport.Broadcast(sender, ((Object)val).name); } return true; } catch (Exception ex) { GuardLog.Warn("dispatch-error", "Error while screening an inbound RPC, letting it through: " + ex.Message); return false; } } private static string ResolveMethodName(Hashtable rpcData) { if (!((Dictionary)(object)rpcData).ContainsKey(KeyRpcIndex)) { return rpcData[KeyMethodName] as string; } int num = (short)rpcData[KeyRpcIndex]; List rpcList = PhotonNetwork.PhotonServerSettings.RpcList; if (rpcList == null || num < 0 || num >= rpcList.Count) { return null; } return rpcList[num]; } private static PhotonView FindView(Hashtable rpcData) { object obj = (((Dictionary)(object)rpcData).ContainsKey(KeyViewId) ? rpcData[KeyViewId] : null); int num = ((obj is int) ? ((int)obj) : 0); if (num == 0) { return null; } try { return PhotonView.Find(num); } catch { return null; } } private static string DescribePosition(Hashtable rpcData) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) object[] array = (((Dictionary)(object)rpcData).ContainsKey(KeyParameters) ? (rpcData[KeyParameters] as object[]) : null); if (array == null || array.Length < 1 || !(array[0] is Vector3)) { return "(unknown)"; } return ((object)(Vector3)array[0]/*cast due to .constrained prefix*/).ToString(); } } internal static class SetPositionRpcGuard { internal static bool Prefix(PhysGrabObject __instance, Vector3 __0) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (!RunGate.HostInGame) { return true; } if (TeleportOrigin.InLocalCall) { return true; } if (RpcOrigin.Available && !RpcOrigin.InDispatch) { return true; } string text = (((Object)(object)__instance != (Object)null) ? ((Object)__instance).name : "(unknown)"); GuardLog.Warn("deny", "[Blocked] Refused a client object position request | target: " + text + $" | position: {__0}" + " | from: " + RpcOrigin.DescribeSender()); return false; } } internal static class VoidRescue { private const float VanillaVoidY = -50f; private const float PerObjectCooldown = 2f; private static readonly Dictionary LastRescue = new Dictionary(); private static float _nextPrune; private static bool _valuableRefResolved; private static FieldRef _isValuable; internal static void Prefix(PhysGrabObject __instance) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (!RunGate.CanMoveObjects || (Object)(object)__instance == (Object)null || __instance.dead || !__instance.spawned) { return; } Vector3 position = ((Component)__instance).transform.position; if (position.y >= -50f || !IsValuable(__instance)) { return; } TruckSafetySpawnPoint instance = TruckSafetySpawnPoint.instance; if (!((Object)(object)instance == (Object)null) && PassesCooldown(((Object)__instance).GetInstanceID())) { if ((Object)(object)__instance.rb != (Object)null) { __instance.rb.velocity = Vector3.zero; __instance.rb.angularVelocity = Vector3.zero; } __instance.Teleport(((Component)instance).transform.position, ((Component)instance).transform.rotation); GuardLog.Warn("rescue", "[Rescued] Returned a valuable that fell out of the world to the truck safety spawn" + $" - {((Object)__instance).name} (Y {position.y:F1})"); } } private static bool IsValuable(PhysGrabObject target) { if (!_valuableRefResolved) { _valuableRefResolved = true; try { _isValuable = AccessTools.FieldRefAccess("isValuable"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("PhysGrabObject.isValuable not accessible, falling back to GetComponent: " + ex.Message)); } } if (_isValuable == null) { return (Object)(object)((Component)target).GetComponent() != (Object)null; } return _isValuable.Invoke(target); } private static bool PassesCooldown(int id) { float unscaledTime = Time.unscaledTime; if (LastRescue.TryGetValue(id, out var value) && unscaledTime - value < 2f) { return false; } LastRescue[id] = unscaledTime; if (unscaledTime >= _nextPrune || LastRescue.Count >= 1024) { _nextPrune = unscaledTime + 30f; List list = new List(); foreach (KeyValuePair item in LastRescue) { if (unscaledTime - item.Value > 30f) { list.Add(item.Key); } } for (int i = 0; i < list.Count; i++) { LastRescue.Remove(list[i]); } } return true; } } } namespace AntiItemTeleport.Core { internal static class CheatReport { private const byte EventCode = 171; private static bool _listening; internal static void StartListening() { if (_listening) { return; } try { PhotonNetwork.NetworkingClient.EventReceived += OnEvent; _listening = true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not subscribe to host block reports: " + ex.Message)); } } internal static void Broadcast(Player culprit, string targetName) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (culprit == null) { return; } try { if (PhotonNetwork.InRoom) { object[] array = new object[3] { culprit.ActorNumber, RpcOrigin.DescribeSender(culprit), targetName }; RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)0 }; PhotonNetwork.RaiseEvent((byte)171, (object)array, val, SendOptions.SendReliable); } } catch (Exception ex) { GuardLog.Warn("report-send", "Could not broadcast the block report: " + ex.Message); } } private static void OnEvent(EventData photonEvent) { if (photonEvent.Code != 171) { return; } Player masterClient = PhotonNetwork.MasterClient; if (masterClient != null && photonEvent.Sender == masterClient.ActorNumber && photonEvent.CustomData is object[] array && array.Length >= 3) { string text = array[1] as string; string text2 = array[2] as string; if (!string.IsNullOrEmpty(text)) { GuardLog.Warn("report", "[Host report] The host blocked a valuable position request | from: " + text + " | target: " + text2); } } } } internal static class GuardLog { private struct Bucket { public float NextEmit; public int Suppressed; } private const float Window = 1f; private static readonly Dictionary Buckets = new Dictionary(); internal static void Warn(string key, string message) { float unscaledTime = Time.unscaledTime; if (!Buckets.TryGetValue(key, out var value)) { value = default(Bucket); } if (unscaledTime < value.NextEmit) { value.Suppressed++; Buckets[key] = value; return; } if (value.Suppressed > 0) { message += $" ({value.Suppressed} more of the same suppressed in the last second)"; } value.Suppressed = 0; value.NextEmit = unscaledTime + 1f; Buckets[key] = value; Plugin.Log.LogWarning((object)message); } } internal static class PatchInstaller { private static Harmony _harmony; private static bool _dispatchGuardDone; internal static void InstallAll(Harmony harmony) { _harmony = harmony; if (Apply(harmony, "Call origin marker (PhysGrabObject.Teleport)", AccessTools.Method(typeof(PhysGrabObject), "Teleport", new Type[2] { typeof(Vector3), typeof(Quaternion) }, (Type[])null), Method(typeof(TeleportOrigin), "Enter"), Method(typeof(TeleportOrigin), "Leave"))) { Apply(harmony, "Second layer (PhysGrabObject.SetPositionRPC)", AccessTools.Method(typeof(PhysGrabObject), "SetPositionRPC", (Type[])null, (Type[])null), Method(typeof(SetPositionRpcGuard), "Prefix")); } else { Plugin.Log.LogError((object)"PhysGrabObject.Teleport not found. Cannot tell legitimate moves from cheats, so the second layer is not installed."); } Apply(harmony, "Fallen valuable rescue (PhysGrabObject.Update)", AccessTools.Method(typeof(PhysGrabObject), "Update", (Type[])null, (Type[])null), Method(typeof(VoidRescue), "Prefix")); if (GumballRosterFix.Resolve()) { Apply(harmony, "Gumball roster fix (GumballValuable.CheckForLeftPlayers)", AccessTools.Method(typeof(GumballValuable), "CheckForLeftPlayers", (Type[])null, (Type[])null), Method(typeof(GumballRosterFix), "Prefix")); } } internal static void TryInstallDispatchGuard() { if (!_dispatchGuardDone && _harmony != null && !((Object)(object)GameObject.Find("PhotonMono") == (Object)null)) { _dispatchGuardDone = true; CheatReport.StartListening(); try { RpcOrigin.Available = Apply(_harmony, "First layer (PhotonNetwork.ExecuteRpc)", AccessTools.Method(typeof(PhotonNetwork), "ExecuteRpc", (Type[])null, (Type[])null), Method(typeof(RpcOrigin), "Enter"), Method(typeof(RpcOrigin), "Leave")); } catch (Exception ex) { RpcOrigin.Available = false; Plugin.Log.LogError((object)("Failed to install the first layer: " + ex.Message)); } if (!RpcOrigin.Available) { Plugin.Log.LogError((object)"Running on the second layer (direct SetPositionRPC check) only."); } } } private static bool Apply(Harmony harmony, string label, MethodBase target, HarmonyMethod prefix = null, HarmonyMethod finalizer = null) { if (target == null) { Plugin.Log.LogWarning((object)("Target not found, skipping - " + label)); return false; } try { harmony.Patch(target, prefix, (HarmonyMethod)null, (HarmonyMethod)null, finalizer, (HarmonyMethod)null); Plugin.Log.LogInfo((object)("Installed - " + label)); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to install - " + label + ": " + ex.Message)); return false; } } private static HarmonyMethod Method(Type type, string name) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(type, name, (Type[])null, (Type[])null); if (methodInfo == null) { throw new InvalidOperationException("Patch method not found: " + type.Name + "." + name); } return new HarmonyMethod(methodInfo); } } internal static class RpcOrigin { private const int SanityLimit = 64; internal static bool Available; private static readonly List Stack = new List(8); private static bool _warned; internal static bool InDispatch => Stack.Count > 0; internal static Player CurrentSender { get { if (Stack.Count <= 0) { return null; } return Stack[Stack.Count - 1]; } } internal static bool Enter(Hashtable rpcData, Player sender) { Stack.Add(sender); if (Stack.Count > 64) { Stack.Clear(); if (!_warned) { _warned = true; Plugin.Log.LogWarning((object)"Inbound RPC sender stack grew abnormally deep and was reset."); } return true; } return !RpcDispatchGuard.ShouldDrop(rpcData, sender); } internal static void Leave() { int num = Stack.Count - 1; if (num >= 0) { Stack.RemoveAt(num); } } internal static string DescribeSender() { return DescribeSender(CurrentSender); } internal static string DescribeSender(Player sender) { if (sender == null) { return "unknown client"; } try { PlayerAvatar val = SemiFunc.PlayerAvatarGetFromPhotonPlayer(sender); if ((Object)(object)val != (Object)null) { return $"{SemiFunc.PlayerGetName(val)} (SteamID {SemiFunc.PlayerGetSteamID(val)}, Actor {sender.ActorNumber})"; } } catch { } return $"{sender.NickName} (Actor {sender.ActorNumber})"; } } internal static class RunGate { private static int _frame = -1; private static bool _inGame; private static bool _host; private static bool _levelReady; internal static bool HostInGame { get { Refresh(); if (_inGame) { return _host; } return false; } } internal static bool CanMoveObjects { get { Refresh(); if (_inGame && _host) { return _levelReady; } return false; } } private static void Refresh() { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Invalid comparison between Unknown and I4 int frameCount = Time.frameCount; if (frameCount == _frame) { return; } _frame = frameCount; _inGame = false; _host = false; _levelReady = false; try { if ((Object)(object)RunManager.instance == (Object)null || SemiFunc.MenuLevel()) { return; } _inGame = true; _host = SemiFunc.IsMasterClientOrSingleplayer(); _levelReady = (Object)(object)LevelGenerator.Instance != (Object)null && LevelGenerator.Instance.Generated && (Object)(object)GameDirector.instance != (Object)null && (int)GameDirector.instance.currentState == 2; } catch (Exception) { _inGame = false; _host = false; _levelReady = false; } PatchInstaller.TryInstallDispatchGuard(); } } internal static class TeleportOrigin { private const int SanityLimit = 64; private static int _depth; private static bool _warned; internal static bool InLocalCall => _depth > 0; internal static void Enter() { _depth++; if (_depth > 64) { _depth = 0; if (!_warned) { _warned = true; Plugin.Log.LogWarning((object)"Teleport call depth grew abnormally large and was reset."); } } } internal static void Leave() { if (_depth > 0) { _depth--; } } } }