Decompiled source of HostOwner v0.1.0

HostOwner.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace HostOwner;

public static class HostOwnerApi
{
	internal static readonly HashSet<string> Prefabs = new HashSet<string>(StringComparer.Ordinal);

	internal static readonly HashSet<string> Components = new HashSet<string>(StringComparer.Ordinal);

	public static void AddPrefab(string prefabName)
	{
		if (!string.IsNullOrEmpty(prefabName) && Prefabs.Add(prefabName.Trim()))
		{
			HostOwnerPlugin.Invalidate();
		}
	}

	public static void AddComponent(string typeName)
	{
		if (!string.IsNullOrEmpty(typeName) && Components.Add(typeName.Trim()))
		{
			HostOwnerPlugin.Invalidate();
		}
	}

	public static bool IsOwnedKind(int prefabHash)
	{
		HostOwnerPlugin instance = HostOwnerPlugin.Instance;
		if ((Object)(object)instance != (Object)null)
		{
			return instance.IsWanted(prefabHash);
		}
		return false;
	}
}
[BepInPlugin("j1ga.hostowner", "Host Owner", "0.1.0")]
public class HostOwnerPlugin : BaseUnityPlugin
{
	public const string Guid = "j1ga.hostowner";

	public const string Name = "Host Owner";

	public const string Version = "0.1.0";

	private const int MaxErrors = 25;

	private ConfigEntry<bool> _cfgEnabled;

	private ConfigEntry<bool> _cfgDebug;

	private ConfigEntry<float> _cfgInterval;

	private ConfigEntry<float> _cfgYield;

	private ConfigEntry<bool> _cfgStations;

	private ConfigEntry<bool> _cfgBosses;

	private ConfigEntry<string> _cfgComponents;

	private ConfigEntry<string> _cfgPrefabs;

	private static readonly Type[] StationTypes = new Type[5]
	{
		typeof(Smelter),
		typeof(CookingStation),
		typeof(Beehive),
		typeof(SapCollector),
		typeof(Fermenter)
	};

	private readonly HashSet<string> _warned = new HashSet<string>();

	public static HostOwnerPlugin Instance;

	private readonly Dictionary<int, string> _wanted = new Dictionary<int, string>();

	private ZNetScene _indexedScene;

	private bool _indexDirty;

	private readonly List<ZDO> _near = new List<ZDO>();

	private float _nextPass;

	private readonly Dictionary<ZDOID, int> _held = new Dictionary<ZDOID, int>();

	private readonly Dictionary<ZDOID, float> _yielded = new Dictionary<ZDOID, float>();

	private readonly List<ZDOID> _expired = new List<ZDOID>();

	private ZNetScene _recordsScene;

	private int _lastWanted;

	private int _lastOwned;

	private int _lastClaimed;

	private int _lastYielded;

	private int _claimedTotal;

	private int _yieldedTotal;

	private int _passes;

	private int _errorCount;

	private bool _disabledByErrors;

	private readonly HashSet<string> _loggedErrors = new HashSet<string>();

