Decompiled source of PlayerModList v1.0.0

plugins/PlayerModList/PlayerModList.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("PlayerModList")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PlayerModList")]
[assembly: AssemblyTitle("PlayerModList")]
[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 PlayerModList
{
	[BepInPlugin("nuckflare.playermodlist", "PlayerModList", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "nuckflare.playermodlist";

		internal const string Rpc = "PlayerModList_ModList";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			new Harmony("nuckflare.playermodlist").PatchAll(typeof(Plugin).Assembly);
		}
	}
	internal struct ModEntry
	{
		internal string Package;

		internal string Name;

		internal string Version;

		internal string Key
		{
			get
			{
				if (!string.IsNullOrEmpty(Package))
				{
					return Package;
				}
				return "dll:" + Name;
			}
		}
	}
	internal static class ModList
	{
		private static readonly Regex PackageDir = new Regex("^[A-Za-z0-9_]+-[A-Za-z0-9_]+$");

		private static readonly Regex ManifestVersion = new Regex("\"version_number\"\\s*:\\s*\"([^\"]+)\"");

		internal static List<ModEntry> Collect()
		{
			Dictionary<string, ModEntry> dictionary = new Dictionary<string, ModEntry>();
			string fullPath = Path.GetFullPath(Paths.PluginPath);
			foreach (PluginInfo item in Chainloader.PluginInfos.Values.OrderBy((PluginInfo i) => i.Metadata.Name))
			{
				string package = "";
				string version = item.Metadata.Version.ToString();
				try
				{
					string fullPath2 = Path.GetFullPath(item.Location);
					if (fullPath2.StartsWith(fullPath, StringComparison.OrdinalIgnoreCase))
					{
						string text = fullPath2.Substring(fullPath.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
						int num = text.IndexOfAny(new char[2] { '/', '\\' });
						string text2 = ((num > 0) ? text.Substring(0, num) : "");
						if (text2.Length > 0)
						{
							string path = Path.Combine(Path.Combine(fullPath, text2), "manifest.json");
							if (File.Exists(path))
							{
								Match match = ManifestVersion.Match(File.ReadAllText(path));
								if (match.Success)
								{
									version = match.Groups[1].Value;
								}
							}
							if (PackageDir.IsMatch(text2))
							{
								package = text2;
							}
						}
					}
				}
				catch
				{
				}
				ModEntry value = new ModEntry
				{
					Package = package,
					Name = item.Metadata.Name,
					Version = version
				};
				if (!dictionary.ContainsKey(value.Key))
				{
					dictionary[value.Key] = value;
				}
			}
			return dictionary.Values.OrderBy<ModEntry, string>((ModEntry x) => (x.Package.Length <= 0) ? x.Name : x.Package.Substring(x.Package.IndexOf('-') + 1), StringComparer.OrdinalIgnoreCase).ToList();
		}

		internal static ZPackage Pack(List<ModEntry> mods)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(mods.Count);
			foreach (ModEntry mod in mods)
			{
				val.Write(mod.Package);
				val.Write(mod.Name);
				val.Write(mod.Version);
			}
			return val;
		}

		internal static List<ModEntry> Unpack(ZPackage pkg)
		{
			List<ModEntry> list = new List<ModEntry>();
			if (pkg.ReadInt() != 1)
			{
				return list;
			}
			int num = Math.Min(pkg.ReadInt(), 1000);
			for (int i = 0; i < num; i++)
			{
				list.Add(new ModEntry
				{
					Package = pkg.ReadString(),
					Name = pkg.ReadString(),
					Version = pkg.ReadString()
				});
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class RegisterPatch
	{
		private static void Postfix()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				instance.Register<ZPackage>("PlayerModList_ModList", (Action<long, ZPackage>)Server.OnModList);
			}
			Client.SentThisSession = false;
		}
	}
	internal static class Client
	{
		internal static bool SentThisSession;
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class SpawnPatch
	{
		private static void Postfix(Player __instance)
		{
			try
			{
				if (!Client.SentThisSession && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer())
				{
					Client.SentThisSession = true;
					List<ModEntry> list = ModList.Collect();
					ZRoutedRpc.instance.InvokeRoutedRPC("PlayerModList_ModList", new object[1] { ModList.Pack(list) });
					Plugin.Log.LogInfo((object)("Mod list sent to server: " + list.Count + " packages"));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not send mod list: " + ex.Message));
			}
		}
	}
	internal static class Server
	{
		private static readonly object Lock = new object();

		private static string Dir => Path.Combine(Paths.ConfigPath, "PlayerModList");

		internal static void OnModList(long sender, ZPackage pkg)
		{
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
				{
					return;
				}
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				if (peer == null)
				{
					return;
				}
				string playerName = peer.m_playerName;
				ISocket socket = peer.m_socket;
				string s = ((socket != null) ? socket.GetHostName() : null) ?? sender.ToString();
				List<ModEntry> list = ModList.Unpack(pkg);
				if (list.Count == 0)
				{
					return;
				}
				Directory.CreateDirectory(Path.Combine(Dir, "anlik"));
				string path = Path.Combine(Path.Combine(Dir, "anlik"), Safe(s) + "_" + Safe(playerName) + ".txt");
				List<ModEntry> list2 = Load(path);
				Save(path, list);
				if (list2 == null)
				{
					Emit("mods_initial", playerName, list, new List<ModEntry>(), new List<(ModEntry, string)>());
					return;
				}
				Dictionary<string, ModEntry> oldByKey = list2.ToDictionary((ModEntry m) => m.Key);
				Dictionary<string, ModEntry> newByKey = list.ToDictionary((ModEntry m) => m.Key);
				List<ModEntry> list3 = list.Where((ModEntry m) => !oldByKey.ContainsKey(m.Key)).ToList();
				List<ModEntry> list4 = list2.Where((ModEntry m) => !newByKey.ContainsKey(m.Key)).ToList();
				ModEntry value;
				List<(ModEntry, string)> list5 = (from m in list
					where oldByKey.TryGetValue(m.Key, out value) && value.Version != m.Version
					select (m: m, Version: oldByKey[m.Key].Version)).ToList();
				if (list3.Count + list4.Count + list5.Count > 0)
				{
					Emit("mods_changed", playerName, list3, list4, list5);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not process mod list: " + ex.Message));
			}
		}

		private static void Emit(string type, string player, List<ModEntry> mods, List<ModEntry> removed, List<(ModEntry m, string from)> updated)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\"t\":").Append(J(type));
			stringBuilder.Append(",\"ts\":").Append(J(DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture)));
			stringBuilder.Append(",\"player\":").Append(J(player));
			stringBuilder.Append(",\"mods\":").Append(Arr(mods.Select(Obj)));
			stringBuilder.Append(",\"removed\":").Append(Arr(removed.Select(Obj)));
			stringBuilder.Append(",\"updated\":").Append(Arr(updated.Select(((ModEntry m, string from) u) => Obj(u.m).TrimEnd(new char[1] { '}' }) + ",\"from\":" + J(u.from) + "}")));
			stringBuilder.Append("}\n");
			lock (Lock)
			{
				File.AppendAllText(Path.Combine(Dir, "olaylar.jsonl"), stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			}
			Plugin.Log.LogInfo((object)$"event {type} {player}: {mods.Count} mods");
		}

		private static string Obj(ModEntry m)
		{
			return "{\"package\":" + J(m.Package) + ",\"name\":" + J(m.Name) + ",\"version\":" + J(m.Version) + "}";
		}

		private static string Arr(IEnumerable<string> items)
		{
			return "[" + string.Join(",", items) + "]";
		}

		private static string J(string s)
		{
			if (s == null)
			{
				return "null";
			}
			StringBuilder stringBuilder = new StringBuilder("\"");
			foreach (char c in s)
			{
				if (c == '"' || c == '\\')
				{
					stringBuilder.Append('\\').Append(c);
				}
				else if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.Append('"').ToString();
		}

		private static string Safe(string s)
		{
			return Regex.Replace(s ?? "", "[^\\w\\-]", "_");
		}

		private static List<ModEntry> Load(string path)
		{
			if (!File.Exists(path))
			{
				return null;
			}
			List<ModEntry> list = new List<ModEntry>();
			string[] array = File.ReadAllLines(path, Encoding.UTF8);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '\t' });
				if (array2.Length == 3)
				{
					list.Add(new ModEntry
					{
						Package = array2[0],
						Name = array2[1],
						Version = array2[2]
					});
				}
			}
			return list;
		}

		private static void Save(string path, List<ModEntry> mods)
		{
			File.WriteAllLines(path, mods.Select((ModEntry m) => m.Package + "\t" + m.Name + "\t" + m.Version), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}
	}
}