using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using MelonLoader; using MelonLoader.Preferences; using SledAutoAFK; using SledAutoAFK.Util; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SledAutoAFK")] [assembly: AssemblyFileVersion("0.1.0")] [assembly: MelonInfo(typeof(Mod), "SledAutoAFK", "0.1.0", "Natebag", null)] [assembly: MelonGame("The Sledding Corporation", "Sledding Game")] [assembly: MelonColor(255, 0, 255, 102)] [assembly: MelonAuthorColor(255, 80, 160, 255)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyVersion("0.1.0.0")] namespace SledAutoAFK { public sealed class Mod : MelonMod { public const string Name = "SledAutoAFK"; public const string Version = "0.1.0"; public const string Author = "Natebag"; private static MelonPreferences_Category _prefCat; private static MelonPreferences_Entry _thresholdEntry; private static float _lastInputTime; private static bool _isAfk; private static string _originalName = ""; private static bool _hasOriginalName; private static readonly string[] _frames = new string[6] { "[A]", "[AF]", "[AFK]", "[AFK.]", "[AFK..]", "[AFK...]" }; private static int _frameIndex; private static float _lastFrameTime; private const float FrameInterval = 0.5f; private static Type _playerUsernameControllerType; private static bool _hookInstalled; public override void OnInitializeMelon() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown SafeLog.Bind(((MelonBase)this).LoggerInstance); try { _prefCat = MelonPreferences.CreateCategory("SledAutoAFK", "SledAutoAFK"); _thresholdEntry = _prefCat.CreateEntry("ThresholdSeconds", 60f, "ThresholdSeconds", "Seconds of no input before marking as AFK (default: 60)", false, false, (ValueValidator)null, (string)null); _lastInputTime = Time.time; _playerUsernameControllerType = Refl.FindType("Assembly-CSharp", "PlayerUsernameController"); if (_playerUsernameControllerType == null) { SafeLog.Warn("SledAutoAFK: PlayerUsernameController not found at init — will retry on first frame"); } if (!_hookInstalled) { ((MelonEventBase)(object)MelonEvents.OnUpdate).Subscribe(new LemonAction(OnFrame), 0, false); _hookInstalled = true; } SafeLog.Info($"SledAutoAFK v{"0.1.0"} ready — threshold {_thresholdEntry.Value}s."); } catch (Exception value) { ((MelonBase)this).LoggerInstance.Error($"SledAutoAFK init failed: {value}"); } } private static void OnFrame() { try { float time = Time.time; bool anyKey = Input.anyKey; bool flag = Mathf.Abs(Input.GetAxis("Mouse X")) > 0.01f; bool flag2 = Mathf.Abs(Input.GetAxis("Mouse Y")) > 0.01f; if (anyKey || flag || flag2) { _lastInputTime = time; if (_isAfk) { _isAfk = false; _frameIndex = 0; if (_hasOriginalName) { WriteSyncUsername(_originalName); SafeLog.Info("SledAutoAFK: AFK ended — restored name '" + _originalName + "'"); } } return; } float num = ((_thresholdEntry != null) ? _thresholdEntry.Value : 60f); float num2 = time - _lastInputTime; if (!(num2 < num)) { if (!_isAfk) { _isAfk = true; _originalName = ReadSyncUsername(); _hasOriginalName = !string.IsNullOrEmpty(_originalName); SafeLog.Info($"SledAutoAFK: AFK triggered after {num2:F0}s idle. Original name: '{_originalName}'"); } if (time - _lastFrameTime >= 0.5f) { _lastFrameTime = time; _frameIndex = (_frameIndex + 1) % _frames.Length; WriteSyncUsername((_hasOriginalName ? (_originalName + " ") : "") + _frames[_frameIndex]); } } } catch (Exception ex) { SafeLog.Error("SledAutoAFK.OnFrame", ex); } } private static string ReadSyncUsername() { try { EnsureControllerType(); if (_playerUsernameControllerType == null) { return ""; } Il2CppReferenceArray val = Object.FindObjectsOfType(Il2CppType.From(_playerUsernameControllerType)); if (val == null) { return ""; } foreach (Object item in (Il2CppArrayBase)(object)val) { if (item == (Object)null) { continue; } object obj = Refl.CastToIl2Cpp(item, _playerUsernameControllerType); if (obj == null) { continue; } PropertyInfo property = obj.GetType().GetProperty("IsOwner", BindingFlags.Instance | BindingFlags.Public); if (property == null) { continue; } object value = property.GetValue(obj); if (value != null && (bool)value) { FieldInfo field = obj.GetType().GetField("sync_Username", BindingFlags.Instance | BindingFlags.Public); object obj2 = ((field != null) ? field.GetValue(obj) : null); if (obj2 != null) { PropertyInfo property2 = obj2.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public); return ((property2 != null) ? (property2.GetValue(obj2) as string) : null) ?? ""; } } } } catch (Exception ex) { SafeLog.Error("SledAutoAFK.ReadSyncUsername", ex); } return ""; } private static void WriteSyncUsername(string value) { try { EnsureControllerType(); if (_playerUsernameControllerType == null) { SafeLog.Warn("SledAutoAFK: PlayerUsernameController type not found"); return; } Il2CppReferenceArray val = Object.FindObjectsOfType(Il2CppType.From(_playerUsernameControllerType)); if (val == null || ((Il2CppArrayBase)(object)val).Length == 0) { return; } object obj = null; foreach (Object item in (Il2CppArrayBase)(object)val) { if (item == (Object)null) { continue; } object obj2 = Refl.CastToIl2Cpp(item, _playerUsernameControllerType); if (obj2 == null) { continue; } PropertyInfo property = obj2.GetType().GetProperty("IsOwner", BindingFlags.Instance | BindingFlags.Public); if (!(property == null)) { object value2 = property.GetValue(obj2); if (value2 != null && (bool)value2) { obj = obj2; break; } } } if (obj == null) { obj = Refl.CastToIl2Cpp(((Il2CppArrayBase)(object)val)[0], _playerUsernameControllerType); } if (obj == null) { return; } FieldInfo field = obj.GetType().GetField("sync_Username", BindingFlags.Instance | BindingFlags.Public); if (field == null) { return; } object value3 = field.GetValue(obj); if (value3 != null) { PropertyInfo property2 = value3.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public); if (!(property2 == null)) { property2.SetValue(value3, value); } } } catch (Exception ex) { SafeLog.Error("SledAutoAFK.WriteSyncUsername", ex); } } private static void EnsureControllerType() { if (!(_playerUsernameControllerType != null)) { _playerUsernameControllerType = Refl.FindType("Assembly-CSharp", "PlayerUsernameController"); } } } } namespace SledAutoAFK.Util { internal static class Refl { private static readonly Dictionary _typeCache = new Dictionary(); private static bool _loggedAssemblies = false; private static MethodInfo _il2cppCastGeneric; public static Type FindType(string assemblyHint, string fullName) { if (_typeCache.TryGetValue(fullName, out var value)) { return value; } try { string[] array = new string[2] { fullName, "Il2Cpp." + fullName }; string[] array2 = new string[3] { assemblyHint, "Il2Cpp" + assemblyHint, assemblyHint + "-firstpass" }; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { string name = assembly.GetName().Name; string[] array3 = array2; foreach (string text in array3) { if (name != text) { continue; } string[] array4 = array; foreach (string name2 in array4) { Type type = assembly.GetType(name2); if (type != null) { _typeCache[fullName] = type; return type; } } } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { string[] array3 = array; foreach (string name3 in array3) { Type type2; try { type2 = assembly2.GetType(name3); } catch { continue; } if (type2 != null) { _typeCache[fullName] = type2; return type2; } } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { Type[] types; try { types = assembly3.GetTypes(); } catch (ReflectionTypeLoadException ex) { types = ex.Types; } catch { continue; } string text2 = fullName; int num = fullName.LastIndexOf('.'); if (num >= 0 && num < fullName.Length - 1) { text2 = fullName.Substring(num + 1); } Type[] array5 = types; foreach (Type type3 in array5) { if (!(type3 == null) && (type3.FullName == fullName || type3.FullName == "Il2Cpp." + fullName || (type3.FullName != null && type3.FullName.EndsWith("." + fullName)) || type3.Name == fullName || type3.Name == text2)) { _typeCache[fullName] = type3; SafeLog.Info($"Refl.FindType: '{fullName}' resolved via full scan as '{type3.FullName}' in {assembly3.GetName().Name}"); return type3; } } } if (!_loggedAssemblies) { SafeLog.Warn("Refl.FindType: '" + fullName + "' not found. Loaded assemblies:"); assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly4 in assemblies) { SafeLog.Warn(" - " + assembly4.GetName().Name); } _loggedAssemblies = true; } return null; } catch (Exception ex2) { SafeLog.Error($"Refl.FindType({assemblyHint},{fullName})", ex2); return null; } } public static MethodInfo Method(Type t, string name, params Type[] argTypes) { if (t == null) { return null; } try { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; if (argTypes.Length == 0) { MethodInfo[] methods = t.GetMethods(bindingAttr); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == name) { return methodInfo; } } return null; } return t.GetMethod(name, bindingAttr, null, argTypes, null); } catch (Exception ex) { SafeLog.Error($"Refl.Method({t.Name}.{name})", ex); return null; } } public static FieldInfo Field(Type t, string name) { if (t == null) { return null; } try { return t.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch (Exception ex) { SafeLog.Error($"Refl.Field({t.Name}.{name})", ex); return null; } } public static object CastToIl2Cpp(object obj, Type targetType) { if (obj == null || targetType == null) { return null; } if (targetType.IsInstanceOfType(obj)) { return obj; } try { if (_il2cppCastGeneric == null) { Type type = FindType("Il2CppInterop.Runtime", "Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase"); if (type == null) { type = obj.GetType(); } while (type != null) { MethodInfo method = type.GetMethod("Cast", BindingFlags.Instance | BindingFlags.Public); if (method != null && method.IsGenericMethodDefinition) { _il2cppCastGeneric = method; break; } type = type.BaseType; } } if (_il2cppCastGeneric == null) { return null; } return _il2cppCastGeneric.MakeGenericMethod(targetType).Invoke(obj, null); } catch (Exception ex) { SafeLog.Error("Refl.CastToIl2Cpp(" + targetType.FullName + ")", ex); return null; } } } internal static class SafeLog { private static Instance _logger; private static readonly LinkedList _ring = new LinkedList(); private const int RingMax = 200; public static IEnumerable Recent => _ring; public static void Bind(Instance logger) { _logger = logger; } public static void Info(string msg) { Instance logger = _logger; if (logger != null) { logger.Msg(msg); } Push("INFO " + msg); } public static void Warn(string msg) { Instance logger = _logger; if (logger != null) { logger.Warning(msg); } Push("WARN " + msg); } public static void Error(string msg, Exception ex = null) { if (_logger != null) { if (ex != null) { _logger.Error($"{msg}: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}"); } else { _logger.Error(msg); } } Push("ERROR " + ((ex != null) ? (msg + ": " + ex.Message) : msg)); } private static void Push(string line) { _ring.AddLast(line); while (_ring.Count > 200) { _ring.RemoveFirst(); } } } }