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 LocalizedContainerSigns v1.1.1
plugins/LocalizedContainerSigns.dll
Decompiled 2 days agousing System; using System.Collections; 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.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("LocalizedContainerSigns")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+71ededdf17f298220b5c52a6a964ab28c9b9e635")] [assembly: AssemblyProduct("LocalizedContainerSigns")] [assembly: AssemblyTitle("LocalizedContainerSigns")] [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 LocalizedContainerSigns { internal static class ChestFill { private const string ListStart = "<i ls></i>"; private const string ListEnd = "<i le></i>"; private static ConfigEntry<bool> _enabled; private static ConfigEntry<string> _format; private static ConfigEntry<string> _separator; private static FieldInfo _stateField; private static MethodInfo _getInventory; private static PropertyInfo _inventoryProperty; private static bool _errorLogged; internal static void Init(ConfigFile config, Harmony harmony, Type processor) { //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Expected O, but got Unknown _enabled = config.Bind<bool>("Fill", "Enabled", true, "Показывать заполненность сундука последней строкой списка на автотабличке."); _format = config.Bind<string>("Fill", "Format", "<color=yellow>{0}/{1}</color>", "Формат строки: {0} — занято ячеек, {1} — всего ячеек, {2} — процент заполнения. Допускаются теги TextMeshPro."); _separator = config.Bind<string>("Fill", "Separator", "<br>", "Разделитель перед строкой заполненности (обычно как ChestSignsContentListSeparator в ServersideQoL)."); if (!_enabled.Value) { Plugin.Log.LogInfo((object)"Заполненность сундуков на табличках выключена."); return; } List<MethodInfo> list = (from m in processor?.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic).SelectMany((Type t) => t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) where m.Name.Contains("EvaluateMatch") && m.ReturnType == typeof(string) select m).Where(delegate(MethodInfo m) { ParameterInfo[] parameters = m.GetParameters(); return parameters.Length == 1 && parameters[0].ParameterType == typeof(Match); }).ToList(); if (list == null || list.Count != 1) { Plugin.Log.LogWarning((object)"ServersideQoL.ContainerSigns изменился (нет EvaluateMatch); заполненность сундуков не показывается."); return; } _stateField = AccessTools.Field(list[0].DeclaringType, "containerState"); _getInventory = ((_stateField == null) ? null : AccessTools.Method(_stateField.FieldType, "GetInventory", (Type[])null, (Type[])null)); _inventoryProperty = ((_getInventory == null) ? null : AccessTools.Property(_getInventory.ReturnType, "Inventory")); if (_inventoryProperty == null || _inventoryProperty.PropertyType != typeof(Inventory)) { Plugin.Log.LogWarning((object)"ServersideQoL изменился (нет containerState.GetInventory().Inventory); заполненность сундуков не показывается."); return; } harmony.Patch((MethodBase)list[0], (HarmonyMethod)null, new HarmonyMethod(typeof(ChestFill), "EvaluateMatchPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)("Заполненность сундуков на табличках: формат «" + _format.Value + "», патч " + list[0].DeclaringType?.Name + "." + list[0].Name + ".")); } private static void EvaluateMatchPostfix(object __instance, ref string __result) { if (__instance == null || __result == null || !__result.StartsWith("<i ls></i>") || !__result.EndsWith("<i le></i>")) { return; } try { object value = _stateField.GetValue(__instance); object obj = ((value == null) ? null : _getInventory.Invoke(value, null)); if (obj == null) { return; } object? value2 = _inventoryProperty.GetValue(obj, null); Inventory val = (Inventory)((value2 is Inventory) ? value2 : null); if (val != null) { string text = FormatLine(val.NrOfItems(), val.GetWidth() * val.GetHeight(), _format.Value); if (text != null) { __result = __result.Substring(0, __result.Length - "<i le></i>".Length) + _separator.Value + text + "<i le></i>"; } } } catch (Exception arg) { if (!_errorLogged) { _errorLogged = true; Plugin.Log.LogError((object)$"Ошибка строки заполненности (дальше не повторяется): {arg}"); } } } internal static string FormatLine(int used, int total, string format) { if (total <= 0) { return null; } int num = (int)Math.Round(100.0 * (double)used / (double)total); try { return string.Format(format, used, total, num); } catch (FormatException) { if (!_errorLogged) { _errorLogged = true; Plugin.Log.LogError((object)("Неверный формат [Fill] Format: «" + format + "»; используется {0}/{1}.")); } return $"{used}/{total}"; } } } [BepInPlugin("sansara.LocalizedContainerSigns", "Localized Container Signs", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "sansara.LocalizedContainerSigns"; public const string Name = "Localized Container Signs"; public const string Version = "1.1.1"; private const string ContainerSignsGuid = "ArgusMagnus.ServersideQoL.ContainerSigns"; internal const string ProcessorType = "ServersideQoL.ContainerSigns.ContainerAndSignProcessor"; private const string NamesFileName = "sansara.LocalizedContainerSigns.names.txt"; internal static ManualLogSource Log; private static ConfigEntry<string> _language; private static string _namesPath; private static Dictionary<string, string> _translations; private static Dictionary<string, string> _overrides; private static readonly Dictionary<string, string> NameCache = new Dictionary<string, string>(); private static bool _patched; private void Awake() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _language = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Language", "Russian", "Язык названий предметов на табличках сундуков: имя столбца из файлов локализации игры (Russian, English, Ukrainian, German …). Применяется после перезапуска."); _namesPath = Path.Combine(Paths.ConfigPath, "sansara.LocalizedContainerSigns.names.txt"); Harmony val = new Harmony("sansara.LocalizedContainerSigns"); ChestFill.Init(((BaseUnityPlugin)this).Config, val, AccessTools.TypeByName("ServersideQoL.ContainerSigns.ContainerAndSignProcessor")); MethodInfo methodInfo = FindNameSelector(); if (methodInfo == null) { Log.LogWarning((object)"ServersideQoL.ContainerSigns не найден или изменился (нет выбора имени предмета для списка содержимого); названия не переводятся."); return; } val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "NameSelectorPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Названия на табличках сундуков: язык " + _language.Value + ", патч " + methodInfo.DeclaringType?.Name + "." + methodInfo.Name + ".")); _patched = true; } private void Start() { if (!_patched) { return; } try { EnsureLoaded(); Log.LogInfo((object)("Пример: Wood → " + Translate("Wood", "$item_wood") + ", TrophyDeer → " + Translate("TrophyDeer", "$item_trophy_deer") + ".")); } catch (Exception arg) { Log.LogError((object)$"Ошибка загрузки названий: {arg}"); } } private static MethodInfo FindNameSelector() { Type type = AccessTools.TypeByName("ServersideQoL.ContainerSigns.ContainerAndSignProcessor"); if (type == null) { return null; } List<MethodInfo> list = (from m in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic).SelectMany((Type t) => t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) where m.ReturnType == typeof(string) select m).Where(delegate(MethodInfo m) { ParameterInfo[] parameters = m.GetParameters(); return parameters.Length == 1 && parameters[0].ParameterType == typeof(ItemData); }).ToList(); if (list.Count != 1) { if (list.Count > 1) { Log.LogWarning((object)("Найдено несколько подходящих методов: " + string.Join(", ", list.Select((MethodInfo m) => m.Name)) + ".")); } return null; } return list[0]; } private static void NameSelectorPostfix(ItemData __0, ref string __result) { if (__0 == null || string.IsNullOrEmpty(__result)) { return; } try { if (NameCache.TryGetValue(__result, out var value)) { __result = value; return; } string text = Translate(((Object)(object)__0.m_dropPrefab != (Object)null) ? ((Object)__0.m_dropPrefab).name : __result, __0.m_shared?.m_name); NameCache[__result] = text; __result = text; } catch (Exception arg) { Log.LogError((object)$"Ошибка перевода названия '{__result}': {arg}"); } } private static string Translate(string prefabName, string token) { EnsureLoaded(); if (_overrides.TryGetValue(prefabName, out var value)) { return value; } if (string.IsNullOrEmpty(token)) { return prefabName; } if (token.StartsWith("$") && _translations.TryGetValue(token.Substring(1), out value) && value.Length > 0) { return value; } Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(token) : null); if (!string.IsNullOrEmpty(text) && !text.StartsWith("[") && !text.StartsWith("$")) { return text; } return prefabName; } private static void EnsureLoaded() { if (_translations != null) { return; } _overrides = LoadOverrides(); _translations = new Dictionary<string, string>(); try { foreach (TextAsset item in LocalizationAssets()) { ReadColumn(item, _language.Value, _translations); } } catch (Exception arg) { Log.LogError((object)$"Не удалось прочитать локализацию игры: {arg}"); } if (_translations.Count == 0) { Log.LogWarning((object)("Язык '" + _language.Value + "' не найден в файлах локализации игры; используются собственные названия из sansara.LocalizedContainerSigns.names.txt и локализация сервера.")); } else { Log.LogInfo((object)$"Загружено строк локализации '{_language.Value}': {_translations.Count}, собственных названий: {_overrides.Count}."); } } private static IEnumerable<TextAsset> LocalizationAssets() { _ = Localization.instance; object obj = AccessTools.Field(typeof(Localization), "m_localizationSettings")?.GetValue(null); if (obj == null) { yield break; } Traverse val = Traverse.Create(obj); if (!((val.Field("Localizations").GetValue() ?? val.Property("Localizations", (object[])null).GetValue()) is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { TextAsset val2 = (TextAsset)((item is TextAsset) ? item : null); if (val2 != null && (Object)(object)val2 != (Object)null) { yield return val2; } } } internal static void ReadColumn(TextAsset asset, string language, Dictionary<string, string> into) { List<List<string>> list = SplitCsv(asset.text); if (list.Count == 0) { return; } int num = list[0].FindIndex((string h) => StripQuotes(h).Trim() == language); if (num < 0) { return; } foreach (List<string> item in list.Skip(1)) { if (item.Count != 0 && item[0].Length != 0 && !item[0].StartsWith("//")) { string text = ((item.Count > num) ? item[num].Trim() : ""); if (text.Length == 0 && item.Count > 1) { text = item[1].Trim(); } into[item[0]] = text; } } } private static string StripQuotes(string s) { if (s.Length < 2 || s[0] != '"' || s[s.Length - 1] != '"') { return s; } return s.Substring(1, s.Length - 2); } internal static List<List<string>> SplitCsv(string text) { List<List<string>> list = new List<List<string>>(); List<string> list2 = new List<string>(); StringBuilder stringBuilder = new StringBuilder(); bool flag = false; for (int i = 0; i < text.Length; i++) { char c = text[i]; switch (c) { case '"': if (flag && i + 1 < text.Length && text[i + 1] == '"') { stringBuilder.Append('"'); i++; } else { flag = !flag; } continue; case ',': if (!flag) { list2.Add(stringBuilder.ToString()); stringBuilder.Length = 0; continue; } break; } if (c == '\n' && !flag) { list2.Add(stringBuilder.ToString().TrimEnd(new char[1] { '\r' })); stringBuilder.Length = 0; list.Add(list2); list2 = new List<string>(); } else { stringBuilder.Append(c); } } if (stringBuilder.Length > 0 || list2.Count > 0) { list2.Add(stringBuilder.ToString()); list.Add(list2); } return list; } private static Dictionary<string, string> LoadOverrides() { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); try { if (!File.Exists(_namesPath)) { File.WriteAllText(_namesPath, "# Собственные названия предметов для табличек сундуков ServersideQoL.\n# Формат: ИмяПрефаба = Название, по одному на строку. Пример:\n# BoneFragments = Кости\n# Применяется после перезапуска сервера.\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); return dictionary; } string[] array = File.ReadAllLines(_namesPath, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text[0] == '#') { continue; } int num = text.IndexOf('='); if (num <= 0) { Log.LogWarning((object)("sansara.LocalizedContainerSigns.names.txt: строка без '=' пропущена: " + text)); continue; } string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 1).Trim(); if (text2.Length > 0 && text3.Length > 0) { dictionary[text2] = text3; } } } catch (Exception arg) { Log.LogError((object)string.Format("Не удалось прочитать {0}: {1}", "sansara.LocalizedContainerSigns.names.txt", arg)); } return dictionary; } } }