Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PlayerModList v1.0.0
plugins/PlayerModList/PlayerModList.dll
Decompiled 2 days agousing 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)); } } }