	private bool Hosting
	{
		get
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance != (Object)null && instance.IsServer() && !instance.IsDedicated() && (Object)(object)Player.m_localPlayer != (Object)null && ZDOMan.instance != null)
			{
				return (Object)(object)ZNetScene.instance != (Object)null;
			}
			return false;
		}
	}

	internal bool IsWanted(int prefabHash)
	{
		return _wanted.ContainsKey(prefabHash);
	}

	private void RegisterCommands()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		new ConsoleCommand("hostowner", "Host Owner. 'hostowner status' - what the host owns; 'hostowner list' - the kinds; 'hostowner now' - a pass right away", (ConsoleEvent)delegate(ConsoleEventArgs args)
		{
			RunCommand(args);
		}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private static void Say(ConsoleEventArgs args, string text)
	{
		if (args != null && (Object)(object)args.Context != (Object)null)
		{
			args.Context.AddString(text);
		}
	}

	private void RunCommand(ConsoleEventArgs args)
	{
		try
		{
			switch ((args.Args.Length > 1) ? args.Args[1].ToLowerInvariant() : "")
			{
			case "status":
				Say(args, StatusText());
				break;
			case "list":
				Say(args, _wanted.Count + " kinds: " + KindList(0));
				break;
			case "now":
				if (!Hosting)
				{
					Say(args, "Not hosting a world with a player; nothing to do.");
					break;
				}
				_indexedScene = null;
				_nextPass = Time.time + _cfgInterval.Value;
				Pass();
				Say(args, StatusText());
				break;
			default:
				Say(args, "hostowner status | list | now");
				break;
			}
		}
		catch (Exception ex)
		{
			Say(args, "hostowner: " + ex.Message);
			Fail("command", ex);
		}
	}

	private string StatusText()
	{
		if (_disabledByErrors)
		{
			return "Host Owner is inert after errors, see the log.";
		}
		if (!_cfgEnabled.Value)
		{
			return "Host Owner is disabled in the config.";
		}
		ZNet instance = ZNet.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return "Host Owner: no world loaded.";
		}
		if (!instance.IsServer())
		{
			return "Host Owner: this client is not the host, nothing is taken over here.";
		}
		if (instance.IsDedicated())
		{
			return "Host Owner: a dedicated server has no player and no active area, nothing is taken over here.";
		}
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.Append(_wanted.Count).Append(" kinds; last pass: ").Append(_lastWanted)
			.Append(" such objects in the active area, ")
			.Append(_lastOwned)
			.Append(" owned by the host, ")
			.Append(_lastClaimed)
			.Append(" taken just now, ")
			.Append(_lastYielded)
			.Append(" left to a player who claimed them back; ")
			.Append(_claimedTotal)
			.Append(" taken and ")
			.Append(_yieldedTotal)
			.Append(" yielded in ")
			.Append(_passes)
			.Append(" passes, every ")
			.Append(F(_cfgInterval.Value))
			.Append(" s.");
		return stringBuilder.ToString();
	}

	private void BindConfig()
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Expected O, but got Unknown
		_cfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 General", "Enabled", true, "Master switch. Off: objects already taken stay with the host until it walks away, as with any owner.");
		_cfgDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("01 General", "Debug", false, "Log every object taken over.");
		_cfgInterval = ((BaseUnityPlugin)this).Config.Bind<float>("01 General", "Interval", 2f, new ConfigDescription("Seconds between two passes over the host's active area. Vanilla hands ownership out every 2 s.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), new object[0]));
		_cfgYield = ((BaseUnityPlugin)this).Config.Bind<float>("01 General", "YieldSeconds", 30f, new ConfigDescription("When a player's client takes an object back right after the host took it - a mod or the game claiming it on purpose, e.g. a taunt pulling a monster or a player using a cart - leave that object alone for this many seconds instead of fighting over it. 0: never yield.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), new object[0]));
		_cfgStations = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Objects", "Stations", true, "Every prefab with a Smelter (smelter, blast furnace, charcoal kiln, spinning wheel, windmill, eitr refinery), CookingStation (cooking stations, oven), Beehive, SapCollector or Fermenter component, from any mod too.");
		_cfgBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("02 Objects", "Bosses", false, "Every creature flagged as a boss (Character.m_boss). Its AI then runs on the host, and mods that read the boss on its owner see every hit.");
		_cfgComponents = ((BaseUnityPlugin)this).Config.Bind<string>("02 Objects", "Components", "", "Component type names from the game, comma separated: every prefab carrying one of them is taken over. Example: Plant,Fireplace,Tameable. Unknown names are reported in the log once.");
		_cfgPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("02 Objects", "Prefabs", "", "Prefab names, comma separated, on top of the groups above. Example: piece_bathtub,Eikthyr. Players are never taken, whatever is listed.");
		((BaseUnityPlugin)this).Config.SettingChanged += delegate
		{
			_indexDirty = true;
		};
	}

	private static IEnumerable<string> Split(string text)
	{
		if (string.IsNullOrEmpty(text))
		{
			yield break;
		}
		try
		{
			string[] array = text.Split(',');
			foreach (string raw in array)
			{
				string s = raw.Trim();
				if (s.Length > 0)
				{
					yield return s;
				}
			}
		}
		finally
		{
		}
	}

	private Type ResolveComponent(string name)
	{
		Type type = typeof(ZNetView).Assembly.GetType(name) ?? Type.GetType(name);
		if (type == null || !typeof(Component).IsAssignableFrom(type))
		{
			if (_warned.Add("type:" + name))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Component type '" + name + "' was not found in the game; ignored."));
			}
			return null;
		}
		return type;
	}

	private void BuildIndex(ZNetScene zs)
	{
		_wanted.Clear();
		if ((Object)(object)zs != (Object)(object)_recordsScene)
		{
			ForgetWorld();
			_recordsScene = zs;
		}
		_indexedScene = zs;
		if (zs.m_prefabs == null)
		{
			return;
		}
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
		foreach (string item in Split(_cfgPrefabs.Value))
		{
			hashSet.Add(item);
		}
		foreach (string prefab in HostOwnerApi.Prefabs)
		{
			hashSet.Add(prefab);
		}
		List<Type> list = new List<Type>();
		if (_cfgStations.Value)
		{
			list.AddRange(StationTypes);
		}
		foreach (string item2 in Split(_cfgComponents.Value))
		{
			Type type = ResolveComponent(item2);
			if (type != null && !list.Contains(type))
			{
				list.Add(type);
			}
		}
		foreach (string component2 in HostOwnerApi.Components)
		{
			Type type2 = ResolveComponent(component2);
			if (type2 != null && !list.Contains(type2))
			{
				list.Add(type2);
			}
		}
		HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
		foreach (GameObject prefab2 in zs.m_prefabs)
		{
			if ((Object)(object)prefab2 == (Object)null)
			{
				continue;
			}
			hashSet2.Add(((Object)prefab2).name);
			if ((Object)(object)prefab2.GetComponent<Player>() != (Object)null)
			{
				continue;
			}
			bool flag = hashSet.Contains(((Object)prefab2).name);
			if (!flag)
			{
				foreach (Type item3 in list)
				{
					if ((Object)(object)prefab2.GetComponent(item3) != (Object)null)
					{
						flag = true;
						break;
					}
				}
			}
			if (!flag && _cfgBosses.Value)
			{
				Character component = prefab2.GetComponent<Character>();
				flag = (Object)(object)component != (Object)null && component.m_boss;
			}
			if (flag)
			{
				_wanted[zs.GetPrefabHash(prefab2)] = ((Object)prefab2).name;
			}
		}
		foreach (string item4 in hashSet)
		{
			if (!hashSet2.Contains(item4) && _warned.Add("prefab:" + item4))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Prefab '" + item4 + "' is not registered in this world; ignored."));
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)(_wanted.Count + " kinds of objects to own on the host: " + KindList(12)));
	}

	private string KindList(int max)
	{
		List<string> list = new List<string>(_wanted.Values);
		list.Sort(StringComparer.Ordinal);
		if (list.Count == 0)
		{
			return "none";
		}
		if (max > 0 && list.Count > max)
		{
			int num = list.Count - max;
			list.RemoveRange(max, num);
			return string.Join(", ", list.ToArray()) + " and " + num + " more";
		}
		return string.Join(", ", list.ToArray());
	}

	private void Awake()
	{
		try
		{
			Instance = this;
			BindConfig();
			RegisterCommands();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Host Owner 0.1.0 loaded. No Harmony patches are applied.");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Awake failed, mod is inert: " + ex));
			_disabledByErrors = true;
		}
	}

	private void Update()
	{
		if (_disabledByErrors)
		{
			return;
		}
		try
		{
			if (_indexDirty)
			{
				_indexDirty = false;
				_indexedScene = null;
			}
			if (!(Time.time < _nextPass))
			{
				_nextPass = Time.time + _cfgInterval.Value;
				Pass();
			}
		}
		catch (Exception e)
		{
			Fail("Update", e);
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	internal static void Invalidate()
	{
		if ((Object)(object)Instance != (Object)null)
		{
			Instance._indexDirty = true;
		}
	}

	private void Pass()
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: 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_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0278: Unknown result type (might be due to invalid IL or missing references)
		//IL_027d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_0295: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		if (!_cfgEnabled.Value || !Hosting)
		{
			return;
		}
		ZNet instance = ZNet.instance;
		ZDOMan instance2 = ZDOMan.instance;
		ZNetScene instance3 = ZNetScene.instance;
		if ((Object)(object)instance3 != (Object)(object)_indexedScene)
		{
			BuildIndex(instance3);
		}
		if (_wanted.Count == 0)
		{
			return;
		}
		Vector3 referencePosition = instance.GetReferencePosition();
		Vector2s zone = ZoneSystem.GetZone(referencePosition);
		SimulationDistance syncedSimulationDistance = instance.GetSyncedSimulationDistance();
		_near.Clear();
		instance2.FindSectorObjects(zone, new SimulationDistance(((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance, 0, ((SimulationDistance)(ref syncedSimulationDistance)).IsClassic), _near, (List<ZDO>)null);
		long sessionID = ZDOMan.GetSessionID();
		float time = Time.time;
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		foreach (ZDO item in _near)
		{
			if (item == null || !item.Persistent || !_wanted.TryGetValue(item.GetPrefab(), out var value))
			{
				continue;
			}
			num++;
			ZDOID uid = item.m_uid;
			long owner = item.GetOwner();
			if (owner == sessionID)
			{
				num2++;
				if (_held.ContainsKey(uid))
				{
					_held[uid] = _passes;
				}
				continue;
			}
			if (_yielded.TryGetValue(uid, out var value2))
			{
				if (time < value2)
				{
					num4++;
					continue;
				}
				_yielded.Remove(uid);
			}
			if (owner != 0 && _cfgYield.Value > 0f && _held.TryGetValue(uid, out var value3) && value3 == _passes - 1)
			{
				_yielded[uid] = time + _cfgYield.Value;
				_held.Remove(uid);
				num4++;
				_yieldedTotal++;
				if (_cfgDebug.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)(value + " " + Pos(item.GetPosition()) + ": claimed back by " + PeerName(instance, owner) + ", left alone for " + F(_cfgYield.Value) + " s"));
				}
			}
			else if (ZNetScene.InActiveArea(item.GetPosition(), zone))
			{
				item.SetOwner(sessionID);
				_held[uid] = _passes;
				num3++;
				if (_cfgDebug.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)(value + " " + Pos(item.GetPosition()) + ": taken from " + PeerName(instance, owner)));
				}
			}
		}
		_near.Clear();
		Prune(time);
		_lastWanted = num;
		_lastOwned = num2 + num3;
		_lastClaimed = num3;
		_lastYielded = num4;
		_claimedTotal += num3;
		_passes++;
	}

	private void Prune(float now)
	{
		//IL_003b: 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)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		_expired.Clear();
		foreach (KeyValuePair<ZDOID, int> item in _held)
		{
			if (item.Value < _passes - 1)
			{
				_expired.Add(item.Key);
			}
		}
		foreach (ZDOID item2 in _expired)
		{
			_held.Remove(item2);
		}
		_expired.Clear();
		foreach (KeyValuePair<ZDOID, float> item3 in _yielded)
		{
			if (now >= item3.Value)
			{
				_expired.Add(item3.Key);
			}
		}
		foreach (ZDOID item4 in _expired)
		{
			_yielded.Remove(item4);
		}
		_expired.Clear();
	}

	private void ForgetWorld()
	{
		_held.Clear();
		_yielded.Clear();
	}

	private static string PeerName(ZNet znet, long uid)
	{
		if (uid == 0)
		{
			return "nobody";
		}
		ZNetPeer peer = znet.GetPeer(uid);
		if (peer != null && !string.IsNullOrEmpty(peer.m_playerName))
		{
			return peer.m_playerName;
		}
		return "peer " + uid;
	}

	private static string Pos(Vector3 p)
	{
		return "(" + p.x.ToString("0", CultureInfo.InvariantCulture) + ", " + p.y.ToString("0", CultureInfo.InvariantCulture) + ", " + p.z.ToString("0", CultureInfo.InvariantCulture) + ")";
	}

	private static string F(float v)
	{
		return v.ToString("0.##", CultureInfo.InvariantCulture);
	}

	private void Fail(string where, Exception e)
	{
		string text = where + ": " + e.GetType().Name + ": " + e.Message;
		if (_loggedErrors.Add(text))
		{
			((BaseUnityPlugin)this).Logger.LogError((object)(text + "\n" + e.StackTrace));
		}
		if (++_errorCount >= 25 && !_disabledByErrors)
		{
			_disabledByErrors = true;
			((BaseUnityPlugin)this).Logger.LogError((object)"Too many errors, Host Owner is now inert until the game restarts.");
		}
	}
}