using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BoneLib.BoneMenu;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using Il2CppTMPro;
using LabFusion.Data;
using LabFusion.Network;
using LabFusion.Network.Serialization;
using LabFusion.Player;
using LabFusion.SDK.Metadata;
using LabFusion.SDK.Modules;
using LabFusion.UI.Popups;
using LabFusion.Utilities;
using LabRP;
using LabRP.Economy;
using LabRP.Network;
using LabRP.UI;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LabRP")]
[assembly: AssemblyFileVersion("1.1.2")]
[assembly: MelonInfo(typeof(Core), "LabRP", "1.1.2", "windudes98", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("1.1.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[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 LabRP
{
public static class BuildInfo
{
public const string Name = "LabRP";
public const string Version = "1.1.2";
public const string Author = "windudes98";
}
public class Core : MelonMod
{
private static bool _fusionActive;
public static Core Instance { get; private set; }
public override void OnInitializeMelon()
{
Instance = this;
LabRPConfig.Initialize();
bool flag = MelonBase.FindMelon("LabFusion", "Lakatrazz") != null;
bool flag2 = MelonBase.FindMelon("BoneLib", "The BONELAB Modding Community") != null;
if (!flag || !flag2)
{
((MelonBase)this).LoggerInstance.Error($"LabRP requires Fusion (installed: {flag}) and BoneLib (installed: {flag2})! " + "Install the missing mod(s) and restart the game.");
return;
}
try
{
LoadFusionSide();
_fusionActive = true;
((MelonBase)this).LoggerInstance.Msg("LabRP initialized. Join or host a Fusion server to use the economy.");
}
catch (Exception value)
{
((MelonBase)this).LoggerInstance.Error($"LabRP failed to initialize. Are Fusion and BoneLib up to date? {value}");
}
}
public override void OnUpdate()
{
if (_fusionActive)
{
FusionUpdate();
}
}
public override void OnApplicationQuit()
{
if (_fusionActive)
{
FusionQuit();
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void LoadFusionSide()
{
ModuleManager.RegisterModule<LabRPModule>();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void FusionUpdate()
{
ServerEconomy.OnUpdate();
WristHud.OnUpdate();
LabRPMenu.Tick();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void FusionQuit()
{
ServerEconomy.SaveIfHosting();
}
}
public static class LabRPConfig
{
public const long MaxBalance = 999999999999L;
public static MelonPreferences_Category Category { get; private set; }
public static MelonPreferences_Entry<long> StartingBalance { get; private set; }
public static MelonPreferences_Entry<long> SalaryAmount { get; private set; }
public static MelonPreferences_Entry<float> SalaryIntervalMinutes { get; private set; }
public static MelonPreferences_Entry<bool> HudEnabled { get; private set; }
public static MelonPreferences_Entry<bool> HudBillboard { get; private set; }
public static MelonPreferences_Entry<float> HudScale { get; private set; }
public static MelonPreferences_Entry<float> HudOffsetX { get; private set; }
public static MelonPreferences_Entry<float> HudOffsetY { get; private set; }
public static MelonPreferences_Entry<float> HudOffsetZ { get; private set; }
public static MelonPreferences_Entry<float> HudYaw { get; private set; }
public static void Initialize()
{
Category = MelonPreferences.CreateCategory("LabRP");
StartingBalance = Category.CreateEntry<long>("StartingBalance", 500L, "Starting Balance", "Money a player starts with the first time they join your server.", false, false, (ValueValidator)null, (string)null);
SalaryAmount = Category.CreateEntry<long>("SalaryAmount", 200L, "Salary Amount", "Money paid to every connected player each paycheck.", false, false, (ValueValidator)null, (string)null);
SalaryIntervalMinutes = Category.CreateEntry<float>("SalaryIntervalMinutes", 5f, "Salary Interval (Minutes)", "How often paychecks are handed out.", false, false, (ValueValidator)null, (string)null);
HudEnabled = Category.CreateEntry<bool>("HudEnabled", true, "Wrist HUD Enabled", (string)null, false, false, (ValueValidator)null, (string)null);
HudBillboard = Category.CreateEntry<bool>("HudBillboard", true, "Wrist HUD Faces You", "When true the money counter always turns to face your head.", false, false, (ValueValidator)null, (string)null);
HudScale = Category.CreateEntry<float>("HudScale", 1f, "Wrist HUD Scale", (string)null, false, false, (ValueValidator)null, (string)null);
HudOffsetX = Category.CreateEntry<float>("HudOffsetX", 0f, "Wrist HUD Offset X", (string)null, false, false, (ValueValidator)null, (string)null);
HudOffsetY = Category.CreateEntry<float>("HudOffsetY", 0.055f, "Wrist HUD Offset Y", (string)null, false, false, (ValueValidator)null, (string)null);
HudOffsetZ = Category.CreateEntry<float>("HudOffsetZ", -0.085f, "Wrist HUD Offset Z", (string)null, false, false, (ValueValidator)null, (string)null);
HudYaw = Category.CreateEntry<float>("HudYaw", 0f, "Wrist HUD Yaw (Fixed Mode)", "Extra rotation applied when the HUD is not in billboard mode.", false, false, (ValueValidator)null, (string)null);
SanitizeFloats();
}
private static void SanitizeFloats()
{
if ((0u | (FixNonFinite(SalaryIntervalMinutes, 5f) ? 1u : 0u) | (FixNonFinite(HudScale, 1f) ? 1u : 0u) | (FixNonFinite(HudOffsetX, 0f) ? 1u : 0u) | (FixNonFinite(HudOffsetY, 0.055f) ? 1u : 0u) | (FixNonFinite(HudOffsetZ, -0.085f) ? 1u : 0u) | (FixNonFinite(HudYaw, 0f) ? 1u : 0u)) != 0)
{
Save();
}
}
private static bool FixNonFinite(MelonPreferences_Entry<float> entry, float fallback)
{
if (float.IsNaN(entry.Value) || float.IsInfinity(entry.Value))
{
entry.Value = fallback;
return true;
}
return false;
}
public static void Save()
{
Category.SaveToFile(false);
}
public static long ClampBalance(long value)
{
if (value < 0)
{
return 0L;
}
if (value > 999999999999L)
{
return 999999999999L;
}
return value;
}
}
public static class NameUtil
{
public const int MaxLength = 32;
public static string Clean(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return "";
}
StringBuilder stringBuilder = new StringBuilder(name.Length);
foreach (char c in name)
{
if (c != '<' && c != '>' && !char.IsControl(c))
{
stringBuilder.Append(c);
if (stringBuilder.Length >= 32)
{
break;
}
}
}
return stringBuilder.ToString().Trim();
}
}
}
namespace LabRP.UI
{
public static class LabRPMenu
{
private static Page _rootPage;
private static FunctionElement _balanceLabel;
private static Page _payPage;
private static Page _payDetailPage;
private static Page _balancesPage;
private static Page _adminPage;
private static Page _adminDetailPage;
private static Page _hudPage;
private static ulong _payTarget;
private static int _payAmount = 100;
private static ulong _adminTarget;
private static int _adminSetAmount = 500;
private static bool _labelDirty;
private static bool _pageDirty;
private static float _rebuildCooldown;
private const float RebuildIntervalSeconds = 0.3f;
private static readonly Color Green = new Color(0.45f, 1f, 0.6f, 1f);
private static readonly Color Gold = new Color(1f, 0.85f, 0.4f, 1f);
private static readonly Color Cyan = new Color(0.5f, 0.9f, 1f, 1f);
private static readonly Color Red = new Color(1f, 0.45f, 0.45f, 1f);
public static void Setup()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
Menu.Initialize();
_rootPage = Page.Root.CreatePage("LabRP", Green, 0, true);
_balanceLabel = _rootPage.CreateFunction("My Balance: $0", Color.white, (Action)delegate
{
});
_payPage = _rootPage.CreatePage("Pay Players", Cyan, 0, true);
_payDetailPage = _payPage.CreatePage("Payment", Cyan, 0, false);
_balancesPage = _rootPage.CreatePage("Balances", Green, 0, true);
_adminPage = _rootPage.CreatePage("Host Admin", Gold, 0, true);
_adminDetailPage = _adminPage.CreatePage("Manage Player", Gold, 0, false);
_hudPage = _rootPage.CreatePage("Wrist HUD", Color.white, 0, true);
BuildHudPage();
Menu.OnPageOpened += OnPageOpened;
ClientEconomy.OnCacheChanged += OnCacheChanged;
UpdateBalanceLabel();
}
private static void OnPageOpened(Page page)
{
if (page == _payPage)
{
BuildPayPage();
}
else if (page == _balancesPage)
{
BuildBalancesPage();
}
else if (page == _adminPage)
{
BuildAdminPage();
}
}
private static void OnCacheChanged()
{
_labelDirty = true;
_pageDirty = true;
}
public static void OnRosterChanged()
{
_pageDirty = true;
}
public static void Tick()
{
if (_rebuildCooldown > 0f)
{
_rebuildCooldown -= Time.unscaledDeltaTime;
}
else if (_labelDirty || _pageDirty)
{
_rebuildCooldown = 0.3f;
if (_labelDirty)
{
_labelDirty = false;
UpdateBalanceLabel();
}
if (_pageDirty)
{
_pageDirty = false;
RefreshCurrentPage();
}
}
}
private static void RefreshCurrentPage()
{
Page currentPage = Menu.CurrentPage;
if (currentPage == _payPage)
{
BuildPayPage();
}
else if (currentPage == _adminPage)
{
BuildAdminPage();
}
else if (currentPage == _balancesPage)
{
BuildBalancesPage();
}
else if (currentPage == _payDetailPage)
{
if (!IsOnline(_payTarget))
{
Menu.OpenPage(_payPage);
}
else
{
BuildPayDetailPage();
}
}
else if (currentPage == _adminDetailPage)
{
if (!IsOnline(_adminTarget))
{
Menu.OpenPage(_adminPage);
}
else
{
BuildAdminDetailPage();
}
}
}
private static bool IsOnline(ulong platformID)
{
PlayerID playerID = PlayerIDManager.GetPlayerID(platformID);
if (playerID != null)
{
return playerID.IsValid;
}
return false;
}
private static void UpdateBalanceLabel()
{
if (_balanceLabel != null)
{
((Element)_balanceLabel).ElementName = (NetworkInfo.HasServer ? $"My Balance: ${ClientEconomy.LocalBalance:N0}" : "My Balance: (join a Fusion lobby)");
}
}
private static void BuildPayPage()
{
//IL_001b: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
_payPage.RemoveAll();
if (!NetworkInfo.HasServer)
{
_payPage.CreateFunction("Join a Fusion lobby first!", Color.gray, (Action)delegate
{
});
return;
}
bool flag = false;
foreach (PlayerID otherPlayer in GetOtherPlayers())
{
flag = true;
ulong platformID = otherPlayer.PlatformID;
string displayName = ClientEconomy.GetDisplayName(platformID);
_payPage.CreateFunction("Pay " + displayName, Cyan, (Action)delegate
{
_payTarget = platformID;
BuildPayDetailPage();
Menu.OpenPage(_payDetailPage);
});
}
if (!flag)
{
_payPage.CreateFunction("Nobody else is here yet...", Color.gray, (Action)delegate
{
});
}
}
private static void BuildPayDetailPage()
{
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: 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_0140: Unknown result type (might be due to invalid IL or missing references)
//IL_0174: Unknown result type (might be due to invalid IL or missing references)
//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
_payDetailPage.RemoveAll();
_payAmount = Math.Clamp(_payAmount, 1, 1000000);
string displayName = ClientEconomy.GetDisplayName(_payTarget);
_payDetailPage.CreateFunction("Paying: " + displayName, Color.white, (Action)delegate
{
});
_payDetailPage.CreateFunction($"Your money: ${ClientEconomy.LocalBalance:N0}", Color.gray, (Action)delegate
{
});
_payDetailPage.CreateInt("Amount", Cyan, _payAmount, 50, 1, 1000000, (Action<int>)delegate(int value)
{
_payAmount = value;
});
_payDetailPage.CreateFunction("+ $100", Color.white, (Action)delegate
{
_payAmount += 100;
BuildPayDetailPage();
});
_payDetailPage.CreateFunction("+ $1,000", Color.white, (Action)delegate
{
_payAmount += 1000;
BuildPayDetailPage();
});
_payDetailPage.CreateFunction("Reset Amount", Color.gray, (Action)delegate
{
_payAmount = 100;
BuildPayDetailPage();
});
_payDetailPage.CreateFunction("CONFIRM PAYMENT", Green, (Action)delegate
{
SendPayRequest(_payTarget, _payAmount);
Menu.OpenPage(_payPage);
});
}
private static void SendPayRequest(ulong targetPlatformID, long amount)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
if (NetworkInfo.HasServer && amount > 0)
{
MessageRelay.RelayModule<PayRequestMessage, PayRequestData>(new PayRequestData
{
TargetPlatformID = targetPlatformID,
Amount = amount
}, new MessageRoute((RelayType)1, (NetworkChannel)0));
}
}
private static void BuildBalancesPage()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_014a: Unknown result type (might be due to invalid IL or missing references)
_balancesPage.RemoveAll();
if (!NetworkInfo.HasServer)
{
_balancesPage.CreateFunction("Join a Fusion lobby first!", Color.gray, (Action)delegate
{
});
return;
}
List<KeyValuePair<ulong, ClientEconomy.CachedPlayer>> leaderboard = ClientEconomy.GetLeaderboard();
if (leaderboard.Count == 0)
{
_balancesPage.CreateFunction("No balances synced yet...", Color.gray, (Action)delegate
{
});
return;
}
ulong localPlatformID = PlayerIDManager.LocalPlatformID;
int num = 1;
foreach (KeyValuePair<ulong, ClientEconomy.CachedPlayer> item in leaderboard)
{
string displayName = ClientEconomy.GetDisplayName(item.Key);
bool num2 = item.Key == localPlatformID;
Color val = (num2 ? Green : Color.white);
string value = (num2 ? " (you)" : "");
_balancesPage.CreateFunction($"#{num} {displayName}{value}: ${item.Value.Balance:N0}", val, (Action)delegate
{
});
num++;
}
}
private static void BuildAdminPage()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: 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_0114: Unknown result type (might be due to invalid IL or missing references)
//IL_0170: Unknown result type (might be due to invalid IL or missing references)
//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
//IL_0266: Unknown result type (might be due to invalid IL or missing references)
//IL_022c: Unknown result type (might be due to invalid IL or missing references)
_adminPage.RemoveAll();
if (!NetworkInfo.HasServer)
{
_adminPage.CreateFunction("Join a Fusion lobby first!", Color.gray, (Action)delegate
{
});
return;
}
if (!NetworkInfo.IsHost)
{
_adminPage.CreateFunction("Host only. You are not the host.", Color.gray, (Action)delegate
{
});
return;
}
_adminPage.CreateFunction("Salary Settings", Gold, (Action)delegate
{
});
_adminPage.CreateInt("Salary Amount", Color.white, (int)Math.Clamp(LabRPConfig.SalaryAmount.Value, 0L, 1000000L), 50, 0, 1000000, (Action<int>)delegate(int value)
{
LabRPConfig.SalaryAmount.Value = value;
LabRPConfig.Save();
});
_adminPage.CreateFloat("Salary Interval (min)", Color.white, Mathf.Clamp(LabRPConfig.SalaryIntervalMinutes.Value, 0.5f, 120f), 0.5f, 0.5f, 120f, (Action<float>)delegate(float value)
{
LabRPConfig.SalaryIntervalMinutes.Value = value;
LabRPConfig.Save();
});
_adminPage.CreateFunction("Pay Salary To Everyone NOW", Green, (Action)ServerEconomy.PayEveryoneSalary);
_adminPage.CreateFunction("Players", Gold, (Action)delegate
{
});
foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
{
if (playerID != null && playerID.IsValid)
{
ulong platformID = playerID.PlatformID;
string text = ClientEconomy.GetDisplayName(platformID);
if (playerID.IsMe)
{
text += " (you)";
}
_adminPage.CreateFunction("Manage " + text, Color.white, (Action)delegate
{
_adminTarget = platformID;
BuildAdminDetailPage();
Menu.OpenPage(_adminDetailPage);
});
}
}
_adminPage.CreateFunction("RESET ALL BALANCES", Red, (Action)delegate
{
Menu.DisplayDialog("Reset Economy", "Reset every player to the starting balance and wipe all saved records?", (Texture2D)null, (Action)ServerEconomy.ResetAllBalances, (Action)null);
});
}
private static void BuildAdminDetailPage()
{
//IL_0056: 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_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00f2: 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)
//IL_015a: Unknown result type (might be due to invalid IL or missing references)
//IL_018e: Unknown result type (might be due to invalid IL or missing references)
//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
_adminDetailPage.RemoveAll();
string displayName = ClientEconomy.GetDisplayName(_adminTarget);
long balance = ServerEconomy.GetBalance(_adminTarget);
_adminDetailPage.CreateFunction($"{displayName}: ${balance:N0}", Color.white, (Action)delegate
{
});
_adminDetailPage.CreateFunction("Give $100", Green, (Action)delegate
{
AdminChange(100L, give: true);
});
_adminDetailPage.CreateFunction("Give $1,000", Green, (Action)delegate
{
AdminChange(1000L, give: true);
});
_adminDetailPage.CreateFunction("Give $10,000", Green, (Action)delegate
{
AdminChange(10000L, give: true);
});
_adminDetailPage.CreateFunction("Take $100", Red, (Action)delegate
{
AdminChange(100L, give: false);
});
_adminDetailPage.CreateFunction("Take $1,000", Red, (Action)delegate
{
AdminChange(1000L, give: false);
});
_adminDetailPage.CreateInt("Set Amount", Color.white, _adminSetAmount, 100, 0, 1000000, (Action<int>)delegate(int value)
{
_adminSetAmount = value;
});
_adminDetailPage.CreateFunction("Apply 'Set Amount'", Gold, (Action)delegate
{
ServerEconomy.AdminSet(_adminTarget, _adminSetAmount);
BuildAdminDetailPage();
});
}
private static void AdminChange(long amount, bool give)
{
if (give)
{
ServerEconomy.AdminGive(_adminTarget, amount);
}
else
{
ServerEconomy.AdminTake(_adminTarget, amount);
}
BuildAdminDetailPage();
}
private static void BuildHudPage()
{
//IL_0014: 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_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
//IL_0224: Unknown result type (might be due to invalid IL or missing references)
_hudPage.RemoveAll();
_hudPage.CreateBool("HUD Enabled", Color.white, LabRPConfig.HudEnabled.Value, (Action<bool>)delegate(bool value)
{
LabRPConfig.HudEnabled.Value = value;
LabRPConfig.Save();
});
_hudPage.CreateBool("Always Face You", Color.white, LabRPConfig.HudBillboard.Value, (Action<bool>)delegate(bool value)
{
LabRPConfig.HudBillboard.Value = value;
LabRPConfig.Save();
WristHud.ApplyTransformPrefs();
});
_hudPage.CreateFloat("Scale", Color.white, LabRPConfig.HudScale.Value, 0.25f, 0.1f, 6f, (Action<float>)delegate(float value)
{
LabRPConfig.HudScale.Value = value;
WristHud.ApplyTransformPrefs();
});
_hudPage.CreateFunction("Position (1cm steps)", Color.gray, (Action)delegate
{
});
CreateNudge("Up", 0f, 0.01f, 0f);
CreateNudge("Down", 0f, -0.01f, 0f);
CreateNudge("Left", -0.01f, 0f, 0f);
CreateNudge("Right", 0.01f, 0f, 0f);
CreateNudge("Toward Elbow", 0f, 0f, -0.01f);
CreateNudge("Toward Fingers", 0f, 0f, 0.01f);
_hudPage.CreateFloat("Yaw (fixed mode)", Color.white, LabRPConfig.HudYaw.Value, 15f, -180f, 180f, (Action<float>)delegate(float value)
{
LabRPConfig.HudYaw.Value = value;
WristHud.ApplyTransformPrefs();
});
_hudPage.CreateFunction("Save HUD Settings", Green, (Action)LabRPConfig.Save);
_hudPage.CreateFunction("Reset HUD Defaults", Red, (Action)delegate
{
LabRPConfig.HudEnabled.Value = true;
LabRPConfig.HudBillboard.Value = true;
LabRPConfig.HudScale.Value = 1f;
LabRPConfig.HudOffsetX.Value = 0f;
LabRPConfig.HudOffsetY.Value = 0.055f;
LabRPConfig.HudOffsetZ.Value = -0.085f;
LabRPConfig.HudYaw.Value = 0f;
LabRPConfig.Save();
WristHud.ApplyTransformPrefs();
BuildHudPage();
});
}
private static void CreateNudge(string label, float dx, float dy, float dz)
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
_hudPage.CreateFunction("Nudge " + label, Color.white, (Action)delegate
{
MelonPreferences_Entry<float> hudOffsetX = LabRPConfig.HudOffsetX;
hudOffsetX.Value += dx;
MelonPreferences_Entry<float> hudOffsetY = LabRPConfig.HudOffsetY;
hudOffsetY.Value += dy;
MelonPreferences_Entry<float> hudOffsetZ = LabRPConfig.HudOffsetZ;
hudOffsetZ.Value += dz;
WristHud.ApplyTransformPrefs();
});
}
private static IEnumerable<PlayerID> GetOtherPlayers()
{
foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
{
if (playerID != null && playerID.IsValid && !playerID.IsMe)
{
yield return playerID;
}
}
}
}
public static class WristHud
{
private static GameObject _root;
private static Transform _rootTransform;
private static TextMeshPro _balanceText;
private static TextMeshPro _popupText;
private static TMP_FontAsset _font;
private static long _targetBalance = 0L;
private static double _displayedBalance = 0.0;
private static long _lastDrawnBalance = -1L;
private static float _popupTimer = 0f;
private const float PopupDuration = 3.5f;
private static readonly Color MoneyGreen = new Color(0.45f, 1f, 0.6f, 1f);
private static readonly Color SoftRed = new Color(1f, 0.42f, 0.42f, 1f);
private static readonly Color HostGold = new Color(1f, 0.85f, 0.4f, 1f);
public static void OnLocalRigCreated()
{
_root = null;
_rootTransform = null;
_balanceText = null;
_popupText = null;
TryBuild();
}
public static void OnJoinedLobby()
{
_targetBalance = ClientEconomy.LocalBalance;
_displayedBalance = _targetBalance;
_popupTimer = 0f;
TryBuild();
}
public static void OnLeftLobby()
{
_targetBalance = 0L;
_displayedBalance = 0.0;
DestroyHud();
}
public static void OnUpdate()
{
if (!NetworkInfo.HasServer)
{
if ((Object)(object)_root != (Object)null)
{
DestroyHud();
}
return;
}
if (!LabRPConfig.HudEnabled.Value)
{
if ((Object)(object)_root != (Object)null && _root.activeSelf)
{
_root.SetActive(false);
}
return;
}
if ((Object)(object)_root != (Object)null && ((Object)(object)_balanceText == (Object)null || (Object)(object)_popupText == (Object)null))
{
DestroyHud();
}
if ((Object)(object)_root == (Object)null)
{
TryBuild();
if ((Object)(object)_root == (Object)null)
{
return;
}
}
if (!_root.activeSelf)
{
_root.SetActive(true);
}
AnimateBalance();
AnimatePopup();
Billboard();
}
public static void OnLocalBalanceChanged(long newBalance, long delta, BalanceReason reason, string detail)
{
//IL_001e: 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_009d: 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_00d9: Unknown result type (might be due to invalid IL or missing references)
_targetBalance = newBalance;
detail = NameUtil.Clean(detail);
switch (reason)
{
case BalanceReason.Initial:
_displayedBalance = newBalance;
return;
case BalanceReason.Denied:
ShowPopup(detail, SoftRed);
return;
}
if (delta != 0L)
{
switch (reason)
{
case BalanceReason.Paycheck:
ShowPopup("+$" + FormatAbs(delta) + " Paycheck", MoneyGreen);
break;
case BalanceReason.PaymentReceived:
ShowPopup("+$" + FormatAbs(delta) + " from " + detail, MoneyGreen);
break;
case BalanceReason.PaymentSent:
ShowPopup("-$" + FormatAbs(delta) + " to " + detail, SoftRed);
break;
case BalanceReason.Admin:
ShowPopup((delta >= 0) ? ("+$" + FormatAbs(delta) + " Host") : ("-$" + FormatAbs(delta) + " Host"), HostGold);
break;
}
}
}
private static string FormatAbs(long value)
{
if (value == long.MinValue)
{
value = long.MaxValue;
}
return Math.Abs(value).ToString("N0");
}
private static void TryBuild()
{
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Expected O, but got Unknown
//IL_0083: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)_root != (Object)null) && NetworkInfo.HasServer && LabRPConfig.HudEnabled.Value && RigData.HasPlayer)
{
Hand leftHand = RigData.Refs.LeftHand;
if (!((Object)(object)leftHand == (Object)null))
{
TMP_FontAsset font = GetFont();
_root = new GameObject("LabRP_WristHUD");
_rootTransform = _root.transform;
_rootTransform.SetParent(((Component)leftHand).transform, false);
ApplyTransformPrefs();
_balanceText = CreateText("BalanceText", font, 30f, MoneyGreen, (FontStyles)1);
_balanceText.transform.localPosition = Vector3.zero;
_popupText = CreateText("PopupText", font, 15f, MoneyGreen, (FontStyles)0);
_popupText.transform.localPosition = new Vector3(0f, -3f, 0f);
SetAlpha((TMP_Text)(object)_popupText, 0f);
_displayedBalance = _targetBalance;
_lastDrawnBalance = -1L;
DrawBalance((long)_displayedBalance);
}
}
}
private static TextMeshPro CreateText(string name, TMP_FontAsset font, float fontSize, Color color, FontStyles style)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: 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)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: 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)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: 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)
GameObject val = new GameObject(name);
val.transform.SetParent(_root.transform, false);
val.transform.localPosition = Vector3.zero;
val.transform.localRotation = Quaternion.identity;
val.transform.localScale = Vector3.one;
TextMeshPro val2 = val.AddComponent<TextMeshPro>();
if ((Object)(object)font != (Object)null)
{
((TMP_Text)val2).font = font;
}
((TMP_Text)val2).fontSize = fontSize;
((TMP_Text)val2).fontStyle = style;
((Graphic)val2).color = color;
((TMP_Text)val2).rectTransform.sizeDelta = new Vector2(40f, 10f);
((TMP_Text)val2).enableWordWrapping = false;
((TMP_Text)val2).text = "";
((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
return val2;
}
public static void ApplyTransformPrefs()
{
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: 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)
if (!((Object)(object)_rootTransform == (Object)null))
{
_rootTransform.localPosition = new Vector3(LabRPConfig.HudOffsetX.Value, LabRPConfig.HudOffsetY.Value, LabRPConfig.HudOffsetZ.Value);
_rootTransform.localRotation = Quaternion.Euler(0f, LabRPConfig.HudYaw.Value, 0f);
float num = 0.01f * Mathf.Clamp(LabRPConfig.HudScale.Value, 0.1f, 6f);
_rootTransform.localScale = new Vector3(num, num, num);
}
}
private static void DestroyHud()
{
if ((Object)(object)_root != (Object)null)
{
Object.Destroy((Object)(object)_root);
}
_root = null;
_rootTransform = null;
_balanceText = null;
_popupText = null;
_popupTimer = 0f;
}
private static void AnimateBalance()
{
if ((Object)(object)_balanceText == (Object)null)
{
return;
}
double value = (double)_targetBalance - _displayedBalance;
if (Math.Abs(value) < 0.5)
{
_displayedBalance = _targetBalance;
}
else
{
double num = Math.Max(Math.Abs(value) * 6.0, 40.0) * (double)Time.unscaledDeltaTime;
if (num >= Math.Abs(value))
{
_displayedBalance = _targetBalance;
}
else
{
_displayedBalance += (double)Math.Sign(value) * num;
}
}
DrawBalance((long)Math.Round(_displayedBalance));
}
private static void DrawBalance(long value)
{
if ((Object)(object)_balanceText != (Object)null && value != _lastDrawnBalance)
{
_lastDrawnBalance = value;
((TMP_Text)_balanceText).text = $"${value:N0}";
}
}
private static void ShowPopup(string message, Color color)
{
//IL_0028: 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_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
_popupTimer = 3.5f;
if (!((Object)(object)_popupText == (Object)null))
{
((TMP_Text)_popupText).text = message;
((Graphic)_popupText).color = new Color(color.r, color.g, color.b, 1f);
}
}
private static void AnimatePopup()
{
if (!((Object)(object)_popupText == (Object)null) && !(_popupTimer <= 0f))
{
_popupTimer -= Time.unscaledDeltaTime;
float alpha = Mathf.Clamp01(_popupTimer / 1f);
SetAlpha((TMP_Text)(object)_popupText, alpha);
if (_popupTimer <= 0f)
{
((TMP_Text)_popupText).text = "";
}
}
}
private static void SetAlpha(TMP_Text text, float alpha)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
Color color = ((Graphic)text).color;
color.a = alpha;
((Graphic)text).color = color;
}
private static void Billboard()
{
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
if (!LabRPConfig.HudBillboard.Value || (Object)(object)_rootTransform == (Object)null)
{
return;
}
Transform val = (RigData.HasPlayer ? RigData.Refs.Head : null);
if (!((Object)(object)val == (Object)null))
{
Vector3 val2 = _rootTransform.position - val.position;
if (!(((Vector3)(ref val2)).sqrMagnitude < 0.0001f))
{
_rootTransform.rotation = Quaternion.LookRotation(((Vector3)(ref val2)).normalized, Vector3.up);
}
}
}
private static TMP_FontAsset GetFont()
{
if ((Object)(object)_font != (Object)null)
{
return _font;
}
try
{
Il2CppReferenceArray<Object> obj = Resources.FindObjectsOfTypeAll(Il2CppType.Of<TMP_FontAsset>());
TMP_FontAsset val = null;
foreach (Object item in (Il2CppArrayBase<Object>)(object)obj)
{
TMP_FontAsset val2 = ((Il2CppObjectBase)item).TryCast<TMP_FontAsset>();
if (!((Object)(object)val2 == (Object)null))
{
if (val == null)
{
val = val2;
}
if (((Object)val2).name.IndexOf("arlon-medium", StringComparison.OrdinalIgnoreCase) >= 0)
{
_font = val2;
return _font;
}
}
}
_font = val;
if ((Object)(object)_font == (Object)null)
{
MelonLogger.Warning("[LabRP] No TMP font assets are loaded yet; HUD text may be invisible until the next rebuild.");
}
}
catch (Exception ex)
{
MelonLogger.Warning("[LabRP] Could not find a TMP font, HUD text may be invisible: " + ex.Message);
}
return _font;
}
}
}
namespace LabRP.Network
{
public enum BalanceReason : byte
{
Initial,
Paycheck,
PaymentSent,
PaymentReceived,
Admin,
Denied
}
public class PayRequestData : INetSerializable
{
public ulong TargetPlatformID;
public long Amount;
public int? GetSize()
{
return 16;
}
public void Serialize(INetSerializer serializer)
{
serializer.SerializeValue(ref TargetPlatformID);
serializer.SerializeValue(ref Amount);
}
}
public class PayRequestMessage : ModuleMessageHandler
{
protected override void OnHandleMessage(ReceivedMessage received)
{
if (((ReceivedMessage)(ref received)).IsServerHandled && NetworkInfo.IsHost && ((ReceivedMessage)(ref received)).Sender.HasValue)
{
PayRequestData payRequestData = ((ReceivedMessage)(ref received)).ReadData<PayRequestData>();
ServerEconomy.TryTransfer(((ReceivedMessage)(ref received)).Sender.Value, payRequestData.TargetPlatformID, payRequestData.Amount);
}
}
}
public class BalanceUpdateData : INetSerializable
{
public ulong PlatformID;
public long NewBalance;
public long Delta;
public BalanceReason Reason;
public string Detail = "";
public int? GetSize()
{
return 25 + SizeExtensions.GetSize(Detail);
}
public void Serialize(INetSerializer serializer)
{
serializer.SerializeValue(ref PlatformID);
serializer.SerializeValue(ref NewBalance);
serializer.SerializeValue(ref Delta);
serializer.SerializeValue<BalanceReason>(ref Reason, (Precision)3);
serializer.SerializeValue(ref Detail);
}
}
public class BalanceUpdateMessage : ModuleMessageHandler
{
protected override void OnHandleMessage(ReceivedMessage received)
{
if (((ReceivedMessage)(ref received)).Sender.HasValue && ((ReceivedMessage)(ref received)).Sender.Value == 0)
{
ClientEconomy.ApplyUpdate(((ReceivedMessage)(ref received)).ReadData<BalanceUpdateData>());
}
}
}
public class SnapshotEntry
{
public ulong PlatformID;
public string Name = "";
public long Balance;
}
public class EconomySnapshotData : INetSerializable
{
public List<SnapshotEntry> Entries = new List<SnapshotEntry>();
public const int MaxEntries = 512;
public int? GetSize()
{
int num = 4;
foreach (SnapshotEntry entry in Entries)
{
num += 8 + SizeExtensions.GetSize(entry.Name ?? "") + 8;
}
return num;
}
public void Serialize(INetSerializer serializer)
{
int num = Entries?.Count ?? 0;
serializer.SerializeValue(ref num);
if (serializer.IsReader)
{
if (num < 0 || num > 512)
{
Entries = new List<SnapshotEntry>();
return;
}
Entries = new List<SnapshotEntry>(num);
for (int i = 0; i < num; i++)
{
ulong platformID = 0uL;
string text = "";
long balance = 0L;
serializer.SerializeValue(ref platformID);
serializer.SerializeValue(ref text);
serializer.SerializeValue(ref balance);
Entries.Add(new SnapshotEntry
{
PlatformID = platformID,
Name = (text ?? ""),
Balance = balance
});
}
}
else
{
for (int j = 0; j < num; j++)
{
SnapshotEntry snapshotEntry = Entries[j];
ulong platformID2 = snapshotEntry.PlatformID;
string text2 = snapshotEntry.Name ?? "";
long balance2 = snapshotEntry.Balance;
serializer.SerializeValue(ref platformID2);
serializer.SerializeValue(ref text2);
serializer.SerializeValue(ref balance2);
}
}
}
}
public class EconomySnapshotMessage : ModuleMessageHandler
{
protected override void OnHandleMessage(ReceivedMessage received)
{
if (((ReceivedMessage)(ref received)).Sender.HasValue && ((ReceivedMessage)(ref received)).Sender.Value == 0)
{
ClientEconomy.ApplySnapshot(((ReceivedMessage)(ref received)).ReadData<EconomySnapshotData>());
}
}
}
public class LabRPModule : Module
{
[CompilerGenerated]
private static class <>O
{
public static ServerEvent <0>__OnStartedServer;
public static ServerEvent <1>__OnJoinedServer;
public static ServerEvent <2>__OnDisconnected;
public static PlayerUpdate <3>__OnPlayerJoined;
public static PlayerUpdate <4>__OnPlayerLeft;
public static Action<PlayerID, string, string> <5>__OnMetadataChanged;
public static Action<long, long, BalanceReason, string> <6>__OnLocalBalanceChanged;
}
private Action<RigManager> _onRigCreated;
private Action<long, long, BalanceReason, string> _onLocalChanged;
private Action<PlayerID, string, string> _onMetadataChanged;
public override string Name => "LabRP";
public override string Author => "windudes98";
public override Version Version => new Version(1, 1, 2);
public override ConsoleColor Color => ConsoleColor.Green;
protected override void OnModuleRegistered()
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Expected O, but got Unknown
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Expected O, but got Unknown
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Expected O, but got Unknown
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Expected O, but got Unknown
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Expected O, but got Unknown
ModuleMessageManager.LoadHandlers(Assembly.GetExecutingAssembly());
object obj = <>O.<0>__OnStartedServer;
if (obj == null)
{
ServerEvent val = OnStartedServer;
<>O.<0>__OnStartedServer = val;
obj = (object)val;
}
MultiplayerHooking.OnStartedServer += (ServerEvent)obj;
object obj2 = <>O.<1>__OnJoinedServer;
if (obj2 == null)
{
ServerEvent val2 = OnJoinedServer;
<>O.<1>__OnJoinedServer = val2;
obj2 = (object)val2;
}
MultiplayerHooking.OnJoinedServer += (ServerEvent)obj2;
object obj3 = <>O.<2>__OnDisconnected;
if (obj3 == null)
{
ServerEvent val3 = OnDisconnected;
<>O.<2>__OnDisconnected = val3;
obj3 = (object)val3;
}
MultiplayerHooking.OnDisconnected += (ServerEvent)obj3;
object obj4 = <>O.<3>__OnPlayerJoined;
if (obj4 == null)
{
PlayerUpdate val4 = OnPlayerJoined;
<>O.<3>__OnPlayerJoined = val4;
obj4 = (object)val4;
}
MultiplayerHooking.OnPlayerJoined += (PlayerUpdate)obj4;
object obj5 = <>O.<4>__OnPlayerLeft;
if (obj5 == null)
{
PlayerUpdate val5 = OnPlayerLeft;
<>O.<4>__OnPlayerLeft = val5;
obj5 = (object)val5;
}
MultiplayerHooking.OnPlayerLeft += (PlayerUpdate)obj5;
_onMetadataChanged = OnMetadataChanged;
PlayerID.OnMetadataChangedEvent += _onMetadataChanged;
_onRigCreated = delegate
{
WristHud.OnLocalRigCreated();
};
LocalPlayer.OnLocalRigCreated = (Action<RigManager>)Delegate.Combine(LocalPlayer.OnLocalRigCreated, _onRigCreated);
_onLocalChanged = OnLocalBalanceChanged;
ClientEconomy.OnLocalChanged += _onLocalChanged;
LabRPMenu.Setup();
((Module)this).LoggerInstance.Log("LabRP module registered: economy online.", ConsoleColor.White);
}
protected override void OnModuleUnregistered()
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Expected O, but got Unknown
//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_003b: Expected O, but got Unknown
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Expected O, but got Unknown
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Expected O, but got Unknown
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Expected O, but got Unknown
object obj = <>O.<0>__OnStartedServer;
if (obj == null)
{
ServerEvent val = OnStartedServer;
<>O.<0>__OnStartedServer = val;
obj = (object)val;
}
MultiplayerHooking.OnStartedServer -= (ServerEvent)obj;
object obj2 = <>O.<1>__OnJoinedServer;
if (obj2 == null)
{
ServerEvent val2 = OnJoinedServer;
<>O.<1>__OnJoinedServer = val2;
obj2 = (object)val2;
}
MultiplayerHooking.OnJoinedServer -= (ServerEvent)obj2;
object obj3 = <>O.<2>__OnDisconnected;
if (obj3 == null)
{
ServerEvent val3 = OnDisconnected;
<>O.<2>__OnDisconnected = val3;
obj3 = (object)val3;
}
MultiplayerHooking.OnDisconnected -= (ServerEvent)obj3;
object obj4 = <>O.<3>__OnPlayerJoined;
if (obj4 == null)
{
PlayerUpdate val4 = OnPlayerJoined;
<>O.<3>__OnPlayerJoined = val4;
obj4 = (object)val4;
}
MultiplayerHooking.OnPlayerJoined -= (PlayerUpdate)obj4;
object obj5 = <>O.<4>__OnPlayerLeft;
if (obj5 == null)
{
PlayerUpdate val5 = OnPlayerLeft;
<>O.<4>__OnPlayerLeft = val5;
obj5 = (object)val5;
}
MultiplayerHooking.OnPlayerLeft -= (PlayerUpdate)obj5;
PlayerID.OnMetadataChangedEvent -= _onMetadataChanged;
LocalPlayer.OnLocalRigCreated = (Action<RigManager>)Delegate.Remove(LocalPlayer.OnLocalRigCreated, _onRigCreated);
ClientEconomy.OnLocalChanged -= _onLocalChanged;
}
private static void OnStartedServer()
{
ServerEconomy.OnServerStarted();
WristHud.OnJoinedLobby();
}
private static void OnJoinedServer()
{
WristHud.OnJoinedLobby();
}
private static void OnDisconnected()
{
ServerEconomy.OnDisconnected();
ClientEconomy.Clear();
WristHud.OnLeftLobby();
}
private static void OnPlayerJoined(PlayerID playerID)
{
ServerEconomy.OnPlayerJoined(playerID);
LabRPMenu.OnRosterChanged();
}
private static void OnPlayerLeft(PlayerID playerID)
{
ServerEconomy.OnPlayerLeft(playerID);
LabRPMenu.OnRosterChanged();
}
private static void OnMetadataChanged(PlayerID playerID, string key, string value)
{
if (key == "Username")
{
ServerEconomy.OnUsernameChanged(playerID);
}
}
private static void OnLocalBalanceChanged(long newBalance, long delta, BalanceReason reason, string detail)
{
//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_008e: 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_00a4: 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_00b5: 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_00cc: Expected O, but got Unknown
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: 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_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: 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_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Expected O, but got Unknown
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: 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_0128: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Expected O, but got Unknown
WristHud.OnLocalBalanceChanged(newBalance, delta, reason, detail);
detail = NameUtil.Clean(detail);
switch (reason)
{
case BalanceReason.PaymentReceived:
if (delta != 0L)
{
Notifier.Send(new Notification
{
Title = NotificationText.op_Implicit("LabRP"),
Message = NotificationText.op_Implicit(detail + " paid you $" + FormatAbs(delta) + "!"),
Type = (NotificationType)3,
ShowPopup = true,
PopupLength = 3f
});
}
break;
case BalanceReason.Denied:
Notifier.Send(new Notification
{
Title = NotificationText.op_Implicit("LabRP"),
Message = NotificationText.op_Implicit("Payment failed: " + detail),
Type = (NotificationType)1,
ShowPopup = true,
PopupLength = 3f
});
break;
case BalanceReason.Admin:
if (delta != 0L)
{
Notifier.Send(new Notification
{
Title = NotificationText.op_Implicit("LabRP"),
Message = NotificationText.op_Implicit((delta >= 0) ? ("The host gave you $" + FormatAbs(delta) + ".") : ("The host took $" + FormatAbs(delta) + " from you.")),
Type = (NotificationType)0,
ShowPopup = true,
PopupLength = 3f
});
}
break;
}
}
private static string FormatAbs(long value)
{
if (value == long.MinValue)
{
value = long.MaxValue;
}
return Math.Abs(value).ToString("N0");
}
}
}
namespace LabRP.Economy
{
public static class ClientEconomy
{
public class CachedPlayer
{
public string Name = "";
public long Balance;
}
private static readonly Dictionary<ulong, CachedPlayer> _cache = new Dictionary<ulong, CachedPlayer>();
public static long LocalBalance
{
get
{
ulong localPlatformID = PlayerIDManager.LocalPlatformID;
if (!_cache.TryGetValue(localPlatformID, out var value))
{
return 0L;
}
return value.Balance;
}
}
public static event Action OnCacheChanged;
public static event Action<long, long, BalanceReason, string> OnLocalChanged;
public static void ApplySnapshot(EconomySnapshotData data)
{
_cache.Clear();
foreach (SnapshotEntry entry in data.Entries)
{
_cache[entry.PlatformID] = new CachedPlayer
{
Name = NameUtil.Clean(entry.Name),
Balance = entry.Balance
};
}
SafeInvokeCacheChanged();
SafeInvokeLocalChanged(LocalBalance, 0L, BalanceReason.Initial, "");
}
public static void ApplyUpdate(BalanceUpdateData data)
{
bool flag = data.PlatformID == PlayerIDManager.LocalPlatformID;
if (data.Reason == BalanceReason.Denied)
{
if (flag)
{
SafeInvokeLocalChanged(LocalBalance, 0L, BalanceReason.Denied, data.Detail ?? "");
}
return;
}
if (!_cache.TryGetValue(data.PlatformID, out var value))
{
value = new CachedPlayer();
_cache[data.PlatformID] = value;
}
value.Balance = data.NewBalance;
if (string.IsNullOrEmpty(value.Name))
{
value.Name = NameUtil.Clean(GetLiveName(data.PlatformID));
}
SafeInvokeCacheChanged();
if (flag)
{
SafeInvokeLocalChanged(data.NewBalance, data.Delta, data.Reason, data.Detail ?? "");
}
}
public static void Clear()
{
_cache.Clear();
SafeInvokeCacheChanged();
}
public static string GetDisplayName(ulong platformID)
{
string text = NameUtil.Clean(GetLiveName(platformID));
if (!string.IsNullOrWhiteSpace(text))
{
return text;
}
if (_cache.TryGetValue(platformID, out var value) && !string.IsNullOrWhiteSpace(value.Name))
{
return value.Name;
}
return "Player";
}
public static long GetBalance(ulong platformID)
{
if (!_cache.TryGetValue(platformID, out var value))
{
return 0L;
}
return value.Balance;
}
public static List<KeyValuePair<ulong, CachedPlayer>> GetLeaderboard()
{
return _cache.OrderByDescending((KeyValuePair<ulong, CachedPlayer> pair) => pair.Value.Balance).ToList();
}
private static string GetLiveName(ulong platformID)
{
PlayerID playerID = PlayerIDManager.GetPlayerID(platformID);
if (playerID == null)
{
return null;
}
PlayerMetadata metadata = playerID.Metadata;
if (metadata == null)
{
return null;
}
MetadataVariable username = metadata.Username;
if (username == null)
{
return null;
}
return username.GetValue();
}
private static void SafeInvokeCacheChanged()
{
try
{
ClientEconomy.OnCacheChanged?.Invoke();
}
catch (Exception value)
{
MelonLogger.Error($"[LabRP] Error in OnCacheChanged listener: {value}");
}
}
private static void SafeInvokeLocalChanged(long balance, long delta, BalanceReason reason, string detail)
{
try
{
ClientEconomy.OnLocalChanged?.Invoke(balance, delta, reason, detail);
}
catch (Exception value)
{
MelonLogger.Error($"[LabRP] Error in OnLocalChanged listener: {value}");
}
}
}
public class PlayerRecord
{
public string Name = "Unknown";
public long Balance;
public string LastSeenUtc = "";
public string FirstSeenUtc = "";
}
public static class EconomySave
{
private static string FolderPath => Path.Combine(MelonEnvironment.UserDataDirectory, "LabRP");
private static string FilePath => Path.Combine(FolderPath, "economy.json");
private static string BackupPath => Path.Combine(FolderPath, "economy.backup.json");
private static string TempPath => Path.Combine(FolderPath, "economy.tmp.json");
private static string DamagedPath => Path.Combine(FolderPath, "economy.damaged.json");
public static Dictionary<ulong, PlayerRecord> Load()
{
Dictionary<ulong, PlayerRecord> dictionary = TryLoadFile(FilePath, "save");
bool flag = dictionary == null && File.Exists(FilePath);
if (flag)
{
PreserveDamagedMain();
}
bool flag2 = false;
if (dictionary == null)
{
dictionary = TryLoadFile(BackupPath, "backup");
flag2 = dictionary != null;
}
if (dictionary == null)
{
if (flag)
{
MelonLogger.Error("[LabRP] Both save files were unreadable. The broken save was kept as economy.damaged.json; starting a fresh economy.");
}
return new Dictionary<ulong, PlayerRecord>();
}
Sanitize(dictionary);
if (flag2)
{
Save(dictionary);
MelonLogger.Msg("[LabRP] Recovered the economy from the backup file. The unreadable save was kept as economy.damaged.json.");
}
MelonLogger.Msg($"[LabRP] Loaded {dictionary.Count} saved player record(s).");
return dictionary;
}
private static Dictionary<ulong, PlayerRecord> TryLoadFile(string path, string label)
{
try
{
if (!File.Exists(path))
{
return null;
}
return JsonConvert.DeserializeObject<Dictionary<ulong, PlayerRecord>>(File.ReadAllText(path));
}
catch (Exception ex)
{
MelonLogger.Error("[LabRP] Failed to read the " + label + " file: " + ex.Message);
return null;
}
}
private static void PreserveDamagedMain()
{
try
{
if (File.Exists(FilePath))
{
File.Copy(FilePath, DamagedPath, overwrite: true);
}
}
catch
{
}
}
private static void Sanitize(Dictionary<ulong, PlayerRecord> records)
{
foreach (ulong item in records.Keys.ToList())
{
if (records[item] == null)
{
records[item] = new PlayerRecord
{
Name = "Unknown",
Balance = LabRPConfig.ClampBalance(LabRPConfig.StartingBalance.Value),
FirstSeenUtc = DateTime.UtcNow.ToString("o"),
LastSeenUtc = DateTime.UtcNow.ToString("o")
};
}
}
foreach (PlayerRecord value in records.Values)
{
value.Balance = LabRPConfig.ClampBalance(value.Balance);
value.Name = (string.IsNullOrWhiteSpace(value.Name) ? "Unknown" : NameUtil.Clean(value.Name));
PlayerRecord playerRecord = value;
if (playerRecord.LastSeenUtc == null)
{
playerRecord.LastSeenUtc = "";
}
playerRecord = value;
if (playerRecord.FirstSeenUtc == null)
{
playerRecord.FirstSeenUtc = "";
}
}
}
public static bool Save(Dictionary<ulong, PlayerRecord> records)
{
try
{
Directory.CreateDirectory(FolderPath);
string contents = JsonConvert.SerializeObject((object)records, (Formatting)1);
File.WriteAllText(TempPath, contents);
if (File.Exists(FilePath))
{
File.Replace(TempPath, FilePath, BackupPath);
}
else
{
File.Move(TempPath, FilePath);
}
return true;
}
catch (Exception ex)
{
MelonLogger.Error("[LabRP] Failed to save economy data: " + ex.Message);
return false;
}
}
}
public static class ServerEconomy
{
private struct RequestWindow
{
public float Start;
public int Count;
public bool Warned;
}
private static Dictionary<ulong, PlayerRecord> _records = new Dictionary<ulong, PlayerRecord>();
private static bool _hosting = false;
private static bool _dirty = false;
private static float _salaryTimer = 0f;
private static float _autosaveTimer = 0f;
private const float AutosaveIntervalSeconds = 60f;
private const int MaxRequestsPerWindow = 8;
private const float RequestWindowSeconds = 5f;
private static readonly Dictionary<byte, RequestWindow> _requestWindows = new Dictionary<byte, RequestWindow>();
public static bool IsHosting => _hosting;
public static void OnServerStarted()
{
if (_dirty && _records.Count > 0 && EconomySave.Save(_records))
{
_dirty = false;
}
_records = EconomySave.Load();
_hosting = true;
_dirty = false;
_salaryTimer = 0f;
_autosaveTimer = 0f;
_requestWindows.Clear();
PlayerID localID = PlayerIDManager.LocalID;
if (localID != null)
{
EnsurePlayer(localID);
}
ClientEconomy.ApplySnapshot(BuildSnapshot());
MelonLogger.Msg("[LabRP] Economy started. Balances will be saved on this machine.");
}
public static void OnPlayerJoined(PlayerID playerID)
{
if (_hosting && playerID != null)
{
EnsurePlayer(playerID);
BroadcastSnapshot();
}
}
public static void OnPlayerLeft(PlayerID playerID)
{
if (_hosting && playerID != null)
{
TouchRecord(playerID);
_requestWindows.Remove(playerID.SmallID);
Save();
BroadcastSnapshot();
}
}
public static void OnUsernameChanged(PlayerID playerID)
{
if (_hosting && playerID != null)
{
TouchRecord(playerID);
}
}
public static void OnDisconnected()
{
if (_hosting)
{
SaveWithRetries();
MelonLogger.Msg("[LabRP] Economy saved and stopped.");
}
_hosting = false;
_salaryTimer = 0f;
_requestWindows.Clear();
}
public static void OnUpdate()
{
if (!_hosting || !NetworkInfo.HasServer)
{
return;
}
float unscaledDeltaTime = Time.unscaledDeltaTime;
float num = LabRPConfig.SalaryIntervalMinutes.Value;
if (float.IsNaN(num) || float.IsInfinity(num))
{
num = 5f;
}
float num2 = Mathf.Max(0.25f, num) * 60f;
_salaryTimer = Mathf.Min(_salaryTimer + unscaledDeltaTime, num2);
if (_salaryTimer >= num2)
{
_salaryTimer = 0f;
PayEveryoneSalary();
}
_autosaveTimer += unscaledDeltaTime;
if (_autosaveTimer >= 60f)
{
_autosaveTimer = 0f;
if (_dirty)
{
StampOnlinePlayers();
Save();
}
}
}
public static void SaveIfHosting()
{
if (_hosting)
{
StampOnlinePlayers();
SaveWithRetries();
}
}
public static void PayEveryoneSalary()
{
if (!_hosting)
{
return;
}
long num = Math.Clamp(LabRPConfig.SalaryAmount.Value, 0L, 999999999999L);
if (num <= 0)
{
return;
}
foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
{
if (playerID != null && playerID.IsValid)
{
ChangeBalance(playerID.PlatformID, num, BalanceReason.Paycheck, "Paycheck");
}
}
}
public static void TryTransfer(byte senderSmallID, ulong targetPlatformID, long amount)
{
if (!_hosting)
{
return;
}
PlayerID playerID = PlayerIDManager.GetPlayerID(senderSmallID);
if (playerID == null || !CheckRequestBudget(senderSmallID))
{
return;
}
PlayerID playerID2 = PlayerIDManager.GetPlayerID(targetPlatformID);
if (amount <= 0)
{
Deny(playerID, "Invalid amount.");
return;
}
if (playerID2 == null || !playerID2.IsValid)
{
Deny(playerID, "That player is no longer in the lobby.");
return;
}
if (playerID2.PlatformID == playerID.PlatformID)
{
Deny(playerID, "You can't pay yourself.");
return;
}
PlayerRecord playerRecord = EnsurePlayer(playerID);
PlayerRecord playerRecord2 = EnsurePlayer(playerID2);
if (playerRecord.Balance < amount)
{
Deny(playerID, "Not enough money.");
return;
}
long num = 999999999999L - playerRecord2.Balance;
if (amount > num)
{
Deny(playerID, "They can't hold that much money.");
return;
}
string text = ResolveName(playerID);
string text2 = ResolveName(playerID2);
ChangeBalance(playerID.PlatformID, -amount, BalanceReason.PaymentSent, text2);
ChangeBalance(playerID2.PlatformID, amount, BalanceReason.PaymentReceived, text);
MelonLogger.Msg($"[LabRP] {text} paid {text2} ${amount:N0}.");
}
public static void AdminGive(ulong platformID, long amount)
{
if (_hosting && amount > 0 && IsOnline(platformID))
{
ChangeBalance(platformID, amount, BalanceReason.Admin, "Host grant");
}
}
public static void AdminTake(ulong platformID, long amount)
{
if (_hosting && amount > 0 && IsOnline(platformID))
{
ChangeBalance(platformID, -amount, BalanceReason.Admin, "Host deduction");
}
}
public static void AdminSet(ulong platformID, long value)
{
if (_hosting && IsOnline(platformID))
{
PlayerRecord orCreateRecord = GetOrCreateRecord(platformID);
long num = LabRPConfig.ClampBalance(value);
long delta = num - orCreateRecord.Balance;
orCreateRecord.Balance = num;
MarkDirty();
BroadcastUpdate(platformID, orCreateRecord.Balance, delta, BalanceReason.Admin, "Host set");
}
}
public static void ResetAllBalances()
{
if (!_hosting)
{
return;
}
_records.Clear();
foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
{
if (playerID != null && playerID.IsValid)
{
EnsurePlayer(playerID);
}
}
MarkDirty();
Save();
BroadcastSnapshot();
ClientEconomy.ApplySnapshot(BuildSnapshot());
MelonLogger.Msg("[LabRP] All balances were reset by the host.");
}
public static long GetBalance(ulong platformID)
{
if (!_records.TryGetValue(platformID, out var value))
{
return 0L;
}
return value.Balance;
}
public static bool IsOnline(ulong platformID)
{
PlayerID playerID = PlayerIDManager.GetPlayerID(platformID);
if (playerID != null)
{
return playerID.IsValid;
}
return false;
}
private static bool CheckRequestBudget(byte senderSmallID)
{
float unscaledTime = Time.unscaledTime;
if (!_requestWindows.TryGetValue(senderSmallID, out var value) || unscaledTime - value.Start > 5f)
{
value = new RequestWindow
{
Start = unscaledTime,
Count = 0,
Warned = false
};
}
value.Count++;
bool num = value.Count <= 8;
if (!num && !value.Warned)
{
value.Warned = true;
MelonLogger.Warning($"[LabRP] Dropping flood of pay requests from player {senderSmallID}.");
}
_requestWindows[senderSmallID] = value;
return num;
}
private static void ChangeBalance(ulong platformID, long amount, BalanceReason reason, string detail)
{
PlayerRecord orCreateRecord = GetOrCreateRecord(platformID);
long balance = orCreateRecord.Balance;
orCreateRecord.Balance = LabRPConfig.ClampBalance(SaturatingAdd(balance, amount));
long delta = orCreateRecord.Balance - balance;
MarkDirty();
BroadcastUpdate(platformID, orCreateRecord.Balance, delta, reason, detail);
}
private static long SaturatingAdd(long a, long b)
{
long num = a + b;
if (b > 0 && num < a)
{
return long.MaxValue;
}
if (b < 0 && num > a)
{
return long.MinValue;
}
return num;
}
private static void Deny(PlayerID payer, string message)
{
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
MessageRelay.RelayModule<BalanceUpdateMessage, BalanceUpdateData>(new BalanceUpdateData
{
PlatformID = payer.PlatformID,
NewBalance = GetBalance(payer.PlatformID),
Delta = 0L,
Reason = BalanceReason.Denied,
Detail = message
}, new MessageRoute(payer.SmallID, (NetworkChannel)0));
}
private static void BroadcastUpdate(ulong platformID, long newBalance, long delta, BalanceReason reason, string detail)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
MessageRelay.RelayModule<BalanceUpdateMessage, BalanceUpdateData>(new BalanceUpdateData
{
PlatformID = platformID,
NewBalance = newBalance,
Delta = delta,
Reason = reason,
Detail = (detail ?? "")
}, new MessageRoute((RelayType)2, (NetworkChannel)0));
}
private static void BroadcastSnapshot()
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
MessageRelay.RelayModule<EconomySnapshotMessage, EconomySnapshotData>(BuildSnapshot(), new MessageRoute((RelayType)2, (NetworkChannel)0));
}
private static EconomySnapshotData BuildSnapshot()
{
EconomySnapshotData economySnapshotData = new EconomySnapshotData();
foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
{
if (playerID != null && playerID.IsValid)
{
PlayerRecord orCreateRecord = GetOrCreateRecord(playerID.PlatformID);
economySnapshotData.Entries.Add(new SnapshotEntry
{
PlatformID = playerID.PlatformID,
Name = ResolveName(playerID),
Balance = orCreateRecord.Balance
});
}
}
return economySnapshotData;
}
private static PlayerRecord EnsurePlayer(PlayerID playerID)
{
PlayerRecord orCreateRecord = GetOrCreateRecord(playerID.PlatformID);
TouchRecord(playerID);
return orCreateRecord;
}
private static PlayerRecord GetOrCreateRecord(ulong platformID)
{
if (!_records.TryGetValue(platformID, out var value))
{
value = new PlayerRecord
{
Name = "Unknown",
Balance = LabRPConfig.ClampBalance(LabRPConfig.StartingBalance.Value),
FirstSeenUtc = DateTime.UtcNow.ToString("o"),
LastSeenUtc = DateTime.UtcNow.ToString("o")
};
_records[platformID] = value;
MarkDirty();
}
return value;
}
private static void TouchRecord(PlayerID playerID)
{
if (playerID != null && _records.TryGetValue(playerID.PlatformID, out var value))
{
PlayerMetadata metadata = playerID.Metadata;
object name;
if (metadata == null)
{
name = null;
}
else
{
MetadataVariable username = metadata.Username;
name = ((username != null) ? username.GetValue() : null);
}
string text = NameUtil.Clean((string)name);
if (!string.IsNullOrWhiteSpace(text) && text != value.Name)
{
value.Name = text;
MarkDirty();
}
value.LastSeenUtc = DateTime.UtcNow.ToString("o");
}
}
private static void StampOnlinePlayers()
{
foreach (PlayerID playerID in PlayerIDManager.PlayerIDs)
{
if (playerID != null && playerID.IsValid)
{
TouchRecord(playerID);
}
}
}
public static string ResolveName(PlayerID playerID)
{
if (playerID == null)
{
return "Unknown";
}
PlayerMetadata metadata = playerID.Metadata;
object name;
if (metadata == null)
{
name = null;
}
else
{
MetadataVariable username = metadata.Username;
name = ((username != null) ? username.GetValue() : null);
}
string text = NameUtil.Clean((string)name);
if (!string.IsNullOrWhiteSpace(text))
{
return text;
}
if (!_records.TryGetValue(playerID.PlatformID, out var value) || string.IsNullOrWhiteSpace(value.Name) || !(value.Name != "Unknown"))
{
return $"Player {playerID.SmallID}";
}
return value.Name;
}
private static void MarkDirty()
{
_dirty = true;
}
private static void Save()
{
if (EconomySave.Save(_records))
{
_dirty = false;
}
}
private static void SaveWithRetries()
{
for (int i = 0; i < 3; i++)
{
if (EconomySave.Save(_records))
{
_dirty = false;
return;
}
Thread.Sleep(150);
}
MelonLogger.Error("[LabRP] Could not save the economy after multiple attempts. Changes since the last successful save may be lost.");
}
}
}