using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
[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("NoSharedMoney")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("NoSharedMoney")]
[assembly: AssemblyTitle("NoSharedMoney")]
[assembly: AssemblyVersion("1.0.0.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 NoSharedMoney
{
public static class ClientMoney
{
public static int Money { get; private set; }
public static void Apply(int money)
{
int num = money - Money;
Money = money;
Refl.SetStaticMoney(money);
Refl.RaiseOnItemSold();
PlayerUI.SetMoney(money, (num < 0) ? (-num) : num, num > 0);
}
public static void Reset()
{
Money = 0;
Refl.SetStaticMoney(0);
}
}
public struct MoneySyncBroadcast : IBroadcast
{
public int Money;
}
public struct ModHandshakeBroadcast : IBroadcast
{
public int Version;
}
public static class Net
{
public const int ProtocolVersion = 1;
private static bool _serializersRegistered;
private static bool _serverRegistered;
private static bool _clientRegistered;
public static void RegisterSerializers()
{
if (!_serializersRegistered)
{
_serializersRegistered = true;
GenericWriter<MoneySyncBroadcast>.SetWrite((Action<Writer, MoneySyncBroadcast>)delegate(Writer w, MoneySyncBroadcast v)
{
w.WriteInt32(v.Money);
});
GenericReader<MoneySyncBroadcast>.SetRead((Func<Reader, MoneySyncBroadcast>)((Reader r) => new MoneySyncBroadcast
{
Money = r.ReadInt32()
}));
GenericWriter<ModHandshakeBroadcast>.SetWrite((Action<Writer, ModHandshakeBroadcast>)delegate(Writer w, ModHandshakeBroadcast v)
{
w.WriteInt32(v.Version);
});
GenericReader<ModHandshakeBroadcast>.SetRead((Func<Reader, ModHandshakeBroadcast>)((Reader r) => new ModHandshakeBroadcast
{
Version = r.ReadInt32()
}));
}
}
public static void TryRegisterHandlers()
{
if (!((Object)(object)InstanceFinder.NetworkManager == (Object)null))
{
RegisterSerializers();
if (!_serverRegistered && (Object)(object)InstanceFinder.ServerManager != (Object)null)
{
_serverRegistered = true;
InstanceFinder.ServerManager.RegisterBroadcast<ModHandshakeBroadcast>((Action<NetworkConnection, ModHandshakeBroadcast, Channel>)OnHandshakeFromClient, true);
Plugin.Log.LogDebug((object)"Registered server broadcast handler.");
}
if (!_clientRegistered && (Object)(object)InstanceFinder.ClientManager != (Object)null)
{
_clientRegistered = true;
InstanceFinder.ClientManager.RegisterBroadcast<MoneySyncBroadcast>((Action<MoneySyncBroadcast, Channel>)OnMoneyFromServer);
Plugin.Log.LogDebug((object)"Registered client broadcast handler.");
}
}
}
private static void OnHandshakeFromClient(NetworkConnection conn, ModHandshakeBroadcast msg, Channel channel)
{
Wallets.MarkModded(conn, msg.Version);
}
private static void OnMoneyFromServer(MoneySyncBroadcast msg, Channel channel)
{
ClientMoney.Apply(msg.Money);
}
public static void SendHandshake()
{
if (!InstanceFinder.IsClientStarted || (Object)(object)InstanceFinder.ClientManager == (Object)null)
{
return;
}
try
{
InstanceFinder.ClientManager.Broadcast<ModHandshakeBroadcast>(new ModHandshakeBroadcast
{
Version = 1
}, (Channel)0);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Failed to send handshake: " + ex));
}
}
public static void SendMoney(NetworkConnection conn, int money)
{
if (!InstanceFinder.IsServerStarted || (Object)(object)InstanceFinder.ServerManager == (Object)null || conn == (NetworkConnection)null || !conn.IsActive)
{
return;
}
try
{
InstanceFinder.ServerManager.Broadcast<MoneySyncBroadcast>(conn, new MoneySyncBroadcast
{
Money = money
}, true, (Channel)0);
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Failed to send money to client " + conn.ClientId + ": " + ex));
}
}
}
public static class PurchaseContext
{
public static Player Buyer;
private static int _depth;
public static bool Active
{
get
{
if (_depth > 0)
{
return (Object)(object)Buyer != (Object)null;
}
return false;
}
}
public static void Enter(Player buyer)
{
_depth++;
if ((Object)(object)buyer != (Object)null)
{
Buyer = buyer;
}
}
public static void Exit()
{
_depth--;
if (_depth <= 0)
{
_depth = 0;
Buyer = null;
}
}
}
public static class Patches
{
private static readonly string[] SpendMethods = new string[8] { "BuyItem", "BuyBait", "BuyAttachment", "BuyBulletUpgrade", "BuySharpnessUpgrade", "BuyBoatMotor", "BuyBoatRadar", "UnlockPocket" };
public static void ApplyAll(Harmony harmony)
{
PatchMoneyManager(harmony);
PatchSaveManager(harmony);
PatchSpendSites(harmony);
}
private static void PatchMoneyManager(Harmony harmony)
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Expected O, but got Unknown
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Expected O, but got Unknown
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Expected O, but got Unknown
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Expected O, but got Unknown
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
//IL_00ed: Expected O, but got Unknown
//IL_0104: Unknown result type (might be due to invalid IL or missing references)
//IL_0111: Expected O, but got Unknown
//IL_0128: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Expected O, but got Unknown
Type typeFromHandle = typeof(MoneyManager);
Type typeFromHandle2 = typeof(Patches);
harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "AddMoney", (Type[])null, (Type[])null), new HarmonyMethod(typeFromHandle2, "AddMoneyPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "RemoveMoney", (Type[])null, (Type[])null), new HarmonyMethod(typeFromHandle2, "RemoveMoneyPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "SellItem", (Type[])null, (Type[])null), new HarmonyMethod(typeFromHandle2, "SellItemPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "CanAfford", (Type[])null, (Type[])null), new HarmonyMethod(typeFromHandle2, "CanAffordPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "OnChangeMoney", (Type[])null, (Type[])null), new HarmonyMethod(typeFromHandle2, "SkipPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "OnStartServer", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeFromHandle2, "OnStartServerPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "OnStartClient", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeFromHandle2, "OnStartClientPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
harmony.Patch((MethodBase)AccessTools.Method(typeFromHandle, "OnStopClient", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeFromHandle2, "OnStopClientPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
private static void PatchSaveManager(Harmony harmony)
{
//IL_0027: 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_0049: Expected O, but got Unknown
//IL_0049: Expected O, but got Unknown
harmony.Patch((MethodBase)AccessTools.Method(typeof(SaveManager), "SaveServer", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "SaveServerPrefix", (Type[])null), new HarmonyMethod(typeof(Patches), "SaveServerPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
private static void PatchSpendSites(Harmony harmony)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Expected O, but got Unknown
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Expected O, but got Unknown
HarmonyMethod val = new HarmonyMethod(typeof(Patches), "SpendPrefix", (Type[])null);
HarmonyMethod val2 = new HarmonyMethod(typeof(Patches), "SpendFinalizer", (Type[])null);
MethodInfo[] methods = typeof(Server).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
List<string> list = new List<string>();
string[] spendMethods = SpendMethods;
foreach (string text in spendMethods)
{
string text2 = "RpcLogic___" + text + "___";
bool flag = false;
MethodInfo[] array = methods;
foreach (MethodInfo methodInfo in array)
{
if (methodInfo.Name.StartsWith(text2, StringComparison.Ordinal))
{
harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null);
list.Add(text);
flag = true;
break;
}
}
if (!flag)
{
Plugin.Log.LogError((object)("Could not find Server." + text2 + "* - purchases through '" + text + "' will be charged to the wrong wallet."));
}
}
Plugin.Log.LogInfo((object)string.Format("Hooked {0}/{1} purchase paths: {2}", list.Count, SpendMethods.Length, string.Join(", ", list.ToArray())));
}
private static bool SkipPrefix()
{
return false;
}
private static bool AddMoneyPrefix(int amount, Player player)
{
if (!InstanceFinder.IsServerStarted)
{
return false;
}
Wallets.Add(player, amount);
Refl.PlayMoneySounds(increase: true, player);
return false;
}
private static bool RemoveMoneyPrefix(int amount, Player player)
{
if (!InstanceFinder.IsServerStarted)
{
return false;
}
Wallets.Remove(player, amount);
Refl.PlayMoneySounds(increase: false, player);
return false;
}
private static bool SellItemPrefix(Item item)
{
if (!InstanceFinder.IsServerStarted || (Object)(object)item == (Object)null)
{
return false;
}
Player val = item.LastHolder;
if ((Object)(object)val == (Object)null)
{
if (!Plugin.UnclaimedSalesGoToHost.Value)
{
Plugin.Log.LogInfo((object)$"Sold '{item.GetName()}' for {item.TotalWorth} but nobody had held it; income discarded.");
return false;
}
val = Player.LocalPlayer;
if ((Object)(object)val == (Object)null)
{
return false;
}
Plugin.Log.LogInfo((object)$"Sold '{item.GetName()}' for {item.TotalWorth} but nobody had held it; credited the host.");
}
Wallets.Add(val, item.TotalWorth);
Refl.PlayMoneySounds(increase: true, val);
return false;
}
private static bool CanAffordPrefix(int cost, ref bool __result)
{
if (InstanceFinder.IsServerStarted && PurchaseContext.Active)
{
__result = cost <= Wallets.Get(PurchaseContext.Buyer);
return false;
}
return true;
}
private static void SpendPrefix(object[] __args)
{
PurchaseContext.Enter(ResolveBuyer(__args));
}
private static void SpendFinalizer()
{
PurchaseContext.Exit();
}
private static Player ResolveBuyer(object[] args)
{
if (args == null)
{
return null;
}
object[] array = args;
foreach (object obj in array)
{
Player val = (Player)((obj is Player) ? obj : null);
if (val != null)
{
return val;
}
}
array = args;
foreach (object obj2 in array)
{
Item val2 = (Item)((obj2 is Item) ? obj2 : null);
if (val2 != null && (Object)(object)val2 != (Object)null)
{
Player val3 = val2.Holder;
if ((Object)(object)val3 == (Object)null)
{
val3 = val2.SyncedHolder;
}
if ((Object)(object)val3 == (Object)null)
{
val3 = val2.LastHolder;
}
if ((Object)(object)val3 != (Object)null)
{
return val3;
}
}
}
Plugin.Log.LogWarning((object)"Could not work out who is buying; the purchase will be refused.");
return null;
}
private static void SaveServerPrefix(out int __state)
{
__state = Refl.GetStaticMoney();
if (InstanceFinder.IsServerStarted)
{
Refl.SetStaticMoney(Wallets.Total());
}
}
private static void SaveServerPostfix(int __state)
{
Refl.SetStaticMoney(__state);
if (InstanceFinder.IsServerStarted)
{
Wallets.Save();
}
}
private static void OnStartServerPostfix()
{
Wallets.LoadForServer();
}
private static void OnStartClientPostfix()
{
ClientMoney.Reset();
Net.SendHandshake();
}
private static void OnStopClientPostfix()
{
ClientMoney.Reset();
if (!InstanceFinder.IsServerStarted)
{
Wallets.Save();
Wallets.OnServerStopped();
}
}
}
[BepInPlugin("dazed.howtofish.nosharedmoney", "NoSharedMoney", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
public const string Guid = "dazed.howtofish.nosharedmoney";
public const string Name = "NoSharedMoney";
public const string Version = "1.0.0";
public static ManualLogSource Log;
public static ConfigEntry<int> StartingMoney;
public static ConfigEntry<bool> WarnInChat;
public static ConfigEntry<bool> UnclaimedSalesGoToHost;
private Harmony _harmony;
private float _nextReconcile;
private void Awake()
{
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
StartingMoney = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StartingMoney", 0, "How much money a player who has never played this save starts with.");
WarnInChat = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "WarnInChat", true, "Host only: announce in chat when a connected player is missing the mod.");
UnclaimedSalesGoToHost = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UnclaimedSalesGoToHost", true, "If something is sold that no player ever held, credit the host instead of discarding the money.");
if (!Refl.Resolve())
{
Log.LogError((object)"Aborting: the game's money internals do not look the way this mod expects.");
return;
}
Net.RegisterSerializers();
_harmony = new Harmony("dazed.howtofish.nosharedmoney");
Patches.ApplyAll(_harmony);
Server.OnServerStopped += OnServerStopped;
Log.LogInfo((object)"NoSharedMoney 1.0.0 loaded. Money is now per-player; every player needs this mod installed.");
}
private void OnDestroy()
{
Server.OnServerStopped -= OnServerStopped;
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
private static void OnServerStopped()
{
Wallets.Save();
Wallets.OnServerStopped();
}
private void Update()
{
Net.TryRegisterHandlers();
if (!(Time.unscaledTime < _nextReconcile))
{
_nextReconcile = Time.unscaledTime + 0.25f;
if (InstanceFinder.IsServerStarted)
{
Wallets.Reconcile();
}
}
}
}
public static class Refl
{
private static FieldInfo _moneyBackingField;
private static FieldInfo _onItemSoldField;
private static MethodInfo _moneySound;
private static MethodInfo _observerMoneySound;
public static bool Resolve()
{
bool flag = true;
_moneyBackingField = AccessTools.Field(typeof(MoneyManager), "<Money>k__BackingField");
flag &= Require(_moneyBackingField, "MoneyManager.<Money>k__BackingField");
_onItemSoldField = AccessTools.Field(typeof(MoneyManager), "OnItemSold");
flag &= Require(_onItemSoldField, "MoneyManager.OnItemSold");
_moneySound = AccessTools.Method(typeof(MoneyManager), "MoneySound", new Type[2]
{
typeof(bool),
typeof(Player)
}, (Type[])null);
flag &= Require(_moneySound, "MoneyManager.MoneySound");
_observerMoneySound = AccessTools.Method(typeof(MoneyManager), "ObserverMoneySound", new Type[2]
{
typeof(bool),
typeof(Player)
}, (Type[])null);
return flag & Require(_observerMoneySound, "MoneyManager.ObserverMoneySound");
}
private static bool Require(MemberInfo member, string name)
{
if (member != null)
{
return true;
}
Plugin.Log.LogError((object)("Could not find " + name + ". The game has probably updated; NoSharedMoney will not work correctly."));
return false;
}
public static void SetStaticMoney(int value)
{
_moneyBackingField?.SetValue(null, value);
}
public static int GetStaticMoney()
{
if (_moneyBackingField == null)
{
return 0;
}
return (int)_moneyBackingField.GetValue(null);
}
public static void RaiseOnItemSold()
{
(_onItemSoldField?.GetValue(null) as Action)?.Invoke();
}
public static void PlayMoneySounds(bool increase, Player player)
{
MoneyManager instance = MoneyManager.Instance;
if ((Object)(object)instance == (Object)null || (Object)(object)player == (Object)null)
{
return;
}
object[] parameters = new object[2] { increase, player };
try
{
_observerMoneySound?.Invoke(instance, parameters);
_moneySound?.Invoke(instance, parameters);
}
catch (Exception ex)
{
Plugin.Log.LogWarning((object)("Money sound failed: " + ex.Message));
}
}
}
public static class Wallets
{
private const ulong SyntheticKeyBase = 18446462598732840960uL;
private static readonly Dictionary<ulong, int> _wallets = new Dictionary<ulong, int>();
private static readonly Dictionary<ulong, int> _lastSent = new Dictionary<ulong, int>();
private static readonly HashSet<int> _moddedClientIds = new HashSet<int>();
private static readonly Dictionary<int, float> _firstSeen = new Dictionary<int, float>();
private static readonly HashSet<int> _warnedClientIds = new HashSet<int>();
private static string _saveName;
private static int _unassignedPot;
private static bool _loaded;
public static bool IsModded(NetworkConnection conn)
{
if (conn != (NetworkConnection)null)
{
return _moddedClientIds.Contains(conn.ClientId);
}
return false;
}
public static void LoadForServer()
{
_wallets.Clear();
_lastSent.Clear();
_moddedClientIds.Clear();
_firstSeen.Clear();
_warnedClientIds.Clear();
_unassignedPot = 0;
_loaded = true;
_saveName = ((SaveManager.CurServerSave != null) ? SaveManager.CurServerSave.Name : null);
WalletFile walletFile = WalletStore.Load(_saveName);
if (walletFile != null)
{
foreach (WalletEntry wallet in walletFile.Wallets)
{
if (ulong.TryParse(wallet.SteamID, out var result) && result != 0L)
{
_wallets[result] = Mathf.Max(0, wallet.Money);
}
}
Plugin.Log.LogInfo((object)$"Loaded {_wallets.Count} individual wallet(s) totalling {Total()} for save '{_saveName}'.");
}
else
{
MigrateSharedPot();
Save();
}
}
private static void MigrateSharedPot()
{
int num = ((SaveManager.CurServerSave != null) ? SaveManager.CurServerSave.Money : 0);
if (num <= 0)
{
Plugin.Log.LogInfo((object)"No shared balance to migrate; everyone starts from the configured amount.");
return;
}
List<ulong> list = new List<ulong>();
if (SaveManager.CurServerSave != null && SaveManager.CurServerSave.Players != null)
{
foreach (SavedPlayer player in SaveManager.CurServerSave.Players)
{
if (player != null && player.SteamID != 0L && !list.Contains(player.SteamID))
{
list.Add(player.SteamID);
}
}
}
if (list.Count == 0)
{
_unassignedPot = num;
Plugin.Log.LogInfo((object)$"Migrating shared balance of {num}: no player records in the save, so it goes to the first player who joins.");
return;
}
int num2 = num / list.Count;
int num3 = num - num2 * list.Count;
for (int i = 0; i < list.Count; i++)
{
_wallets[list[i]] = num2 + ((i == 0) ? num3 : 0);
}
Plugin.Log.LogInfo((object)$"Migrated shared balance of {num} evenly across {list.Count} saved player(s) ({num2} each, remainder {num3} to the first).");
}
public static void OnServerStopped()
{
_loaded = false;
_wallets.Clear();
_lastSent.Clear();
_moddedClientIds.Clear();
_firstSeen.Clear();
_warnedClientIds.Clear();
_saveName = null;
_unassignedPot = 0;
}
private static ulong KeyFor(Player player)
{
if ((Object)(object)player == (Object)null)
{
return 0uL;
}
ulong steamID = player.SteamID;
if (steamID != 0L)
{
return steamID;
}
NetworkConnection owner = ((NetworkBehaviour)player).Owner;
if (owner != (NetworkConnection)null && owner.ClientId >= 0)
{
return (ulong)(-281474976710656L | (uint)owner.ClientId);
}
return 0uL;
}
private static bool IsSynthetic(ulong key)
{
return (key & 0xFFFF000000000000uL) == 18446462598732840960uL;
}
public static int Get(Player player)
{
ulong num = KeyFor(player);
if (num == 0L)
{
return 0;
}
return EnsureWallet(num);
}
private static int EnsureWallet(ulong key)
{
if (_wallets.TryGetValue(key, out var value))
{
return value;
}
int num = Plugin.StartingMoney.Value;
if (_unassignedPot > 0)
{
num += _unassignedPot;
Plugin.Log.LogInfo((object)$"Handed the migrated shared balance of {_unassignedPot} to the first joining player.");
_unassignedPot = 0;
}
num = Mathf.Max(0, num);
_wallets[key] = num;
return num;
}
public static void Add(Player player, int amount)
{
Set(player, Get(player) + Mathf.Abs(amount));
}
public static void Remove(Player player, int amount)
{
Set(player, Get(player) - Mathf.Abs(amount));
}
public static void Set(Player player, int value)
{
ulong num = KeyFor(player);
if (num == 0L)
{
Plugin.Log.LogWarning((object)"Tried to change the wallet of a player with no identity; ignoring.");
return;
}
EnsureWallet(num);
_wallets[num] = Mathf.Clamp(value, 0, int.MaxValue);
Push(player);
}
public static int Total()
{
int num = 0;
foreach (int value in _wallets.Values)
{
num += value;
}
return num;
}
public static void Push(Player player)
{
ulong num = KeyFor(player);
if (num == 0L || !_wallets.TryGetValue(num, out var value) || (_lastSent.TryGetValue(num, out var value2) && value2 == value))
{
return;
}
_lastSent[num] = value;
if ((Object)(object)Player.LocalPlayer == (Object)(object)player)
{
ClientMoney.Apply(value);
return;
}
NetworkConnection owner = ((NetworkBehaviour)player).Owner;
if (!(owner == (NetworkConnection)null) && IsModded(owner))
{
Net.SendMoney(owner, value);
}
}
public static void MarkModded(NetworkConnection conn, int version)
{
if (conn == (NetworkConnection)null)
{
return;
}
if (version != 1)
{
Plugin.Log.LogWarning((object)$"Client {conn.ClientId} runs NoSharedMoney protocol {version}, this server is {1}. Wallet sync may misbehave; use matching mod versions.");
}
if (_moddedClientIds.Add(conn.ClientId))
{
Plugin.Log.LogInfo((object)$"Client {conn.ClientId} has NoSharedMoney installed.");
}
foreach (Player player in PlayerManager.Players)
{
if ((Object)(object)player != (Object)null && ((NetworkBehaviour)player).Owner == conn)
{
_lastSent.Remove(KeyFor(player));
Push(player);
break;
}
}
}
public static void Reconcile()
{
if (!_loaded || !InstanceFinder.IsServerStarted)
{
return;
}
List<Player> players = PlayerManager.Players;
if (players == null)
{
return;
}
for (int i = 0; i < players.Count; i++)
{
Player val = players[i];
if ((Object)(object)val == (Object)null || ((NetworkBehaviour)val).IsDeinitializing)
{
continue;
}
ulong num = KeyFor(val);
if (num == 0L)
{
continue;
}
EnsureWallet(num);
Push(val);
NetworkConnection owner = ((NetworkBehaviour)val).Owner;
if (owner == (NetworkConnection)null || (Object)(object)val == (Object)(object)Player.LocalPlayer)
{
continue;
}
if (!_firstSeen.ContainsKey(owner.ClientId))
{
_firstSeen[owner.ClientId] = Time.time;
}
if (!IsModded(owner) && Time.time - _firstSeen[owner.ClientId] > 10f && _warnedClientIds.Add(owner.ClientId))
{
string text = "'" + val.SteamName + "' does not have NoSharedMoney installed. Their money display will be wrong and they will not be able to buy anything.";
Plugin.Log.LogWarning((object)text);
if (Plugin.WarnInChat.Value)
{
ChatManager.ChatMessage("<i>[NoSharedMoney] " + text + "</i>");
}
}
}
}
public static void Save()
{
if (!_loaded || string.IsNullOrEmpty(_saveName))
{
return;
}
WalletFile walletFile = new WalletFile();
foreach (KeyValuePair<ulong, int> wallet in _wallets)
{
if (!IsSynthetic(wallet.Key))
{
walletFile.Wallets.Add(new WalletEntry
{
SteamID = wallet.Key.ToString(),
Money = wallet.Value
});
}
}
WalletStore.Save(_saveName, walletFile);
Plugin.Log.LogInfo((object)$"Saved {walletFile.Wallets.Count} wallet(s) totalling {Total()} for save '{_saveName}'.");
}
}
[Serializable]
public class WalletEntry
{
public string SteamID;
public int Money;
}
[Serializable]
public class WalletFile
{
public List<WalletEntry> Wallets = new List<WalletEntry>();
}
public static class WalletStore
{
private static string Folder => Path.Combine(Application.persistentDataPath, "NoSharedMoney");
private static string PathFor(string saveName)
{
char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
foreach (char oldChar in invalidFileNameChars)
{
saveName = saveName.Replace(oldChar, '_');
}
return Path.Combine(Folder, saveName + ".json");
}
public static WalletFile Load(string saveName)
{
if (string.IsNullOrEmpty(saveName))
{
return null;
}
try
{
string path = PathFor(saveName);
if (!File.Exists(path))
{
return null;
}
WalletFile walletFile = JsonConvert.DeserializeObject<WalletFile>(File.ReadAllText(path));
if (walletFile == null)
{
return null;
}
if (walletFile.Wallets == null)
{
walletFile.Wallets = new List<WalletEntry>();
}
return walletFile;
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Failed to read wallets: " + ex));
return null;
}
}
public static void Save(string saveName, WalletFile file)
{
if (string.IsNullOrEmpty(saveName))
{
return;
}
try
{
Directory.CreateDirectory(Folder);
string text = JsonConvert.SerializeObject((object)file, (Formatting)1);
if (string.IsNullOrEmpty(text) || text == "{}")
{
Plugin.Log.LogError((object)"Wallet serialization produced nothing; refusing to overwrite the file.");
}
else
{
File.WriteAllText(PathFor(saveName), text);
}
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("Failed to write wallets: " + ex));
}
}
}
}