using System; using System.Buffers; using System.Buffers.Binary; using System.Buffers.Text; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.Tracing; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Numerics; using System.Numerics.Hashing; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.AccessControl; using System.Security.Cryptography; using System.Security.Principal; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FxResources.System.Buffers; using FxResources.System.Memory; using Glitnir.Ranking.Patches; using HarmonyLib; using JetBrains.Annotations; using LiteDB; using LiteDB.Engine; using LiteDB.Utils; using LiteDB.Utils.Extensions; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: Guid("72b7e2b4-c4d2-4b2f-91db-99d0311d97bc")] [assembly: ComVisible(false)] [assembly: AssemblyTrademark("")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyProduct("GlitnirRanking")] [assembly: AssemblyCompany("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyDescription("")] [assembly: AssemblyTitle("GlitnirRanking")] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace Glitnir.Ranking { internal static class GlitnirRankingServerSync { private static readonly HashSet RegisteredConfigEntries = new HashSet(); private static bool _lockingConfigRegistered; internal static readonly ConfigSync ConfigSync = new ConfigSync("com.glitnir.ranking") { DisplayName = "Glitnir Ranking", CurrentVersion = "0.7.81", MinimumRequiredVersion = "0.7.81" }; internal static ConfigEntry BindConfig(this ConfigFile config, string group, string name, T value, string description, bool synced = true) { ConfigEntry val = config.Bind(group, name, value, description); if (synced && RegisteredConfigEntries.Add(group + "\u001f" + name)) { ConfigSync.AddConfigEntry(val); } return val; } internal static ConfigEntry BindLockingConfig(this ConfigFile config) { ConfigEntry val = config.Bind("General", "LockConfiguration", true, "Se ativo, o servidor sincroniza e força as configurações do Glitnir Ranking nos clientes."); if (!_lockingConfigRegistered) { ConfigSync.AddLockingConfigEntry(val); _lockingConfigRegistered = true; } return val; } } [BepInPlugin("com.glitnir.ranking", "Glitnir Ranking", "0.7.81")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class GlitnirRankingPlugin : BaseUnityPlugin { private enum DebugCategory { General, Hit, Kill, Skill, PendingKill, Snapshot, Points } private sealed class SnapshotTopEntryData { public int Position; public string PlayerName = ""; public int Points; public bool IsLocalPlayer; public int TotalKillsPontuadas; public int TotalBossesPontuadas; public int TotalSkillLevelUpsPontuados; public int TotalMarketplaceQuestsPontuadas; public int MarketplaceQuestPointsTotal; public int TotalFishingPontuadas; public int TotalCraftPontuadas; public int TotalFarmJackpotsPontuados; public int TotalUniqueCraftJackpotsPontuados; public int TotalDeaths; public int KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; public int FishingPointsTotal; public int CraftPointsTotal; public int FarmJackpotPointsTotal; public int UniqueCraftJackpotPointsTotal; public int DeathPenaltyPointsTotal; public int TotalPointsExchanges; public int PointsExchangePenaltyTotal; public int PointsExchangeCoinsTotal; public int ExplorationMapJackpotPointsTotal; public string LastReason = ""; public string LastUpdateUtc = ""; } private sealed class SnapshotPlayerData { public bool HasData; public int Position; public string PlayerName = ""; public int Points; public int TotalKillsPontuadas; public int TotalBossesPontuadas; public int TotalSkillLevelUpsPontuados; public int TotalMarketplaceQuestsPontuadas; public int KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; public int MarketplaceQuestPointsTotal; public int TotalFishingPontuadas; public int TotalCraftPontuadas; public int TotalFarmJackpotsPontuados; public int TotalUniqueCraftJackpotsPontuados; public int TotalDeaths; public int FishingPointsTotal; public int CraftPointsTotal; public int FarmJackpotPointsTotal; public int UniqueCraftJackpotPointsTotal; public int DeathPenaltyPointsTotal; public int TotalPointsExchanges; public int PointsExchangePenaltyTotal; public int PointsExchangeCoinsTotal; public int ExplorationMapJackpotPointsTotal; public string LastReason = ""; public string LastUpdateUtc = ""; public bool RankingEnabled; public bool EnableKillPoints; public bool EnableBossPoints; public bool EnableSkillPoints; public bool EnableMarketplaceQuestPoints; public int TopCount; public bool RewardClaimsEnabled; public bool RewardCanClaim; public bool RewardAlreadyClaimed; public int RewardRank; public int RewardMinPoints; public string RewardLabel = ""; public string RewardPrefabName = ""; public int RewardAmount; public string RewardBlockReason = ""; public string RewardClaimCycleId = ""; public string HudKillRules = ""; public string HudBossRules = ""; public string HudSkillJackpotRules = ""; public string HudMarketplaceQuestRules = ""; public string HudFishingRules = ""; public string HudCraftRules = ""; public string HudFarmJackpotRules = ""; public string HudUniqueCraftJackpotRules = ""; public string HudDeathPenaltyRules = ""; public bool EnableDeathPenalty; public bool DeathPenaltyUseMultiplier; public int DeathPenaltyPerDeath; public string HudExplorationMapJackpotRules = ""; public Dictionary ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary FloatProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } private sealed class RankRewardInfo { public int Rank; public int MinPoints; public string Label = ""; public string PrefabName = ""; public int Amount; } private sealed class InventoryGuideRuleGroup { public readonly string Name; public readonly List Rows = new List(); public InventoryGuideRuleGroup(string name) { Name = (string.IsNullOrWhiteSpace(name) ? "Todos" : name); } } private struct RectOffsetSpec { public float Left; public float Bottom; public float Right; public float Top; } private sealed class RuleGuideGroup { public readonly string Name; public readonly List Rows = new List(); public RuleGuideGroup(string name) { Name = (string.IsNullOrWhiteSpace(name) ? "Outros" : name); } } private class SkillGuideGroup { public string Name; public List Milestones; public SkillGuideGroup(string name) { Name = (string.IsNullOrWhiteSpace(name) ? "Habilidade" : name); Milestones = new List(); } } private class SkillGuideMilestone { public string Level; public string Points; public SkillGuideMilestone(string level, string points) { Level = (string.IsNullOrWhiteSpace(level) ? "?" : level); Points = (string.IsNullOrWhiteSpace(points) ? "0" : points); } } private struct UnityGuideCategory { public readonly string Key; public readonly string Label; public readonly string Title; public readonly string Description; public UnityGuideCategory(string key, string label, string title, string description) { Key = key; Label = label; Title = title; Description = description; } } private struct UnityGuideRuleLine { public readonly string Action; public readonly string Points; public readonly string Category; public readonly string RawKey; public UnityGuideRuleLine(string action, string points, string category = "", string rawKey = "") { Action = action ?? ""; Points = points ?? ""; Category = category ?? ""; RawKey = rawKey ?? action ?? ""; } } private sealed class GlitnirButtonFx : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler { private RectTransform _rect; private GameObject _hoverGlow; private GameObject _selectedGlow; private Vector3 _baseScale = Vector3.one; private Vector3 _targetScale = Vector3.one; private bool _hovered; private bool _pressed; private bool _selected; private void Awake() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) ref RectTransform rect = ref _rect; Transform transform = ((Component)this).transform; rect = (RectTransform)(object)((transform is RectTransform) ? transform : null); _baseScale = ((Component)this).transform.localScale; _targetScale = _baseScale; Transform val = ((Component)this).transform.Find("HoverGlow"); Transform val2 = ((Component)this).transform.Find("SelectedGlow"); _hoverGlow = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null); _selectedGlow = (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : null); RefreshGlow(); } private void OnEnable() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) _pressed = false; _hovered = false; _targetScale = _baseScale; RefreshGlow(); } private void Update() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_rect == (Object)null)) { ((Transform)_rect).localScale = Vector3.Lerp(((Transform)_rect).localScale, _targetScale, Time.unscaledDeltaTime * 14f); } } public void SetSelected(bool selected) { _selected = selected; RefreshGlow(); } public void OnPointerEnter(PointerEventData eventData) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) _hovered = true; _targetScale = _baseScale * 1.035f; RefreshGlow(); } public void OnPointerExit(PointerEventData eventData) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) _hovered = false; _pressed = false; _targetScale = _baseScale; RefreshGlow(); } public void OnPointerDown(PointerEventData eventData) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) _pressed = true; _targetScale = _baseScale * 0.975f; RefreshGlow(); } public void OnPointerUp(PointerEventData eventData) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) _pressed = false; _targetScale = (_hovered ? (_baseScale * 1.035f) : _baseScale); RefreshGlow(); } private void RefreshGlow() { if ((Object)(object)_hoverGlow != (Object)null) { _hoverGlow.SetActive(_hovered || _pressed); } if ((Object)(object)_selectedGlow != (Object)null) { _selectedGlow.SetActive(_selected); } } } private struct RectSpec { public Vector2 AnchorMin; public Vector2 AnchorMax; public Vector2 Pivot; public Vector2 OffsetMin; public Vector2 OffsetMax; public Vector2 AnchoredPosition; public Vector2 SizeDelta; public bool UseOffsets; } private readonly Dictionary> _cfgProductionCategoryRuleEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgUniqueCraftJackpotCategoryEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); public const string ModGuid = "com.glitnir.ranking"; public const string ModName = "Glitnir Ranking"; public const string ModVersion = "0.7.81"; internal static GlitnirRankingPlugin Instance; internal static ManualLogSource Log; private const string RpcRequestSnapshot = "glitnir.ranking.requestsnapshot"; private const string RpcReceiveSnapshot = "glitnir.ranking.receivesnapshot"; private const string RpcReportKill = "glitnir.ranking.reportkill"; private const string RpcReportSkillGain = "glitnir.ranking.reportskillgain"; private const string RpcRequestRewardClaim = "glitnir.ranking.requestrewardclaim"; private const string RpcGrantRewardItem = "glitnir.ranking.grantrewarditem"; private const string RpcFinalizeRewardClaim = "glitnir.ranking.finalizerewardclaim"; private const string RpcRewardClaimFeedback = "glitnir.ranking.rewardclaimfeedback"; private const string RpcRequestPointsExchange = "glitnir.ranking.requestpointsexchange"; private const string RpcGrantExchangeCoins = "glitnir.ranking.grantexchangecoins"; private const string RpcFinalizePointsExchange = "glitnir.ranking.finalizepointsexchange"; private const string RpcPointsExchangeFeedback = "glitnir.ranking.pointsexchangefeedback"; private const string RpcReportMarketplaceQuestComplete = "glitnir.ranking.marketquestcomplete"; private const string RpcReportFishCaught = "glitnir.ranking.reportfishcaught"; private const string RpcReportCraftedItem = "glitnir.ranking.reportcrafteditem"; private const string RpcReportFarmHarvest = "glitnir.ranking.reportfarmharvest"; private const string RpcReportPlayerDeath = "glitnir.ranking.reportplayerdeath"; private const string RpcReportExplorationMap = "glitnir.ranking.reportexplorationmap"; private const float DamageCreditLifetimeSeconds = 180f; private const float ProcessedKillLifetimeSeconds = 180f; private const float ProcessedKillCleanupIntervalSeconds = 15f; private const float LocalRecentHitLifetimeSeconds = 15f; private const float AdminIdentifierRefreshIntervalSeconds = 30f; private const float ExplorationReportIntervalSeconds = 180f; private const double DatabaseSaveIntervalSeconds = 30.0; private const int MaxHudChars = 3000; private const int MaxPlayerNameLength = 32; private const int MaxReasonLength = 64; private const float ClientRefreshInterval = 300f; private readonly Dictionary _hudPrefabDisplayNameCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _hudPrefabObjectCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private int _hudObjectDbItemCountCached = -1; private int _hudZNetScenePrefabCountCached = -1; private Type _hudLocalizationType; private FieldInfo _hudLocalizationInstanceField; private PropertyInfo _hudLocalizationInstanceProperty; private MethodInfo _hudLocalizationLocalizeMethod; private bool _hudLocalizationReflectionReady; private FieldInfo _minimapExploredField; private Harmony _harmony; private bool _rpcsRegistered = false; private ZRoutedRpc _registeredRoutedRpcInstance; private readonly object _databaseSaveLock = new object(); private bool _databaseSavePending; private DateTime _nextDatabaseSaveUtc = DateTime.MinValue; private float _nextProcessedKillCleanupAt; private string _rulesFilePath; private string _uiFolderPath; private string _uiFilePath; private string _databaseFilePath; private string _legacyDatabaseFilePath; private ConfigFile _rulesConfig; private FileSystemWatcher _rulesConfigWatcher; private ConfigEntry _cfgLockConfiguration; private bool _rulesConfigEventsBound; private bool _rulesConfigReloadQueued; private float _rulesConfigReloadAt; private float _rulesConfigApplyAt; private const float RulesConfigApplyInterval = 1f; private ConfigEntry _cfgRankingEnabled; private ConfigEntry _cfgEnableKillPoints; private ConfigEntry _cfgEnableBossPoints; private ConfigEntry _cfgDefaultKillPoints; private ConfigEntry _cfgTopCount; private ConfigEntry _cfgAllowRepeatedBossPoints; private ConfigEntry _cfgDebugLogging; private ConfigEntry _cfgLogHitReports; private ConfigEntry _cfgLogKillReports; private ConfigEntry _cfgLogSkillReports; private ConfigEntry _cfgLogPendingKillReports; private ConfigEntry _cfgLogSnapshotRequests; private ConfigEntry _cfgLogPointsChanges; private ConfigEntry _cfgEnableSkillPoints; private ConfigEntry _cfgIgnoreTamedKills; private ConfigEntry _cfgEnableMarketplaceQuestPoints; private ConfigEntry _cfgEnableFishingPoints; private ConfigEntry _cfgEnableDeathPenalty; private ConfigEntry _cfgEnableExplorationJackpots; private ConfigEntry _cfgExplorationMapJackpotRules; private ConfigEntry _cfgDeathPenaltyRules; private ConfigEntry _cfgDeathPenaltyUseMultiplier; private ConfigEntry _cfgDeathPenaltyPerDeath; private readonly Dictionary> _cfgCombatBiomeRuleEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private ConfigEntry _cfgProductionCategoryRules; private ConfigEntry _cfgMarketplaceQuestPointMap; private ConfigEntry _cfgFarmJackpotRules; private ConfigEntry _cfgRewardClaimsEnabled; private ConfigEntry _cfgRewardClaimCycleId; private ConfigEntry _cfgRewardTop1MinPoints; private ConfigEntry _cfgRewardTop1Label; private ConfigEntry _cfgRewardTop1Prefab; private ConfigEntry _cfgRewardTop1Amount; private ConfigEntry _cfgRewardTop2MinPoints; private ConfigEntry _cfgRewardTop2Label; private ConfigEntry _cfgRewardTop2Prefab; private ConfigEntry _cfgRewardTop2Amount; private ConfigEntry _cfgRewardTop3MinPoints; private ConfigEntry _cfgRewardTop3Label; private ConfigEntry _cfgRewardTop3Prefab; private ConfigEntry _cfgRewardTop3Amount; private ConfigEntry _cfgPointsExchangeEnabled; private ConfigEntry _cfgPointsExchangePrefab; private ConfigEntry _cfgPointsExchangeUseCoinsPerPoint; private ConfigEntry _cfgPointsExchangeCoinsPerPoint; private ConfigEntry _cfgPointsExchangeUsePointsPerCoin; private ConfigEntry _cfgPointsExchangePointsPerCoin; private ConfigEntry _cfgPointsExchangeMinPoints; private ConfigEntry _cfgPointsExchangeMaxPointsPerRequest; private ConfigEntry _cfgDiscordTop3WebhookEnabled; private ConfigEntry _cfgDiscordTop3WebhookUrl; private ConfigEntry _cfgIgnoreAdminsInRanking; private ConfigEntry _cfgIgnoredAdminNames; private readonly Dictionary> _cfgKillPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgBossPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgFishingPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgCraftPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgFarmJackpotEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgUniqueCraftJackpotEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgSkillMilestoneJackpotPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private RankingDatabase _database = new RankingDatabase(); private RankingRules _rules = new RankingRules(); private readonly Dictionary _marketplaceQuestPointsByUid = new Dictionary(); private readonly Dictionary> _damageCredits = new Dictionary>(StringComparer.Ordinal); private readonly Dictionary _processedKills = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _ignoredTamedKills = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _localRecentHits = new Dictionary(StringComparer.Ordinal); private HashSet _cachedAdminIdentifiers = new HashSet(StringComparer.OrdinalIgnoreCase); private float _nextAdminIdentifierRefreshAt; private bool _adminIdentifiersCacheReady; private bool _hudVisible; private float _hudOpenTime; private Rect _windowRect = new Rect(660f, 56f, 548f, 804f); private Rect _iconRect = new Rect(36f, 596f, 50f, 50f); private GameObject _rankingInputBlockerObject; private Canvas _rankingInputBlockerCanvas; private RectTransform _rankingInputBlockerRect; private Vector2 _rankingScrollPosition = Vector2.zero; private Vector2 _playerInfoScrollPosition = Vector2.zero; private Vector2 _rulesInfoScrollPosition = Vector2.zero; private Vector2 _actionsScrollPosition = Vector2.zero; private readonly HashSet _expandedActionPlayers = new HashSet(StringComparer.OrdinalIgnoreCase); private int _hudTabIndex = 0; private float _hudTabSwitchTime = 0f; private readonly HashSet _expandedRuleCategories = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _expandedGuideSkillRows = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _expandedRuleSubCategories = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _ruleCategoriesInitialized; private readonly HashSet _expandedPerformanceCategories = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _performanceCategoriesInitialized; private float _clientRefreshTimer; private float _lastSnapshotRequestTime = -9999f; private float _explorationReportTimer; private float _lastReportedMapExplorePercent = -1f; private long _lastKnownServerPeerUid; private bool _requestedInitialServerSnapshot; private string _lastSnapshotSyncPlayerName = ""; private bool _lastSnapshotHadLocalPlayer; private string _cachedTopText = "Carregando ranking..."; private string _cachedPlayerText = "Aguardando dados do servidor..."; private string _statusText = "Sincronizando..."; private float _statusUntil = 0f; private float _rankingHudTransparentUntil = 0f; private bool _rewardClaimRequestPending; private string _rewardClaimPendingCycleId = ""; private int _rewardClaimPendingRank = 0; private readonly HashSet _pendingRewardClaims = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _pendingPointsExchanges = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _pointsExchangeRequestPending; private string _pointsExchangeAmountInput = ""; private bool _iconDragging; private Vector2 _iconDragOffset = Vector2.zero; private Vector2 _iconMouseDownPosition = Vector2.zero; private bool _iconMovedDuringDrag; private bool _rankingHudToggleQueued; private readonly List _cachedTopEntries = new List(); private SnapshotPlayerData _cachedPlayerData = new SnapshotPlayerData(); private GUIStyle _windowStyle; private GUIStyle _headerStyle; private GUIStyle _sectionTitleStyle; private GUIStyle _rankingTextStyle; private GUIStyle _playerTextStyle; private GUIStyle _footerStyle; private GUIStyle _statusStyle; private GUIStyle _iconButtonStyle; private GUIStyle _panelHeadingStyle; private GUIStyle _cardLabelStyle; private GUIStyle _cardValueStyle; private GUIStyle _bodyRowStyle; private GUIStyle _bodyValueStyle; private GUIStyle _mutedBodyStyle; private GUIStyle _rankIndexStyle; private GUIStyle _rankNameStyle; private GUIStyle _rankPointsStyle; private GUIStyle _youTagStyle; private GUIStyle _emptyStateStyle; private GUIStyle _configStyle; private bool _stylesReady; private KeyCode _uiToggleKey = (KeyCode)121; private float _uiIconZoom = 0.96f; private Font _uiTitleFont; private Font _uiBodyFont; private Font _uiAccentFont; private Texture2D _uiTransparentTexture = null; private Texture2D _uiWhiteTexture = null; private Texture2D _uiPanelTexture = null; private Texture2D _uiBackgroundTexture = null; private Texture2D _uiRankingIconTexture = null; private Texture2D _uiTitleRankingTexture = null; private Texture2D _uiTitleTopTexture = null; private Texture2D _uiTitleGuideTexture = null; private Texture2D _uiTitlePlayerTexture = null; private Texture2D _uiTitleActionsTexture = null; private readonly Dictionary _uiRuleCategoryIcons = new Dictionary(StringComparer.OrdinalIgnoreCase); private const float RuleCategoryIconSize = 24f; private Texture2D _uiRank1IconTexture = null; private Texture2D _uiRank2IconTexture = null; private Texture2D _uiRank3IconTexture = null; private readonly HashSet _missingUiTextureWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); private const string DefaultKillPointRules = "Boar:1"; private const string DefaultCombatCategoryRules = "Prados:Boar:1,Deer:1,Neck:1;Floresta Negra:Greydwarf:1,Greydwarf_Elite:2,Greydwarf_Shaman:2,Troll:5;Pântano:Draugr:2,Draugr_Elite:3,Blob:2,Abomination:8;Montanha:Wolf:2,Hatchling:2,StoneGolem:8;Planícies:Goblin:3,GoblinBrute:5,Lox:6,Deathsquito:3;Oceano:Serpent:6;Mistlands:Seeker:4,SeekerBrute:8,Gjall:10;Ashlands:Charred_Melee:4,Charred_Archer:4,Volture:4;Especiais:Haldor:0"; private const string DefaultProductionCategoryRules = "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; private static readonly Dictionary ManualPortuguesePrefabNames = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "deerstew", "Ensopado de cervo" }, { "honeysglazedchicken", "Frango glaceado com mel" }, { "mushroomjotun", "Cogumelo Jotun" }, { "helmetbronze", "Elmo de bronze" }, { "helmetcarapace", "Elmo de carapaça" }, { "helmetdrake", "Elmo de draco" }, { "helmetdverger", "Tiara Dvergr" }, { "helmetfenris", "Capuz de Fenris" }, { "helmetiron", "Elmo de ferro" }, { "helmetleather", "Elmo de couro" }, { "helmetmage", "Capuz de mago" }, { "helmetpadded", "Elmo acolchoado" }, { "helmetroot", "Máscara de raiz" }, { "helmettrollleather", "Capuz de couro de troll" }, { "helmetflametal", "Elmo de flametal" }, { "armorbronzechest", "Peitoral de bronze" }, { "armorbronzelegs", "Perneiras de bronze" }, { "armorcarapacechest", "Peitoral de carapaça" }, { "armorcarapacelegs", "Perneiras de carapaça" }, { "armorfenringchest", "Peitoral de Fenris" }, { "armorfenringlegs", "Perneiras de Fenris" }, { "armorironchest", "Peitoral de ferro" }, { "armorironlegs", "Perneiras de ferro" }, { "armorleatherchest", "Túnica de couro" }, { "armorleatherlegs", "Calças de couro" }, { "armormagechest", "Manto de mago" }, { "armormagelegs", "Calças de mago" }, { "armorpaddedcuirass", "Couraça acolchoada" }, { "armorpaddedgreaves", "Grevas acolchoadas" }, { "armorragschest", "Túnica de trapos" }, { "armorragslegs", "Calças de trapos" }, { "armorrootchest", "Armadura de raiz" }, { "armorrootlegs", "Perneiras de raiz" }, { "armortrollleatherchest", "Túnica de couro de troll" }, { "armortrollleatherlegs", "Calças de couro de troll" }, { "knifeblackmetal", "Faca de metal negro" }, { "knifecopper", "Faca de cobre" }, { "knifeflint", "Faca de sílex" }, { "knifesilver", "Faca de prata" }, { "swordiron", "Espada de ferro" }, { "swordsilver", "Espada de prata" }, { "swordblackmetal", "Espada de metal negro" }, { "swordbronze", "Espada de bronze" }, { "swordmistwalker", "Mistwalker" }, { "swordflametal", "Espada de flametal" }, { "axeiron", "Machado de ferro" }, { "axebronze", "Machado de bronze" }, { "axeblackmetal", "Machado de metal negro" }, { "maceiron", "Maça de ferro" }, { "macebronze", "Maça de bronze" }, { "macesilver", "Frostner" }, { "sledgeiron", "Marreta de ferro" }, { "sledge_demolisher", "Demolidor" }, { "bow", "Arco bruto" }, { "bowfinewood", "Arco de madeira fina" }, { "bowhuntsman", "Arco do caçador" }, { "bowdraugrfang", "Presa de Draugr" }, { "bowspine", "Estilhaçador de espinha" }, { "crossbowarbalest", "Arbalest" }, { "crossbowripper", "Ripper" }, { "shieldwood", "Escudo de madeira" }, { "shieldbronze buckler", "Broquel de bronze" }, { "shieldironbuckler", "Broquel de ferro" }, { "shieldblackmetal", "Escudo de metal negro" }, { "shieldcarapace", "Escudo de carapaça" }, { "shieldflametal", "Escudo de flametal" }, { "atgierbronze", "Atgeir de bronze" }, { "atgieriron", "Atgeir de ferro" }, { "atgierblackmetal", "Atgeir de metal negro" }, { "spearflint", "Lança de sílex" }, { "spearbronze", "Lança de bronze" }, { "spearwolffang", "Lança de presas" }, { "carrot", "Cenoura" }, { "turnip", "Nabo" }, { "onion", "Cebola" }, { "barley", "Cevada" }, { "flax", "Linho" }, { "sap", "Seiva" }, { "jotunpuffs", "Cogumelo Jotun" }, { "magecap", "Chapéu de Mago" }, { "vineberry", "Baga de Videira" } }; private static readonly Dictionary BossDisplayNamesPtBr = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Eikthyr", "Eikthyr" }, { "gd_king", "O Ancião" }, { "Bonemass", "Massa Óssea" }, { "Dragon", "Moder" }, { "GoblinKing", "Yagluth" }, { "SeekerQueen", "Rainha Seeker" }, { "Fader", "Fader" }, { "Charred_Melee_Dyrnwyn", "Guerreiro Carbonizado Dyrnwyn" }, { "Fenring_Cultist_Hildir", "Cultista Fenring da Hildir" }, { "GoblinBruteBros", "Irmãos Berserker Fuling" }, { "GoblinBrute_Hildir", "Berserker Fuling da Hildir" }, { "GoblinShaman_Hildir", "Xamã Fuling da Hildir" }, { "Skeleton_Hildir", "Esqueleto da Hildir" } }; private static readonly Dictionary BossPortugueseToPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Eikthyr", "Eikthyr" }, { "O Ancião", "gd_king" }, { "Ancião", "gd_king" }, { "O Anciao", "gd_king" }, { "Anciao", "gd_king" }, { "Massa Óssea", "Bonemass" }, { "Massa Ossea", "Bonemass" }, { "Bonemass", "Bonemass" }, { "Moder", "Dragon" }, { "Yagluth", "GoblinKing" }, { "Rainha Seeker", "SeekerQueen" }, { "Rainha dos Seeker", "SeekerQueen" }, { "Seeker Queen", "SeekerQueen" }, { "Fader", "Fader" }, { "Guerreiro Carbonizado Dyrnwyn", "Charred_Melee_Dyrnwyn" }, { "Dyrnwyn", "Charred_Melee_Dyrnwyn" }, { "Cultista Fenring da Hildir", "Fenring_Cultist_Hildir" }, { "Irmãos Berserker Fuling", "GoblinBruteBros" }, { "Irmaos Berserker Fuling", "GoblinBruteBros" }, { "Berserker Fuling da Hildir", "GoblinBrute_Hildir" }, { "Xamã Fuling da Hildir", "GoblinShaman_Hildir" }, { "Xama Fuling da Hildir", "GoblinShaman_Hildir" }, { "Esqueleto da Hildir", "Skeleton_Hildir" } }; private static readonly Dictionary FishDisplayNamesPtBr = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Fish1", "Perca" }, { "Fish2", "Lúcio" }, { "Fish3", "Atum" }, { "Fish4_cave", "Tetra" }, { "Fish5", "Peixe Troll" }, { "Fish6", "Arenque gigante" }, { "Fish7", "Garoupa" }, { "Fish8", "Garoupa estrelada" }, { "Fish9", "Peixe-pescador" }, { "Fish10", "Salmão do norte" }, { "Fish11", "Peixe magma" }, { "Fish12", "Baiacu" } }; private static readonly Dictionary FishPortugueseToPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Perca", "Fish1" }, { "Lúcio", "Fish2" }, { "Lucio", "Fish2" }, { "Fish3", "Fish3" }, { "Atum", "Fish3" }, { "Tetra", "Fish4_cave" }, { "Peixe Troll", "Fish5" }, { "Arenque gigante", "Fish6" }, { "Garoupa", "Fish7" }, { "Garoupa estrelada", "Fish8" }, { "Peixe-pescador", "Fish9" }, { "Peixe pescador", "Fish9" }, { "Salmão do norte", "Fish10" }, { "Salmao do norte", "Fish10" }, { "Peixe magma", "Fish11" }, { "Baiacu", "Fish12" } }; private static readonly Dictionary HudFriendlyNames = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Boar", "Javali" }, { "Neck", "Nixe" }, { "Greyling", "Greydwarf Jovem" }, { "Greydwarf", "Greydwarf" }, { "Greydwarf_Elite", "Greydwarf Elite" }, { "Greydwarf_Shaman", "Xamã Greydwarf" }, { "Troll", "Troll" }, { "Skeleton", "Esqueleto" }, { "Skeleton_Poison", "Esqueleto Venenoso" }, { "Draugr", "Draugr" }, { "Draugr_Elite", "Draugr Elite" }, { "Blob", "Gosma" }, { "BlobElite", "Gosma Elite" }, { "Leech", "Sanguessuga" }, { "Surtling", "Surtling" }, { "Abomination", "Abominação" }, { "Wolf", "Lobo" }, { "Hatchling", "Draco" }, { "Fenring", "Fenring" }, { "StoneGolem", "Golem de Pedra" }, { "Goblin", "Fuling" }, { "GoblinArcher", "Fuling Arqueiro" }, { "GoblinBrute", "Fuling Berserker" }, { "GoblinShaman", "Xamã Fuling" }, { "Lox", "Lox" }, { "Deathsquito", "Mosquito da Morte" }, { "Serpent", "Serpente Marinha" }, { "Bat", "Morcego" }, { "Ulv", "Ulv" }, { "Hare", "Lebre" }, { "Tick", "Carrapato" }, { "Seeker", "Seeker" }, { "SeekerBrute", "Seeker Brutamontes" }, { "Gjall", "Gjall" }, { "Dverger", "Dvergr" }, { "Charred_Melee", "Carbonizado Guerreiro" }, { "Charred_Archer", "Carbonizado Arqueiro" }, { "Charred_Mage", "Carbonizado Mago" }, { "Morgen", "Morgen" }, { "Asksvin", "Asksvin" }, { "Volture", "Abutre das Cinzas" }, { "Eikthyr", "Eikthyr" }, { "gd_king", "O Ancião" }, { "Bonemass", "Massa Óssea" }, { "Dragon", "Moder" }, { "GoblinKing", "Yagluth" }, { "SeekerQueen", "Rainha Seeker" }, { "Fader", "Fader" }, { "Fish1", "Perca" }, { "Fish2", "Lúcio" }, { "Fish3", "Atum" }, { "Fish4_cave", "Tetra" }, { "Fish5", "Peixe Troll" }, { "Fish6", "Arenque gigante" }, { "Fish7", "Garoupa" }, { "Fish8", "Garoupa estrelada" }, { "Fish9", "Peixe-pescador" }, { "Fish10", "Salmão do norte" }, { "Fish11", "Peixe magma" }, { "Fish12", "Baiacu" }, { "Carrot", "Cenoura" }, { "Turnip", "Nabo" }, { "Onion", "Cebola" }, { "Barley", "Cevada" }, { "Flax", "Linho" }, { "Sap", "Seiva" }, { "JotunPuffs", "Cogumelo Jotun" }, { "Magecap", "Chapéu de Mago" }, { "Vineberry", "Baga de Videira" }, { "Fiddleheadfern", "Samambaia" }, { "Run", "Corrida" }, { "Jump", "Salto" }, { "Swords", "Espadas" }, { "Knives", "Facas" }, { "Clubs", "Porretes" }, { "Polearms", "Armas de Haste" }, { "Spears", "Lanças" }, { "Blocking", "Bloqueio" }, { "Axes", "Machados" }, { "Bows", "Arcos" }, { "Crossbows", "Bestas" }, { "ElementalMagic", "Magia Elemental" }, { "BloodMagic", "Magia de Sangue" }, { "Fishing", "Pesca" }, { "Cooking", "Culinária" }, { "Deer", "Cervo" }, { "EvilHeart_Forest", "Coração Maligno da Floresta" }, { "Ghost", "Fantasma" }, { "Wraith", "Aparição" }, { "Fenring_Cultist", "Cultista Fenring" }, { "SeekerBrood", "Cria de Seeker" }, { "DvergerMage", "Mago Dvergr" }, { "DvergerMageFire", "Mago Dvergr de Fogo" }, { "DvergerMageIce", "Mago Dvergr de Gelo" }, { "DvergerMageSupport", "Mago Dvergr de Suporte" }, { "DvergerRogue", "Ladino Dvergr" }, { "Charred_Twitcher", "Carbonizado Retorcido" }, { "Charred_Melee_Dyrnwyn", "Guerreiro Carbonizado Dyrnwyn" }, { "GoblinBruteBros", "Irmãos Berserker Fuling" }, { "GoblinBrute_Hildir", "Berserker Fuling da Hildir" }, { "GoblinShaman_Hildir", "Xamã Fuling da Hildir" }, { "Fenring_Cultist_Hildir", "Cultista Fenring da Hildir" }, { "Skeleton_Hildir", "Esqueleto da Hildir" }, { "ExploreBlackForest", "Explorador da Floresta Negra" }, { "ExploreSwamp", "Explorador do Pântano" }, { "ExploreMountain", "Explorador das Montanhas" }, { "ExplorePlains", "Explorador das Planícies" }, { "ExploreMistlands", "Explorador das Terras Nebulosas" }, { "ExploreAshlands", "Explorador das Terras das Cinzas" }, { "Kill100Enemies", "Exterminador" }, { "CraftMaster", "Mestre Artesão" }, { "Mapa 5%", "Mapa 5%" } }; private GameObject _inventoryRankingButton; private GameObject _inventoryRankingPanel; private Text _inventoryRankingTitle; private Text _inventoryRankingBody; private RectTransform _inventoryRankingBodyRect; private ScrollRect _inventoryRankingScrollRect; private GameObject _inventoryGuideSidebar; private GameObject _inventoryGuideSubSidebar; private readonly List _inventoryGuideCategoryButtons = new List(); private readonly List _inventoryGuideSubCategoryButtons = new List(); private int _inventoryGuideCategoryIndex; private string _inventoryGuideSubCategory = ""; private string[] _inventoryGuideVisibleSubCategories = new string[0]; private readonly HashSet _inventoryGuideExpandedBlocks = new HashSet(); private GameObject _inventoryRankingPrimaryAction; private Text _inventoryRankingPrimaryActionLabel; private GameObject _inventoryRankingSecondaryAction; private Text _inventoryRankingSecondaryActionLabel; private GameObject _inventoryExchangeMinusAction; private Text _inventoryExchangeMinusLabel; private GameObject _inventoryExchangePlusAction; private Text _inventoryExchangePlusLabel; private GameObject _inventoryExchangeMaxAction; private Text _inventoryExchangeMaxLabel; private Text _inventoryExchangeAmountLabel; private bool _inventoryRankingOpen; private float _inventoryRankingNextRefresh; private int _inventoryRankingTab; private int _inventoryExchangePoints; private const float ActionPlayerCollapsedHeight = 58f; private const float ActionPlayerExpandedHeight = 738f; private const string HudColorPoints = "#00FF7F"; private const string HudColorDanger = "#FF5555"; private const string HudColorProgress = "#4FC3F7"; private const string HudColorHighlight = "#FFD700"; private const string HudColorMuted = "#D7DCE5"; private const string HudColorName = "#FFFFFF"; private const string HudColorSeparator = "#8F6E38"; private const string UnityHudBundleResourceSuffix = "glitnirrankinghud"; private AssetBundle _unityHudBundle; private GameObject _unityHudCanvas; private readonly Dictionary _unityHudTransforms = new Dictionary(StringComparer.OrdinalIgnoreCase); private bool _unityHudLoadAttempted; private bool _unityHudAvailable; private bool _unityHudButtonsBound; private float _unityHudNextRefresh; private int _unityHudTabIndex; private int _unityHudGuideCategoryIndex; private int _unityHudExchangePoints; private int _unityHudLeaderboardScrollIndex; private int _unityHudGuideScrollIndex; private int _unityHudPlayersScrollIndex; private int _unityHudGuideSubCategoryIndex; private InputField _unityHudExchangeInputField; private float _unityHudExchangePendingSince; private GameObject _unityHudInputShield; private GameObject _unityShortcutIconObject; private RawImage _unityShortcutIconImage; private static readonly UnityGuideCategory[] UnityGuideCategories = new UnityGuideCategory[9] { new UnityGuideCategory("COMBAT", "COMBATE", "COMBATE [ATIVO]", "Derrote criaturas configuradas para ganhar pontos."), new UnityGuideCategory("BOSSES", "CHEFES", "CHEFES [ATIVO]", "Vitórias contra chefes e encontros especiais."), new UnityGuideCategory("SKILLS", "HABILID.", "HABILIDADES [ATIVO]", "Marcos de progressao de habilidade."), new UnityGuideCategory("FISHING", "PESCA", "PESCA [ATIVO]", "Peixes e capturas que geram honra."), new UnityGuideCategory("FARMING", "CULTIVO", "CULTIVO [ATIVO]", "Colheitas e jackpots de producao."), new UnityGuideCategory("CRAFTING", "PRODUCAO", "PRODUCAO [ATIVO]", "Itens criados, raridades e bonus unicos."), new UnityGuideCategory("QUESTS", "QUESTS", "QUESTS [ATIVO]", "Missoes do Marketplace que contam para o ranking."), new UnityGuideCategory("EXPLORATION", "EXPLOR.", "EXPLORACAO [ATIVO]", "Revele o mapa para ativar marcos de exploracao."), new UnityGuideCategory("PENALTIES", "PENAL.", "PENALIDADES", "Mortes e ajustes que removem pontos.") }; private Font _runtimeUnityHudFont; private const float CraftRankingVerifyIntervalSeconds = 0.25f; private const float CraftRankingVerifyTimeoutSeconds = 12f; private readonly Dictionary _entriesByPlayerName = new Dictionary(StringComparer.OrdinalIgnoreCase); private List _orderedRankingCache = new List(); private Dictionary _rankingSnapshotCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private bool _rankingCacheDirty = true; private readonly HashSet _marketplaceQuestCreditKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _fishingCatchCreditKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _marketplaceQuestCreditsByPlayer = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _fishingCreditsByPlayer = new Dictionary>(StringComparer.OrdinalIgnoreCase); private void EnsureRulesFileExists() { try { if (!File.Exists(_rulesFilePath)) { File.WriteAllText(_rulesFilePath, BuildDefaultRulesFileContents(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao criar arquivo de regras: " + ex)); } } private string BuildDefaultRulesFileContents() { StringBuilder stringBuilder = new StringBuilder(16384); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine("# GLITNIR RANKING - CONFIGURACAO DE PONTUACAO"); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine("# Este arquivo controla as regras do ranking do servidor Glitnir."); stringBuilder.AppendLine("# Prefab = nome interno do item/criatura/colheita no Valheim."); stringBuilder.AppendLine("# Pontos = valor entregue ao jogador quando a regra for validada."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Organizacao principal:"); stringBuilder.AppendLine("# - KillPoints: pontos por criatura normal. Peixes NAO ficam aqui."); stringBuilder.AppendLine("# - FishingPoints: somente pontos por peixe realmente pescado."); stringBuilder.AppendLine("# - BossPoints: pontos por bosses e minibosses."); stringBuilder.AppendLine("# - CraftPoints.Rules: pontos por craft, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("# - FarmJackpots.Rules: jackpots de colheita, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("# - UniqueCraftJackpots.Rules: jackpots unicos por craft, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Nao use virgulas nas listas de producao/jackpot. Use ;"); stringBuilder.AppendLine("# Exemplos:"); stringBuilder.AppendLine("# CraftPoints.Rules=MeadHealthMinor:20;ArrowNeedle:2"); stringBuilder.AppendLine("# FarmJackpots.Rules=Carrot:200:1000"); stringBuilder.AppendLine("# UniqueCraftJackpots.Rules=IronSword:1:800"); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine(); stringBuilder.AppendLine("RankingEnabled=true"); stringBuilder.AppendLine("EnableKillPoints=true"); stringBuilder.AppendLine("EnableBossPoints=true"); stringBuilder.AppendLine("DefaultKillPoints=1"); stringBuilder.AppendLine("TopCount=10"); stringBuilder.AppendLine("AllowRepeatedBossPoints=false"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Logging"); stringBuilder.AppendLine("DebugLogging=false"); stringBuilder.AppendLine("LogHitReports=false"); stringBuilder.AppendLine("LogKillReports=false"); stringBuilder.AppendLine("LogSkillReports=false"); stringBuilder.AppendLine("LogPendingKillReports=false"); stringBuilder.AppendLine("LogSnapshotRequests=false"); stringBuilder.AppendLine("LogPointsChanges=false"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Skill milestone jackpot points"); stringBuilder.AppendLine("EnableSkillPoints=true"); stringBuilder.AppendLine("IgnoreTamedKills=true"); stringBuilder.AppendLine("EnableMarketplaceQuestPoints=true"); stringBuilder.AppendLine("EnableFishingPoints=true"); stringBuilder.AppendLine("EnableDeathPenalty=true"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Marketplace quest points"); stringBuilder.AppendLine("[MarketplaceQuestPoints]"); stringBuilder.AppendLine("QuestPointMap=ccq_b21:450,ccq_b22:150"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Penalidade por morte"); stringBuilder.AppendLine("[DeathPenalty]"); stringBuilder.AppendLine("# UseMultiplierMode=false usa a tabela Rules abaixo."); stringBuilder.AppendLine("# UseMultiplierMode=true ignora Rules e usa PenaltyPerDeath por morte."); stringBuilder.AppendLine("# Exemplo multiplicador: PenaltyPerDeath=100 e 15 mortes = -1500."); stringBuilder.AppendLine("UseMultiplierMode=false"); stringBuilder.AppendLine("PenaltyPerDeath=100"); stringBuilder.AppendLine("# Formato do modo tabela: quantidadeDeMortes:pontosPerdidos,quantidadeDeMortes:pontosPerdidos"); stringBuilder.AppendLine("# Exemplo: 1:0,2:100,5:300,10:800"); stringBuilder.AppendLine("Rules=1:0,2:100,5:300,10:800"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Reward claims"); stringBuilder.AppendLine("RewardClaimsEnabled=true"); stringBuilder.AppendLine("RewardClaimCycleId=temporada_001"); stringBuilder.AppendLine("RewardTop1MinPoints=300"); stringBuilder.AppendLine("RewardTop1Label=Recompensa do 1 lugar"); stringBuilder.AppendLine("RewardTop1Prefab=Coins"); stringBuilder.AppendLine("RewardTop1Amount=500"); stringBuilder.AppendLine("RewardTop2MinPoints=200"); stringBuilder.AppendLine("RewardTop2Label=Recompensa do 2 lugar"); stringBuilder.AppendLine("RewardTop2Prefab=AmberPearl"); stringBuilder.AppendLine("RewardTop2Amount=10"); stringBuilder.AppendLine("RewardTop3MinPoints=100"); stringBuilder.AppendLine("RewardTop3Label=Recompensa do 3 lugar"); stringBuilder.AppendLine("RewardTop3Prefab=Ruby"); stringBuilder.AppendLine("RewardTop3Amount=5"); stringBuilder.AppendLine(); AppendBossPoints(stringBuilder); AppendKillPoints(stringBuilder); AppendFishingPoints(stringBuilder); AppendSkillMilestoneJackpotPoints(stringBuilder); AppendProductionRankingRules(stringBuilder); return stringBuilder.ToString(); } private void AppendBossPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# BOSS POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Cada boss/miniboss fica em uma chave própria."); sb.AppendLine("BossPoints.Bonemass=2200"); sb.AppendLine("BossPoints.Charred_Melee_Dyrnwyn=40"); sb.AppendLine("BossPoints.Dragon=3000"); sb.AppendLine("BossPoints.Eikthyr=1200"); sb.AppendLine("BossPoints.Fader=10000"); sb.AppendLine("BossPoints.Fenring_Cultist_Hildir=30"); sb.AppendLine("BossPoints.gd_king=4200"); sb.AppendLine("BossPoints.GoblinBruteBros=35"); sb.AppendLine("BossPoints.GoblinBrute_Hildir=18"); sb.AppendLine("BossPoints.GoblinKing=6000"); sb.AppendLine("BossPoints.GoblinShaman_Hildir=18"); sb.AppendLine("BossPoints.SeekerQueen=8600"); sb.AppendLine("BossPoints.Skeleton_Hildir=25"); sb.AppendLine(); } private void AppendPoint(StringBuilder sb, string section, string key, int points) { sb.AppendLine(section + "." + key + "=" + points); } private void AppendKillPoint(StringBuilder sb, string prefabName, int points, string comment = null) { } private void AppendKillPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# KILL POINTS / COMBATE"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos por criaturas normais. Peixes nao devem ficar aqui; use [FishingPoints]."); sb.AppendLine("# Formato gerado pelo BepInEx: [KillPoints] Prefab = pontos."); sb.AppendLine("# Mantido no modo performance: o mod NAO envia RPC por hit; pontua apenas kill confirmada."); sb.AppendLine("KillPoints.Boar=1"); sb.AppendLine("KillPoints.Neck=1"); sb.AppendLine("KillPoints.Greyling=1"); sb.AppendLine("KillPoints.Greydwarf=1"); sb.AppendLine("KillPoints.Greydwarf_Elite=2"); sb.AppendLine("KillPoints.Greydwarf_Shaman=2"); sb.AppendLine("KillPoints.Skeleton=1"); sb.AppendLine("KillPoints.Troll=5"); sb.AppendLine("KillPoints.Draugr=2"); sb.AppendLine("KillPoints.Draugr_Elite=3"); sb.AppendLine("KillPoints.Blob=2"); sb.AppendLine("KillPoints.Wolf=2"); sb.AppendLine("KillPoints.Hatchling=2"); sb.AppendLine("KillPoints.StoneGolem=8"); sb.AppendLine("KillPoints.Deathsquito=3"); sb.AppendLine("KillPoints.Goblin=3"); sb.AppendLine("KillPoints.GoblinBrute=5"); sb.AppendLine("KillPoints.GoblinShaman=4"); sb.AppendLine("KillPoints.Lox=6"); sb.AppendLine("KillPoints.Serpent=6"); sb.AppendLine("KillPoints.Seeker=4"); sb.AppendLine("KillPoints.SeekerBrute=8"); sb.AppendLine("KillPoints.Gjall=10"); sb.AppendLine("KillPoints.Charred_Melee=4"); sb.AppendLine("KillPoints.Charred_Archer=4"); sb.AppendLine("KillPoints.Morgen=12"); sb.AppendLine(); } private void AppendFishingPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# FISHING POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# As chaves podem ficar em português; o código converte para o prefab real internamente."); sb.AppendLine("# Mapeamento: Perca=Fish1, Lúcio=Fish2, Fish3=Fish3, Tetra=Fish4_cave, Peixe Troll=Fish5, Arenque gigante=Fish6, Garoupa=Fish7, Garoupa estrelada=Fish8, Peixe-pescador=Fish9, Salmão do norte=Fish10, Peixe magma=Fish11, Baiacu=Fish12"); AppendFishingPoint(sb, "Perca", 1); AppendFishingPoint(sb, "Lúcio", 1); AppendFishingPoint(sb, "Fish3", 2); AppendFishingPoint(sb, "Tetra", 3); AppendFishingPoint(sb, "Peixe Troll", 2); AppendFishingPoint(sb, "Arenque gigante", 3); AppendFishingPoint(sb, "Garoupa", 3000); AppendFishingPoint(sb, "Garoupa estrelada", 4); AppendFishingPoint(sb, "Peixe-pescador", 4); AppendFishingPoint(sb, "Salmão do norte", 5); AppendFishingPoint(sb, "Peixe magma", 5); AppendFishingPoint(sb, "Baiacu", 6); sb.AppendLine(); } private void AppendFishingPoint(StringBuilder sb, string prefabName, int points) { sb.AppendLine("FishingPoints." + prefabName + "=" + points); } private void AppendSkillPoint(StringBuilder sb, string skillKey, int points, string comment = null) { if (!string.IsNullOrWhiteSpace(comment)) { sb.AppendLine(comment); } sb.AppendLine("SkillPoints." + skillKey + "=" + points); } private void AppendSkillMilestoneJackpotPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# SKILL MILESTONE JACKPOT POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos únicos por marco de skill. Formato: SkillMilestoneJackpotPoints.Skill=20:pontos,40:pontos,60:pontos,80:pontos,100:pontos"); sb.AppendLine("# Exemplo: Bows 20/40/60/80/100. Cada marco pontua uma única vez por jogador/ciclo."); AppendSkillMilestoneJackpotPoint(sb, "BloodMagic", "20:1500,40:3000,60:5000,80:7500,100:12000"); AppendSkillMilestoneJackpotPoint(sb, "ElementalMagic", "20:1000,40:2000,60:3500,80:5500,100:8000"); AppendSkillMilestoneJackpotPoint(sb, "Bows", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Crossbows", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Swords", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Axes", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Clubs", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Spears", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Polearms", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Knives", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Blocking", "20:400,40:800,60:1500,80:2500,100:3500"); AppendSkillMilestoneJackpotPoint(sb, "Run", "20:300,40:700,60:1200,80:2000,100:3000"); AppendSkillMilestoneJackpotPoint(sb, "Jump", "20:300,40:700,60:1200,80:2000,100:3000"); AppendSkillMilestoneJackpotPoint(sb, "WoodCutting", "20:300,40:700,60:1200,80:1800,100:2500"); AppendSkillMilestoneJackpotPoint(sb, "Pickaxes", "20:300,40:700,60:1200,80:1800,100:2500"); AppendSkillMilestoneJackpotPoint(sb, "Farming", "20:0,40:0,60:0,80:0,100:0"); sb.AppendLine(); } private void AppendSkillMilestoneJackpotPoint(StringBuilder sb, string skillKey, string milestones) { sb.AppendLine("SkillMilestoneJackpotPoints." + skillKey + "=" + milestones); } private void AppendProductionRankingRules(StringBuilder sb) { sb.AppendLine("# ======================================================================="); sb.AppendLine("# PRODUCAO E JACKPOTS - GLITNIR RANKING"); sb.AppendLine("# ======================================================================="); sb.AppendLine("# Estes blocos usam UMA entrada por sistema para manter o arquivo limpo."); sb.AppendLine("# Nao use virgulas. Separe cada regra com ponto e virgula (;)."); sb.AppendLine("# Formatos:"); sb.AppendLine("# CraftPoints.Rules = Prefab:pontos;OutroPrefab:pontos"); sb.AppendLine("# FarmJackpots.Rules = Prefab:quantidade:pontos;OutroPrefab:quantidade:pontos"); sb.AppendLine("# UniqueCraftJackpots.Rules = Prefab:quantidade:pontos;OutroPrefab:quantidade:pontos"); sb.AppendLine("# Exemplos reais:"); sb.AppendLine("# MeadHealthMinor:20"); sb.AppendLine("# Carrot:200:1000"); sb.AppendLine("# IronSword:1:800"); sb.AppendLine("# ======================================================================="); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# CRAFT POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos por craft de qualquer prefab configurado."); sb.AppendLine("# Exemplo de item: MeadHealthMinor:20"); sb.AppendLine("CraftPoints.Rules=MeadHealthMinor:20"); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# FARM JACKPOTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Jackpot por colheita. Premia uma vez por jogador/ciclo quando bater a quantidade."); sb.AppendLine("# Exemplo de item: Carrot:200:1000"); sb.AppendLine("FarmJackpots.Rules=Carrot:200:1000"); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# UNIQUE CRAFT JACKPOTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Jackpot unico por craft. Ideal para armas, armaduras e marcos especiais."); sb.AppendLine("# Exemplo de item: IronSword:1:800"); sb.AppendLine("UniqueCraftJackpots.Rules=IronSword:1:800"); sb.AppendLine(); } private void AppendStringPoint(StringBuilder sb, string section, string key, string value) { sb.AppendLine(section + "." + key + "=" + value); } private void LoadRules() { _rules = new RankingRules(); try { if (!File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); foreach (string text in array) { string text2 = text.Trim(); if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#") || text2.StartsWith(";") || text2.StartsWith("//")) { continue; } int num = text2.IndexOf('='); if (num <= 0) { continue; } string text3 = text2.Substring(0, num).Trim(); string text4 = text2.Substring(num + 1).Trim(); if (text3.Equals("RankingEnabled", StringComparison.OrdinalIgnoreCase)) { _rules.RankingEnabled = ParseBool(text4, _rules.RankingEnabled); } else if (text3.Equals("EnableKillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableKillPoints = ParseBool(text4, _rules.EnableKillPoints); } else if (text3.Equals("EnableBossPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableBossPoints = ParseBool(text4, _rules.EnableBossPoints); } else if (text3.Equals("DefaultKillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.DefaultKillPoints = ParseInt(text4, _rules.DefaultKillPoints); } else if (text3.Equals("TopCount", StringComparison.OrdinalIgnoreCase)) { _rules.TopCount = Mathf.Clamp(ParseInt(text4, _rules.TopCount), 1, 50); } else if (text3.Equals("AllowRepeatedBossPoints", StringComparison.OrdinalIgnoreCase)) { _rules.AllowRepeatedBossPoints = ParseBool(text4, _rules.AllowRepeatedBossPoints); } else if (text3.Equals("DebugLogging", StringComparison.OrdinalIgnoreCase)) { _rules.DebugLogging = ParseBool(text4, _rules.DebugLogging); } else if (text3.Equals("LogHitReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogHitReports = ParseBool(text4, _rules.LogHitReports); } else if (text3.Equals("LogKillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogKillReports = ParseBool(text4, _rules.LogKillReports); } else if (text3.Equals("LogPendingKillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogPendingKillReports = ParseBool(text4, _rules.LogPendingKillReports); } else if (text3.Equals("LogSnapshotRequests", StringComparison.OrdinalIgnoreCase)) { _rules.LogSnapshotRequests = ParseBool(text4, _rules.LogSnapshotRequests); } else if (text3.Equals("LogPointsChanges", StringComparison.OrdinalIgnoreCase)) { _rules.LogPointsChanges = ParseBool(text4, _rules.LogPointsChanges); } else if (text3.Equals("EnableSkillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableSkillPoints = ParseBool(text4, _rules.EnableSkillPoints); } else if (text3.Equals("LogSkillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogSkillReports = ParseBool(text4, _rules.LogSkillReports); } else if (text3.Equals("EnableFishingPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableFishingPoints = ParseBool(text4, _rules.EnableFishingPoints); } else if (text3.Equals("EnableDeathPenalty", StringComparison.OrdinalIgnoreCase)) { _rules.EnableDeathPenalty = ParseBool(text4, _rules.EnableDeathPenalty); } else if (text3.Equals("DeathPenalty.UseMultiplierMode", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyUseMultiplier", StringComparison.OrdinalIgnoreCase) || text3.Equals("UseMultiplierMode", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyUseMultiplier = ParseBool(text4, _rules.DeathPenaltyUseMultiplier); } else if (text3.Equals("DeathPenalty.PenaltyPerDeath", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyPerDeath", StringComparison.OrdinalIgnoreCase) || text3.Equals("PenaltyPerDeath", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyPerDeath = Mathf.Max(0, ParseInt(text4, _rules.DeathPenaltyPerDeath)); } else if (text3.Equals("DeathPenalty.Rules", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyRules", StringComparison.OrdinalIgnoreCase) || text3.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyRules = ParseDeathPenaltyRules(text4); } else if (text3.Equals("RewardClaimsEnabled", StringComparison.OrdinalIgnoreCase)) { _rules.RewardClaimsEnabled = ParseBool(text4, _rules.RewardClaimsEnabled); } else if (text3.Equals("RewardClaimCycleId", StringComparison.OrdinalIgnoreCase)) { _rules.RewardClaimCycleId = SafeLimit(text4, 64); } else if (text3.Equals("RewardTop1MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop1MinPoints)); } else if (text3.Equals("RewardTop1Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop1Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop1Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop1Amount)); } else if (text3.Equals("RewardTop2MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop2MinPoints)); } else if (text3.Equals("RewardTop2Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop2Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop2Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop2Amount)); } else if (text3.Equals("RewardTop3MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop3MinPoints)); } else if (text3.Equals("RewardTop3Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop3Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop3Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop3Amount)); } else if (text3.StartsWith("KillPoints.", StringComparison.OrdinalIgnoreCase)) { string text5 = SafeKey(text3.Substring("KillPoints.".Length)); if (!IsFishPrefab(text5)) { _rules.KillPoints[text5] = ParseInt(text4, 0); } } else if (text3.StartsWith("BossPoints.", StringComparison.OrdinalIgnoreCase)) { _rules.BossPoints[NormalizeBossPrefabName(text3.Substring("BossPoints.".Length))] = ParseInt(text4, 0); } else if (text3.StartsWith("FishingPoints.", StringComparison.OrdinalIgnoreCase)) { string text6 = NormalizeFishPrefabName(text3.Substring("FishingPoints.".Length)); if (IsFishPrefab(text6)) { _rules.FishingPoints[text6] = ParseInt(text4, 0); } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao carregar regras do ranking: " + ex)); } } private bool ParseBool(string raw, bool fallback) { bool result; return bool.TryParse(raw, out result) ? result : fallback; } private int ParseInt(string raw, int fallback) { int result; return int.TryParse(raw, out result) ? result : fallback; } private void InitializeSyncedRulesConfig() { try { string filePath = Path.Combine(Paths.ConfigPath, "glitnir.ranking.rules.cfg"); Dictionary dictionary = LoadLegacyRulesOverrides(filePath); MergeSectionOverrides(dictionary, LoadSectionRulesOverrides(filePath)); _rulesConfig = ((BaseUnityPlugin)this).Config; _cfgLockConfiguration = _rulesConfig.BindLockingConfig(); BindSyncedRulesConfigEntries(dictionary); BindRulesConfigEvents(); ApplyRulesFromSyncedConfig(); _rulesConfig.Save(); CleanupFishKillPointConfigEntries(); RewriteRulesConfigHeaderAndGroupedSections(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao inicializar config sincronizada do ranking: " + ex)); EnsureRulesFileExists(); LoadRules(); } } private void SetupRulesConfigWatcher() { try { if (!string.IsNullOrWhiteSpace(_rulesFilePath)) { string directoryName = Path.GetDirectoryName(_rulesFilePath); string fileName = Path.GetFileName(_rulesFilePath); if (!string.IsNullOrWhiteSpace(directoryName) && !string.IsNullOrWhiteSpace(fileName)) { _rulesConfigWatcher = new FileSystemWatcher(directoryName, fileName); _rulesConfigWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime; _rulesConfigWatcher.Changed += OnRulesConfigFileChanged; _rulesConfigWatcher.Created += OnRulesConfigFileChanged; _rulesConfigWatcher.Renamed += OnRulesConfigFileChanged; _rulesConfigWatcher.EnableRaisingEvents = true; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Não foi possível iniciar watcher da config do ranking: " + ex.Message)); } } private void OnRulesConfigFileChanged(object sender, FileSystemEventArgs args) { _rulesConfigReloadQueued = true; _rulesConfigReloadAt = Time.realtimeSinceStartup + 0.5f; } private void ProcessRulesConfigReloadIfNeeded() { if (!_rulesConfigReloadQueued || Time.realtimeSinceStartup < _rulesConfigReloadAt) { return; } _rulesConfigReloadQueued = false; try { ConfigFile rulesConfig = _rulesConfig; if (rulesConfig != null) { rulesConfig.Reload(); } BindSyncedRulesConfigEntries(null); BindRulesConfigEvents(); ApplyRulesFromSyncedConfig(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao recarregar config do ranking: " + ex)); } } private void RefreshRulesFromSyncedConfigIfNeeded() { if (!(Time.realtimeSinceStartup < _rulesConfigApplyAt)) { _rulesConfigApplyAt = Time.realtimeSinceStartup + 1f; ApplyRulesFromSyncedConfig(); } } private void BindRulesConfigEvents() { if (_rulesConfig != null && !_rulesConfigEventsBound) { _rulesConfig.SettingChanged += OnRulesConfigSettingChanged; _rulesConfig.ConfigReloaded += OnRulesConfigReloaded; _rulesConfigEventsBound = true; } } private void OnRulesConfigSettingChanged(object sender, SettingChangedEventArgs args) { QueueRulesConfigApply(); } private void OnRulesConfigReloaded(object sender, EventArgs args) { QueueRulesConfigApply(); } private void QueueRulesConfigApply() { _rulesConfigReloadQueued = false; _rulesConfigApplyAt = Time.realtimeSinceStartup; } private Dictionary LoadLegacyRulesOverrides(string filePath) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return dictionary; } bool flag = false; string[] array = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text in array) { string text2 = text.Trim(); if (text2.StartsWith("[") && text2.EndsWith("]")) { flag = true; break; } } if (flag) { return dictionary; } string[] array2 = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text3 in array2) { string text4 = text3.Trim(); if (string.IsNullOrWhiteSpace(text4) || text4.StartsWith("#") || text4.StartsWith(";") || text4.StartsWith("//")) { continue; } int num = text4.IndexOf('='); if (num > 0) { string text5 = text4.Substring(0, num).Trim(); string value = text4.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text5)) { dictionary[text5] = value; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao ler overrides legados da config do ranking: " + ex.Message)); } return dictionary; } private void MergeSectionOverrides(Dictionary target, Dictionary source) { if (target == null || source == null) { return; } foreach (KeyValuePair item in source) { if (!string.IsNullOrWhiteSpace(item.Key)) { target[item.Key] = item.Value; } } } private Dictionary LoadSectionRulesOverrides(string filePath) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return dictionary; } string text = string.Empty; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase) { "BossPoints", "FishingPoints", "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "SkillMilestoneJackpotPoints", "DeathPenalty", "ExplorationMapJackpots" }; string[] array = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text2 in array) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text3) || text3.StartsWith("#") || text3.StartsWith(";") || text3.StartsWith("//")) { continue; } if (text3.StartsWith("[") && text3.EndsWith("]")) { text = SafeKey(text3.Substring(1, text3.Length - 2)); continue; } int num = text3.IndexOf('='); if (num <= 0) { continue; } string text4 = text3.Substring(0, num).Trim(); string value = text3.Substring(num + 1).Trim(); if (string.IsNullOrWhiteSpace(text4)) { continue; } string text5 = text4; if (!text4.Contains(".") && !string.IsNullOrWhiteSpace(text)) { text5 = text + "." + text4; } int num2 = text5.IndexOf('.'); if (num2 > 0) { string text6 = text5.Substring(0, num2).Trim(); string text7 = SafeKey(text5.Substring(num2 + 1)); if (hashSet.Contains(text6) && !string.IsNullOrWhiteSpace(text7) && (!text6.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text7))) { dictionary[text6 + "." + text7] = value; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao ler entradas dinâmicas da config do ranking: " + ex.Message)); } return dictionary; } private bool IsLegacyRulesFileFormat(string filePath) { try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return false; } string[] array = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text in array) { string text2 = text.Trim(); if (!string.IsNullOrWhiteSpace(text2) && !text2.StartsWith("#") && !text2.StartsWith(";") && !text2.StartsWith("//")) { return !text2.StartsWith("[") || !text2.EndsWith("]"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao detectar formato legado da config do ranking: " + ex.Message)); } return false; } private void BindSyncedRulesConfigEntries(Dictionary legacyOverrides) { if (_rulesConfig != null) { legacyOverrides = legacyOverrides ?? new Dictionary(StringComparer.OrdinalIgnoreCase); RankingRules rankingRules = new RankingRules(); _cfgRankingEnabled = _rulesConfig.BindConfig("General", "RankingEnabled", ReadLegacyBool(legacyOverrides, "RankingEnabled", rankingRules.RankingEnabled), "Ativa o sistema de ranking."); _cfgEnableKillPoints = _rulesConfig.BindConfig("General", "EnableKillPoints", ReadLegacyBool(legacyOverrides, "EnableKillPoints", rankingRules.EnableKillPoints), "Ativa a pontuação por criaturas normais."); _cfgEnableBossPoints = _rulesConfig.BindConfig("General", "EnableBossPoints", ReadLegacyBool(legacyOverrides, "EnableBossPoints", rankingRules.EnableBossPoints), "Ativa a pontuação por bosses e minibosses."); _cfgDefaultKillPoints = _rulesConfig.BindConfig("General", "DefaultKillPoints", ReadLegacyInt(legacyOverrides, "DefaultKillPoints", rankingRules.DefaultKillPoints), "Pontos padrão para kills sem regra específica."); _cfgTopCount = _rulesConfig.BindConfig("General", "TopCount", ReadLegacyInt(legacyOverrides, "TopCount", rankingRules.TopCount), "Quantidade de jogadores exibidos no topo do ranking."); _cfgAllowRepeatedBossPoints = _rulesConfig.BindConfig("General", "AllowRepeatedBossPoints", ReadLegacyBool(legacyOverrides, "AllowRepeatedBossPoints", rankingRules.AllowRepeatedBossPoints), "Permite pontuar boss repetido."); _cfgEnableSkillPoints = _rulesConfig.BindConfig("General", "EnableSkillPoints", ReadLegacyBool(legacyOverrides, "EnableSkillPoints", rankingRules.EnableSkillPoints), "Ativa jackpots por marcos de skill."); _cfgIgnoreTamedKills = _rulesConfig.BindConfig("General", "IgnoreTamedKills", ReadLegacyBool(legacyOverrides, "IgnoreTamedKills", rankingRules.IgnoreTamedKills), "Ignora pontos por matar criaturas domadas."); _cfgEnableMarketplaceQuestPoints = _rulesConfig.BindConfig("General", "EnableMarketplaceQuestPoints", ReadLegacyBool(legacyOverrides, "EnableMarketplaceQuestPoints", rankingRules.EnableMarketplaceQuestPoints), "Ativa pontuação por conclusão de quests do Marketplace."); _cfgEnableFishingPoints = _rulesConfig.BindConfig("General", "EnableFishingPoints", ReadLegacyBool(legacyOverrides, "EnableFishingPoints", rankingRules.EnableFishingPoints), "Ativa pontuação própria por pesca."); _cfgEnableDeathPenalty = _rulesConfig.BindConfig("General", "EnableDeathPenalty", ReadLegacyBool(legacyOverrides, "EnableDeathPenalty", rankingRules.EnableDeathPenalty), "Ativa penalidade por morte no ranking."); _cfgDebugLogging = _rulesConfig.BindConfig("Logging", "DebugLogging", ReadLegacyBool(legacyOverrides, "DebugLogging", rankingRules.DebugLogging), "Ativa logs de depuração."); _cfgLogHitReports = _rulesConfig.BindConfig("Logging", "LogHitReports", ReadLegacyBool(legacyOverrides, "LogHitReports", rankingRules.LogHitReports), "Ativa logs de hit reports."); _cfgLogKillReports = _rulesConfig.BindConfig("Logging", "LogKillReports", ReadLegacyBool(legacyOverrides, "LogKillReports", rankingRules.LogKillReports), "Ativa logs de kill reports."); _cfgLogSkillReports = _rulesConfig.BindConfig("Logging", "LogSkillReports", ReadLegacyBool(legacyOverrides, "LogSkillReports", rankingRules.LogSkillReports), "Ativa logs de skill reports."); _cfgLogPendingKillReports = _rulesConfig.BindConfig("Logging", "LogPendingKillReports", ReadLegacyBool(legacyOverrides, "LogPendingKillReports", rankingRules.LogPendingKillReports), "Ativa logs das checagens pendentes de kill."); _cfgLogSnapshotRequests = _rulesConfig.BindConfig("Logging", "LogSnapshotRequests", ReadLegacyBool(legacyOverrides, "LogSnapshotRequests", rankingRules.LogSnapshotRequests), "Ativa logs de snapshots do ranking."); _cfgLogPointsChanges = _rulesConfig.BindConfig("Logging", "LogPointsChanges", ReadLegacyBool(legacyOverrides, "LogPointsChanges", rankingRules.LogPointsChanges), "Ativa logs de alterações de pontos."); _cfgRewardClaimsEnabled = _rulesConfig.BindConfig("Rewards", "RewardClaimsEnabled", ReadLegacyBool(legacyOverrides, "RewardClaimsEnabled", rankingRules.RewardClaimsEnabled), "Ativa o resgate de recompensas no HUD."); _cfgRewardClaimCycleId = _rulesConfig.BindConfig("Rewards", "RewardClaimCycleId", ReadLegacyString(legacyOverrides, "RewardClaimCycleId", rankingRules.RewardClaimCycleId), "Identificador do ciclo atual de recompensas."); _cfgRewardTop1MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop1MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop1MinPoints", rankingRules.RewardTop1MinPoints), "Pontos mínimos para o Top 1 resgatar."); _cfgRewardTop1Label = _rulesConfig.BindConfig("Rewards", "RewardTop1Label", ReadLegacyString(legacyOverrides, "RewardTop1Label", rankingRules.RewardTop1Label), "Texto exibido para a recompensa do Top 1."); _cfgRewardTop1Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop1Prefab", ReadLegacyString(legacyOverrides, "RewardTop1Prefab", rankingRules.RewardTop1Prefab), "Prefab do item entregue ao Top 1."); _cfgRewardTop1Amount = _rulesConfig.BindConfig("Rewards", "RewardTop1Amount", ReadLegacyInt(legacyOverrides, "RewardTop1Amount", rankingRules.RewardTop1Amount), "Quantidade do item entregue ao Top 1."); _cfgRewardTop2MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop2MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop2MinPoints", rankingRules.RewardTop2MinPoints), "Pontos mínimos para o Top 2 resgatar."); _cfgRewardTop2Label = _rulesConfig.BindConfig("Rewards", "RewardTop2Label", ReadLegacyString(legacyOverrides, "RewardTop2Label", rankingRules.RewardTop2Label), "Texto exibido para a recompensa do Top 2."); _cfgRewardTop2Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop2Prefab", ReadLegacyString(legacyOverrides, "RewardTop2Prefab", rankingRules.RewardTop2Prefab), "Prefab do item entregue ao Top 2."); _cfgRewardTop2Amount = _rulesConfig.BindConfig("Rewards", "RewardTop2Amount", ReadLegacyInt(legacyOverrides, "RewardTop2Amount", rankingRules.RewardTop2Amount), "Quantidade do item entregue ao Top 2."); _cfgRewardTop3MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop3MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop3MinPoints", rankingRules.RewardTop3MinPoints), "Pontos mínimos para o Top 3 resgatar."); _cfgRewardTop3Label = _rulesConfig.BindConfig("Rewards", "RewardTop3Label", ReadLegacyString(legacyOverrides, "RewardTop3Label", rankingRules.RewardTop3Label), "Texto exibido para a recompensa do Top 3."); _cfgRewardTop3Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop3Prefab", ReadLegacyString(legacyOverrides, "RewardTop3Prefab", rankingRules.RewardTop3Prefab), "Prefab do item entregue ao Top 3."); _cfgRewardTop3Amount = _rulesConfig.BindConfig("Rewards", "RewardTop3Amount", ReadLegacyInt(legacyOverrides, "RewardTop3Amount", rankingRules.RewardTop3Amount), "Quantidade do item entregue ao Top 3."); _cfgPointsExchangeEnabled = _rulesConfig.BindConfig("PointsExchange", "Enabled", ReadLegacyBool(legacyOverrides, "PointsExchangeEnabled", rankingRules.PointsExchangeEnabled), "Ativa o botão de câmbio de pontos por moedas no HUD."); _cfgPointsExchangePrefab = _rulesConfig.BindConfig("PointsExchange", "Prefab", ReadLegacyString(legacyOverrides, "PointsExchangePrefab", rankingRules.PointsExchangePrefab), "Prefab entregue no câmbio de pontos. Exemplo: Coins."); _cfgPointsExchangeUseCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "UseCoinsPerPoint", ReadLegacyBool(legacyOverrides, "PointsExchangeUseCoinsPerPoint", rankingRules.PointsExchangeUseCoinsPerPoint), "Ativa o modo antigo: moedas por ponto. Por padrão fica false."); _cfgPointsExchangeCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "CoinsPerPoint", ReadLegacyInt(legacyOverrides, "PointsExchangeCoinsPerPoint", rankingRules.PointsExchangeCoinsPerPoint), "Quantidade de moedas entregues por cada ponto trocado. Só é usado se UseCoinsPerPoint=true."); _cfgPointsExchangeUsePointsPerCoin = _rulesConfig.BindConfig("PointsExchange", "UsePointsPerCoin", ReadLegacyBool(legacyOverrides, "PointsExchangeUsePointsPerCoin", rankingRules.PointsExchangeUsePointsPerCoin), "Ativa o modo recomendado: pontos necessários para receber 1 moeda."); _cfgPointsExchangePointsPerCoin = _rulesConfig.BindConfig("PointsExchange", "PointsPerCoin", ReadLegacyInt(legacyOverrides, "PointsExchangePointsPerCoin", rankingRules.PointsExchangePointsPerCoin), "Quantidade de pontos necessários para receber 1 moeda. Exemplo: 100 = a cada 100 pontos, 1 coin."); _cfgPointsExchangeMinPoints = _rulesConfig.BindConfig("PointsExchange", "MinPoints", ReadLegacyInt(legacyOverrides, "PointsExchangeMinPoints", rankingRules.PointsExchangeMinPoints), "Valor legado. O câmbio não exige requisito mínimo; basta o jogador ter pontos disponíveis."); _cfgPointsExchangeMaxPointsPerRequest = _rulesConfig.BindConfig("PointsExchange", "MaxPointsPerRequest", ReadLegacyInt(legacyOverrides, "PointsExchangeMaxPointsPerRequest", rankingRules.PointsExchangeMaxPointsPerRequest), "Máximo de pontos convertidos por clique. Use 0 para trocar todos."); _cfgMarketplaceQuestPointMap = _rulesConfig.BindConfig("MarketplaceQuestPoints", "QuestPointMap", BuildMarketplaceQuestPointMapDefault(legacyOverrides), "Formato: questId:pontos separados por vírgula. Ex: ccq_b21:450,ccq_b22:150"); _cfgDeathPenaltyRules = _rulesConfig.BindConfig("DeathPenalty", "Rules", ReadLegacyString(legacyOverrides, "DeathPenalty.Rules", ReadLegacyString(legacyOverrides, "DeathPenaltyRules", "1:0,2:100,5:300,10:800")), "Penalidade por morte em marcos. Formato: mortes:pontosPerdidos,mortes:pontosPerdidos. Exemplo: 1:0,2:100,5:300,10:800"); _cfgDeathPenaltyUseMultiplier = _rulesConfig.BindConfig("DeathPenalty", "UseMultiplierMode", ReadLegacyBool(legacyOverrides, "DeathPenalty.UseMultiplierMode", ReadLegacyBool(legacyOverrides, "DeathPenaltyUseMultiplier", fallback: false)), "Se true, ignora a tabela Rules e aplica PenaltyPerDeath a cada morte. Exemplo: 15 mortes x 100 = -1500."); _cfgDeathPenaltyPerDeath = _rulesConfig.BindConfig("DeathPenalty", "PenaltyPerDeath", Mathf.Max(0, ReadLegacyInt(legacyOverrides, "DeathPenalty.PenaltyPerDeath", ReadLegacyInt(legacyOverrides, "DeathPenaltyPerDeath", 100))), "Valor perdido por cada morte quando UseMultiplierMode=true."); _cfgEnableExplorationJackpots = _rulesConfig.BindConfig("ExplorationMapJackpots", "Enabled", ReadLegacyBool(legacyOverrides, "ExplorationMapJackpots.Enabled", fallback: true), "Ativa jackpots por porcentagem de mapa revelado."); _cfgExplorationMapJackpotRules = _rulesConfig.BindConfig("ExplorationMapJackpots", "Rules", ReadLegacyString(legacyOverrides, "ExplorationMapJackpots.Rules", ReadLegacyString(legacyOverrides, "ExplorationMapJackpotRules", "5:25;10:50;25:150;50:400;75:800;100:1500")), "Jackpots por porcentagem do mapa revelado. Formato: porcentagem:pontos;porcentagem:pontos. Exemplo: 5:25;10:50;25:150"); BindCombatBiomeRuleEntries(legacyOverrides); BindProductionCategoryEntries(legacyOverrides); BindPointSectionEntries("BossPoints", "Pontos por bosses e minibosses.", legacyOverrides, _cfgBossPointEntries); BindPointSectionEntries("FishingPoints", "Pontos por peixe pescado.", legacyOverrides, _cfgFishingPointEntries); BindStringPointSectionEntries("SkillMilestoneJackpotPoints", "Pontos únicos por marco de skill. Formato: 20:500,40:1000,60:2000,80:3000,100:4000.", legacyOverrides, _cfgSkillMilestoneJackpotPointEntries); _cfgFarmJackpotRules = _rulesConfig.BindConfig("FarmJackpots", "Rules", ReadGroupedRulesDefault(legacyOverrides, "FarmJackpots", "Carrot:200:1000;Turnip:200:1200;Onion:200:1500;Barley:500:2000;Flax:500:2000"), "Jackpots de colheita. Edite somente esta linha. Formato: Prefab:quantidade:pontos;Prefab:quantidade:pontos. Exemplo: Carrot:200:1000;Barley:500:2000."); BindUniqueCraftJackpotCategoryEntries(legacyOverrides); } } private void BindUniqueCraftJackpotCategoryEntries(Dictionary legacyOverrides) { if (_cfgUniqueCraftJackpotCategoryEntries == null) { return; } _cfgUniqueCraftJackpotCategoryEntries.Clear(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Armas", "SwordIron;1;800,SwordSilver;1;1500,SwordBlackmetal;1;2500" }, { "Armaduras", "ArmorWolfChest;1;2000,ArmorCarapaceChest;1;3500" }, { "Outros", "" } }; string text = ReadLegacyString(legacyOverrides, "UniqueCraftJackpots.Rules", null); if (!string.IsNullOrWhiteSpace(text)) { foreach (KeyValuePair item in SplitUniqueCraftJackpotsIntoCategories(text)) { dictionary[item.Key] = MergeCommaRules(dictionary.ContainsKey(item.Key) ? dictionary[item.Key] : string.Empty, item.Value); } } if (legacyOverrides != null) { foreach (KeyValuePair legacyOverride in legacyOverrides) { if (string.IsNullOrWhiteSpace(legacyOverride.Key) || !legacyOverride.Key.StartsWith("UniqueCraftJackpots.", StringComparison.OrdinalIgnoreCase)) { continue; } string text2 = NormalizeHudCategoryName(legacyOverride.Key.Substring("UniqueCraftJackpots.".Length)); if (!string.IsNullOrWhiteSpace(text2) && !text2.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { string value = ConvertUniqueCraftJackpotItemsToConfigValue(legacyOverride.Value); if (!string.IsNullOrWhiteSpace(value) || !dictionary.ContainsKey(text2)) { dictionary[text2] = value; } } } } if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } foreach (string item2 in GetPreferredCategoryOrder(dictionary, new string[5] { "Armas", "Armaduras", "Ferramentas", "Comidas", "Outros" })) { BindSingleUniqueCraftJackpotCategory(item2, legacyOverrides, dictionary[item2]); } } private void BindSingleUniqueCraftJackpotCategory(string category, Dictionary legacyOverrides, string defaultValue) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { string raw = ReadLegacyString(legacyOverrides, "UniqueCraftJackpots." + category, defaultValue); raw = ConvertUniqueCraftJackpotItemsToConfigValue(raw); ConfigEntry value = _rulesConfig.BindConfig("UniqueCraftJackpots", category, raw, "Jackpot único por craft para esta categoria. Formato: Prefab;Quantidade;Pontos,OutroPrefab;Quantidade;Pontos. Exemplo: SwordIron;1;800,ArmorWolfChest;1;2000. Aceita qualquer prefab, inclusive de mods. O nome desta chave vira a categoria da config."); _cfgUniqueCraftJackpotCategoryEntries[category] = value; } } private Dictionary SplitUniqueCraftJackpotsIntoCategories(string raw) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase) { { "Armas", new List() }, { "Armaduras", new List() }, { "Outros", new List() } }; foreach (KeyValuePair item in ParseFlexibleJackpotRules(raw)) { string key = GuessUniqueCraftJackpotCategory(item.Key); if (!dictionary.ContainsKey(key)) { dictionary[key] = new List(); } JackpotRule value = item.Value; dictionary[key].Add(item.Key + ";" + Mathf.Max(1, value.RequiredAmount) + ";" + Mathf.Max(0, value.Points)); } Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair> item2 in dictionary) { dictionary2[item2.Key] = string.Join(",", item2.Value.ToArray()); } return dictionary2; } private string GuessUniqueCraftJackpotCategory(string prefab) { string text = SafeKey(prefab); if (string.IsNullOrWhiteSpace(text)) { return "Outros"; } if (text.StartsWith("Sword", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Axe", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Mace", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Knife", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Atgeir", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Spear", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Bow", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Crossbow", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Shield", StringComparison.OrdinalIgnoreCase)) { return "Armas"; } if (text.StartsWith("Armor", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Helmet", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Cape", StringComparison.OrdinalIgnoreCase)) { return "Armaduras"; } return "Outros"; } private string MergeCommaRules(string current, string extra) { current = ((current != null) ? current.Trim() : string.Empty); extra = ((extra != null) ? extra.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(current)) { return extra; } if (string.IsNullOrWhiteSpace(extra)) { return current; } return current + "," + extra; } private string BuildUniqueCraftJackpotRulesFromCategoryEntries() { if (_cfgUniqueCraftJackpotCategoryEntries == null || _cfgUniqueCraftJackpotCategoryEntries.Count == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair> cfgUniqueCraftJackpotCategoryEntry in _cfgUniqueCraftJackpotCategoryEntries) { if (cfgUniqueCraftJackpotCategoryEntry.Value == null) { continue; } foreach (KeyValuePair item in ParseFlexibleJackpotRules(cfgUniqueCraftJackpotCategoryEntry.Value.Value)) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } JackpotRule value = item.Value; stringBuilder.Append(SafeKey(item.Key)); stringBuilder.Append(':'); stringBuilder.Append(Mathf.Max(1, value.RequiredAmount)); stringBuilder.Append(':'); stringBuilder.Append(Mathf.Max(0, value.Points)); } } return stringBuilder.ToString(); } private string ConvertUniqueCraftJackpotItemsToConfigValue(string raw) { List list = new List(); foreach (KeyValuePair item in ParseFlexibleJackpotRules(raw)) { JackpotRule value = item.Value; list.Add(SafeKey(item.Key) + ";" + Mathf.Max(1, value.RequiredAmount) + ";" + Mathf.Max(0, value.Points)); } return string.Join(",", list.ToArray()); } private Dictionary ParseFlexibleJackpotRules(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string text = raw.Replace("\r", "\n").Replace("|", ","); List list = new List(); if (text.IndexOf(',') >= 0 || text.IndexOf('\n') >= 0) { string[] array = text.Split(new char[2] { ',', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text3)) { list.Add(text3); } } } else if (text.IndexOf(':') >= 0) { string[] array2 = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text4 in array2) { string text5 = ((text4 != null) ? text4.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text5)) { list.Add(text5); } } } else { string[] array3 = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int k = 0; k + 2 < array3.Length; k += 3) { list.Add(array3[k].Trim() + ";" + array3[k + 1].Trim() + ";" + array3[k + 2].Trim()); } } foreach (string item in list) { if (TryParseFlexibleJackpotItem(item, out var prefab, out var amount, out var points)) { dictionary[prefab] = new JackpotRule { RequiredAmount = Mathf.Max(1, amount), Points = Mathf.Max(0, points) }; } } return dictionary; } private bool TryParseFlexibleJackpotItem(string entry, out string prefab, out int amount, out int points) { prefab = string.Empty; amount = 0; points = 0; if (string.IsNullOrWhiteSpace(entry)) { return false; } string[] array = ((entry.IndexOf(';') >= 0) ? entry.Split(new char[1] { ';' }, 3, StringSplitOptions.None) : entry.Split(new char[1] { ':' }, 3, StringSplitOptions.None)); if (array.Length != 3) { return false; } prefab = SafeKey(array[0]); if (string.IsNullOrWhiteSpace(prefab)) { return false; } if (!int.TryParse(array[1].Trim(), out amount)) { return false; } if (!int.TryParse(array[2].Trim(), out points)) { return false; } amount = Mathf.Max(1, amount); points = Mathf.Max(0, points); return true; } private void BindProductionCategoryEntries(Dictionary legacyOverrides) { if (_cfgProductionCategoryRuleEntries == null) { return; } _cfgProductionCategoryRuleEntries.Clear(); Dictionary dictionary = ParseProductionCategoryDefaults("Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"); string text = ReadLegacyString(legacyOverrides, "HudCategories.Production", null); if (!string.IsNullOrWhiteSpace(text)) { foreach (KeyValuePair item in ParseProductionCategoryDefaults(text)) { dictionary[item.Key] = item.Value; } } if (legacyOverrides != null) { foreach (KeyValuePair legacyOverride in legacyOverrides) { if (string.IsNullOrWhiteSpace(legacyOverride.Key) || !legacyOverride.Key.StartsWith("HudCategories.", StringComparison.OrdinalIgnoreCase)) { continue; } string text2 = NormalizeHudCategoryName(legacyOverride.Key.Substring("HudCategories.".Length)); if (!string.IsNullOrWhiteSpace(text2) && !text2.Equals("Production", StringComparison.OrdinalIgnoreCase) && !text2.Equals("Combat", StringComparison.OrdinalIgnoreCase)) { string value = ConvertProductionCategoryItemsToConfigValue(legacyOverride.Value); if (!string.IsNullOrWhiteSpace(value)) { dictionary[text2] = value; } } } } if (dictionary.Count <= 0) { dictionary["Armas"] = "SwordIron;800"; dictionary["Armaduras"] = "HelmetBronze;800"; dictionary["Comidas"] = "DeerStew;25"; } if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } foreach (string item2 in GetPreferredCategoryOrder(dictionary, new string[6] { "Armas", "Armaduras", "Comidas", "Ferramentas", "Materiais", "Outros" })) { BindSingleProductionCategory(item2, legacyOverrides, dictionary[item2]); } } private void BindSingleProductionCategory(string category, Dictionary legacyOverrides, string defaultValue) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { string raw = ReadLegacyString(legacyOverrides, "HudCategories." + category, defaultValue); raw = ConvertProductionCategoryItemsToConfigValue(raw); ConfigEntry val = _rulesConfig.BindConfig("HudCategories", category, raw, "Produção/Conquistas para esta aba do HUD. Formato: Prefab;Pontos,OutroPrefab;Pontos. Exemplo: SwordIron;800,HelmetBronze;800,DeerStew;25. Aceita prefabs de mods. O nome desta chave vira a categoria no HUD."); _cfgProductionCategoryRuleEntries[category] = val; if (_cfgProductionCategoryRules == null) { _cfgProductionCategoryRules = val; } } } private Dictionary ParseProductionCategoryDefaults(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.IndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = NormalizeHudCategoryName(text2.Substring(0, num).Trim()); string text4 = text2.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text3) && !string.IsNullOrWhiteSpace(text4)) { string value = ConvertProductionCategoryItemsToConfigValue(text4); if (!string.IsNullOrWhiteSpace(value)) { dictionary[text3] = value; } } } return dictionary; } private string BuildProductionCategoryRules() { if (_cfgProductionCategoryRuleEntries == null || _cfgProductionCategoryRuleEntries.Count == 0) { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } try { List list = new List(); foreach (KeyValuePair> cfgProductionCategoryRuleEntry in _cfgProductionCategoryRuleEntries) { if (cfgProductionCategoryRuleEntry.Value == null) { continue; } string text = NormalizeHudCategoryName(cfgProductionCategoryRuleEntry.Key); if (!string.IsNullOrWhiteSpace(text)) { string text2 = ConvertProductionCategoryItemsToHudCategoryItems(cfgProductionCategoryRuleEntry.Value.Value); if (!string.IsNullOrWhiteSpace(text2)) { list.Add(text + ":" + text2); } } } if (list.Count <= 0) { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } return string.Join(";", list.ToArray()); } catch { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } } private string ConvertProductionCategoryItemsToConfigValue(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return string.Empty; } List list = new List(); string[] array = raw.Split(new char[4] { ',', '|', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string itemRaw in array2) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + ";" + Mathf.Max(0, points)); } } return string.Join(",", list.ToArray()); } private string ConvertProductionCategoryItemsToHudCategoryItems(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return string.Empty; } List list = new List(); string[] array = raw.Split(new char[4] { ',', '|', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string itemRaw in array2) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + "=" + Mathf.Max(0, points)); } } return string.Join(",", list.ToArray()); } private void BindCombatBiomeRuleEntries(Dictionary legacyOverrides) { if (_cfgCombatBiomeRuleEntries == null) { return; } _cfgCombatBiomeRuleEntries.Clear(); Dictionary dictionary = ParseCombatBiomeDefaults("Prados:Boar:1,Deer:1,Neck:1;Floresta Negra:Greydwarf:1,Greydwarf_Elite:2,Greydwarf_Shaman:2,Troll:5;Pântano:Draugr:2,Draugr_Elite:3,Blob:2,Abomination:8;Montanha:Wolf:2,Hatchling:2,StoneGolem:8;Planícies:Goblin:3,GoblinBrute:5,Lox:6,Deathsquito:3;Oceano:Serpent:6;Mistlands:Seeker:4,SeekerBrute:8,Gjall:10;Ashlands:Charred_Melee:4,Charred_Archer:4,Volture:4;Especiais:Haldor:0"); if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } if (legacyOverrides != null) { foreach (KeyValuePair legacyOverride in legacyOverrides) { if (!string.IsNullOrWhiteSpace(legacyOverride.Key) && legacyOverride.Key.StartsWith("Combat.", StringComparison.OrdinalIgnoreCase)) { string text = NormalizeHudCategoryName(legacyOverride.Key.Substring("Combat.".Length)); if (!string.IsNullOrWhiteSpace(text)) { string value = ConvertProductionCategoryItemsToConfigValue(legacyOverride.Value); dictionary[text] = value; } } } } foreach (string item in GetPreferredCategoryOrder(dictionary, new string[10] { "Prados", "Floresta Negra", "Pântano", "Montanha", "Planícies", "Oceano", "Mistlands", "Ashlands", "Especiais", "Outros" })) { BindSingleCombatBiome(item, dictionary, legacyOverrides); } } private void BindSingleCombatBiome(string category, Dictionary defaults, Dictionary legacyOverrides) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { if (defaults == null || !defaults.TryGetValue(category, out var value)) { value = string.Empty; } string raw = ReadLegacyString(legacyOverrides, "Combat." + category, value); raw = ConvertProductionCategoryItemsToConfigValue(raw); _cfgCombatBiomeRuleEntries[category] = _rulesConfig.BindConfig("Combat", category, raw, "Mobs e pontos para esta aba do HUD. Formato: Prefab;Pontos,OutroPrefab;Pontos. Exemplo: Boar;2,Deer;3. Aceita qualquer quantidade de prefabs, inclusive de mods. O nome desta chave vira a categoria no HUD."); } } private IEnumerable GetPreferredCategoryOrder(Dictionary categories, string[] preferredOrder) { HashSet emitted = new HashSet(StringComparer.OrdinalIgnoreCase); if (preferredOrder != null) { foreach (string preferred in preferredOrder) { string category = NormalizeHudCategoryName(preferred); if (!string.IsNullOrWhiteSpace(category) && categories != null && categories.ContainsKey(category) && emitted.Add(category)) { yield return category; } } } if (categories == null) { yield break; } foreach (string category2 in categories.Keys.OrderBy((string k) => k, StringComparer.OrdinalIgnoreCase)) { string clean = NormalizeHudCategoryName(category2); if (!string.IsNullOrWhiteSpace(clean) && emitted.Add(clean)) { yield return clean; } } } private Dictionary ParseCombatBiomeDefaults(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.IndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = NormalizeHudCategoryName(text2.Substring(0, num).Trim()); string text4 = text2.Substring(num + 1).Trim(); if (string.IsNullOrWhiteSpace(text3) || string.IsNullOrWhiteSpace(text4)) { continue; } List list = new List(); string[] array3 = text4.Split(new char[2] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries); string[] array4 = array3; foreach (string itemRaw in array4) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + ";" + Mathf.Max(0, points)); } } dictionary[text3] = string.Join(",", list.ToArray()); } return dictionary; } private string BuildCombatCategoryRulesFromBiomeEntries() { if (_cfgCombatBiomeRuleEntries == null || _cfgCombatBiomeRuleEntries.Count == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair> cfgCombatBiomeRuleEntry in _cfgCombatBiomeRuleEntries) { if (cfgCombatBiomeRuleEntry.Value == null) { continue; } string value = NormalizeHudCategoryName(cfgCombatBiomeRuleEntry.Key); if (string.IsNullOrWhiteSpace(value)) { continue; } string value2 = ConvertCombatBiomeItemsToHudCategoryItems(cfgCombatBiomeRuleEntry.Value.Value); if (!string.IsNullOrWhiteSpace(value2)) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } stringBuilder.Append(value); stringBuilder.Append(':'); stringBuilder.Append(value2); } } return stringBuilder.ToString(); } private string ConvertCombatBiomeItemsToHudCategoryItems(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return string.Empty; } List list = new List(); string[] array = raw.Split(new char[4] { ',', '|', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string itemRaw in array2) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + ":" + Mathf.Max(0, points)); } } return string.Join(",", list.ToArray()); } private bool TryParseCombatBiomeItem(string itemRaw, out string prefab, out int points) { prefab = string.Empty; points = 0; if (string.IsNullOrWhiteSpace(itemRaw)) { return false; } string text = itemRaw.Trim(); int num = text.LastIndexOf(';'); if (num <= 0) { num = text.LastIndexOf('='); } if (num <= 0) { num = text.LastIndexOf(':'); } if (num <= 0 || num >= text.Length - 1) { return false; } string value = text.Substring(0, num).Trim(); string s = text.Substring(num + 1).Trim(); if (!int.TryParse(s, out var result)) { return false; } prefab = SafeKey(value); points = Mathf.Max(0, result); return !string.IsNullOrWhiteSpace(prefab); } private void RewriteRulesConfigHeaderAndGroupedSections() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List list = new List(array.Length + 40); string a = string.Empty; bool flag = false; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase) { "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "DeathPenalty", "ExplorationMapJackpots" }; list.Add("## Glitnir Ranking - Regras de pontuação"); bool flag2 = false; bool flag3 = false; bool flag4 = false; bool flag5 = false; string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (text2.StartsWith("## Glitnir Ranking - Regras de pontuação", StringComparison.OrdinalIgnoreCase)) { continue; } if (string.Equals(a, "HudCategories", StringComparison.OrdinalIgnoreCase) && text2.IndexOf('=') > 0) { string text3 = text2.Substring(0, text2.IndexOf('=')).Trim(); if (text3.Equals("Combat", StringComparison.OrdinalIgnoreCase) || text3.Equals("Production", StringComparison.OrdinalIgnoreCase)) { continue; } } if (text2.StartsWith("[") && text2.EndsWith("]")) { string text4 = text2.Substring(1, text2.Length - 2).Trim(); flag = hashSet.Contains(text4); a = text4; if (flag) { if (text4.Equals("FarmJackpots", StringComparison.OrdinalIgnoreCase) && !flag2) { AppendGroupedFarmSection(list); flag2 = true; } else if (text4.Equals("UniqueCraftJackpots", StringComparison.OrdinalIgnoreCase) && !flag3) { AppendGroupedUniqueCraftSection(list); flag3 = true; } else if (text4.Equals("DeathPenalty", StringComparison.OrdinalIgnoreCase) && !flag4) { AppendGroupedDeathPenaltySection(list); flag4 = true; } else if (text4.Equals("ExplorationMapJackpots", StringComparison.OrdinalIgnoreCase) && !flag5) { AppendGroupedExplorationMapSection(list); flag5 = true; } } else { list.Add(text); } } else if (!flag) { list.Add(text); } } if (!flag2) { AppendGroupedFarmSection(list); } if (!flag3) { AppendGroupedUniqueCraftSection(list); } if (!flag4) { AppendGroupedDeathPenaltySection(list); } if (!flag5) { AppendGroupedExplorationMapSection(list); } File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao organizar config do ranking: " + ex.Message)); } } private void AppendGroupedFarmSection(List lines) { lines.Add(""); lines.Add("[FarmJackpots]"); lines.Add(""); lines.Add("## Jackpot de colheita por prefab configurado."); lines.Add("## Formato: Prefab:quantidade:pontos;Prefab:quantidade:pontos"); lines.Add("## Exemplo: Carrot:200:1000;Barley:500:2000"); lines.Add("Rules = " + ((_cfgFarmJackpotRules != null) ? SanitizeDelimitedJackpotRules(_cfgFarmJackpotRules.Value) : "Carrot:200:1000;Barley:500:2000")); } private void AppendGroupedUniqueCraftSection(List lines) { lines.Add(""); lines.Add("[UniqueCraftJackpots]"); lines.Add(""); lines.Add("## Jackpot único por craft separado por categoria, igual ao modelo do Combat."); lines.Add("## Formato: Prefab;Quantidade;Pontos,OutroPrefab;Quantidade;Pontos"); lines.Add("## Exemplo: SwordIron;1;800,SwordSilver;1;1500"); lines.Add("## Aceita qualquer prefab, inclusive de mods. Use Outros para o que não couber nas categorias."); if (_cfgUniqueCraftJackpotCategoryEntries != null && _cfgUniqueCraftJackpotCategoryEntries.Count > 0) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair> cfgUniqueCraftJackpotCategoryEntry in _cfgUniqueCraftJackpotCategoryEntries) { string text = NormalizeHudCategoryName(cfgUniqueCraftJackpotCategoryEntry.Key); if (!string.IsNullOrWhiteSpace(text)) { dictionary[text] = ((cfgUniqueCraftJackpotCategoryEntry.Value != null) ? ConvertUniqueCraftJackpotItemsToConfigValue(cfgUniqueCraftJackpotCategoryEntry.Value.Value) : string.Empty); } } if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } { foreach (string item in GetPreferredCategoryOrder(dictionary, new string[5] { "Armas", "Armaduras", "Ferramentas", "Comidas", "Outros" })) { lines.Add(item + " = " + dictionary[item]); } return; } } lines.Add("Armas = SwordIron;1;800,SwordSilver;1;1500,SwordBlackmetal;1;2500"); lines.Add("Armaduras = ArmorWolfChest;1;2000,ArmorCarapaceChest;1;3500"); lines.Add("Outros = "); } private void AppendGroupedDeathPenaltySection(List lines) { lines.Add(""); lines.Add("[DeathPenalty]"); lines.Add(""); lines.Add("## Penalidade por morte."); lines.Add("## UseMultiplierMode=false usa a tabela Rules abaixo."); lines.Add("## UseMultiplierMode=true ignora Rules e aplica PenaltyPerDeath para cada morte."); lines.Add("## Exemplo multiplicador: PenaltyPerDeath=100 e 15 mortes = -1500 pontos."); lines.Add("UseMultiplierMode = " + ((_cfgDeathPenaltyUseMultiplier != null) ? _cfgDeathPenaltyUseMultiplier.Value.ToString() : "false")); lines.Add("PenaltyPerDeath = " + ((_cfgDeathPenaltyPerDeath != null) ? Mathf.Max(0, _cfgDeathPenaltyPerDeath.Value).ToString() : "100")); lines.Add(""); lines.Add("## Formato do modo tabela: mortes:pontosPerdidos,mortes:pontosPerdidos"); lines.Add("## Exemplo: 1:0,2:100,5:300,10:800"); lines.Add("Rules = " + ((_cfgDeathPenaltyRules != null) ? SanitizeDeathPenaltyRules(_cfgDeathPenaltyRules.Value) : "1:0,2:100,5:300,10:800")); } private void AppendGroupedExplorationMapSection(List lines) { lines.Add(""); lines.Add("[ExplorationMapJackpots]"); lines.Add(""); lines.Add("## Jackpot por porcentagem de mapa revelado."); lines.Add("## Formato: porcentagem:pontos;porcentagem:pontos"); lines.Add("## Exemplo: 5:25;10:50;25:150;50:400;75:800;100:1500"); lines.Add("Enabled = " + ((_cfgEnableExplorationJackpots != null) ? _cfgEnableExplorationJackpots.Value.ToString() : "true")); lines.Add("Rules = " + ((_cfgExplorationMapJackpotRules != null) ? _cfgExplorationMapJackpotRules.Value : "5:25;10:50;25:150;50:400;75:800;100:1500")); } private void CleanupFishKillPointConfigEntries() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List list = new List(array.Length); bool flag = false; bool flag2 = false; string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (text2.StartsWith("[") && text2.EndsWith("]")) { flag = text2.Equals("[KillPoints]", StringComparison.OrdinalIgnoreCase); } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string text4 = text3; if (text3.StartsWith("KillPoints.", StringComparison.OrdinalIgnoreCase)) { text4 = text3.Substring("KillPoints.".Length).Trim(); } else if (!flag) { text4 = null; } if (!string.IsNullOrWhiteSpace(text4) && IsFishPrefab(text4)) { flag2 = true; continue; } } list.Add(text); } if (flag2) { File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao limpar prefabs de peixe do config: " + ex.Message)); } } private void CleanupGroupedProductionConfigEntries() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List list = new List(array.Length); string text = string.Empty; bool flag = false; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase) { "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "DeathPenalty", "ExplorationMapJackpots" }; string[] array2 = array; foreach (string text2 in array2) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (text3.StartsWith("[") && text3.EndsWith("]")) { text = text3.Substring(1, text3.Length - 2).Trim(); list.Add(text2); continue; } int num = text3.IndexOf('='); if (num > 0 && hashSet.Contains(text)) { string text4 = text3.Substring(0, num).Trim(); bool flag2 = text4.Equals("Rules", StringComparison.OrdinalIgnoreCase); if (text.Equals("DeathPenalty", StringComparison.OrdinalIgnoreCase)) { flag2 = flag2 || text4.Equals("UseMultiplierMode", StringComparison.OrdinalIgnoreCase) || text4.Equals("PenaltyPerDeath", StringComparison.OrdinalIgnoreCase); } else if (text.Equals("ExplorationMapJackpots", StringComparison.OrdinalIgnoreCase)) { flag2 = flag2 || text4.Equals("Enabled", StringComparison.OrdinalIgnoreCase); } if (!flag2) { flag = true; continue; } } list.Add(text2); } if (flag) { File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao reorganizar config de produção: " + ex.Message)); } } private string BuildMarketplaceQuestPointMapDefault(Dictionary legacyOverrides) { string text = ReadLegacyString(legacyOverrides, "MarketplaceQuestPoints.QuestPointMap", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeMarketplaceQuestPointMap(text); } text = ReadLegacyString(legacyOverrides, "QuestPointMap", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeMarketplaceQuestPointMap(text); } Dictionary mergedDefaultPointEntries = GetMergedDefaultPointEntries("MarketplaceQuestPoints", legacyOverrides); if (mergedDefaultPointEntries.Count <= 0) { return "ccq_b21:450,ccq_b22:150"; } return string.Join(",", from p in mergedDefaultPointEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) select SafeMarketplaceQuestKey(p.Key) + ":" + Mathf.Max(0, p.Value)); } private Dictionary ParseMarketplaceQuestPointMap(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2) { string text3 = SafeMarketplaceQuestKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result)) { dictionary[text3] = result; } } } return dictionary; } private string SanitizeMarketplaceQuestPointMap(string raw) { Dictionary dictionary = ParseMarketplaceQuestPointMap(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) select p.Key + ":" + Mathf.Max(0, p.Value)); } private Dictionary ParseSkillMilestoneMap(string raw) { Dictionary dictionary = new Dictionary(); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text2)) { string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2 && int.TryParse(array3[0].Trim(), out var result) && int.TryParse(array3[1].Trim(), out var result2)) { result = Mathf.Clamp(result, 1, 100); dictionary[result] = Mathf.Max(0, result2); } } } return dictionary; } private string SanitizeSkillMilestoneMap(string raw) { Dictionary dictionary = ParseSkillMilestoneMap(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary orderby p.Key select p.Key + ":" + Mathf.Max(0, p.Value)); } private JackpotRule ParseJackpotRule(string raw) { JackpotRule jackpotRule = new JackpotRule(); if (string.IsNullOrWhiteSpace(raw)) { return jackpotRule; } string[] array = raw.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array.Length != 2) { return jackpotRule; } if (!int.TryParse(array[0].Trim(), out var result)) { return jackpotRule; } if (!int.TryParse(array[1].Trim(), out var result2)) { return jackpotRule; } jackpotRule.RequiredAmount = Mathf.Max(1, result); jackpotRule.Points = Mathf.Max(0, result2); return jackpotRule; } private string SanitizeJackpotRule(string raw) { JackpotRule jackpotRule = ParseJackpotRule(raw); if (jackpotRule == null || jackpotRule.RequiredAmount <= 0) { return "1:0"; } return jackpotRule.RequiredAmount + ":" + Mathf.Max(0, jackpotRule.Points); } private string StripHudCategoryPointPart(string itemRaw) { if (string.IsNullOrWhiteSpace(itemRaw)) { return ""; } string text = itemRaw.Trim(); int num = text.IndexOf('='); if (num > 0) { text = text.Substring(0, num).Trim(); } int num2 = text.LastIndexOf(':'); if (num2 > 0 && num2 < text.Length - 1) { string s = text.Substring(num2 + 1).Trim(); if (int.TryParse(s, out var _)) { text = text.Substring(0, num2).Trim(); } } return SafeKey(text); } private bool TryParseHudCategoryPointItem(string itemRaw, out string prefab, out int points) { prefab = ""; points = 0; if (string.IsNullOrWhiteSpace(itemRaw)) { return false; } string text = itemRaw.Trim(); int num = text.LastIndexOf('='); if (num <= 0) { num = text.LastIndexOf(':'); } if (num <= 0 || num >= text.Length - 1) { return false; } string value = text.Substring(0, num).Trim(); string s = text.Substring(num + 1).Trim(); if (!int.TryParse(s, out var result)) { return false; } prefab = SafeKey(value); points = Mathf.Max(0, result); return !string.IsNullOrWhiteSpace(prefab); } private Dictionary ParseHudCategoryRules(string raw) { ParseHudCategorizedPointRules(raw, out var categories); return categories; } private static string NormalizeHudCategoryName(string category) { if (string.IsNullOrWhiteSpace(category)) { return category; } string text = category.Trim(); if (text.Equals("Arcos e Munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Arcos e Municoes", StringComparison.OrdinalIgnoreCase) || text.Equals("Armas, Armaduras e Munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Armas, Armaduras e Municoes", StringComparison.OrdinalIgnoreCase)) { return "Armas"; } if (text.Equals("Comidas e Consumíveis", StringComparison.OrdinalIgnoreCase) || text.Equals("Comidas e Consumiveis", StringComparison.OrdinalIgnoreCase) || text.Equals("Comidas e bebidas", StringComparison.OrdinalIgnoreCase)) { return "Comidas"; } return text; } private Dictionary ParseHudCategorizedPointRules(string raw, out Dictionary categories) { categories = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : ""); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.IndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string value = NormalizeHudCategoryName(text2.Substring(0, num).Trim()); if (string.IsNullOrWhiteSpace(value)) { continue; } string text3 = text2.Substring(num + 1); string[] array3 = text3.Split(new char[2] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries); string[] array4 = array3; foreach (string itemRaw in array4) { string text4 = StripHudCategoryPointPart(itemRaw); if (!string.IsNullOrWhiteSpace(text4)) { categories[text4] = value; if (TryParseHudCategoryPointItem(itemRaw, out var prefab, out var points)) { dictionary[prefab] = points; } } } } return dictionary; } private Dictionary ParseDelimitedPointRules(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ';', ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2) { string text3 = SafeKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result)) { dictionary[text3] = Mathf.Max(0, result); } } } return dictionary; } private Dictionary ParseDelimitedJackpotRules(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ';', ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 3, StringSplitOptions.None); if (array3.Length == 3) { string text3 = SafeKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result) && int.TryParse(array3[2].Trim(), out var result2)) { dictionary[text3] = new JackpotRule { RequiredAmount = Mathf.Max(1, result), Points = Mathf.Max(0, result2) }; } } } return dictionary; } private Dictionary ParseDeathPenaltyRules(string raw) { Dictionary dictionary = new Dictionary(); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text2)) { string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2 && int.TryParse(array3[0].Trim(), out var result) && int.TryParse(array3[1].Trim(), out var result2)) { result = Mathf.Max(1, result); result2 = (dictionary[result] = Mathf.Max(0, result2)); } } } return dictionary; } private string SanitizeDeathPenaltyRules(string raw) { Dictionary dictionary = ParseDeathPenaltyRules(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary orderby p.Key select Mathf.Max(1, p.Key) + ":" + Mathf.Max(0, p.Value)); } private string SanitizeDelimitedPointRules(string raw) { return SanitizeDelimitedPointRules(raw, ";"); } private string SanitizeDelimitedPointRules(string raw, string delimiter) { Dictionary dictionary = ParseDelimitedPointRules(raw); if (dictionary.Count <= 0) { return string.Empty; } if (string.IsNullOrEmpty(delimiter)) { delimiter = ";"; } return string.Join(delimiter, from p in dictionary.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) where !IsFishPrefab(p.Key) select p.Key + ":" + Mathf.Max(0, p.Value)); } private string SanitizeDelimitedJackpotRules(string raw) { Dictionary dictionary = ParseDelimitedJackpotRules(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(";", from p in dictionary.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) select p.Key + ":" + Mathf.Max(1, p.Value.RequiredAmount) + ":" + Mathf.Max(0, p.Value.Points)); } private string ReadGroupedRulesDefault(Dictionary legacyOverrides, string section, string fallback) { string text = ReadLegacyString(legacyOverrides, section + ".Rules", null); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } text = ReadLegacyString(legacyOverrides, "Rules", null); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } return fallback; } private string BuildDelimitedPointRulesDefault(string section, Dictionary legacyOverrides, string fallback) { string text = ReadLegacyString(legacyOverrides, section + ".Rules", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeDelimitedPointRules(text); } Dictionary mergedDefaultPointEntries = GetMergedDefaultPointEntries(section, legacyOverrides); if (mergedDefaultPointEntries.Count <= 0) { return fallback; } return string.Join(";", from p in mergedDefaultPointEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) select SafeKey(p.Key) + ":" + Mathf.Max(0, p.Value)); } private string BuildDelimitedJackpotRulesDefault(string section, Dictionary legacyOverrides, string fallback) { string text = ReadLegacyString(legacyOverrides, section + ".Rules", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeDelimitedJackpotRules(text); } Dictionary mergedDefaultStringEntries = GetMergedDefaultStringEntries(section, legacyOverrides); if (mergedDefaultStringEntries.Count <= 0) { return fallback; } List list = new List(); foreach (KeyValuePair item in mergedDefaultStringEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase)) { if (!item.Key.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { JackpotRule jackpotRule = ParseJackpotRule(item.Value); if (jackpotRule != null && jackpotRule.RequiredAmount > 0) { list.Add(SafeKey(item.Key) + ":" + Mathf.Max(1, jackpotRule.RequiredAmount) + ":" + Mathf.Max(0, jackpotRule.Points)); } } } return (list.Count > 0) ? string.Join(";", list) : fallback; } private void BindPointSectionEntries(string prefix, string descriptionPrefix, Dictionary legacyOverrides, Dictionary> target) { Dictionary mergedDefaultPointEntries = GetMergedDefaultPointEntries(prefix, legacyOverrides); List list = new List(target.Keys); foreach (string item in list) { if (!mergedDefaultPointEntries.ContainsKey(item)) { mergedDefaultPointEntries[item] = target[item].Value; } } foreach (KeyValuePair item2 in mergedDefaultPointEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase)) { string text = SafeKey(item2.Key); if (!string.IsNullOrWhiteSpace(text) && (!prefix.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text)) && (!prefix.Equals("FishingPoints", StringComparison.OrdinalIgnoreCase) || IsFishPrefab(text)) && !target.TryGetValue(text, out var value)) { value = (target[text] = _rulesConfig.BindConfig(prefix, text, item2.Value, descriptionPrefix + " Chave: " + text)); } } } private void BindStringPointSectionEntries(string prefix, string descriptionPrefix, Dictionary legacyOverrides, Dictionary> target) { Dictionary mergedDefaultStringEntries = GetMergedDefaultStringEntries(prefix, legacyOverrides); List list = new List(target.Keys); foreach (string item in list) { if (!mergedDefaultStringEntries.ContainsKey(item)) { mergedDefaultStringEntries[item] = target[item].Value; } } foreach (KeyValuePair item2 in mergedDefaultStringEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase)) { string text = SafeKey(item2.Key); if (!string.IsNullOrWhiteSpace(text) && !target.TryGetValue(text, out var value)) { value = (target[text] = _rulesConfig.BindConfig(prefix, text, SanitizeSkillMilestoneMap(item2.Value), descriptionPrefix + " Chave: " + text)); } } } private void BindGenericJackpotSectionEntries(string prefix, string descriptionPrefix, Dictionary legacyOverrides, Dictionary> target) { Dictionary mergedDefaultStringEntries = GetMergedDefaultStringEntries(prefix, legacyOverrides); List list = new List(target.Keys); foreach (string item in list) { if (!mergedDefaultStringEntries.ContainsKey(item)) { mergedDefaultStringEntries[item] = target[item].Value; } } foreach (KeyValuePair item2 in mergedDefaultStringEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase)) { string text = SafeKey(item2.Key); if (!string.IsNullOrWhiteSpace(text) && !target.TryGetValue(text, out var value)) { value = (target[text] = _rulesConfig.BindConfig(prefix, text, SanitizeJackpotRule(item2.Value), descriptionPrefix + " Chave: " + text)); } } } private Dictionary GetMergedDefaultStringEntries(string prefix, Dictionary legacyOverrides) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item in ParseRuleTemplate(BuildDefaultRulesFileContents())) { if (item.Key.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)) { string text = SafeKey(item.Key.Substring(prefix.Length + 1)); if (!string.IsNullOrWhiteSpace(text)) { dictionary[text] = item.Value; } } } foreach (KeyValuePair legacyOverride in legacyOverrides) { if (legacyOverride.Key.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)) { string text2 = SafeKey(legacyOverride.Key.Substring(prefix.Length + 1)); if (!string.IsNullOrWhiteSpace(text2)) { dictionary[text2] = legacyOverride.Value; } } } return dictionary; } private Dictionary GetMergedDefaultPointEntries(string prefix, Dictionary legacyOverrides) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item in ParseRuleTemplate(BuildDefaultRulesFileContents())) { if (!item.Key.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)) { continue; } string text = SafeKey(item.Key.Substring(prefix.Length + 1)); if (!string.IsNullOrWhiteSpace(text) && (!prefix.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text)) && (!prefix.Equals("FishingPoints", StringComparison.OrdinalIgnoreCase) || IsFishPrefab(text))) { if (!int.TryParse(item.Value, out var result)) { result = 0; } dictionary[text] = result; } } foreach (KeyValuePair legacyOverride in legacyOverrides) { if (legacyOverride.Key.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)) { string text2 = SafeKey(legacyOverride.Key.Substring(prefix.Length + 1)); if (!string.IsNullOrWhiteSpace(text2) && (!prefix.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text2)) && (!prefix.Equals("FishingPoints", StringComparison.OrdinalIgnoreCase) || IsFishPrefab(text2)) && int.TryParse(legacyOverride.Value, out var result2)) { dictionary[text2] = result2; } } } return dictionary; } private Dictionary ParseRuleTemplate(string contents) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(contents)) { return dictionary; } using (StringReader stringReader = new StringReader(contents)) { string text; while ((text = stringReader.ReadLine()) != null) { string text2 = text.Trim(); if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#") || text2.StartsWith(";") || text2.StartsWith("//")) { continue; } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string value = text2.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text3)) { dictionary[text3] = value; } } } } return dictionary; } private bool ReadLegacyBool(Dictionary values, string key, bool fallback) { string value; return (values != null && values.TryGetValue(key, out value)) ? ParseBool(value, fallback) : fallback; } private int ReadLegacyInt(Dictionary values, string key, int fallback) { string value; return (values != null && values.TryGetValue(key, out value)) ? ParseInt(value, fallback) : fallback; } private string ReadLegacyString(Dictionary values, string key, string fallback) { if (values != null && values.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } return fallback; } private void ApplyRulesFromSyncedConfig() { if (_rulesConfig == null) { return; } RankingRules rankingRules = new RankingRules(); if (_cfgRankingEnabled != null) { rankingRules.RankingEnabled = _cfgRankingEnabled.Value; } if (_cfgEnableKillPoints != null) { rankingRules.EnableKillPoints = _cfgEnableKillPoints.Value; } if (_cfgEnableBossPoints != null) { rankingRules.EnableBossPoints = _cfgEnableBossPoints.Value; } if (_cfgDefaultKillPoints != null) { rankingRules.DefaultKillPoints = Mathf.Max(0, _cfgDefaultKillPoints.Value); } if (_cfgTopCount != null) { rankingRules.TopCount = Mathf.Clamp(_cfgTopCount.Value, 1, 50); } if (_cfgAllowRepeatedBossPoints != null) { rankingRules.AllowRepeatedBossPoints = _cfgAllowRepeatedBossPoints.Value; } if (_cfgDebugLogging != null) { rankingRules.DebugLogging = _cfgDebugLogging.Value; } if (_cfgLogHitReports != null) { rankingRules.LogHitReports = _cfgLogHitReports.Value; } if (_cfgLogKillReports != null) { rankingRules.LogKillReports = _cfgLogKillReports.Value; } if (_cfgLogSkillReports != null) { rankingRules.LogSkillReports = _cfgLogSkillReports.Value; } if (_cfgLogPendingKillReports != null) { rankingRules.LogPendingKillReports = _cfgLogPendingKillReports.Value; } if (_cfgLogSnapshotRequests != null) { rankingRules.LogSnapshotRequests = _cfgLogSnapshotRequests.Value; } if (_cfgLogPointsChanges != null) { rankingRules.LogPointsChanges = _cfgLogPointsChanges.Value; } if (_cfgEnableSkillPoints != null) { rankingRules.EnableSkillPoints = _cfgEnableSkillPoints.Value; } if (_cfgIgnoreTamedKills != null) { rankingRules.IgnoreTamedKills = _cfgIgnoreTamedKills.Value; } if (_cfgEnableMarketplaceQuestPoints != null) { rankingRules.EnableMarketplaceQuestPoints = _cfgEnableMarketplaceQuestPoints.Value; } if (_cfgEnableFishingPoints != null) { rankingRules.EnableFishingPoints = _cfgEnableFishingPoints.Value; } if (_cfgEnableDeathPenalty != null) { rankingRules.EnableDeathPenalty = _cfgEnableDeathPenalty.Value; } if (_cfgDeathPenaltyUseMultiplier != null) { rankingRules.DeathPenaltyUseMultiplier = _cfgDeathPenaltyUseMultiplier.Value; } if (_cfgDeathPenaltyPerDeath != null) { rankingRules.DeathPenaltyPerDeath = Mathf.Max(0, _cfgDeathPenaltyPerDeath.Value); } if (_cfgEnableExplorationJackpots != null) { rankingRules.EnableExplorationJackpots = _cfgEnableExplorationJackpots.Value; } if (_cfgRewardClaimsEnabled != null) { rankingRules.RewardClaimsEnabled = _cfgRewardClaimsEnabled.Value; } if (_cfgRewardClaimCycleId != null) { rankingRules.RewardClaimCycleId = SafeLimit(_cfgRewardClaimCycleId.Value, 64); } if (_cfgRewardTop1MinPoints != null) { rankingRules.RewardTop1MinPoints = Mathf.Max(0, _cfgRewardTop1MinPoints.Value); } if (_cfgRewardTop1Label != null) { rankingRules.RewardTop1Label = SafeLimit(_cfgRewardTop1Label.Value, 96); } if (_cfgRewardTop1Prefab != null) { rankingRules.RewardTop1Prefab = SafeLimit(_cfgRewardTop1Prefab.Value, 96); } if (_cfgRewardTop1Amount != null) { rankingRules.RewardTop1Amount = Mathf.Max(0, _cfgRewardTop1Amount.Value); } if (_cfgRewardTop2MinPoints != null) { rankingRules.RewardTop2MinPoints = Mathf.Max(0, _cfgRewardTop2MinPoints.Value); } if (_cfgRewardTop2Label != null) { rankingRules.RewardTop2Label = SafeLimit(_cfgRewardTop2Label.Value, 96); } if (_cfgRewardTop2Prefab != null) { rankingRules.RewardTop2Prefab = SafeLimit(_cfgRewardTop2Prefab.Value, 96); } if (_cfgRewardTop2Amount != null) { rankingRules.RewardTop2Amount = Mathf.Max(0, _cfgRewardTop2Amount.Value); } if (_cfgRewardTop3MinPoints != null) { rankingRules.RewardTop3MinPoints = Mathf.Max(0, _cfgRewardTop3MinPoints.Value); } if (_cfgRewardTop3Label != null) { rankingRules.RewardTop3Label = SafeLimit(_cfgRewardTop3Label.Value, 96); } if (_cfgRewardTop3Prefab != null) { rankingRules.RewardTop3Prefab = SafeLimit(_cfgRewardTop3Prefab.Value, 96); } if (_cfgRewardTop3Amount != null) { rankingRules.RewardTop3Amount = Mathf.Max(0, _cfgRewardTop3Amount.Value); } if (_cfgPointsExchangeEnabled != null) { rankingRules.PointsExchangeEnabled = _cfgPointsExchangeEnabled.Value; } if (_cfgPointsExchangePrefab != null) { rankingRules.PointsExchangePrefab = SafeLimit(_cfgPointsExchangePrefab.Value, 96); } if (_cfgPointsExchangeUseCoinsPerPoint != null) { rankingRules.PointsExchangeUseCoinsPerPoint = _cfgPointsExchangeUseCoinsPerPoint.Value; } if (_cfgPointsExchangeCoinsPerPoint != null) { rankingRules.PointsExchangeCoinsPerPoint = Mathf.Max(1, _cfgPointsExchangeCoinsPerPoint.Value); } if (_cfgPointsExchangeUsePointsPerCoin != null) { rankingRules.PointsExchangeUsePointsPerCoin = _cfgPointsExchangeUsePointsPerCoin.Value; } if (_cfgPointsExchangePointsPerCoin != null) { rankingRules.PointsExchangePointsPerCoin = Mathf.Max(1, _cfgPointsExchangePointsPerCoin.Value); } if (_cfgPointsExchangeMinPoints != null) { rankingRules.PointsExchangeMinPoints = Mathf.Max(0, _cfgPointsExchangeMinPoints.Value); } if (_cfgPointsExchangeMaxPointsPerRequest != null) { rankingRules.PointsExchangeMaxPointsPerRequest = Mathf.Max(0, _cfgPointsExchangeMaxPointsPerRequest.Value); } string raw = BuildCombatCategoryRulesFromBiomeEntries(); string raw2 = BuildProductionCategoryRules(); Dictionary categories; foreach (KeyValuePair item in ParseHudCategorizedPointRules(raw, out categories)) { if (!IsFishPrefab(item.Key)) { rankingRules.KillPoints[item.Key] = Mathf.Max(0, item.Value); } } Dictionary categories2; foreach (KeyValuePair item2 in ParseHudCategorizedPointRules(raw2, out categories2)) { rankingRules.CraftPoints[item2.Key] = Mathf.Max(0, item2.Value); } rankingRules.CombatHudCategories = categories; rankingRules.ProductionHudCategories = categories2; foreach (KeyValuePair> cfgKillPointEntry in _cfgKillPointEntries) { if (cfgKillPointEntry.Value != null) { string text = SafeKey(cfgKillPointEntry.Key); if (!string.IsNullOrWhiteSpace(text) && !IsFishPrefab(text)) { rankingRules.KillPoints[text] = Mathf.Max(0, cfgKillPointEntry.Value.Value); } } } foreach (KeyValuePair> cfgBossPointEntry in _cfgBossPointEntries) { rankingRules.BossPoints[cfgBossPointEntry.Key] = ((cfgBossPointEntry.Value != null) ? cfgBossPointEntry.Value.Value : 0); } foreach (KeyValuePair> cfgFishingPointEntry in _cfgFishingPointEntries) { string text2 = NormalizeFishPrefabName(cfgFishingPointEntry.Key); if (IsFishPrefab(text2)) { rankingRules.FishingPoints[text2] = ((cfgFishingPointEntry.Value != null) ? cfgFishingPointEntry.Value.Value : 0); } } foreach (KeyValuePair> cfgSkillMilestoneJackpotPointEntry in _cfgSkillMilestoneJackpotPointEntries) { rankingRules.SkillMilestoneJackpotPoints[cfgSkillMilestoneJackpotPointEntry.Key] = ((cfgSkillMilestoneJackpotPointEntry.Value != null) ? ParseSkillMilestoneMap(cfgSkillMilestoneJackpotPointEntry.Value.Value) : new Dictionary()); } if (_cfgFarmJackpotRules != null) { foreach (KeyValuePair item3 in ParseDelimitedJackpotRules(_cfgFarmJackpotRules.Value)) { rankingRules.FarmJackpots[item3.Key] = item3.Value; } } string raw3 = BuildUniqueCraftJackpotRulesFromCategoryEntries(); foreach (KeyValuePair item4 in ParseFlexibleJackpotRules(raw3)) { rankingRules.UniqueCraftJackpots[item4.Key] = item4.Value; } if (_cfgMarketplaceQuestPointMap != null) { foreach (KeyValuePair item5 in ParseMarketplaceQuestPointMap(_cfgMarketplaceQuestPointMap.Value)) { rankingRules.MarketplaceQuestPoints[item5.Key] = item5.Value; } } if (_cfgDeathPenaltyRules != null) { rankingRules.DeathPenaltyRules = ParseDeathPenaltyRules(_cfgDeathPenaltyRules.Value); } if (_cfgExplorationMapJackpotRules != null) { rankingRules.ExplorationMapJackpots = ParseDeathPenaltyRules(_cfgExplorationMapJackpotRules.Value); } _rules = rankingRules; RebuildMarketplaceQuestPointCache(); } private void RebuildMarketplaceQuestPointCache() { _marketplaceQuestPointsByUid.Clear(); if (_rules == null || _rules.MarketplaceQuestPoints == null) { return; } foreach (KeyValuePair marketplaceQuestPoint in _rules.MarketplaceQuestPoints) { string text = SafeMarketplaceQuestKey(marketplaceQuestPoint.Key); int value = marketplaceQuestPoint.Value; if (!string.IsNullOrWhiteSpace(text) && value != 0) { int num = MarketplaceQuestKeyToUid(text); if (num != 0) { _marketplaceQuestPointsByUid[num] = new MarketplaceQuestPointRule { QuestKey = text, Points = value }; } } } } private string SafeMarketplaceQuestKey(string questKey) { questKey = SafeLimit(questKey, 128).Trim().ToLowerInvariant(); questKey = questKey.Replace("|", "/"); return questKey; } private int MarketplaceQuestKeyToUid(string questKey) { questKey = SafeMarketplaceQuestKey(questKey); if (string.IsNullOrWhiteSpace(questKey)) { return 0; } try { return StringExtensionMethods.GetStableHashCode(questKey); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao converter quest key do Marketplace em UID: " + questKey + " erro=" + ex.Message)); return 0; } } private bool TryGetMarketplaceQuestRule(int questUid, out MarketplaceQuestPointRule rule) { rule = null; if (_rules == null || !_rules.EnableMarketplaceQuestPoints) { return false; } return _marketplaceQuestPointsByUid.TryGetValue(questUid, out rule) && rule != null && rule.Points != 0; } private LiteDatabase OpenRankingDatabase() { return new LiteDatabase(new ConnectionString { Filename = _databaseFilePath, Connection = ConnectionType.Shared }); } private void LoadDatabase() { _database = new RankingDatabase(); if (!IsDedicatedServerInstance()) { DebugLog(DebugCategory.General, "LoadDatabase ignorado fora do servidor dedicado. Banco LiteDB fica somente no servidor."); return; } try { if (string.IsNullOrWhiteSpace(_databaseFilePath)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[Ranking] Caminho do banco LiteDB nao definido."); return; } EnsureDatabaseDirectoryExists(); using LiteDatabase liteDatabase = OpenRankingDatabase(); ILiteCollection collection = liteDatabase.GetCollection("ranking_entries"); ILiteCollection collection2 = liteDatabase.GetCollection("reward_claims"); ILiteCollection collection3 = liteDatabase.GetCollection("marketplace_quest_credits"); ILiteCollection collection4 = liteDatabase.GetCollection("fishing_catch_credits"); collection.EnsureIndex((RankingEntryDocument x) => x.PlayerName, unique: true); collection2.EnsureIndex((RewardClaimDocument x) => x.Key, unique: true); collection3.EnsureIndex((MarketplaceQuestCreditDocument x) => x.Key, unique: true); collection4.EnsureIndex((FishingCatchCreditDocument x) => x.ZdoKey, unique: true); RankingDatabase rankingDatabase = new RankingDatabase(); foreach (RankingEntryDocument item in collection.FindAll()) { RankingEntry rankingEntry = ConvertFromDocument(item); if (rankingEntry != null && !string.IsNullOrWhiteSpace(rankingEntry.PlayerName)) { rankingDatabase.Entries.Add(rankingEntry); } } foreach (RewardClaimDocument item2 in collection2.FindAll()) { RewardClaimRecord rewardClaimRecord = ConvertFromDocument(item2); if (rewardClaimRecord != null && !string.IsNullOrWhiteSpace(rewardClaimRecord.PlayerName)) { rankingDatabase.Claims.Add(rewardClaimRecord); } } foreach (MarketplaceQuestCreditDocument item3 in collection3.FindAll()) { MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = ConvertFromDocument(item3); if (marketplaceQuestCreditRecord != null && !string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.PlayerName) && !string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.QuestKey)) { rankingDatabase.MarketplaceQuestCredits.Add(marketplaceQuestCreditRecord); } } foreach (FishingCatchCreditDocument item4 in collection4.FindAll()) { FishingCatchCreditRecord fishingCatchCreditRecord = ConvertFromDocument(item4); if (fishingCatchCreditRecord != null && !string.IsNullOrWhiteSpace(fishingCatchCreditRecord.ZdoKey)) { rankingDatabase.FishingCatchCredits.Add(fishingCatchCreditRecord); } } _database = NormalizeDatabase(rankingDatabase); RebuildDatabaseIndexes(); } catch (Exception ex) { _database = new RankingDatabase(); RebuildDatabaseIndexes(); ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao carregar banco LiteDB do ranking: " + ex)); } } private void EnsureDatabaseDirectoryExists() { if (!IsDedicatedServerInstance()) { return; } try { if (!string.IsNullOrWhiteSpace(_databaseFilePath)) { string directoryName = Path.GetDirectoryName(_databaseFilePath); if (!string.IsNullOrWhiteSpace(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Nao foi possivel preparar pasta do banco LiteDB: " + ex.Message)); } } private RankingDatabase LoadLegacyDatabaseFile(string filePath) { RankingDatabase result = new RankingDatabase(); try { if (string.IsNullOrWhiteSpace(filePath)) { return result; } if (!File.Exists(filePath)) { return result; } RankingDatabase rankingDatabase = new RankingDatabase(); string[] array = File.ReadAllLines(filePath, Encoding.UTF8); string[] array2 = array; foreach (string text in array2) { string text2 = ((text == null) ? "" : text.Trim()); if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#")) { continue; } if (text2.StartsWith("Claim|", StringComparison.Ordinal)) { string[] array3 = text2.Split(new char[1] { '|' }); if (array3.Length < 5) { continue; } RewardClaimRecord rewardClaimRecord = new RewardClaimRecord(); rewardClaimRecord.CycleId = SafeLimit(DecodeDatabaseField(array3[1]), 64); rewardClaimRecord.PlayerName = SanitizePlayerName(DecodeDatabaseField(array3[2])); rewardClaimRecord.Rank = Mathf.Clamp(ParseInt(array3[3], 0), 0, int.MaxValue); rewardClaimRecord.ClaimedAtUtc = SafeLimit(DecodeDatabaseField(array3[4]), 64); rewardClaimRecord.Status = ((array3.Length >= 6) ? SafeLimit(DecodeDatabaseField(array3[5]), 16) : "claimed"); if (!string.IsNullOrWhiteSpace(rewardClaimRecord.CycleId) && !string.IsNullOrWhiteSpace(rewardClaimRecord.PlayerName) && rewardClaimRecord.Rank > 0) { string claimKey = BuildRewardClaimKey(rewardClaimRecord.CycleId, rewardClaimRecord.PlayerName, rewardClaimRecord.Rank); if (!rankingDatabase.Claims.Any((RewardClaimRecord x) => string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), claimKey, StringComparison.OrdinalIgnoreCase))) { rankingDatabase.Claims.Add(rewardClaimRecord); } } } else if (text2.StartsWith("QuestCredit|", StringComparison.Ordinal)) { string[] array4 = text2.Split(new char[1] { '|' }); if (array4.Length < 4) { continue; } MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = new MarketplaceQuestCreditRecord(); marketplaceQuestCreditRecord.PlayerName = SanitizePlayerName(DecodeDatabaseField(array4[1])); marketplaceQuestCreditRecord.QuestKey = SafeKey(DecodeDatabaseField(array4[2])); marketplaceQuestCreditRecord.GrantedAtUtc = ((array4.Length >= 4) ? SafeLimit(DecodeDatabaseField(array4[3]), 64) : ""); if (!string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.PlayerName) && !string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.QuestKey)) { string creditKey = BuildMarketplaceQuestCreditKey(marketplaceQuestCreditRecord.PlayerName, marketplaceQuestCreditRecord.QuestKey); if (!rankingDatabase.MarketplaceQuestCredits.Any((MarketplaceQuestCreditRecord x) => string.Equals(BuildMarketplaceQuestCreditKey(x.PlayerName, x.QuestKey), creditKey, StringComparison.OrdinalIgnoreCase))) { rankingDatabase.MarketplaceQuestCredits.Add(marketplaceQuestCreditRecord); } } } else { if (!text2.StartsWith("Entry|", StringComparison.Ordinal)) { continue; } string[] array5 = text2.Split(new char[1] { '|' }); if (array5.Length < 10) { continue; } RankingEntry entry = new RankingEntry(); entry.PlayerName = SanitizePlayerName(DecodeDatabaseField(array5[1])); entry.Points = Mathf.Clamp(ParseInt(array5[2], 0), -2147483647, int.MaxValue); entry.LastReason = SafeReason(DecodeDatabaseField(array5[3])); entry.LastUpdateUtc = SafeLimit(DecodeDatabaseField(array5[4]), 64); entry.TotalKillsPontuadas = Mathf.Clamp(ParseInt(array5[5], 0), 0, int.MaxValue); entry.TotalBossesPontuadas = Mathf.Clamp(ParseInt(array5[6], 0), 0, int.MaxValue); entry.KillPointsTotal = Mathf.Clamp(ParseInt(array5[7], 0), -2147483647, int.MaxValue); entry.BossPointsTotal = Mathf.Clamp(ParseInt(array5[8], 0), -2147483647, int.MaxValue); entry.BossCredits = new HashSet(StringComparer.OrdinalIgnoreCase); string text3 = DecodeDatabaseField(array5[9]); if (!string.IsNullOrWhiteSpace(text3)) { string[] array6 = text3.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); string[] array7 = array6; foreach (string value in array7) { string text4 = SafeKey(value); if (!string.IsNullOrWhiteSpace(text4)) { entry.BossCredits.Add(text4); } } } if (array5.Length >= 11) { entry.TotalSkillLevelUpsPontuados = Mathf.Clamp(ParseInt(array5[10], 0), 0, int.MaxValue); } if (array5.Length >= 12) { entry.SkillPointsTotal = Mathf.Clamp(ParseInt(array5[11], 0), -2147483647, int.MaxValue); } if (array5.Length >= 13) { entry.TotalCraftPontuadas = Mathf.Clamp(ParseInt(array5[12], 0), 0, int.MaxValue); } if (array5.Length >= 14) { entry.CraftPointsTotal = Mathf.Clamp(ParseInt(array5[13], 0), -2147483647, int.MaxValue); } if (array5.Length >= 15) { entry.TotalFarmJackpotsPontuados = Mathf.Clamp(ParseInt(array5[14], 0), 0, int.MaxValue); } if (array5.Length >= 16) { entry.FarmJackpotPointsTotal = Mathf.Clamp(ParseInt(array5[15], 0), -2147483647, int.MaxValue); } if (array5.Length >= 17) { entry.TotalUniqueCraftJackpotsPontuados = Mathf.Clamp(ParseInt(array5[16], 0), 0, int.MaxValue); } if (array5.Length >= 18) { entry.UniqueCraftJackpotPointsTotal = Mathf.Clamp(ParseInt(array5[17], 0), -2147483647, int.MaxValue); } if (array5.Length >= 19) { entry.ProgressCounters = DeserializeStringIntDictionary(DecodeDatabaseField(array5[18])); } if (array5.Length >= 20) { entry.GenericJackpotCredits = DeserializeStringSet(DecodeDatabaseField(array5[19])); } if (array5.Length >= 21) { entry.TotalDeaths = Mathf.Clamp(ParseInt(array5[20], 0), 0, int.MaxValue); } if (array5.Length >= 22) { entry.DeathPenaltyPointsTotal = Mathf.Clamp(ParseInt(array5[21], 0), 0, int.MaxValue); } if (array5.Length >= 23) { entry.TotalPointsExchanges = Mathf.Clamp(ParseInt(array5[22], 0), 0, int.MaxValue); } if (array5.Length >= 24) { entry.PointsExchangePenaltyTotal = Mathf.Clamp(ParseInt(array5[23], 0), 0, int.MaxValue); } if (array5.Length >= 25) { entry.PointsExchangeCoinsTotal = Mathf.Clamp(ParseInt(array5[24], 0), 0, int.MaxValue); } if (array5.Length >= 26) { entry.ExplorationMapJackpotPointsTotal = Mathf.Clamp(ParseInt(array5[25], 0), 0, int.MaxValue); } if (!string.IsNullOrWhiteSpace(entry.PlayerName)) { RankingEntry rankingEntry = rankingDatabase.Entries.FirstOrDefault((RankingEntry x) => string.Equals(x.PlayerName, entry.PlayerName, StringComparison.OrdinalIgnoreCase)); if (rankingEntry != null) { rankingEntry.Points = entry.Points; rankingEntry.LastReason = entry.LastReason; rankingEntry.LastUpdateUtc = entry.LastUpdateUtc; rankingEntry.TotalKillsPontuadas = entry.TotalKillsPontuadas; rankingEntry.TotalBossesPontuadas = entry.TotalBossesPontuadas; rankingEntry.KillPointsTotal = entry.KillPointsTotal; rankingEntry.BossPointsTotal = entry.BossPointsTotal; rankingEntry.BossCredits = entry.BossCredits; rankingEntry.TotalSkillLevelUpsPontuados = entry.TotalSkillLevelUpsPontuados; rankingEntry.SkillPointsTotal = entry.SkillPointsTotal; rankingEntry.TotalCraftPontuadas = entry.TotalCraftPontuadas; rankingEntry.CraftPointsTotal = entry.CraftPointsTotal; rankingEntry.TotalFarmJackpotsPontuados = entry.TotalFarmJackpotsPontuados; rankingEntry.FarmJackpotPointsTotal = entry.FarmJackpotPointsTotal; rankingEntry.TotalUniqueCraftJackpotsPontuados = entry.TotalUniqueCraftJackpotsPontuados; rankingEntry.UniqueCraftJackpotPointsTotal = entry.UniqueCraftJackpotPointsTotal; rankingEntry.ProgressCounters = entry.ProgressCounters ?? new Dictionary(StringComparer.OrdinalIgnoreCase); rankingEntry.GenericJackpotCredits = entry.GenericJackpotCredits ?? new HashSet(StringComparer.OrdinalIgnoreCase); rankingEntry.TotalDeaths = entry.TotalDeaths; rankingEntry.DeathPenaltyPointsTotal = entry.DeathPenaltyPointsTotal; rankingEntry.TotalPointsExchanges = entry.TotalPointsExchanges; rankingEntry.PointsExchangePenaltyTotal = entry.PointsExchangePenaltyTotal; rankingEntry.PointsExchangeCoinsTotal = entry.PointsExchangeCoinsTotal; rankingEntry.ExplorationMapJackpotPointsTotal = entry.ExplorationMapJackpotPointsTotal; } else { rankingDatabase.Entries.Add(entry); } } } } return NormalizeDatabase(rankingDatabase); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao importar banco legado do ranking: " + ex)); return result; } } private RankingDatabase NormalizeDatabase(RankingDatabase database) { if (database == null) { database = new RankingDatabase(); } if (database.Entries == null) { database.Entries = new List(); } if (database.Claims == null) { database.Claims = new List(); } if (database.MarketplaceQuestCredits == null) { database.MarketplaceQuestCredits = new List(); } if (database.FishingCatchCredits == null) { database.FishingCatchCredits = new List(); } foreach (RankingEntry entry in database.Entries) { if (entry != null) { entry.PlayerName = SanitizePlayerName(entry.PlayerName); entry.LastReason = SafeReason(entry.LastReason); entry.LastUpdateUtc = SafeLimit(entry.LastUpdateUtc, 64); entry.Points = Mathf.Clamp(entry.Points, -2147483647, int.MaxValue); entry.TotalKillsPontuadas = Mathf.Clamp(entry.TotalKillsPontuadas, 0, int.MaxValue); entry.TotalBossesPontuadas = Mathf.Clamp(entry.TotalBossesPontuadas, 0, int.MaxValue); entry.KillPointsTotal = Mathf.Clamp(entry.KillPointsTotal, -2147483647, int.MaxValue); entry.BossPointsTotal = Mathf.Clamp(entry.BossPointsTotal, -2147483647, int.MaxValue); entry.TotalSkillLevelUpsPontuados = Mathf.Clamp(entry.TotalSkillLevelUpsPontuados, 0, int.MaxValue); entry.SkillPointsTotal = Mathf.Clamp(entry.SkillPointsTotal, -2147483647, int.MaxValue); entry.TotalFishingPontuadas = Mathf.Clamp(entry.TotalFishingPontuadas, 0, int.MaxValue); entry.FishingPointsTotal = Mathf.Clamp(entry.FishingPointsTotal, -2147483647, int.MaxValue); entry.TotalCraftPontuadas = Mathf.Clamp(entry.TotalCraftPontuadas, 0, int.MaxValue); entry.CraftPointsTotal = Mathf.Clamp(entry.CraftPointsTotal, -2147483647, int.MaxValue); entry.TotalFarmJackpotsPontuados = Mathf.Clamp(entry.TotalFarmJackpotsPontuados, 0, int.MaxValue); entry.FarmJackpotPointsTotal = Mathf.Clamp(entry.FarmJackpotPointsTotal, -2147483647, int.MaxValue); entry.TotalUniqueCraftJackpotsPontuados = Mathf.Clamp(entry.TotalUniqueCraftJackpotsPontuados, 0, int.MaxValue); entry.UniqueCraftJackpotPointsTotal = Mathf.Clamp(entry.UniqueCraftJackpotPointsTotal, -2147483647, int.MaxValue); entry.TotalDeaths = Mathf.Clamp(entry.TotalDeaths, 0, int.MaxValue); entry.DeathPenaltyPointsTotal = Mathf.Clamp(entry.DeathPenaltyPointsTotal, 0, int.MaxValue); entry.TotalPointsExchanges = Mathf.Clamp(entry.TotalPointsExchanges, 0, int.MaxValue); entry.PointsExchangePenaltyTotal = Mathf.Clamp(entry.PointsExchangePenaltyTotal, 0, int.MaxValue); entry.PointsExchangeCoinsTotal = Mathf.Clamp(entry.PointsExchangeCoinsTotal, 0, int.MaxValue); entry.ExplorationMapJackpotPointsTotal = Mathf.Clamp(entry.ExplorationMapJackpotPointsTotal, 0, int.MaxValue); entry.BossCredits = ((entry.BossCredits == null) ? new HashSet(StringComparer.OrdinalIgnoreCase) : new HashSet(entry.BossCredits.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select(SafeKey), StringComparer.OrdinalIgnoreCase)); entry.GenericJackpotCredits = ((entry.GenericJackpotCredits == null) ? new HashSet(StringComparer.OrdinalIgnoreCase) : new HashSet(entry.GenericJackpotCredits.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select(SafeKey), StringComparer.OrdinalIgnoreCase)); entry.SkillLevel100JackpotCredits = ((entry.SkillLevel100JackpotCredits == null) ? new HashSet(StringComparer.OrdinalIgnoreCase) : new HashSet(entry.SkillLevel100JackpotCredits.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select(SafeKey), StringComparer.OrdinalIgnoreCase)); entry.ProgressCounters = ((entry.ProgressCounters == null) ? new Dictionary(StringComparer.OrdinalIgnoreCase) : entry.ProgressCounters.Where((KeyValuePair x) => !string.IsNullOrWhiteSpace(x.Key)).GroupBy, string>((KeyValuePair x) => SafeKey(x.Key), StringComparer.OrdinalIgnoreCase).ToDictionary>, string, int>((IGrouping> x) => x.Key, (IGrouping> x) => Mathf.Clamp(x.Last().Value, 0, int.MaxValue), StringComparer.OrdinalIgnoreCase)); } } database.Entries = (from x in database.Entries.Where((RankingEntry x) => x != null && !string.IsNullOrWhiteSpace(x.PlayerName)).GroupBy((RankingEntry x) => x.PlayerName, StringComparer.OrdinalIgnoreCase) select x.First()).OrderBy((RankingEntry x) => x.PlayerName, StringComparer.OrdinalIgnoreCase).ToList(); database.Claims = (from x in database.Claims.Where((RewardClaimRecord x) => x != null && !string.IsNullOrWhiteSpace(x.CycleId) && !string.IsNullOrWhiteSpace(x.PlayerName) && x.Rank > 0).Select(delegate(RewardClaimRecord x) { x.CycleId = SafeLimit(x.CycleId, 64); x.PlayerName = SanitizePlayerName(x.PlayerName); x.Rank = Mathf.Clamp(x.Rank, 1, int.MaxValue); x.ClaimedAtUtc = SafeLimit(x.ClaimedAtUtc, 64); x.Status = SafeLimit(string.IsNullOrWhiteSpace(x.Status) ? "claimed" : x.Status, 16); return x; }).GroupBy((RewardClaimRecord x) => BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), StringComparer.OrdinalIgnoreCase) select x.First()).ToList(); database.MarketplaceQuestCredits = (from x in database.MarketplaceQuestCredits.Where((MarketplaceQuestCreditRecord x) => x != null && !string.IsNullOrWhiteSpace(x.PlayerName) && !string.IsNullOrWhiteSpace(x.QuestKey)).Select(delegate(MarketplaceQuestCreditRecord x) { x.PlayerName = SanitizePlayerName(x.PlayerName); x.QuestKey = SafeKey(x.QuestKey); x.GrantedAtUtc = SafeLimit(x.GrantedAtUtc, 64); return x; }).GroupBy((MarketplaceQuestCreditRecord x) => BuildMarketplaceQuestCreditKey(x.PlayerName, x.QuestKey), StringComparer.OrdinalIgnoreCase) select x.First()).ToList(); database.FishingCatchCredits = (from x in database.FishingCatchCredits.Where((FishingCatchCreditRecord x) => x != null && !string.IsNullOrWhiteSpace(x.ZdoKey)).Select(delegate(FishingCatchCreditRecord x) { x.PlayerName = SanitizePlayerName(x.PlayerName); x.FishPrefab = SafeKey(x.FishPrefab); x.ZdoKey = SafeKey(x.ZdoKey); x.GrantedAtUtc = SafeLimit(x.GrantedAtUtc, 64); return x; }).GroupBy((FishingCatchCreditRecord x) => x.ZdoKey, StringComparer.OrdinalIgnoreCase) select x.First()).ToList(); return database; } private void SaveDatabase(bool force = false) { lock (_databaseSaveLock) { try { if (!IsDedicatedServerInstance() || string.IsNullOrWhiteSpace(_databaseFilePath) || _database == null) { return; } _databaseSavePending = true; DateTime utcNow = DateTime.UtcNow; if (!force && utcNow < _nextDatabaseSaveUtc) { return; } EnsureDatabaseDirectoryExists(); RankingDatabase rankingDatabase = NormalizeDatabase(_database); MarkRankingCacheDirty(); using (LiteDatabase liteDatabase = OpenRankingDatabase()) { ILiteCollection collection = liteDatabase.GetCollection("ranking_entries"); ILiteCollection collection2 = liteDatabase.GetCollection("reward_claims"); ILiteCollection collection3 = liteDatabase.GetCollection("marketplace_quest_credits"); ILiteCollection collection4 = liteDatabase.GetCollection("fishing_catch_credits"); collection.EnsureIndex((RankingEntryDocument x) => x.PlayerName, unique: true); collection2.EnsureIndex((RewardClaimDocument x) => x.Key, unique: true); collection3.EnsureIndex((MarketplaceQuestCreditDocument x) => x.Key, unique: true); collection4.EnsureIndex((FishingCatchCreditDocument x) => x.ZdoKey, unique: true); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet2 = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet3 = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet4 = new HashSet(StringComparer.OrdinalIgnoreCase); if (rankingDatabase.Entries != null) { foreach (RankingEntry entry in rankingDatabase.Entries) { RankingEntryDocument rankingEntryDocument = ConvertToDocument(entry); if (rankingEntryDocument != null && !string.IsNullOrWhiteSpace(rankingEntryDocument.PlayerName)) { hashSet.Add(rankingEntryDocument.PlayerName); collection.Upsert(rankingEntryDocument); } } } if (rankingDatabase.Claims != null) { foreach (RewardClaimRecord claim in rankingDatabase.Claims) { RewardClaimDocument rewardClaimDocument = ConvertToDocument(claim); if (rewardClaimDocument != null && !string.IsNullOrWhiteSpace(rewardClaimDocument.Key)) { hashSet2.Add(rewardClaimDocument.Key); collection2.Upsert(rewardClaimDocument); } } } if (rankingDatabase.MarketplaceQuestCredits != null) { foreach (MarketplaceQuestCreditRecord marketplaceQuestCredit in rankingDatabase.MarketplaceQuestCredits) { MarketplaceQuestCreditDocument marketplaceQuestCreditDocument = ConvertToDocument(marketplaceQuestCredit); if (marketplaceQuestCreditDocument != null && !string.IsNullOrWhiteSpace(marketplaceQuestCreditDocument.Key)) { hashSet3.Add(marketplaceQuestCreditDocument.Key); collection3.Upsert(marketplaceQuestCreditDocument); } } } if (rankingDatabase.FishingCatchCredits != null) { foreach (FishingCatchCreditRecord fishingCatchCredit in rankingDatabase.FishingCatchCredits) { FishingCatchCreditDocument fishingCatchCreditDocument = ConvertToDocument(fishingCatchCredit); if (fishingCatchCreditDocument != null && !string.IsNullOrWhiteSpace(fishingCatchCreditDocument.ZdoKey)) { hashSet4.Add(fishingCatchCreditDocument.ZdoKey); collection4.Upsert(fishingCatchCreditDocument); } } } DeleteStaleRankingDocuments(collection, hashSet); DeleteStaleRewardClaimDocuments(collection2, hashSet2); DeleteStaleMarketplaceCreditDocuments(collection3, hashSet3); DeleteStaleFishingCreditDocuments(collection4, hashSet4); } _database = rankingDatabase; RebuildDatabaseIndexes(); _databaseSavePending = false; _nextDatabaseSaveUtc = utcNow.AddSeconds(30.0); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao salvar banco LiteDB do ranking: " + ex)); } } } private void SaveRankingEntry(RankingEntry entry) { lock (_databaseSaveLock) { try { if (!IsDedicatedServerInstance() || string.IsNullOrWhiteSpace(_databaseFilePath) || entry == null) { return; } RankingEntryDocument rankingEntryDocument = ConvertToDocument(entry); if (rankingEntryDocument == null || string.IsNullOrWhiteSpace(rankingEntryDocument.PlayerName)) { return; } EnsureDatabaseDirectoryExists(); using LiteDatabase liteDatabase = OpenRankingDatabase(); ILiteCollection collection = liteDatabase.GetCollection("ranking_entries"); collection.EnsureIndex((RankingEntryDocument x) => x.PlayerName, unique: true); collection.Upsert(rankingEntryDocument); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao salvar jogador no LiteDB do ranking: " + ex)); } } } private void SaveMarketplaceQuestCredit(MarketplaceQuestCreditRecord credit) { lock (_databaseSaveLock) { try { if (!IsDedicatedServerInstance() || string.IsNullOrWhiteSpace(_databaseFilePath) || credit == null) { return; } MarketplaceQuestCreditDocument marketplaceQuestCreditDocument = ConvertToDocument(credit); if (marketplaceQuestCreditDocument == null || string.IsNullOrWhiteSpace(marketplaceQuestCreditDocument.Key)) { return; } EnsureDatabaseDirectoryExists(); using LiteDatabase liteDatabase = OpenRankingDatabase(); ILiteCollection collection = liteDatabase.GetCollection("marketplace_quest_credits"); collection.EnsureIndex((MarketplaceQuestCreditDocument x) => x.Key, unique: true); collection.Upsert(marketplaceQuestCreditDocument); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao salvar credito de quest Marketplace no LiteDB: " + ex)); } } } private void SaveFishingCatchCredit(FishingCatchCreditRecord credit) { lock (_databaseSaveLock) { try { if (!IsDedicatedServerInstance() || string.IsNullOrWhiteSpace(_databaseFilePath) || credit == null) { return; } FishingCatchCreditDocument fishingCatchCreditDocument = ConvertToDocument(credit); if (fishingCatchCreditDocument == null || string.IsNullOrWhiteSpace(fishingCatchCreditDocument.ZdoKey)) { return; } EnsureDatabaseDirectoryExists(); using LiteDatabase liteDatabase = OpenRankingDatabase(); ILiteCollection collection = liteDatabase.GetCollection("fishing_catch_credits"); collection.EnsureIndex((FishingCatchCreditDocument x) => x.ZdoKey, unique: true); collection.Upsert(fishingCatchCreditDocument); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao salvar credito de pesca no LiteDB: " + ex)); } } } private void SaveRewardClaim(string cycleId, string playerName, int rank) { lock (_databaseSaveLock) { try { if (!IsDedicatedServerInstance() || string.IsNullOrWhiteSpace(_databaseFilePath) || _database == null || _database.Claims == null) { return; } string key = BuildRewardClaimKey(cycleId, playerName, rank); RewardClaimRecord rewardClaimRecord = _database.Claims.FirstOrDefault((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase)); if (rewardClaimRecord == null) { return; } RewardClaimDocument rewardClaimDocument = ConvertToDocument(rewardClaimRecord); if (rewardClaimDocument == null || string.IsNullOrWhiteSpace(rewardClaimDocument.Key)) { return; } EnsureDatabaseDirectoryExists(); using LiteDatabase liteDatabase = OpenRankingDatabase(); ILiteCollection collection = liteDatabase.GetCollection("reward_claims"); collection.EnsureIndex((RewardClaimDocument x) => x.Key, unique: true); collection.Upsert(rewardClaimDocument); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao salvar claim de recompensa no LiteDB: " + ex)); } } } private void DeleteRewardClaim(string cycleId, string playerName, int rank) { lock (_databaseSaveLock) { try { if (!IsDedicatedServerInstance() || string.IsNullOrWhiteSpace(_databaseFilePath)) { return; } string text = BuildRewardClaimKey(cycleId, playerName, rank); if (string.IsNullOrWhiteSpace(text)) { return; } EnsureDatabaseDirectoryExists(); using LiteDatabase liteDatabase = OpenRankingDatabase(); ILiteCollection collection = liteDatabase.GetCollection("reward_claims"); collection.Delete(text); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao remover claim de recompensa no LiteDB: " + ex)); } } } private void FlushPendingDatabaseSaveIfDue() { if (_databaseSavePending && !(DateTime.UtcNow < _nextDatabaseSaveUtc)) { SaveDatabase(force: true); } } private void DeleteStaleRankingDocuments(ILiteCollection collection, HashSet validKeys) { foreach (RankingEntryDocument item in collection.FindAll().ToList()) { if (item != null && (string.IsNullOrWhiteSpace(item.PlayerName) || !validKeys.Contains(item.PlayerName))) { collection.Delete(item.PlayerName); } } } private void DeleteStaleRewardClaimDocuments(ILiteCollection collection, HashSet validKeys) { foreach (RewardClaimDocument item in collection.FindAll().ToList()) { if (item != null && (string.IsNullOrWhiteSpace(item.Key) || !validKeys.Contains(item.Key))) { collection.Delete(item.Key); } } } private void DeleteStaleMarketplaceCreditDocuments(ILiteCollection collection, HashSet validKeys) { foreach (MarketplaceQuestCreditDocument item in collection.FindAll().ToList()) { if (item != null && (string.IsNullOrWhiteSpace(item.Key) || !validKeys.Contains(item.Key))) { collection.Delete(item.Key); } } } private void DeleteStaleFishingCreditDocuments(ILiteCollection collection, HashSet validKeys) { foreach (FishingCatchCreditDocument item in collection.FindAll().ToList()) { if (item != null && (string.IsNullOrWhiteSpace(item.ZdoKey) || !validKeys.Contains(item.ZdoKey))) { collection.Delete(item.ZdoKey); } } } private RankingEntryDocument ConvertToDocument(RankingEntry entry) { if (entry == null) { return null; } RankingEntryDocument rankingEntryDocument = new RankingEntryDocument(); rankingEntryDocument.PlayerName = SanitizePlayerName(entry.PlayerName); rankingEntryDocument.Points = entry.Points; rankingEntryDocument.LastReason = SafeReason(entry.LastReason); rankingEntryDocument.LastUpdateUtc = SafeLimit(entry.LastUpdateUtc, 64); rankingEntryDocument.TotalKillsPontuadas = entry.TotalKillsPontuadas; rankingEntryDocument.TotalBossesPontuadas = entry.TotalBossesPontuadas; rankingEntryDocument.KillPointsTotal = entry.KillPointsTotal; rankingEntryDocument.BossPointsTotal = entry.BossPointsTotal; rankingEntryDocument.TotalSkillLevelUpsPontuados = entry.TotalSkillLevelUpsPontuados; rankingEntryDocument.SkillPointsTotal = entry.SkillPointsTotal; rankingEntryDocument.TotalFishingPontuadas = entry.TotalFishingPontuadas; rankingEntryDocument.FishingPointsTotal = entry.FishingPointsTotal; rankingEntryDocument.TotalCraftPontuadas = entry.TotalCraftPontuadas; rankingEntryDocument.CraftPointsTotal = entry.CraftPointsTotal; rankingEntryDocument.TotalFarmJackpotsPontuados = entry.TotalFarmJackpotsPontuados; rankingEntryDocument.FarmJackpotPointsTotal = entry.FarmJackpotPointsTotal; rankingEntryDocument.TotalUniqueCraftJackpotsPontuados = entry.TotalUniqueCraftJackpotsPontuados; rankingEntryDocument.UniqueCraftJackpotPointsTotal = entry.UniqueCraftJackpotPointsTotal; rankingEntryDocument.TotalDeaths = entry.TotalDeaths; rankingEntryDocument.DeathPenaltyPointsTotal = entry.DeathPenaltyPointsTotal; rankingEntryDocument.TotalPointsExchanges = entry.TotalPointsExchanges; rankingEntryDocument.PointsExchangePenaltyTotal = entry.PointsExchangePenaltyTotal; rankingEntryDocument.PointsExchangeCoinsTotal = entry.PointsExchangeCoinsTotal; rankingEntryDocument.ExplorationMapJackpotPointsTotal = entry.ExplorationMapJackpotPointsTotal; rankingEntryDocument.ProgressCounters = SerializeStringIntDictionary(entry.ProgressCounters); rankingEntryDocument.FloatProgressCounters = SerializeStringFloatDictionary(entry.FloatProgressCounters); rankingEntryDocument.GenericJackpotCredits = SerializeStringSet(entry.GenericJackpotCredits); rankingEntryDocument.BossCredits = SerializeStringSet(entry.BossCredits); rankingEntryDocument.SkillLevel100JackpotCredits = SerializeStringSet(entry.SkillLevel100JackpotCredits); return rankingEntryDocument; } private RankingEntry ConvertFromDocument(RankingEntryDocument doc) { if (doc == null) { return null; } RankingEntry rankingEntry = new RankingEntry(); rankingEntry.PlayerName = SanitizePlayerName(doc.PlayerName); rankingEntry.Points = doc.Points; rankingEntry.LastReason = SafeReason(doc.LastReason); rankingEntry.LastUpdateUtc = SafeLimit(doc.LastUpdateUtc, 64); rankingEntry.TotalKillsPontuadas = doc.TotalKillsPontuadas; rankingEntry.TotalBossesPontuadas = doc.TotalBossesPontuadas; rankingEntry.KillPointsTotal = doc.KillPointsTotal; rankingEntry.BossPointsTotal = doc.BossPointsTotal; rankingEntry.TotalSkillLevelUpsPontuados = doc.TotalSkillLevelUpsPontuados; rankingEntry.SkillPointsTotal = doc.SkillPointsTotal; rankingEntry.TotalFishingPontuadas = doc.TotalFishingPontuadas; rankingEntry.FishingPointsTotal = doc.FishingPointsTotal; rankingEntry.TotalCraftPontuadas = doc.TotalCraftPontuadas; rankingEntry.CraftPointsTotal = doc.CraftPointsTotal; rankingEntry.TotalFarmJackpotsPontuados = doc.TotalFarmJackpotsPontuados; rankingEntry.FarmJackpotPointsTotal = doc.FarmJackpotPointsTotal; rankingEntry.TotalUniqueCraftJackpotsPontuados = doc.TotalUniqueCraftJackpotsPontuados; rankingEntry.UniqueCraftJackpotPointsTotal = doc.UniqueCraftJackpotPointsTotal; rankingEntry.TotalDeaths = doc.TotalDeaths; rankingEntry.DeathPenaltyPointsTotal = doc.DeathPenaltyPointsTotal; rankingEntry.TotalPointsExchanges = doc.TotalPointsExchanges; rankingEntry.PointsExchangePenaltyTotal = doc.PointsExchangePenaltyTotal; rankingEntry.PointsExchangeCoinsTotal = doc.PointsExchangeCoinsTotal; rankingEntry.ExplorationMapJackpotPointsTotal = doc.ExplorationMapJackpotPointsTotal; rankingEntry.ProgressCounters = DeserializeStringIntDictionary(doc.ProgressCounters); rankingEntry.FloatProgressCounters = DeserializeStringFloatDictionary(doc.FloatProgressCounters); rankingEntry.GenericJackpotCredits = DeserializeStringSet(doc.GenericJackpotCredits); rankingEntry.BossCredits = DeserializeStringSet(doc.BossCredits); rankingEntry.SkillLevel100JackpotCredits = DeserializeStringSet(doc.SkillLevel100JackpotCredits); return rankingEntry; } private RewardClaimDocument ConvertToDocument(RewardClaimRecord claim) { if (claim == null) { return null; } RewardClaimDocument rewardClaimDocument = new RewardClaimDocument(); rewardClaimDocument.CycleId = SafeKey(claim.CycleId); rewardClaimDocument.PlayerName = SanitizePlayerName(claim.PlayerName); rewardClaimDocument.Rank = claim.Rank; rewardClaimDocument.ClaimedAtUtc = SafeLimit(claim.ClaimedAtUtc, 64); rewardClaimDocument.Status = SafeLimit(claim.Status, 32); rewardClaimDocument.Key = BuildRewardClaimKey(rewardClaimDocument.CycleId, rewardClaimDocument.PlayerName, rewardClaimDocument.Rank); return rewardClaimDocument; } private RewardClaimRecord ConvertFromDocument(RewardClaimDocument doc) { if (doc == null) { return null; } RewardClaimRecord rewardClaimRecord = new RewardClaimRecord(); rewardClaimRecord.CycleId = SafeKey(doc.CycleId); rewardClaimRecord.PlayerName = SanitizePlayerName(doc.PlayerName); rewardClaimRecord.Rank = doc.Rank; rewardClaimRecord.ClaimedAtUtc = SafeLimit(doc.ClaimedAtUtc, 64); rewardClaimRecord.Status = SafeLimit(doc.Status, 32); return rewardClaimRecord; } private MarketplaceQuestCreditDocument ConvertToDocument(MarketplaceQuestCreditRecord credit) { if (credit == null) { return null; } MarketplaceQuestCreditDocument marketplaceQuestCreditDocument = new MarketplaceQuestCreditDocument(); marketplaceQuestCreditDocument.PlayerName = SanitizePlayerName(credit.PlayerName); marketplaceQuestCreditDocument.QuestKey = SafeKey(credit.QuestKey); marketplaceQuestCreditDocument.GrantedAtUtc = SafeLimit(credit.GrantedAtUtc, 64); marketplaceQuestCreditDocument.Key = BuildMarketplaceQuestCreditKey(marketplaceQuestCreditDocument.PlayerName, marketplaceQuestCreditDocument.QuestKey); return marketplaceQuestCreditDocument; } private MarketplaceQuestCreditRecord ConvertFromDocument(MarketplaceQuestCreditDocument doc) { if (doc == null) { return null; } MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = new MarketplaceQuestCreditRecord(); marketplaceQuestCreditRecord.PlayerName = SanitizePlayerName(doc.PlayerName); marketplaceQuestCreditRecord.QuestKey = SafeKey(doc.QuestKey); marketplaceQuestCreditRecord.GrantedAtUtc = SafeLimit(doc.GrantedAtUtc, 64); return marketplaceQuestCreditRecord; } private FishingCatchCreditDocument ConvertToDocument(FishingCatchCreditRecord credit) { if (credit == null) { return null; } FishingCatchCreditDocument fishingCatchCreditDocument = new FishingCatchCreditDocument(); fishingCatchCreditDocument.PlayerName = SanitizePlayerName(credit.PlayerName); fishingCatchCreditDocument.FishPrefab = SafeKey(credit.FishPrefab); fishingCatchCreditDocument.ZdoKey = SafeKey(credit.ZdoKey); fishingCatchCreditDocument.GrantedAtUtc = SafeLimit(credit.GrantedAtUtc, 64); return fishingCatchCreditDocument; } private FishingCatchCreditRecord ConvertFromDocument(FishingCatchCreditDocument doc) { if (doc == null) { return null; } FishingCatchCreditRecord fishingCatchCreditRecord = new FishingCatchCreditRecord(); fishingCatchCreditRecord.PlayerName = SanitizePlayerName(doc.PlayerName); fishingCatchCreditRecord.FishPrefab = SafeKey(doc.FishPrefab); fishingCatchCreditRecord.ZdoKey = SafeKey(doc.ZdoKey); fishingCatchCreditRecord.GrantedAtUtc = SafeLimit(doc.GrantedAtUtc, 64); return fishingCatchCreditRecord; } private string SerializeStringSet(HashSet values) { if (values == null || values.Count == 0) { return ""; } return string.Join("\n", values.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select(SafeKey).Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase) .ToArray()); } private HashSet DeserializeStringSet(string raw) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return hashSet; } string[] array = raw.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string value in array) { string text = SafeKey(value); if (!string.IsNullOrWhiteSpace(text)) { hashSet.Add(text); } } return hashSet; } private string SerializeStringIntDictionary(Dictionary values) { if (values == null || values.Count == 0) { return ""; } return string.Join("\n", (from p in values where !string.IsNullOrWhiteSpace(p.Key) select EncodeDatabaseField(SafeKey(p.Key)) + ":" + Mathf.Clamp(p.Value, 0, int.MaxValue)).OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase).ToArray()); } private Dictionary DeserializeStringIntDictionary(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string text2 = ((text == null) ? "" : text.Trim()); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.LastIndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = text2.Substring(0, num); string s = text2.Substring(num + 1); if (int.TryParse(s, out var result)) { string value = DecodeDatabaseField(text3); if (string.IsNullOrWhiteSpace(value)) { value = text3; } value = SafeKey(value); if (!string.IsNullOrWhiteSpace(value)) { dictionary[value] = Mathf.Clamp(result, 0, int.MaxValue); } } } return dictionary; } private string SerializeStringFloatDictionary(Dictionary values) { if (values == null || values.Count == 0) { return ""; } return string.Join("\n", (from p in values where !string.IsNullOrWhiteSpace(p.Key) select EncodeDatabaseField(SafeKey(p.Key)) + ":" + Mathf.Clamp(p.Value, 0f, 1000000f).ToString("R", CultureInfo.InvariantCulture)).OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase).ToArray()); } private Dictionary DeserializeStringFloatDictionary(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string text2 = ((text == null) ? "" : text.Trim()); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.LastIndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = text2.Substring(0, num); string s = text2.Substring(num + 1); if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { string value = DecodeDatabaseField(text3); if (string.IsNullOrWhiteSpace(value)) { value = text3; } value = SafeKey(value); if (!string.IsNullOrWhiteSpace(value)) { dictionary[value] = Mathf.Clamp(result, 0f, 1000000f); } } } return dictionary; } private string EncodeDatabaseField(string value) { try { byte[] bytes = Encoding.UTF8.GetBytes(value ?? ""); return Convert.ToBase64String(bytes); } catch { return ""; } } private string DecodeDatabaseField(string value) { try { if (string.IsNullOrWhiteSpace(value)) { return ""; } byte[] bytes = Convert.FromBase64String(value); return Encoding.UTF8.GetString(bytes); } catch { return ""; } } private void DebugLog(DebugCategory category, string message) { if (_rules == null || !_rules.DebugLogging) { return; } bool flag = false; if (category switch { DebugCategory.Hit => _rules.LogHitReports, DebugCategory.Kill => _rules.LogKillReports, DebugCategory.Skill => _rules.LogSkillReports, DebugCategory.PendingKill => _rules.LogPendingKillReports, DebugCategory.Snapshot => _rules.LogSnapshotRequests, DebugCategory.Points => _rules.LogPointsChanges, _ => true, }) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[" + category.ToString() + "] " + message)); } } } public void AddPoints(string playerName, int amount, string reason) { if (IsServerInstance() && _rules.RankingEnabled && !string.IsNullOrWhiteSpace(playerName)) { playerName = SanitizePlayerName(playerName); if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Pontuação ignorada para admin: " + playerName + " motivo=" + reason); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); ApplyPointsToEntry(orCreateEntry, amount, reason); } } private void ApplyPointsToEntry(RankingEntry entry, int amount, string reason) { if (entry == null) { return; } if (ShouldIgnorePlayerForRanking(entry.PlayerName)) { DebugLog(DebugCategory.Points, "Pontuação ignorada para admin: " + entry.PlayerName + " motivo=" + reason); return; } bool flag = IsDiscordTop3WebhookEnabled(); Dictionary oldRanks = ((IsServerInstance() && flag) ? GetRankingSnapshot() : new Dictionary(StringComparer.OrdinalIgnoreCase)); entry.Points = Mathf.Clamp(entry.Points + amount, -2147483647, int.MaxValue); entry.LastReason = SafeReason(reason); entry.LastUpdateUtc = DateTime.UtcNow.ToString("O"); RegisterRankingEntry(entry); Dictionary newRanks = ((IsServerInstance() && flag) ? GetRankingSnapshot() : new Dictionary(StringComparer.OrdinalIgnoreCase)); DebugLog(DebugCategory.Points, "Pontuação alterada: player=" + entry.PlayerName + " amount=" + amount + " total=" + entry.Points + " motivo=" + entry.LastReason); if (IsServerInstance()) { if (flag) { TrySendTop3DiscordWebhooks(oldRanks, newRanks, entry.LastReason); } SaveRankingEntry(entry); } } private bool IsDiscordTop3WebhookEnabled() { return IsServerInstance() && _cfgDiscordTop3WebhookEnabled != null && _cfgDiscordTop3WebhookEnabled.Value && _cfgDiscordTop3WebhookUrl != null && !string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value); } private void TrySendTop3DiscordWebhooks(Dictionary oldRanks, Dictionary newRanks, string reason) { try { if (!IsServerInstance() || _cfgDiscordTop3WebhookEnabled == null || !_cfgDiscordTop3WebhookEnabled.Value || _cfgDiscordTop3WebhookUrl == null || string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value)) { return; } if (oldRanks == null) { oldRanks = new Dictionary(StringComparer.OrdinalIgnoreCase); } if (newRanks == null) { newRanks = new Dictionary(StringComparer.OrdinalIgnoreCase); } HashSet hashSet = new HashSet(oldRanks.Keys, StringComparer.OrdinalIgnoreCase); foreach (string key in newRanks.Keys) { hashSet.Add(key); } foreach (string item in hashSet) { if (string.IsNullOrWhiteSpace(item)) { continue; } int value; int num = (oldRanks.TryGetValue(item, out value) ? value : int.MaxValue); int value2; int num2 = (newRanks.TryGetValue(item, out value2) ? value2 : int.MaxValue); bool flag = num > 3 && num2 >= 1 && num2 <= 3; bool flag2 = num >= 1 && num <= 3 && num2 > 3; bool flag3 = num >= 1 && num <= 3 && num2 >= 1 && num2 <= 3 && num2 > num; if (flag || flag2 || flag3) { int points = FindRankingEntryByName(item)?.Points ?? 0; if (flag) { SendDiscordTop3EnteredWebhook(item, num2, points, reason); } else if (flag2) { SendDiscordTop3LostWebhook(item, num, num2, points, reason); } else if (flag3) { SendDiscordTop3DroppedWebhook(item, num, num2, points, reason); } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Erro ao preparar webhook Top 3 do Discord: " + ex.Message)); } } private RankRewardInfo GetRankRewardInfo(int rank) { return rank switch { 1 => new RankRewardInfo { Rank = 1, MinPoints = Mathf.Max(0, _rules.RewardTop1MinPoints), Label = SafeLimit(_rules.RewardTop1Label, 96), PrefabName = SafeLimit(_rules.RewardTop1Prefab, 96), Amount = Mathf.Max(0, _rules.RewardTop1Amount) }, 2 => new RankRewardInfo { Rank = 2, MinPoints = Mathf.Max(0, _rules.RewardTop2MinPoints), Label = SafeLimit(_rules.RewardTop2Label, 96), PrefabName = SafeLimit(_rules.RewardTop2Prefab, 96), Amount = Mathf.Max(0, _rules.RewardTop2Amount) }, 3 => new RankRewardInfo { Rank = 3, MinPoints = Mathf.Max(0, _rules.RewardTop3MinPoints), Label = SafeLimit(_rules.RewardTop3Label, 96), PrefabName = SafeLimit(_rules.RewardTop3Prefab, 96), Amount = Mathf.Max(0, _rules.RewardTop3Amount) }, _ => null, }; } private string BuildRewardClaimKey(string cycleId, string playerName, int rank) { return SafeLimit(cycleId, 64) + "|" + SanitizePlayerName(playerName) + "|" + Mathf.Clamp(rank, 0, int.MaxValue); } private string BuildPendingRewardKey(long sender, string cycleId, int rank) { return sender + "|" + SafeLimit(cycleId, 64) + "|" + Mathf.Clamp(rank, 0, int.MaxValue); } private bool HasClaimedReward(string cycleId, string playerName, int rank) { if (_database == null || _database.Claims == null) { return false; } string key = BuildRewardClaimKey(cycleId, playerName, rank); return _database.Claims.Any((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase) && string.Equals(SafeLimit(x.Status, 16), "claimed", StringComparison.OrdinalIgnoreCase)); } private bool HasPendingRewardClaim(string cycleId, string playerName, int rank) { if (_database == null || _database.Claims == null) { return false; } string key = BuildRewardClaimKey(cycleId, playerName, rank); return _database.Claims.Any((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase) && string.Equals(SafeLimit(x.Status, 16), "pending", StringComparison.OrdinalIgnoreCase)); } private void ReserveRewardClaim(string cycleId, string playerName, int rank) { if (_database == null) { _database = new RankingDatabase(); } if (_database.Claims == null) { _database.Claims = new List(); } string cycleId2 = SafeLimit(cycleId, 64); string playerName2 = SanitizePlayerName(playerName); int rank2 = Mathf.Clamp(rank, 1, int.MaxValue); string key = BuildRewardClaimKey(cycleId2, playerName2, rank2); RewardClaimRecord rewardClaimRecord = _database.Claims.FirstOrDefault((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase)); if (rewardClaimRecord != null) { rewardClaimRecord.Status = "pending"; rewardClaimRecord.ClaimedAtUtc = ""; return; } _database.Claims.Add(new RewardClaimRecord { CycleId = cycleId2, PlayerName = playerName2, Rank = rank2, ClaimedAtUtc = "", Status = "pending" }); } private void ClearPendingRewardClaim(string cycleId, string playerName, int rank) { if (_database != null && _database.Claims != null) { string key = BuildRewardClaimKey(cycleId, playerName, rank); _database.Claims.RemoveAll((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase) && string.Equals(SafeLimit(x.Status, 16), "pending", StringComparison.OrdinalIgnoreCase)); } } private void MarkRewardClaimed(string cycleId, string playerName, int rank) { if (_database == null) { _database = new RankingDatabase(); } if (_database.Claims == null) { _database.Claims = new List(); } string cycleId2 = SafeLimit(cycleId, 64); string playerName2 = SanitizePlayerName(playerName); int rank2 = Mathf.Clamp(rank, 1, int.MaxValue); string key = BuildRewardClaimKey(cycleId2, playerName2, rank2); RewardClaimRecord rewardClaimRecord = _database.Claims.FirstOrDefault((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase)); if (rewardClaimRecord != null) { rewardClaimRecord.Status = "claimed"; rewardClaimRecord.ClaimedAtUtc = DateTime.UtcNow.ToString("O"); return; } _database.Claims.Add(new RewardClaimRecord { CycleId = cycleId2, PlayerName = playerName2, Rank = rank2, ClaimedAtUtc = DateTime.UtcNow.ToString("O"), Status = "claimed" }); } private void FillRewardSnapshotData(SnapshotPlayerData playerData) { if (playerData == null) { return; } playerData.RewardClaimsEnabled = _rules != null && _rules.RewardClaimsEnabled; playerData.RewardCanClaim = false; playerData.RewardAlreadyClaimed = false; playerData.RewardRank = Mathf.Max(0, playerData.Position); playerData.RewardMinPoints = 0; playerData.RewardLabel = ""; playerData.RewardPrefabName = ""; playerData.RewardAmount = 0; playerData.RewardBlockReason = ""; playerData.RewardClaimCycleId = ((_rules != null) ? SafeLimit(_rules.RewardClaimCycleId, 64) : ""); if (_rules == null || !_rules.RewardClaimsEnabled) { playerData.RewardBlockReason = "Resgate de recompensa desativado."; return; } if (!playerData.HasData) { playerData.RewardBlockReason = "Você ainda não possui pontuação no ranking."; return; } RankRewardInfo rankRewardInfo = GetRankRewardInfo(playerData.Position); if (rankRewardInfo == null) { playerData.RewardBlockReason = "Somente Top 1, 2 e 3 podem resgatar."; return; } playerData.RewardRank = rankRewardInfo.Rank; playerData.RewardMinPoints = rankRewardInfo.MinPoints; playerData.RewardLabel = rankRewardInfo.Label ?? ""; playerData.RewardPrefabName = rankRewardInfo.PrefabName ?? ""; playerData.RewardAmount = Mathf.Max(0, rankRewardInfo.Amount); if (string.IsNullOrWhiteSpace(playerData.RewardClaimCycleId)) { playerData.RewardBlockReason = "Ciclo de recompensa não configurado."; } else if (string.IsNullOrWhiteSpace(playerData.RewardPrefabName) || playerData.RewardAmount <= 0) { playerData.RewardBlockReason = "Recompensa não configurada para esta posição."; } else if (playerData.Points < rankRewardInfo.MinPoints) { playerData.RewardBlockReason = "Requer " + rankRewardInfo.MinPoints + " pontos para resgatar."; } else if (HasClaimedReward(playerData.RewardClaimCycleId, playerData.PlayerName, rankRewardInfo.Rank)) { playerData.RewardAlreadyClaimed = true; playerData.RewardBlockReason = "Recompensa já resgatada."; } else { playerData.RewardCanClaim = true; playerData.RewardBlockReason = "Recompensa disponível para resgate."; } } private bool TryAddRewardItemToLocalInventory(string prefabName, int amount, out string message) { message = ""; try { if ((Object)(object)Player.m_localPlayer == (Object)null) { message = "Jogador local não encontrado."; return false; } if (string.IsNullOrWhiteSpace(prefabName)) { message = "Prefab da recompensa não configurado."; return false; } if (amount <= 0) { message = "Quantidade de recompensa inválida."; return false; } GameObject val = null; if ((Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(prefabName); } if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(prefabName); } if ((Object)(object)val == (Object)null) { message = "Prefab '" + prefabName + "' não encontrado."; return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { message = "Prefab '" + prefabName + "' não é um item válido."; return false; } Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); if (inventory == null) { message = "Inventário não encontrado."; return false; } int num = Mathf.Max(0, amount); int num2 = 0; int num3 = 0; int num4 = 1; int num5 = 0; int num6 = 1; string text = prefabName; if (component.m_itemData != null) { num4 = Mathf.Max(1, component.m_itemData.m_quality); num5 = Mathf.Max(0, component.m_itemData.m_variant); if (component.m_itemData.m_shared != null) { num6 = Mathf.Max(1, component.m_itemData.m_shared.m_maxStackSize); if (!string.IsNullOrWhiteSpace(component.m_itemData.m_shared.m_name)) { text = component.m_itemData.m_shared.m_name; } } } while (num > 0) { int num7 = Mathf.Min(num6, num); ItemData val2 = inventory.AddItem(prefabName, num7, num4, num5, 0L, "", false); if (val2 == null) { break; } num2 += num7; num -= num7; } if (num > 0) { num3 = DropExchangeItemOverflowNearPlayer(val, component, num, num6, num4, num5); num -= num3; } if (num > 0) { message = "Não foi possível entregar toda a recompensa."; return false; } try { if (num3 > 0 && num2 > 0) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Recompensa recebida: " + text + " x" + num2 + " no inventário, x" + num3 + " no chão.", 0, (Sprite)null); } else if (num3 > 0) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Inventário cheio. Recompensa caiu no chão: " + text + " x" + num3 + ".", 0, (Sprite)null); } else { ((Character)Player.m_localPlayer).Message((MessageType)2, "Recompensa recebida: " + text + " x" + num2 + ".", 0, (Sprite)null); } } catch { } if (num3 > 0 && num2 > 0) { message = "Parte da recompensa foi para o inventário e o restante caiu no chão."; } else if (num3 > 0) { message = "Inventário cheio: a recompensa caiu no chão."; } else { message = "Recompensa adicionada ao inventário."; } return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao entregar recompensa: " + ex)); message = "Erro ao entregar recompensa."; return false; } } private bool TryAddExchangeItemToLocalInventoryOrDropOverflow(string prefabName, int amount, out string message) { message = ""; try { if ((Object)(object)Player.m_localPlayer == (Object)null) { message = "Jogador local não encontrado."; return false; } if (string.IsNullOrWhiteSpace(prefabName)) { message = "Prefab do câmbio não configurado."; return false; } if (amount <= 0) { message = "Quantidade de câmbio inválida."; return false; } GameObject val = null; if ((Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(prefabName); } if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(prefabName); } if ((Object)(object)val == (Object)null) { message = "Prefab '" + prefabName + "' não encontrado."; return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { message = "Prefab '" + prefabName + "' não é um item válido."; return false; } Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); if (inventory == null) { message = "Inventário não encontrado."; return false; } int num = Mathf.Max(0, amount); int num2 = 0; int num3 = 0; int num4 = 1; int num5 = 0; int num6 = 1; string text = prefabName; if (component.m_itemData != null) { num4 = Mathf.Max(1, component.m_itemData.m_quality); num5 = Mathf.Max(0, component.m_itemData.m_variant); if (component.m_itemData.m_shared != null) { num6 = Mathf.Max(1, component.m_itemData.m_shared.m_maxStackSize); if (!string.IsNullOrWhiteSpace(component.m_itemData.m_shared.m_name)) { text = component.m_itemData.m_shared.m_name; } } } while (num > 0) { int num7 = Mathf.Min(num6, num); ItemData val2 = inventory.AddItem(prefabName, num7, num4, num5, 0L, "", false); if (val2 == null) { break; } num2 += num7; num -= num7; } if (num > 0) { num3 = DropExchangeItemOverflowNearPlayer(val, component, num, num6, num4, num5); num -= num3; } if (num > 0) { message = "Não foi possível entregar todo o câmbio."; return false; } try { if (num3 > 0 && num2 > 0) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Câmbio recebido: " + text + " x" + num2 + " no inventário, x" + num3 + " no chão.", 0, (Sprite)null); } else if (num3 > 0) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Inventário cheio. Câmbio caiu no chão: " + text + " x" + num3 + ".", 0, (Sprite)null); } else { ((Character)Player.m_localPlayer).Message((MessageType)2, "Câmbio recebido: " + text + " x" + num2 + ".", 0, (Sprite)null); } } catch { } if (num3 > 0 && num2 > 0) { message = "Parte foi para o inventário e o restante caiu no chão."; } else if (num3 > 0) { message = "Inventário cheio: as moedas caíram no chão."; } else { message = "Moedas adicionadas ao inventário."; } return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao entregar moedas do câmbio: " + ex)); message = "Erro ao entregar moedas do câmbio."; return false; } } private int DropExchangeItemOverflowNearPlayer(GameObject itemPrefab, ItemDrop sourceDrop, int amount, int maxStackSize, int quality, int variant) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)itemPrefab == (Object)null || amount <= 0) { return 0; } int num = amount; int num2 = 0; Transform transform = ((Component)Player.m_localPlayer).transform; Vector3 val = transform.position + transform.forward * 1.25f + Vector3.up * 0.65f; Vector3 val2 = default(Vector3); while (num > 0) { int num3 = Mathf.Min(Mathf.Max(1, maxStackSize), num); ((Vector3)(ref val2))..ctor(Random.Range(-0.35f, 0.35f), 0f, Random.Range(-0.35f, 0.35f)); GameObject val3 = Object.Instantiate(itemPrefab, val + val2, Quaternion.identity); ItemDrop val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent() : null); if ((Object)(object)val4 != (Object)null && val4.m_itemData != null) { val4.m_itemData.m_stack = num3; val4.m_itemData.m_quality = Mathf.Max(1, quality); val4.m_itemData.m_variant = Mathf.Max(0, variant); } Rigidbody val5 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent() : null); if ((Object)(object)val5 != (Object)null) { val5.linearVelocity = transform.forward * 1.5f + Vector3.up * 1.25f; } num2 += num3; num -= num3; } return num2; } private void RegisterRpcsIfNeeded() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && (!_rpcsRegistered || _registeredRoutedRpcInstance != instance)) { instance.Register("glitnir.ranking.requestsnapshot", (Action)RPC_RequestSnapshot); instance.Register("glitnir.ranking.receivesnapshot", (Action)RPC_ReceiveSnapshot); instance.Register("glitnir.ranking.reportkill", (Action)RPC_ReportKill); instance.Register("glitnir.ranking.reportskillgain", (Action)RPC_ReportSkillGain); instance.Register("glitnir.ranking.requestrewardclaim", (Action)RPC_RequestRewardClaim); instance.Register("glitnir.ranking.grantrewarditem", (Action)RPC_GrantRewardItem); instance.Register("glitnir.ranking.finalizerewardclaim", (Action)RPC_FinalizeRewardClaim); instance.Register("glitnir.ranking.rewardclaimfeedback", (Action)RPC_RewardClaimFeedback); instance.Register("glitnir.ranking.requestpointsexchange", (Action)RPC_RequestPointsExchange); instance.Register("glitnir.ranking.grantexchangecoins", (Action)RPC_GrantExchangeCoins); instance.Register("glitnir.ranking.finalizepointsexchange", (Action)RPC_FinalizePointsExchange); instance.Register("glitnir.ranking.pointsexchangefeedback", (Action)RPC_PointsExchangeFeedback); instance.Register("glitnir.ranking.marketquestcomplete", (Action)RPC_ReportMarketplaceQuestComplete); instance.Register("glitnir.ranking.reportfishcaught", (Action)RPC_ReportFishCaught); instance.Register("glitnir.ranking.reportcrafteditem", (Action)RPC_ReportCraftedItem); instance.Register("glitnir.ranking.reportfarmharvest", (Action)RPC_ReportFarmHarvest); instance.Register("glitnir.ranking.reportplayerdeath", (Action)RPC_ReportPlayerDeath); instance.Register("glitnir.ranking.reportexplorationmap", (Action)RPC_ReportExplorationMap); _registeredRoutedRpcInstance = instance; _rpcsRegistered = true; } } private void Awake() { //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _rulesFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; _uiFolderPath = string.Empty; _uiFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; _databaseFilePath = Path.Combine(Paths.ConfigPath, "GlitnirRanking", "SavedData", "DB.db"); _legacyDatabaseFilePath = Path.Combine(Paths.ConfigPath, "glitnir.ranking.database.legacy.db"); if (IsDedicatedServerInstance()) { EnsureDatabaseDirectoryExists(); } LoadUiSettings(); _cfgDiscordTop3WebhookEnabled = ((BaseUnityPlugin)this).Config.Bind("Discord", "EnableTop3Webhook", true, "Ativa o webhook do Discord quando um jogador entra no Top 3 do ranking."); _cfgDiscordTop3WebhookUrl = ((BaseUnityPlugin)this).Config.Bind("Discord", "Top3WebhookUrl", "", "URL do webhook do Discord para anunciar ascensão ao Top 3."); _cfgIgnoreAdminsInRanking = ((BaseUnityPlugin)this).Config.Bind("Ranking", "IgnoreAdminsInRanking", true, "Se ativo, jogadores presentes na adminlist do servidor nao pontuam nem aparecem no ranking."); _cfgIgnoredAdminNames = ((BaseUnityPlugin)this).Config.Bind("Ranking", "IgnoredAdminNames", "", "Fallback opcional: nomes ou IDs de admins para ignorar. Separe por virgula, ponto e virgula ou quebra de linha."); InitializeSyncedRulesConfig(); SetupRulesConfigWatcher(); if (IsDedicatedServerInstance()) { LoadDatabase(); } else { _database = new RankingDatabase(); } _harmony = new Harmony("com.glitnir.ranking"); _harmony.PatchAll(); try { GlitnirAAACraftMaxClamp.Apply(_harmony); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Glitnir Ranking] Falha ao aplicar AAA Craft Max Clamp: " + ex.Message)); } } private void OnDestroy() { try { if (IsDedicatedServerInstance()) { SaveDatabase(force: true); } try { if (_rulesConfigWatcher != null) { _rulesConfigWatcher.EnableRaisingEvents = false; _rulesConfigWatcher.Dispose(); _rulesConfigWatcher = null; } } catch { } DestroyRankingInputBlocker(); SaveUiSettings(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } } private void Update() { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) RegisterRpcsIfNeeded(); ProcessRulesConfigReloadIfNeeded(); RefreshRulesFromSyncedConfigIfNeeded(); if (!Application.isBatchMode) { CleanupOldLocalRecentHits(); CheckInitialServerSnapshotRequest(); } if (Application.isBatchMode) { CleanupOldProcessedKills(); FlushPendingDatabaseSaveIfDue(); return; } TickUnityRankingHud(); bool rankingHudToggleQueued = _rankingHudToggleQueued; _rankingHudToggleQueued = false; if (rankingHudToggleQueued || ((int)_uiToggleKey != 0 && Input.GetKeyDown(_uiToggleKey)) || Input.GetKeyDown((KeyCode)121)) { try { ToggleRankingHud(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Glitnir Ranking] Falha no toggle do ranking: " + ex)); } } if (_hudVisible) { if (Input.GetKeyDown((KeyCode)27)) { CloseRankingHudAndCollapseAll(); return; } _clientRefreshTimer += Time.unscaledDeltaTime; if (_clientRefreshTimer >= 300f) { _clientRefreshTimer = 0f; RequestSnapshotFromServer(); } ProcessLocalExplorationMapReport(); } else { SetRankingInputBlockerVisible(visible: false); } if (IsServerInstance()) { CleanupOldProcessedKills(); FlushPendingDatabaseSaveIfDue(); } } private bool IsServerInstance() { if (Application.isBatchMode) { return true; } try { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } } catch { } return false; } private bool IsDedicatedServerInstance() { return Application.isBatchMode; } private bool IsConnectedToDedicatedServer() { return !Application.isBatchMode && GetServerPeerUid() != 0; } public void ReportLocalMarketplaceQuestCompletion(int questUid) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown try { if (_rules != null && _rules.RankingEnabled && _rules.EnableMarketplaceQuestPoints && TryGetMarketplaceQuestRule(questUid, out var rule) && !((Object)(object)Player.m_localPlayer == (Object)null) && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(questUid); val.Write(SafeMarketplaceQuestKey(rule.QuestKey)); val.Write(rule.Points); val.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(GetServerPeerUid(), "glitnir.ranking.marketquestcomplete", new object[1] { val }); DebugLog(DebugCategory.Points, "Quest Marketplace reportada: uid=" + questUid + " key=" + rule.QuestKey + " points=" + rule.Points); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reportar conclusão de quest do Marketplace: " + ex)); } } private void RPC_ReportMarketplaceQuestComplete(long sender, ZPackage pkg) { try { if (!IsServerInstance() || pkg == null) { return; } int questUid = pkg.ReadInt(); string questKey = SafeMarketplaceQuestKey(pkg.ReadString()); int requestedPoints = Mathf.Clamp(pkg.ReadInt(), -2147483647, int.MaxValue); string text = SanitizePlayerName(pkg.ReadString()); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { text2 = text; } if (!string.IsNullOrWhiteSpace(text2)) { if (ShouldIgnoreSenderForRanking(sender, text2)) { DebugLog(DebugCategory.Points, "Quest Marketplace ignorada para admin: " + text2); } else { ProcessMarketplaceQuestReport(text2, questUid, questKey, requestedPoints, "rpc-marketplace-quest"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportMarketplaceQuestComplete: " + ex)); } } private void ProcessMarketplaceQuestReport(string playerName, int questUid, string questKey, int requestedPoints, string source) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled || !_rules.EnableMarketplaceQuestPoints) { return; } if (!TryGetMarketplaceQuestRule(questUid, out var rule)) { DebugLog(DebugCategory.Points, "Quest do Marketplace ignorada sem regra: uid=" + questUid + " key=" + questKey + " source=" + source); return; } if (!string.IsNullOrWhiteSpace(questKey) && !string.Equals(rule.QuestKey, questKey, StringComparison.OrdinalIgnoreCase)) { DebugLog(DebugCategory.Points, "Quest do Marketplace rejeitada por chave divergente: uid=" + questUid + " key=" + questKey + " esperado=" + rule.QuestKey); return; } playerName = SanitizePlayerName(playerName); if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Quest Marketplace ignorada para admin: " + playerName); return; } if (HasMarketplaceQuestCredit(playerName, rule.QuestKey)) { DebugLog(DebugCategory.Points, "Quest Marketplace já creditada: player=" + playerName + " key=" + rule.QuestKey); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry != null) { int points = rule.Points; if (requestedPoints != points) { DebugLog(DebugCategory.Points, "Quest Marketplace com pontos normalizados: player=" + playerName + " key=" + rule.QuestKey + " solicitado=" + requestedPoints + " configurado=" + points); } ApplyPointsToEntry(orCreateEntry, points, "Quest Marketplace: " + rule.QuestKey); MarkMarketplaceQuestCredit(playerName, rule.QuestKey); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar conclusão de quest do Marketplace: " + ex)); } } private string BuildMarketplaceQuestCreditKey(string playerName, string questKey) { return SanitizePlayerName(playerName).ToLowerInvariant() + "|" + SafeMarketplaceQuestKey(questKey); } private bool HasMarketplaceQuestCredit(string playerName, string questKey) { return HasMarketplaceQuestCreditCached(playerName, questKey); } private void MarkMarketplaceQuestCredit(string playerName, string questKey) { if (_database == null) { _database = new RankingDatabase(); } if (_database.MarketplaceQuestCredits == null) { _database.MarketplaceQuestCredits = new List(); } if (!HasMarketplaceQuestCredit(playerName, questKey)) { MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = new MarketplaceQuestCreditRecord { PlayerName = SanitizePlayerName(playerName), QuestKey = SafeMarketplaceQuestKey(questKey), GrantedAtUtc = DateTime.UtcNow.ToString("O") }; _database.MarketplaceQuestCredits.Add(marketplaceQuestCreditRecord); RebuildDatabaseIndexes(); if (IsServerInstance()) { SaveMarketplaceQuestCredit(marketplaceQuestCreditRecord); } } } private void ProcessLocalExplorationMapReport() { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown try { if (_rules == null || !_rules.RankingEnabled || !_rules.EnableExplorationJackpots || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Minimap.instance == (Object)null) { return; } _explorationReportTimer += Time.unscaledDeltaTime; if (_explorationReportTimer < 180f) { return; } _explorationReportTimer = 0f; float localMapExplorationPercent = GetLocalMapExplorationPercent(); if (!(localMapExplorationPercent < 0f) && !(localMapExplorationPercent <= _lastReportedMapExplorePercent + 0.05f)) { _lastReportedMapExplorePercent = localMapExplorationPercent; long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0L && _rpcsRegistered && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(localMapExplorationPercent); val.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportexplorationmap", new object[1] { val }); DebugLog(DebugCategory.Points, "Exploração de mapa reportada: " + localMapExplorationPercent + "%"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao reportar exploração de mapa: " + ex.Message)); } } private float GetLocalMapExplorationPercent() { try { Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { return -1f; } if (_minimapExploredField == null) { _minimapExploredField = AccessTools.Field(typeof(Minimap), "m_explored"); } if (_minimapExploredField == null) { return -1f; } if (!(_minimapExploredField.GetValue(instance) is bool[] array) || array.Length == 0) { return -1f; } int num = 0; for (int i = 0; i < array.Length; i++) { if (array[i]) { num++; } } float num2 = (float)num * 100f / (float)Mathf.Max(1, array.Length); return Mathf.Clamp(num2, 0f, 100f); } catch { return -1f; } } private void RPC_ReportExplorationMap(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { float percent = Mathf.Clamp(pkg.ReadSingle(), 0f, 100f); string text = SanitizePlayerName(pkg.ReadString()); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { text2 = text; } ProcessExplorationMapReport(text2, percent); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportExplorationMap: " + ex)); } } private void ProcessExplorationMapReport(string playerName, float percent) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled || !_rules.EnableExplorationJackpots) { return; } playerName = SanitizePlayerName(playerName); if (string.IsNullOrWhiteSpace(playerName) || ShouldIgnorePlayerForRanking(playerName) || _rules.ExplorationMapJackpots == null || _rules.ExplorationMapJackpots.Count == 0) { return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry == null) { return; } if (orCreateEntry.ProgressCounters == null) { orCreateEntry.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } if (orCreateEntry.GenericJackpotCredits == null) { orCreateEntry.GenericJackpotCredits = new HashSet(StringComparer.OrdinalIgnoreCase); } if (orCreateEntry.FloatProgressCounters == null) { orCreateEntry.FloatProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } float num = 0f; int value = 0; if (orCreateEntry.FloatProgressCounters.TryGetValue("ExplorationMap:Percent", out var value2)) { num = value2; } else if (orCreateEntry.ProgressCounters.TryGetValue("ExplorationMap:Percent", out value)) { num = value; } float num2 = Mathf.Clamp(Mathf.Max(num, percent), 0f, 100f); int value3 = Mathf.Clamp(Mathf.FloorToInt(num2), 0, 100); orCreateEntry.FloatProgressCounters["ExplorationMap:Percent"] = num2; orCreateEntry.FloatProgressCounters["Exploration:Map"] = num2; orCreateEntry.FloatProgressCounters["Exploracao:Mapa"] = num2; orCreateEntry.ProgressCounters["ExplorationMap:Percent"] = value3; orCreateEntry.ProgressCounters["Exploration:Map"] = value3; orCreateEntry.ProgressCounters["Exploracao:Mapa"] = value3; string text = SafeLimit(_rules.RewardClaimCycleId, 64); foreach (KeyValuePair item2 in _rules.ExplorationMapJackpots.OrderBy((KeyValuePair p) => p.Key)) { int num3 = Mathf.Clamp(item2.Key, 1, 100); int num4 = Mathf.Max(0, item2.Value); if (!(num2 < (float)num3) && num4 > 0) { string item = text + ":ExplorationMap:" + num3; if (!orCreateEntry.GenericJackpotCredits.Contains(item)) { orCreateEntry.GenericJackpotCredits.Add(item); ApplyPointsToEntry(orCreateEntry, num4, "Exploração do mapa: " + num3 + "%"); orCreateEntry.ExplorationMapJackpotPointsTotal = Mathf.Clamp(orCreateEntry.ExplorationMapJackpotPointsTotal + num4, 0, int.MaxValue); } } } SaveRankingEntry(orCreateEntry); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar exploração de mapa: " + ex)); } } private void EnsureUiDirectoryExists() { } private void MigrateLegacyUiSettingsIfNeeded() { try { string text = Path.Combine(Paths.ConfigPath, "glitnir.ranking.ui.cfg"); if (!string.IsNullOrWhiteSpace(_uiFilePath) && !string.Equals(text, _uiFilePath, StringComparison.OrdinalIgnoreCase) && !File.Exists(_uiFilePath) && File.Exists(text)) { File.Copy(text, _uiFilePath, overwrite: true); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao migrar configuração antiga da UI do ranking: " + ex)); } } private void LoadUiSettings() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) try { _uiToggleKey = ((BaseUnityPlugin)this).Config.Bind("UI", "HudShortcutKey", _uiToggleKey, "Tecla para abrir/fechar o HUD. Use nomes do Unity KeyCode, ex.: F, G, Alpha1, None.").Value; ConfigEntry val = ((BaseUnityPlugin)this).Config.Bind("UI", "IconX", ((Rect)(ref _iconRect)).x, "Posição X do botão do ranking."); ConfigEntry val2 = ((BaseUnityPlugin)this).Config.Bind("UI", "IconY", ((Rect)(ref _iconRect)).y, "Posição Y do botão do ranking."); ConfigEntry val3 = ((BaseUnityPlugin)this).Config.Bind("UI", "IconSize", ((Rect)(ref _iconRect)).width, "Tamanho do botão do ranking."); if (Mathf.Abs(val.Value - 24f) < 0.01f && Mathf.Abs(val2.Value - 180f) < 0.01f && Mathf.Abs(val3.Value - 56f) < 0.01f) { val.Value = ((Rect)(ref _iconRect)).x; val2.Value = ((Rect)(ref _iconRect)).y; val3.Value = ((Rect)(ref _iconRect)).width; ((BaseUnityPlugin)this).Config.Save(); } ((Rect)(ref _iconRect)).x = val.Value; ((Rect)(ref _iconRect)).y = val2.Value; float value = val3.Value; ((Rect)(ref _iconRect)).width = Mathf.Clamp(value, 36f, 120f); ((Rect)(ref _iconRect)).height = ((Rect)(ref _iconRect)).width; _uiIconZoom = Mathf.Clamp(((BaseUnityPlugin)this).Config.Bind("UI", "IconZoom", _uiIconZoom, "Zoom interno do ícone do botão.").Value, 0.75f, 1.3f); ((Rect)(ref _windowRect)).x = ((BaseUnityPlugin)this).Config.Bind("UI", "WindowX", ((Rect)(ref _windowRect)).x, "Posição X da janela do ranking.").Value; ((Rect)(ref _windowRect)).y = ((BaseUnityPlugin)this).Config.Bind("UI", "WindowY", ((Rect)(ref _windowRect)).y, "Posição Y da janela do ranking.").Value; ((Rect)(ref _windowRect)).width = Mathf.Max(420f, ((BaseUnityPlugin)this).Config.Bind("UI", "WindowW", ((Rect)(ref _windowRect)).width, "Largura da janela do ranking.").Value); ((Rect)(ref _windowRect)).height = Mathf.Max(760f, ((BaseUnityPlugin)this).Config.Bind("UI", "WindowH", ((Rect)(ref _windowRect)).height, "Altura da janela do ranking.").Value); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao carregar UI do ranking: " + ex)); } } private void SaveUiSettings() { try { ((BaseUnityPlugin)this).Config.Save(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao salvar configuração do ranking: " + ex)); } } private Texture2D LoadTextureFromUiFolder(params string[] fileNames) { if (fileNames == null || fileNames.Length == 0) { return null; } foreach (string text in fileNames) { if (!string.IsNullOrWhiteSpace(text)) { Texture2D val = LoadTextureFromUiFolder(text); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private Texture2D LoadTextureFromUiFolder(string fileName) { try { if (!string.IsNullOrWhiteSpace(_uiFolderPath)) { string text = Path.Combine(_uiFolderPath, fileName); if (File.Exists(text)) { byte[] array = File.ReadAllBytes(text); if (array == null || array.Length == 0) { if (_missingUiTextureWarnings.Add(fileName + ":empty")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Arquivo vazio ou inválido: " + text)); } return null; } Texture2D val = CreateTextureFromBytes(array, fileName); if ((Object)(object)val != (Object)null) { return val; } if (_missingUiTextureWarnings.Add(fileName + ":decoder")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Não foi possível decodificar a textura: " + text + ". Verifique se o PNG/JPG é válido.")); } } } } catch (Exception ex) { if (_missingUiTextureWarnings.Add(fileName + ":exception")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Falha ao carregar textura " + fileName + ": " + ex.Message)); } } return LoadEmbeddedTexture(fileName); } private Texture2D LoadEmbeddedTexture(string fileName) { if (string.IsNullOrWhiteSpace(fileName)) { return null; } try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); if (executingAssembly == null) { return null; } string[] manifestResourceNames = executingAssembly.GetManifestResourceNames(); if (manifestResourceNames == null || manifestResourceNames.Length == 0) { return null; } string normalizedFileName = fileName.Replace('/', '.').Replace('\\', '.'); StringComparison comparison = StringComparison.OrdinalIgnoreCase; string text = manifestResourceNames.FirstOrDefault((string name) => !string.IsNullOrWhiteSpace(name) && (name.Equals(normalizedFileName, comparison) || name.EndsWith("." + normalizedFileName, comparison) || name.EndsWith(fileName, comparison) || name.IndexOf(normalizedFileName, comparison) >= 0)); if (string.IsNullOrWhiteSpace(text)) { return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { return null; } byte[] array = ReadAllBytes(stream); if (array == null || array.Length == 0) { return null; } Texture2D val = CreateTextureFromBytes(array, text); if ((Object)(object)val != (Object)null) { return val; } } catch (Exception ex) { if (_missingUiTextureWarnings.Add(fileName + ":embeddedexception")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Falha ao carregar textura embutida " + fileName + ": " + ex.Message)); } } return null; } private Texture2D CreateTextureFromBytes(byte[] bytes, string textureName) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if (bytes == null || bytes.Length == 0) { return null; } Texture2D val = null; try { val = new Texture2D(2, 2, (TextureFormat)5, false); ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; ((Object)val).name = textureName; if (TryLoadTextureBytes(val, bytes)) { return val; } } catch (Exception ex) { if (_missingUiTextureWarnings.Add((textureName ?? "texture") + ":createtextureexception")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Falha ao criar textura " + textureName + ": " + ex.Message)); } } if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } return null; } private byte[] ReadAllBytes(Stream stream) { if (stream == null) { return null; } if (stream is MemoryStream memoryStream) { return memoryStream.ToArray(); } using MemoryStream memoryStream2 = new MemoryStream(); stream.CopyTo(memoryStream2); return memoryStream2.ToArray(); } private bool TryLoadTextureBytes(Texture2D texture, byte[] bytes) { if ((Object)(object)texture == (Object)null || bytes == null || bytes.Length == 0) { return false; } try { Type type = FindTypeInLoadedAssemblies("UnityEngine.ImageConversion"); if (type != null) { MethodInfo method = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Texture2D), typeof(byte[]) }, null); if (method != null) { if (!(method.Invoke(null, new object[2] { texture, bytes }) is bool result)) { return true; } return result; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Falha no carregamento refletido da textura: " + ex.Message)); } return false; } private Type FindTypeInLoadedAssemblies(string fullTypeName) { if (string.IsNullOrWhiteSpace(fullTypeName)) { return null; } try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!(assembly == null)) { Type type = assembly.GetType(fullTypeName, throwOnError: false); if (type != null) { return type; } } } } catch { } return null; } private Texture2D CreateSolidTexture(Color color) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_000e: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 1, (TextureFormat)5, false); val.SetPixel(0, 0, color); val.Apply(false, true); ((Texture)val).wrapMode = (TextureWrapMode)1; return val; } private void EnsureUiTexturesLoaded() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_uiWhiteTexture == (Object)null) { _uiWhiteTexture = CreateSolidTexture(Color.white); } if ((Object)(object)_uiTransparentTexture == (Object)null) { _uiTransparentTexture = CreateSolidTexture(new Color(0f, 0f, 0f, 0f)); } if ((Object)(object)_uiPanelTexture == (Object)null) { _uiPanelTexture = CreateSolidTexture(new Color(0.075f, 0.045f, 0.018f, 0.92f)); } if ((Object)(object)_uiBackgroundTexture == (Object)null) { _uiBackgroundTexture = CreateSolidTexture(new Color(0.018f, 0.015f, 0.012f, 0.96f)); } if ((Object)(object)_uiRankingIconTexture == (Object)null) { _uiRankingIconTexture = LoadTextureFromUiFolder("icon_ranking.png", "ranking_icon.png", "RankingIcon.png") ?? CreateRankingShortcutIconTexture(); } } private Texture2D CreateRankingShortcutIconTexture() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(64, 64, (TextureFormat)5, false); ((Object)val).name = "GlitnirRankingShortcutIcon"; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; Color val2 = default(Color); ((Color)(ref val2))..ctor(0f, 0f, 0f, 0f); Color color = default(Color); ((Color)(ref color))..ctor(0.9f, 0.92f, 0.86f, 0.98f); Color color2 = default(Color); ((Color)(ref color2))..ctor(0.2f, 0.14f, 0.09f, 0.95f); Color color3 = default(Color); ((Color)(ref color3))..ctor(1f, 0.62f, 0.16f, 1f); for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { val.SetPixel(j, i, val2); } } DrawThickLine(val, 15, 12, 47, 50, color2, 7); DrawThickLine(val, 49, 12, 17, 50, color2, 7); DrawThickLine(val, 15, 12, 47, 50, color, 4); DrawThickLine(val, 49, 12, 17, 50, color, 4); DrawThickLine(val, 12, 15, 24, 8, color3, 5); DrawThickLine(val, 52, 15, 40, 8, color3, 5); DrawThickLine(val, 28, 28, 36, 36, color3, 3); DrawThickLine(val, 36, 28, 28, 36, color3, 3); val.Apply(false, true); return val; } private void DrawThickLine(Texture2D texture, int x0, int y0, int x1, int y1, Color color, int width) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)texture == (Object)null) { return; } int num = Math.Abs(x1 - x0); int num2 = Math.Abs(y1 - y0); int num3 = ((x0 < x1) ? 1 : (-1)); int num4 = ((y0 < y1) ? 1 : (-1)); int num5 = num - num2; int radius = Mathf.Max(1, width / 2); while (true) { FillIconPixel(texture, x0, y0, color, radius); if (x0 == x1 && y0 == y1) { break; } int num6 = num5 * 2; if (num6 > -num2) { num5 -= num2; x0 += num3; } if (num6 < num) { num5 += num; y0 += num4; } } } private void FillIconPixel(Texture2D texture, int cx, int cy, Color color, int radius) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) for (int i = -radius; i <= radius; i++) { for (int j = -radius; j <= radius; j++) { if (j * j + i * i <= radius * radius) { int num = cx + j; int num2 = cy + i; if (num >= 0 && num2 >= 0 && num < ((Texture)texture).width && num2 < ((Texture)texture).height) { texture.SetPixel(num, num2, color); } } } } } private void LoadRuleCategoryIcons() { _uiRuleCategoryIcons.Clear(); RegisterRuleCategoryIcon("Combate", "0 (5)"); RegisterRuleCategoryIcon("Bosses", "0 (14)"); RegisterRuleCategoryIcon("Skills", "0 (1)"); RegisterRuleCategoryIcon("Quests", "0 (31)"); RegisterRuleCategoryIcon("Pesca", "0 (32)"); RegisterRuleCategoryIcon("Exploração", "0 (27)"); RegisterRuleCategoryIcon("Exploracao", "0 (27)"); RegisterRuleCategoryIcon("Exploration", "0 (27)"); RegisterRuleCategoryIcon("Exploração", "icon_exploration.png"); RegisterRuleCategoryIcon("Exploracao", "icon_exploration.png"); RegisterRuleCategoryIcon("Exploration", "icon_exploration.png"); RegisterRuleCategoryIcon("Produção", "0 (26)"); RegisterRuleCategoryIcon("Cultivo", "0 (11)"); RegisterRuleCategoryIcon("Conquistas", "0 (29)"); RegisterRuleCategoryIcon("Penalidades", "0 (23)"); RegisterRuleCategoryIcon("Resumo", "0 (29)"); RegisterRuleCategoryIcon("Origem", "0 (12)"); RegisterRuleCategoryIcon("Último registro", "0 (7)"); RegisterRuleCategoryIcon("Recompensa", "0 (29)"); RegisterRuleCategoryIcon("Configuração", "0 (27)"); } private void RegisterRuleCategoryIcon(string categoryKey, string iconFileNameWithoutExtension) { if (!string.IsNullOrWhiteSpace(categoryKey) && !string.IsNullOrWhiteSpace(iconFileNameWithoutExtension)) { string text = (iconFileNameWithoutExtension.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ? iconFileNameWithoutExtension : (iconFileNameWithoutExtension + ".png")); Texture2D val = LoadTextureFromUiFolder("icon/" + text, "icons/" + text, text); if ((Object)(object)val != (Object)null) { _uiRuleCategoryIcons[categoryKey] = val; } } } private bool IsLocalPlayerAttackAuthor(HitData hit) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (hit == null || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } try { ZDOID attacker = hit.m_attacker; ZDOID zDOID = ((Character)Player.m_localPlayer).GetZDOID(); if (!((ZDOID)(ref attacker)).IsNone() && !((ZDOID)(ref zDOID)).IsNone() && ((ZDOID)(ref attacker)).UserID == ((ZDOID)(ref zDOID)).UserID && ((ZDOID)(ref attacker)).ID == ((ZDOID)(ref zDOID)).ID) { return true; } } catch { } try { Character attacker2 = hit.GetAttacker(); if ((Object)(object)attacker2 == (Object)(object)Player.m_localPlayer) { return true; } } catch { } return false; } private string GetLocalPlayerKey() { try { if ((Object)(object)Player.m_localPlayer != (Object)null) { return GetZdoKey((Character)(object)Player.m_localPlayer); } } catch { } return ""; } internal void ReportLocalPlayerDeathToServer() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown try { if (!((Object)(object)Player.m_localPlayer == (Object)null) && ZRoutedRpc.instance != null) { long serverPeerUid = GetServerPeerUid(); ZPackage val = new ZPackage(); val.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportplayerdeath", new object[1] { val }); DebugLog(DebugCategory.Points, "Cliente reportando morte: player=" + GetLocalPlayerName()); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao enviar morte local: " + ex)); } } private void RPC_ReportPlayerDeath(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { string text = SanitizePlayerName(pkg.ReadString()); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { text2 = text; } if (ShouldIgnoreSenderForRanking(sender, text2)) { DebugLog(DebugCategory.Points, "Morte ignorada para admin: " + text2); } else { ProcessPlayerDeathReport(text2, "rpc-death"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportPlayerDeath: " + ex)); } } private void ProcessPlayerDeathReport(string playerName, string source) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled || !_rules.EnableDeathPenalty) { return; } playerName = SanitizePlayerName(playerName); if (string.IsNullOrWhiteSpace(playerName)) { return; } if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Morte ignorada para admin: " + playerName); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry == null) { return; } if (orCreateEntry.ProgressCounters == null) { orCreateEntry.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } if (orCreateEntry.GenericJackpotCredits == null) { orCreateEntry.GenericJackpotCredits = new HashSet(StringComparer.OrdinalIgnoreCase); } string key = "DeathPenalty:Deaths"; int value = 0; orCreateEntry.ProgressCounters.TryGetValue(key, out value); value = Mathf.Clamp(value + 1, 0, int.MaxValue); orCreateEntry.ProgressCounters[key] = value; orCreateEntry.TotalDeaths = value; DebugLog(DebugCategory.Points, "Morte registrada: player=" + playerName + " deaths=" + value + " source=" + source); if (_rules.DeathPenaltyUseMultiplier) { int num = Mathf.Max(0, _rules.DeathPenaltyPerDeath); if (num > 0) { ApplyPointsToEntry(orCreateEntry, -num, "Penalidade por morte: " + value + "x -" + num); orCreateEntry.DeathPenaltyPointsTotal = Mathf.Clamp(orCreateEntry.DeathPenaltyPointsTotal + num, 0, int.MaxValue); } SaveRankingEntry(orCreateEntry); return; } if (_rules.DeathPenaltyRules != null && _rules.DeathPenaltyRules.Count > 0) { string text = SafeLimit(_rules.RewardClaimCycleId, 64); foreach (KeyValuePair item2 in _rules.DeathPenaltyRules.OrderBy((KeyValuePair p) => p.Key)) { int num2 = Mathf.Max(1, item2.Key); int num3 = Mathf.Max(0, item2.Value); if (value < num2) { continue; } string item = text + ":DeathPenalty:" + num2; if (!orCreateEntry.GenericJackpotCredits.Contains(item)) { orCreateEntry.GenericJackpotCredits.Add(item); if (num3 > 0) { ApplyPointsToEntry(orCreateEntry, -num3, "Penalidade por morte: " + value + " mortes"); orCreateEntry.DeathPenaltyPointsTotal = Mathf.Clamp(orCreateEntry.DeathPenaltyPointsTotal + num3, 0, int.MaxValue); } } } } SaveRankingEntry(orCreateEntry); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar penalidade por morte: " + ex)); } } private void IncrementProgressCounter(RankingEntry entry, string category, string key, int amount) { if (entry != null && !string.IsNullOrWhiteSpace(category) && !string.IsNullOrWhiteSpace(key) && amount > 0) { if (entry.ProgressCounters == null) { entry.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } string key2 = SafeKey(category) + ":" + SafeKey(key); int value = 0; entry.ProgressCounters.TryGetValue(key2, out value); entry.ProgressCounters[key2] = Mathf.Clamp(value + amount, 0, int.MaxValue); } } private void IncrementProgressCounterAlias(RankingEntry entry, string category, string key, int amount) { IncrementProgressCounter(entry, category, key, amount); } private void AddProgressAndCheckJackpot(RankingEntry entry, string category, string key, int amount, JackpotRule rule, string reason, bool unique) { if (entry == null || rule == null || rule.RequiredAmount <= 0 || rule.Points <= 0) { return; } if (entry.ProgressCounters == null) { entry.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } if (entry.GenericJackpotCredits == null) { entry.GenericJackpotCredits = new HashSet(StringComparer.OrdinalIgnoreCase); } string text = SafeKey(category) + ":" + SafeKey(key); string item = SafeLimit(_rules.RewardClaimCycleId, 64) + ":" + text + ":" + rule.RequiredAmount; if (entry.GenericJackpotCredits.Contains(item)) { return; } int value = 0; entry.ProgressCounters.TryGetValue(text, out value); value = (unique ? Mathf.Max(value, amount) : Mathf.Clamp(value + amount, 0, int.MaxValue)); entry.ProgressCounters[text] = value; if (value < rule.RequiredAmount) { if (IsDedicatedServerInstance()) { SaveRankingEntry(entry); } return; } entry.GenericJackpotCredits.Add(item); ApplyPointsToEntry(entry, rule.Points, reason + " (" + value + "/" + rule.RequiredAmount + ")"); if (category.Equals("Farm", StringComparison.OrdinalIgnoreCase)) { entry.TotalFarmJackpotsPontuados++; entry.FarmJackpotPointsTotal = Mathf.Clamp(entry.FarmJackpotPointsTotal + rule.Points, -2147483647, int.MaxValue); } else if (category.Equals("UniqueCraft", StringComparison.OrdinalIgnoreCase)) { entry.TotalUniqueCraftJackpotsPontuados++; entry.UniqueCraftJackpotPointsTotal = Mathf.Clamp(entry.UniqueCraftJackpotPointsTotal + rule.Points, -2147483647, int.MaxValue); } if (IsServerInstance()) { SaveRankingEntry(entry); } } private void SendDiscordTop3EnteredWebhook(string playerName, int rank, int points, string reason) { if (_cfgDiscordTop3WebhookUrl != null && !string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value)) { string text = rank switch { 2 => "\ud83e\udd48", 1 => "\ud83e\udd47", _ => "\ud83e\udd49", }; string field1Value = (string.IsNullOrWhiteSpace(playerName) ? "Jogador" : playerName.Trim()); string field4Value = (string.IsNullOrWhiteSpace(reason) ? "Ascensão no ranking" : reason.Trim()); string json = BuildDiscordTop3EmbedJson("\ud83c\udfdb\ufe0f ORÁCULO DO RANKING", "✨ Um novo nome ascendeu ao pódio de Glitnir.", 15844367, "\ud83d\udc64 Jogador", field1Value, "\ud83c\udfc6 Posição", text + " #" + rank, "⭐ Pontos", points.ToString(), "\ud83d\udcdc Registro", field4Value, "\ud83c\udf1f Seu nome agora ecoa nos salões de Glitnir."); PostDiscordWebhook(json); } } private void SendDiscordTop3LostWebhook(string playerName, int oldRank, int newRank, int points, string reason) { if (_cfgDiscordTop3WebhookUrl != null && !string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value)) { string field1Value = (string.IsNullOrWhiteSpace(playerName) ? "Jogador" : playerName.Trim()); string field4Value = (string.IsNullOrWhiteSpace(reason) ? "Movimento no ranking" : reason.Trim()); string text = ((newRank == int.MaxValue) ? "Fora do ranking" : ("#" + newRank)); string json = BuildDiscordTop3EmbedJson("\ud83c\udfdb\ufe0f ORÁCULO DO RANKING", "⚠\ufe0f Um campeão perdeu seu lugar entre os três maiores.", 15158332, "\ud83d\udc64 Jogador", field1Value, "\ud83d\udcc9 Mudança", "#" + oldRank + " → " + text, "⭐ Pontos", points.ToString(), "\ud83d\udcdc Registro", field4Value, "⏳ Os salões de Glitnir aguardam sua retomada."); PostDiscordWebhook(json); } } private void SendDiscordTop3DroppedWebhook(string playerName, int oldRank, int newRank, int points, string reason) { if (_cfgDiscordTop3WebhookUrl != null && !string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value)) { string field1Value = (string.IsNullOrWhiteSpace(playerName) ? "Jogador" : playerName.Trim()); string field4Value = (string.IsNullOrWhiteSpace(reason) ? "Movimento no ranking" : reason.Trim()); string json = BuildDiscordTop3EmbedJson("\ud83c\udfdb\ufe0f ORÁCULO DO RANKING", "⚖\ufe0f A balança da honra se moveu dentro do Top 3.", 3447003, "\ud83d\udc64 Jogador", field1Value, "\ud83d\udcc9 Posição", "#" + oldRank + " → #" + newRank, "⭐ Pontos", points.ToString(), "\ud83d\udcdc Registro", field4Value, "\ud83d\udd25 A disputa pelo topo continua."); PostDiscordWebhook(json); } } private string BuildDiscordTop3EmbedJson(string title, string description, int color, string field1Name, string field1Value, string field2Name, string field2Value, string field3Name, string field3Value, string field4Name, string field4Value, string footerText) { string value = DateTime.UtcNow.ToString("o"); string value2 = BuildDiscordTop3PodiumValue(); return "{\"username\":\"Glitnir Ranking\",\"embeds\":[{\"title\":\"" + EscapeJson(title) + "\",\"description\":\"" + EscapeJson(description) + "\",\"color\":" + color + ",\"fields\":[{\"name\":\"\ud83c\udfc6 Pódio de Glitnir\",\"value\":\"" + EscapeJson(value2) + "\",\"inline\":false},{\"name\":\"" + EscapeJson(field1Name) + "\",\"value\":\"" + EscapeJson(field1Value) + "\",\"inline\":true},{\"name\":\"" + EscapeJson(field2Name) + "\",\"value\":\"" + EscapeJson(field2Value) + "\",\"inline\":true},{\"name\":\"" + EscapeJson(field3Name) + "\",\"value\":\"" + EscapeJson(field3Value) + "\",\"inline\":true},{\"name\":\"" + EscapeJson(field4Name) + "\",\"value\":\"" + EscapeJson(field4Value) + "\",\"inline\":false}],\"footer\":{\"text\":\"" + EscapeJson(footerText) + "\"},\"timestamp\":\"" + EscapeJson(value) + "\"}]}"; } private string BuildDiscordTop3PodiumValue() { try { if (_database == null || _database.Entries == null) { return "Nenhum nome registrado no pódio ainda."; } List list = (from x in _database.Entries where x != null && !string.IsNullOrWhiteSpace(x.PlayerName) && !ShouldIgnorePlayerForRanking(x.PlayerName) orderby x.Points descending, x.BossPointsTotal descending, x.KillPointsTotal descending, x.SkillPointsTotal descending, x.PlayerName select x).Take(3).ToList(); if (list.Count == 0) { return "Nenhum nome registrado no pódio ainda."; } StringBuilder stringBuilder = new StringBuilder(); for (int num = 0; num < list.Count; num++) { RankingEntry rankingEntry = list[num]; string value = num switch { 1 => "\ud83e\udd48", 0 => "\ud83e\udd47", _ => "\ud83e\udd49", }; string value2 = (string.IsNullOrWhiteSpace(rankingEntry.PlayerName) ? "Jogador" : rankingEntry.PlayerName.Trim()); stringBuilder.Append(value).Append(" **#").Append(num + 1) .Append("** — ") .Append(value2) .Append("\n") .Append("⭐ **") .Append(rankingEntry.Points) .Append(" pts**"); if (num < list.Count - 1) { stringBuilder.Append("\n\n"); } } return stringBuilder.ToString(); } catch (Exception ex) { Log.LogWarning((object)("Erro ao montar pódio do webhook: " + ex.Message)); return "Pódio indisponível no momento."; } } private void PostDiscordWebhook(string json) { string url = ((_cfgDiscordTop3WebhookUrl != null) ? _cfgDiscordTop3WebhookUrl.Value : ""); if (string.IsNullOrWhiteSpace(url)) { return; } ThreadPool.QueueUserWorkItem(delegate { try { byte[] bytes = Encoding.UTF8.GetBytes(json); HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url); httpWebRequest.Method = "POST"; httpWebRequest.ContentType = "application/json"; httpWebRequest.ContentLength = bytes.Length; httpWebRequest.Timeout = 10000; using (Stream stream = httpWebRequest.GetRequestStream()) { stream.Write(bytes, 0, bytes.Length); } using HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); int statusCode = (int)httpWebResponse.StatusCode; if (statusCode < 200 || statusCode >= 300) { Log.LogWarning((object)("Webhook Top 3 Discord retornou status: " + statusCode)); } } catch (Exception ex) { Log.LogWarning((object)("Webhook Top 3 Discord falhou: " + ex.Message)); } }); } private string EscapeJson(string value) { if (string.IsNullOrEmpty(value)) { return ""; } return value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r") .Replace("\n", "\\n"); } private string SafeLimit(string value, int maxLength) { if (string.IsNullOrEmpty(value)) { return ""; } value = value.Replace("\r", " ").Replace("\n", " ").Replace("\t", " ") .Trim(); if (value.Length > maxLength) { value = value.Substring(0, maxLength); } return value; } private string SafeKey(string value) { value = SafeLimit(value, 128); value = value.Replace("|", "/"); return value; } private string SafeReason(string reason) { reason = SafeLimit(reason, 64); reason = reason.Replace("|", "/"); return reason; } private string SanitizePlayerName(string playerName) { playerName = SafeLimit(playerName, 32); playerName = playerName.Replace("|", "/"); if (string.IsNullOrWhiteSpace(playerName)) { return "Jogador"; } return playerName; } private RankingEntry GetOrCreateEntry(string playerName) { string playerName2 = SanitizePlayerName(playerName); RankingEntry rankingEntry = FindRankingEntryByName(playerName2); if (rankingEntry != null) { return rankingEntry; } rankingEntry = new RankingEntry { PlayerName = playerName2, Points = 0, LastReason = "", LastUpdateUtc = DateTime.UtcNow.ToString("O"), TotalSkillLevelUpsPontuados = 0, SkillPointsTotal = 0, BossCredits = new HashSet(StringComparer.OrdinalIgnoreCase), TotalDeaths = 0, DeathPenaltyPointsTotal = 0 }; _database.Entries.Add(rankingEntry); RegisterRankingEntry(rankingEntry); return rankingEntry; } private string GetLocalPlayerName() { try { if ((Object)(object)Player.m_localPlayer != (Object)null) { return SanitizePlayerName(Player.m_localPlayer.GetPlayerName()); } } catch { } return "Jogador"; } private long GetServerPeerUid() { try { if ((Object)(object)ZNet.instance == (Object)null) { return 0L; } if (ZNet.instance.IsServer()) { return ZDOMan.GetSessionID(); } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { return serverPeer.m_uid; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao obter peer do servidor: " + ex.Message)); } return 0L; } public string GetPrefabName(GameObject go) { if ((Object)(object)go == (Object)null) { return ""; } try { string text = ((Object)go).name; if (string.IsNullOrWhiteSpace(text)) { return ""; } int num = text.IndexOf('('); if (num > 0) { text = text.Substring(0, num); } return SafeKey(text.Trim()); } catch { } return ""; } private string GetZdoKey(GameObject go) { if ((Object)(object)go == (Object)null) { return ""; } try { ZNetView component = go.GetComponent(); if ((Object)(object)component != (Object)null && component.GetZDO() != null) { ZDO zDO = component.GetZDO(); return ((ZDOID)(ref zDO.m_uid)).UserID + ":" + ((ZDOID)(ref zDO.m_uid)).ID; } } catch { } return ""; } private string GetZdoKey(Character character) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null) { return ""; } try { ZDOID zDOID = character.GetZDOID(); if (((ZDOID)(ref zDOID)).IsNone()) { return ""; } return ((ZDOID)(ref zDOID)).UserID + ":" + ((ZDOID)(ref zDOID)).ID; } catch { } try { ZNetView component = ((Component)character).GetComponent(); if ((Object)(object)component != (Object)null && component.GetZDO() != null) { ZDO zDO = component.GetZDO(); return ((ZDOID)(ref zDO.m_uid)).UserID + ":" + ((ZDOID)(ref zDO.m_uid)).ID; } } catch { } return ""; } private bool IsLocalPlayerSkillsInstance(Skills skills) { if ((Object)(object)skills == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } try { Player value = Traverse.Create((object)skills).Field("m_player").GetValue(); if ((Object)(object)value != (Object)null && (Object)(object)value == (Object)(object)Player.m_localPlayer) { return true; } } catch { } return false; } private float GetSafeSkillLevel(Skills skills, SkillType skillType) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)skills != (Object)null) { return Mathf.Clamp(skills.GetSkillLevel(skillType), 0f, 100f); } } catch { } return 0f; } private int GetEffectiveSkillLevel(float rawLevel) { return Mathf.Clamp(Mathf.FloorToInt(rawLevel + 0.0001f), 0, 100); } private bool IsTrackableSkill(SkillType skillType) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) string skillKey = GetSkillKey(skillType); if (string.IsNullOrWhiteSpace(skillKey)) { return false; } if (string.Equals(skillKey, "None", StringComparison.OrdinalIgnoreCase)) { return false; } if (string.Equals(skillKey, "All", StringComparison.OrdinalIgnoreCase)) { return false; } return true; } private unsafe string GetSkillKey(SkillType skillType) { return SafeKey(((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString()); } private string GetSkillDisplayName(string skillKey) { skillKey = SafeKey(skillKey); switch (skillKey) { case "WoodCutting": return "Wood Cutting"; case "BloodMagic": return "Blood Magic"; case "ElementalMagic": return "Elemental Magic"; default: { StringBuilder stringBuilder = new StringBuilder(skillKey.Length + 8); for (int i = 0; i < skillKey.Length; i++) { char c = skillKey[i]; if (i > 0 && char.IsUpper(c) && char.IsLetter(skillKey[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(c); } return stringBuilder.ToString().Trim(); } } } public float CaptureLocalSkillLevelBeforeRaise(Skills skills, SkillType skillType) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)skills == (Object)null || !IsTrackableSkill(skillType)) { return 0f; } if (!IsLocalPlayerSkillsInstance(skills)) { return 0f; } return GetSafeSkillLevel(skills, skillType); } public void NotifyLocalSkillGain(Skills skills, SkillType skillType, float oldRawLevel) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_0100: Expected O, but got Unknown try { if ((Object)(object)skills == (Object)null || !IsTrackableSkill(skillType) || (Object)(object)Player.m_localPlayer == (Object)null || !IsLocalPlayerSkillsInstance(skills)) { return; } float safeSkillLevel = GetSafeSkillLevel(skills, skillType); int effectiveSkillLevel = GetEffectiveSkillLevel(oldRawLevel); int effectiveSkillLevel2 = GetEffectiveSkillLevel(safeSkillLevel); int num = Mathf.Clamp(effectiveSkillLevel2 - effectiveSkillLevel, 0, 100); if (num <= 0) { return; } string skillKey = GetSkillKey(skillType); if (string.IsNullOrWhiteSpace(skillKey)) { return; } if (IsServerInstance()) { ProcessSkillGainReport(GetLocalPlayerName(), skillKey, num, effectiveSkillLevel, effectiveSkillLevel2, "local-host"); return; } long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0L && _rpcsRegistered && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(skillKey); val.Write(num); val.Write(effectiveSkillLevel); val.Write(effectiveSkillLevel2); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportskillgain", new object[1] { val }); DebugLog(DebugCategory.Skill, "Cliente reportando SKILL: player=" + GetLocalPlayerName() + " skill=" + skillKey + " gainedLevels=" + num + " oldLevel=" + effectiveSkillLevel + " newLevel=" + effectiveSkillLevel2); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reportar ganho de skill local do ranking: " + ex)); } } private void RPC_ReportSkillGain(long sender, ZPackage pkg) { try { if (!IsServerInstance() || pkg == null) { return; } string text = SafeKey(pkg.ReadString()); int num = Mathf.Clamp(pkg.ReadInt(), 0, 100); int oldLevel = Mathf.Clamp(pkg.ReadInt(), 0, 100); int newLevel = Mathf.Clamp(pkg.ReadInt(), 0, 100); string text2 = ResolvePlayerNameFromSender(sender); DebugLog(DebugCategory.Skill, "Servidor recebeu SKILL: sender=" + sender + " player=" + text2 + " skill=" + text + " gainedLevels=" + num + " oldLevel=" + oldLevel + " newLevel=" + newLevel); if (!string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text) && num > 0) { if (ShouldIgnoreSenderForRanking(sender, text2)) { DebugLog(DebugCategory.Skill, "Skill ignorada para admin: " + text2); } else { ProcessSkillGainReport(text2, text, num, oldLevel, newLevel, "rpc-skill"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportSkillGain: " + ex)); } } private void ProcessSkillGainReport(string playerName, string skillKey, int gainedLevels, int oldLevel, int newLevel, string source) { try { if (!IsServerInstance() || !_rules.RankingEnabled || !_rules.EnableSkillPoints) { return; } playerName = SanitizePlayerName(playerName); skillKey = SafeKey(skillKey); gainedLevels = Mathf.Clamp(gainedLevels, 0, 100); if (string.IsNullOrWhiteSpace(playerName) || string.IsNullOrWhiteSpace(skillKey) || gainedLevels <= 0) { return; } if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Skill, "Skill ignorada para admin: " + playerName); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); int num = 0; int num2 = 0; List list = new List(); Dictionary value = null; if (_rules.SkillMilestoneJackpotPoints != null) { _rules.SkillMilestoneJackpotPoints.TryGetValue(skillKey, out value); } if (value != null && value.Count > 0) { foreach (KeyValuePair item2 in value.OrderBy((KeyValuePair p) => p.Key)) { int num3 = Mathf.Clamp(item2.Key, 1, 100); int num4 = Mathf.Max(0, item2.Value); if (oldLevel < num3 && newLevel >= num3) { string item = skillKey + ":" + num3; if (!orCreateEntry.SkillLevel100JackpotCredits.Contains(item)) { orCreateEntry.SkillLevel100JackpotCredits.Add(item); num2++; list.Add(num3); num = Mathf.Clamp(num + num4, -2147483647, int.MaxValue); } } } if (num > 0) { orCreateEntry.TotalSkillLevelUpsPontuados = Mathf.Clamp(orCreateEntry.TotalSkillLevelUpsPontuados + num2, 0, int.MaxValue); orCreateEntry.SkillPointsTotal = Mathf.Clamp(orCreateEntry.SkillPointsTotal + num, -2147483647, int.MaxValue); string reason = "Jackpot Skill: " + GetSkillDisplayName(skillKey) + " " + string.Join("/", list.Select((int l) => l.ToString()).ToArray()); ApplyPointsToEntry(orCreateEntry, num, reason); return; } } DebugLog(DebugCategory.Skill, "Skill ignorada fora dos jackpots de marco: player=" + playerName + " skill=" + skillKey + " oldLevel=" + oldLevel + " newLevel=" + newLevel + " source=" + source); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar skill reportado: " + ex)); } } private string FormatHudLastUpdate(string rawUtc) { if (string.IsNullOrWhiteSpace(rawUtc)) { return "sem registro"; } try { return DateTime.Parse(rawUtc, null, DateTimeStyles.RoundtripKind).ToLocalTime().ToString("dd/MM/yyyy HH:mm"); } catch { return rawUtc; } } private string FormatIntRulesForHud(Dictionary rules) { if (rules == null || rules.Count == 0) { return ""; } return string.Join(",", (from x in rules orderby x.Key select SafeKey(x.Key) + ":" + x.Value).ToArray()); } private string FormatCategorizedIntRulesForHud(Dictionary rules, Dictionary manualCategories = null, bool combatMode = false) { if (rules == null || rules.Count == 0) { return ""; } List list = new List(); foreach (IGrouping> item in from x in rules orderby GetHudRuleCategoryOrder(x.Key, manualCategories, combatMode), combatMode ? SafeKey(x.Key) : FriendlyRuleNameForHud(x.Key) group x by GetHudRuleCategoryName(x.Key, manualCategories, combatMode)) { list.Add(BuildHudCategoryHeader(item.Key)); foreach (KeyValuePair item2 in item) { string text = (combatMode ? SafeKey(item2.Key) : item2.Key); list.Add(text + ":" + item2.Value); } } return string.Join(",", list.ToArray()); } private string FormatCategorizedJackpotRulesForHud(Dictionary rules, Dictionary manualCategories = null, bool combatMode = false) { if (rules == null || rules.Count == 0) { return ""; } List list = new List(); foreach (IGrouping> item in from x in rules orderby GetHudRuleCategoryOrder(x.Key, manualCategories, combatMode), FriendlyRuleNameForHud(x.Key) group x by GetHudRuleCategoryName(x.Key, manualCategories, combatMode)) { list.Add(BuildHudCategoryHeader(item.Key)); foreach (KeyValuePair item2 in item) { JackpotRule jackpotRule = item2.Value ?? new JackpotRule(); list.Add(item2.Key + ":" + Mathf.Max(1, jackpotRule.RequiredAmount) + ":" + jackpotRule.Points); } } return string.Join(",", list.ToArray()); } private string BuildHudCategoryHeader(string categoryName) { return "__GLITNIR_CAT__|" + (string.IsNullOrWhiteSpace(categoryName) ? "Outros" : categoryName.Trim()); } private bool IsHudCategoryHeader(string row) { return !string.IsNullOrWhiteSpace(row) && row.StartsWith("__GLITNIR_CAT__|", StringComparison.Ordinal); } private string GetHudCategoryHeaderTitle(string row) { if (!IsHudCategoryHeader(row)) { return ""; } return row.Substring("__GLITNIR_CAT__|".Length).Trim(); } private string GetHudRuleCategoryName(string prefabName, Dictionary manualCategories, bool combatMode) { if (manualCategories != null && manualCategories.TryGetValue(SafeKey(prefabName), out var value) && !string.IsNullOrWhiteSpace(value)) { return NormalizeHudCategoryName(value.Trim(), prefabName, combatMode); } return combatMode ? GetHudCombatCategoryName(prefabName) : GetHudPrefabCategoryName(prefabName); } private string NormalizeHudCategoryName(string category, string prefabName, bool combatMode) { if (combatMode || string.IsNullOrWhiteSpace(category)) { return category; } string text = category.Trim(); if (text.Equals("Arcos e Munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Arcos e munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Armas e escudos", StringComparison.OrdinalIgnoreCase)) { return GetHudPrefabCategoryName(prefabName); } return text; } private int GetHudRuleCategoryOrder(string prefabName, Dictionary manualCategories, bool combatMode) { string hudRuleCategoryName = GetHudRuleCategoryName(prefabName, manualCategories, combatMode); return combatMode ? GetHudCombatCategoryOrder(hudRuleCategoryName) : GetHudProductionCategoryOrder(hudRuleCategoryName); } private int GetHudProductionCategoryOrder(string category) { return category switch { "Armas, armaduras e munições" => 10, "Armaduras" => 10, "Armas" => 10, "Escudos" => 10, "Munições" => 10, "Comidas" => 40, "Comidas e bebidas" => 40, "Ferramentas" => 50, "Materiais" => 60, "Troféus" => 70, "Cultivo" => 80, _ => 999, }; } private int GetHudCombatCategoryOrder(string category) { switch (category) { case "Prados": return 10; case "Floresta Negra": return 20; case "Pântano": return 30; case "Montanha": return 40; case "Planícies": return 50; case "Oceano": return 60; case "Mistlands": return 70; case "Ashlands": return 80; default: if (!(category == "Criaturas Especiais")) { return 999; } goto case "Especiais"; case "Especiais": return 90; } } private string GetHudCombatCategoryName(string prefabName) { string text = SafeKey(prefabName).ToLowerInvariant().Replace("_", "").Replace("-", "") .Replace(" ", ""); if (text.Contains("boar") || text.Contains("deer") || text.Contains("greyling") || text.Contains("neck") || text.Contains("crow") || text.Contains("hen") || text.Contains("chicken")) { return "Prados"; } if (text.Contains("greydwarf") || text.Contains("skeleton") || text.Contains("ghost") || text.Contains("troll")) { return "Floresta Negra"; } if (text.Contains("blob") || text.Contains("abomination") || text.Contains("draugr") || text.Contains("leech") || text.Contains("surtling") || text.Contains("wraith")) { return "Pântano"; } if (text.Contains("hatchling") || text.Contains("fenring") || text.Contains("stonegolem") || text.Contains("ulv") || text.Contains("wolf") || text.Contains("bat")) { return "Montanha"; } if (text.Contains("deathsquito") || text.Contains("goblin") || text.Contains("fuling") || text.Contains("lox")) { return "Planícies"; } if (text.Contains("serpent") || text.Contains("bonemaw") || text.Contains("leviathan") || text.Contains("seagal")) { return "Oceano"; } if (text.Contains("seeker") || text.Contains("gjall") || text.Contains("tick") || text.Contains("hare") || text.Contains("dverger")) { return "Mistlands"; } if (text.Contains("asksvin") || text.Contains("charred") || text.Contains("morgen") || text.Contains("valkyrie") || text.Contains("volture") || text.Contains("tentaroot") || text.Contains("bloblava")) { return "Ashlands"; } if (text.Contains("haldor") || text.Contains("hildir") || text.Contains("odin") || text.Contains("wisp") || text.Contains("trainingdummy")) { return "Especiais"; } return "Outros"; } private int GetHudPrefabCategoryOrder(string prefabName) { return GetHudPrefabCategoryName(prefabName) switch { "Armaduras" => 10, "Armas" => 20, "Escudos" => 25, "Munições" => 30, "Comidas" => 40, "Comidas e bebidas" => 40, "Ferramentas" => 50, "Materiais" => 60, "Troféus" => 70, "Cultivo" => 80, _ => 999, }; } private string GetHudPrefabCategoryName(string prefabName) { string text = SafeKey(prefabName).ToLowerInvariant().Replace("_", "").Replace("-", "") .Replace(" ", ""); if (text.Contains("helmet") || text.Contains("armor") || text.Contains("cape") || text.Contains("chest") || text.Contains("legs") || text.Contains("greaves")) { return "Armaduras"; } if (text.Contains("shield")) { return "Escudos"; } if (text.Contains("arrow") || text.Contains("bolt") || text.Contains("bomb") || text.Contains("oozebomb") || text.Contains("missile")) { return "Munições"; } if (text.Contains("sword") || text.Contains("knife") || text.Contains("axe") || text.Contains("mace") || text.Contains("spear") || text.Contains("atgeir") || text.Contains("bow") || text.Contains("crossbow") || text.Contains("staff") || text.Contains("club") || text.Contains("pickaxe")) { return "Armas"; } if (text.Contains("stew") || text.Contains("soup") || text.Contains("mead") || text.Contains("wine") || text.Contains("cooked") || text.Contains("jerky") || text.Contains("honey") || text.Contains("chicken") || text.Contains("fish") || text.Contains("meat") || text.Contains("mushroom") || text.Contains("bread") || text.Contains("pie") || text.Contains("pudding") || text.Contains("sausages") || text.Contains("eyescream") || text.Contains("salad") || text.Contains("omelette") || text.Contains("seekeraspic") || text.Contains("wolfskewer") || text.Contains("mincemeatsauce") || text.Contains("bloodpudding")) { return "Comidas e bebidas"; } if (text.Contains("hoe") || text.Contains("hammer") || text.Contains("cultivator") || text.Contains("fishingrod") || text.Contains("torch") || text.Contains("wishbone")) { return "Ferramentas"; } if (text.Contains("trophy")) { return "Troféus"; } if (text.Contains("seed") || text.Contains("sapling") || text.Contains("barley") || text.Contains("flax") || text.Contains("carrot") || text.Contains("turnip") || text.Contains("onion") || text.Contains("jotunpuffs") || text.Contains("magecap") || text.Contains("vineberry")) { return "Cultivo"; } if (text.Contains("wood") || text.Contains("stone") || text.Contains("ore") || text.Contains("metal") || text.Contains("bar") || text.Contains("ingot") || text.Contains("hide") || text.Contains("leather") || text.Contains("scale") || text.Contains("chitin") || text.Contains("thread") || text.Contains("linen") || text.Contains("resin") || text.Contains("coal") || text.Contains("core") || text.Contains("crystal") || text.Contains("bone") || text.Contains("fang") || text.Contains("feather") || text.Contains("needle") || text.Contains("claw") || text.Contains("carapace") || text.Contains("mandible") || text.Contains("bloodbag")) { return "Materiais"; } return "Outros"; } private string FormatJackpotRulesForHud(Dictionary rules) { if (rules == null || rules.Count == 0) { return ""; } return string.Join(",", rules.OrderBy((KeyValuePair x) => x.Key).Select(delegate(KeyValuePair x) { JackpotRule jackpotRule = x.Value ?? new JackpotRule(); return SafeKey(x.Key) + ":" + Mathf.Max(1, jackpotRule.RequiredAmount) + ":" + jackpotRule.Points; }).ToArray()); } private string FriendlyRuleNameForHud(string rawKey) { string text = SafeKey(rawKey); if (string.IsNullOrWhiteSpace(text)) { return "Regra"; } string prefabDisplayNameForHud = GetPrefabDisplayNameForHud(text); if (!string.IsNullOrWhiteSpace(prefabDisplayNameForHud) && !string.Equals(prefabDisplayNameForHud, text, StringComparison.OrdinalIgnoreCase)) { return prefabDisplayNameForHud; } if (BossDisplayNamesPtBr.TryGetValue(text, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } if (BossPortugueseToPrefab.TryGetValue(text, out var value2) && BossDisplayNamesPtBr.TryGetValue(value2, out value) && !string.IsNullOrWhiteSpace(value)) { return value; } if (FishDisplayNamesPtBr.TryGetValue(text, out var value3) && !string.IsNullOrWhiteSpace(value3)) { return value3; } if (FishPortugueseToPrefab.TryGetValue(text, out var value4) && FishDisplayNamesPtBr.TryGetValue(value4, out value3) && !string.IsNullOrWhiteSpace(value3)) { return value3; } if (HudFriendlyNames.TryGetValue(text, out var value5) && !string.IsNullOrWhiteSpace(value5)) { return value5; } string text2 = TryManualPortugueseNameForHud(text); if (!string.IsNullOrWhiteSpace(text2)) { return text2; } if (!string.IsNullOrWhiteSpace(prefabDisplayNameForHud)) { return prefabDisplayNameForHud; } return HumanizePrefabNamePt(text); } private string GetPrefabDisplayNameForHud(string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return ""; } string text = CleanPrefabNameForHud(prefabName); if (string.IsNullOrWhiteSpace(text)) { return ""; } if (_hudPrefabDisplayNameCache.TryGetValue(text, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } string text2 = ""; bool flag = false; try { GameObject val = ResolvePrefabForHud(text); if ((Object)(object)val != (Object)null) { text2 = TryGetLocalizedPrefabNameForHud(val, text); flag = !string.IsNullOrWhiteSpace(text2); } } catch (Exception ex) { if (_rules != null && _rules.DebugLogging) { Log.LogWarning((object)("[Glitnir Ranking] Falha ao localizar nome do prefab '" + prefabName + "': " + ex.Message)); } } if (string.IsNullOrWhiteSpace(text2) && BossDisplayNamesPtBr.TryGetValue(text, out var value2) && !string.IsNullOrWhiteSpace(value2)) { text2 = value2; flag = true; } if (string.IsNullOrWhiteSpace(text2) && BossPortugueseToPrefab.TryGetValue(text, out var value3) && BossDisplayNamesPtBr.TryGetValue(value3, out var value4) && !string.IsNullOrWhiteSpace(value4)) { text2 = value4; flag = true; } if (string.IsNullOrWhiteSpace(text2) && FishDisplayNamesPtBr.TryGetValue(text, out var value5) && !string.IsNullOrWhiteSpace(value5)) { text2 = value5; flag = true; } if (string.IsNullOrWhiteSpace(text2) && FishPortugueseToPrefab.TryGetValue(text, out var value6) && FishDisplayNamesPtBr.TryGetValue(value6, out var value7) && !string.IsNullOrWhiteSpace(value7)) { text2 = value7; flag = true; } if (string.IsNullOrWhiteSpace(text2) && HudFriendlyNames.TryGetValue(text, out var value8) && !string.IsNullOrWhiteSpace(value8)) { text2 = value8; flag = true; } if (string.IsNullOrWhiteSpace(text2)) { string text3 = TryManualPortugueseNameForHud(text); if (!string.IsNullOrWhiteSpace(text3)) { text2 = text3; flag = true; } } if (string.IsNullOrWhiteSpace(text2)) { return HumanizePrefabNamePt(text); } if (flag) { _hudPrefabDisplayNameCache[text] = text2; } return text2; } private string TryGetLocalizedPrefabNameForHud(GameObject prefab, string fallbackPrefabName) { if ((Object)(object)prefab == (Object)null) { return ""; } string text = ""; try { ItemDrop component = prefab.GetComponent(); if ((Object)(object)component != (Object)null && component.m_itemData != null && component.m_itemData.m_shared != null && !string.IsNullOrWhiteSpace(component.m_itemData.m_shared.m_name)) { text = component.m_itemData.m_shared.m_name; } if (string.IsNullOrWhiteSpace(text)) { Character component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null && !string.IsNullOrWhiteSpace(component2.m_name)) { text = component2.m_name; } } if (string.IsNullOrWhiteSpace(text)) { Piece component3 = prefab.GetComponent(); if ((Object)(object)component3 != (Object)null && !string.IsNullOrWhiteSpace(component3.m_name)) { text = component3.m_name; } } if (string.IsNullOrWhiteSpace(text)) { Pickable component4 = prefab.GetComponent(); if ((Object)(object)component4 != (Object)null && (Object)(object)component4.m_itemPrefab != (Object)null) { ItemDrop component5 = component4.m_itemPrefab.GetComponent(); if ((Object)(object)component5 != (Object)null && component5.m_itemData != null && component5.m_itemData.m_shared != null && !string.IsNullOrWhiteSpace(component5.m_itemData.m_shared.m_name)) { text = component5.m_itemData.m_shared.m_name; } } } if (string.IsNullOrWhiteSpace(text)) { return ""; } string text2 = TryLocalizeTokenForHud(text); if (string.IsNullOrWhiteSpace(text2)) { return ""; } text2 = text2.Trim(); if (text2.StartsWith("$", StringComparison.Ordinal)) { text2 = HumanizeTokenNameForHud(text2); } if (string.IsNullOrWhiteSpace(text2) || text2 == fallbackPrefabName) { return text2; } return text2; } catch { return ""; } } private GameObject ResolvePrefabForHud(string cleanName) { if (string.IsNullOrWhiteSpace(cleanName)) { return null; } if (_hudPrefabObjectCache.TryGetValue(cleanName, out var value)) { return value; } RebuildHudPrefabObjectCacheIfNeeded(); if (_hudPrefabObjectCache.TryGetValue(cleanName, out value)) { return value; } GameObject val = null; if ((Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(cleanName); } if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(cleanName); } if ((Object)(object)val != (Object)null) { _hudPrefabObjectCache[cleanName] = val; } return val; } private void RebuildHudPrefabObjectCacheIfNeeded() { int num = (((Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items != null) ? ObjectDB.instance.m_items.Count : (-1)); int num2 = (((Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.m_prefabs != null) ? ZNetScene.instance.m_prefabs.Count : (-1)); if (_hudObjectDbItemCountCached == num && _hudZNetScenePrefabCountCached == num2 && _hudPrefabObjectCache.Count > 0) { return; } _hudPrefabObjectCache.Clear(); if ((Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items != null) { for (int i = 0; i < ObjectDB.instance.m_items.Count; i++) { GameObject val = ObjectDB.instance.m_items[i]; if (!((Object)(object)val == (Object)null) && !string.IsNullOrWhiteSpace(((Object)val).name) && !_hudPrefabObjectCache.ContainsKey(((Object)val).name)) { _hudPrefabObjectCache[((Object)val).name] = val; } } } if ((Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.m_prefabs != null) { for (int j = 0; j < ZNetScene.instance.m_prefabs.Count; j++) { GameObject val2 = ZNetScene.instance.m_prefabs[j]; if (!((Object)(object)val2 == (Object)null) && !string.IsNullOrWhiteSpace(((Object)val2).name) && !_hudPrefabObjectCache.ContainsKey(((Object)val2).name)) { _hudPrefabObjectCache[((Object)val2).name] = val2; } } } _hudObjectDbItemCountCached = num; _hudZNetScenePrefabCountCached = num2; } private string HumanizeTokenNameForHud(string tokenOrName) { if (string.IsNullOrWhiteSpace(tokenOrName)) { return ""; } string text = tokenOrName.Trim(); if (text.StartsWith("$item_", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(6); } else if (text.StartsWith("$", StringComparison.Ordinal)) { text = text.Substring(1); } return HumanizePrefabName(text); } private string CleanPrefabNameForHud(string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return ""; } string text = prefabName.Trim(); int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num).Trim(); } return text; } private string TryLocalizeTokenForHud(string tokenOrName) { if (string.IsNullOrWhiteSpace(tokenOrName)) { return ""; } try { if (!_hudLocalizationReflectionReady) { _hudLocalizationReflectionReady = true; _hudLocalizationType = AccessTools.TypeByName("Localization"); if (_hudLocalizationType == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type[] types; try { types = assemblies[i].GetTypes(); } catch { continue; } foreach (Type type in types) { if (type != null && type.Name == "Localization") { _hudLocalizationType = type; break; } } if (_hudLocalizationType != null) { break; } } } if (_hudLocalizationType != null) { _hudLocalizationInstanceField = AccessTools.Field(_hudLocalizationType, "instance"); _hudLocalizationInstanceProperty = AccessTools.Property(_hudLocalizationType, "instance"); _hudLocalizationLocalizeMethod = AccessTools.Method(_hudLocalizationType, "Localize", new Type[1] { typeof(string) }, (Type[])null); if (_hudLocalizationLocalizeMethod == null) { MethodInfo[] methods = _hudLocalizationType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo == null) && !(methodInfo.Name != "Localize")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length >= 1 && parameters[0].ParameterType == typeof(string)) { _hudLocalizationLocalizeMethod = methodInfo; break; } } } } } } if (_hudLocalizationType == null || (_hudLocalizationInstanceField == null && _hudLocalizationInstanceProperty == null) || _hudLocalizationLocalizeMethod == null) { return tokenOrName; } object obj2 = null; if (_hudLocalizationInstanceField != null) { obj2 = _hudLocalizationInstanceField.GetValue(null); } if (obj2 == null && _hudLocalizationInstanceProperty != null) { obj2 = _hudLocalizationInstanceProperty.GetValue(null, null); } if (obj2 == null) { return tokenOrName; } ParameterInfo[] parameters2 = _hudLocalizationLocalizeMethod.GetParameters(); object[] array = new object[parameters2.Length]; array[0] = tokenOrName; for (int l = 1; l < array.Length; l++) { if (parameters2[l].ParameterType == typeof(bool)) { array[l] = true; } else { array[l] = parameters2[l].DefaultValue; } } object obj3 = _hudLocalizationLocalizeMethod.Invoke(obj2, array); return (obj3 as string) ?? tokenOrName; } catch { return tokenOrName; } } private string TryManualPortugueseNameForHud(string key) { if (string.IsNullOrWhiteSpace(key)) { return ""; } string text = key.Trim(); if (text.StartsWith("$item_", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(6); } else if (text.StartsWith("$", StringComparison.Ordinal)) { text = text.Substring(1); } text = text.Replace("_", "").Replace("-", "").Replace(" ", ""); if (ManualPortuguesePrefabNames.TryGetValue(text, out var value)) { return value; } return ""; } private string HumanizePrefabNamePt(string key) { if (string.IsNullOrWhiteSpace(key)) { return "Regra"; } string text = TryManualPortugueseNameForHud(key); if (!string.IsNullOrWhiteSpace(text)) { return text; } string text2 = key.Trim(); int num = text2.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text2 = text2.Substring(0, num); } text2 = Regex.Replace(text2, "([a-z])([A-Z])", "$1 $2"); text2 = text2.Replace("_", " ").Replace("-", " ").Trim(); string text3 = text2.ToLowerInvariant(); text3 = text3.Replace("helmet", "elmo"); text3 = text3.Replace("armor", "armadura"); text3 = text3.Replace("chest", "peitoral"); text3 = text3.Replace("legs", "perneiras"); text3 = text3.Replace("greaves", "grevas"); text3 = text3.Replace("knife", "faca"); text3 = text3.Replace("sword", "espada"); text3 = text3.Replace("axe", "machado"); text3 = text3.Replace("mace", "maça"); text3 = text3.Replace("bow", "arco"); text3 = text3.Replace("shield", "escudo"); text3 = text3.Replace("spear", "lança"); text3 = text3.Replace("atgier", "atgeir"); text3 = text3.Replace("crossbow", "besta"); text3 = text3.Replace("bronze", "bronze"); text3 = text3.Replace("iron", "ferro"); text3 = text3.Replace("leather", "couro"); text3 = text3.Replace("rag", "trapos"); text3 = text3.Replace("padded", "acolchoado"); text3 = text3.Replace("root", "raiz"); text3 = text3.Replace("trollleather", "couro de troll"); text3 = text3.Replace("troll leather", "couro de troll"); text3 = text3.Replace("blackmetal", "metal negro"); text3 = text3.Replace("black metal", "metal negro"); text3 = text3.Replace("silver", "prata"); text3 = text3.Replace("copper", "cobre"); text3 = text3.Replace("flint", "sílex"); text3 = text3.Replace("carapace", "carapaça"); text3 = text3.Replace("flametal", "flametal"); text3 = text3.Replace("mage", "mago"); text3 = text3.Replace("fenris", "fenris"); text3 = text3.Replace("dverger", "dvergr"); text3 = text3.Replace("drake", "draco"); while (text3.IndexOf(" ", StringComparison.Ordinal) >= 0) { text3 = text3.Replace(" ", " "); } text3 = text3.Trim(); if (text3.Length == 0) { return key; } return char.ToUpperInvariant(text3[0]) + text3.Substring(1); } private string StripRichText(string value) { if (string.IsNullOrWhiteSpace(value)) { return ""; } try { return Regex.Replace(value, "<.*?>", ""); } catch { return value; } } private string HumanizePrefabName(string key) { if (string.IsNullOrWhiteSpace(key)) { return "Regra"; } string text = key.Trim(); if (text.StartsWith("$", StringComparison.Ordinal)) { text = text.Substring(1); } int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } text = text.Replace("_", " ").Replace("-", " "); while (text.IndexOf(" ", StringComparison.Ordinal) >= 0) { text = text.Replace(" ", " "); } text = text.Trim(); if (text.Length == 0) { return key; } string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = array[i]; if (text2.Length <= 1) { array[i] = text2.ToUpperInvariant(); } else { array[i] = char.ToUpperInvariant(text2[0]) + text2.Substring(1).ToLowerInvariant(); } } return string.Join(" ", array); } private string FormatExplorationMapJackpotRulesForHud(Dictionary rules) { if (rules == null || rules.Count == 0) { return ""; } return string.Join(";", (from p in rules where p.Key > 0 && p.Value > 0 orderby p.Key select "Mapa " + Mathf.Clamp(p.Key, 1, 100) + "%:" + p.Value).ToArray()); } private string FormatDeathPenaltyRulesForHud(Dictionary rules) { if (rules == null || rules.Count == 0) { return ""; } return string.Join(",", (from x in rules orderby x.Key select Mathf.Max(1, x.Key) + ":" + Mathf.Max(0, x.Value)).ToArray()); } private string FormatSkillJackpotRulesForHud(Dictionary> rules) { if (rules == null || rules.Count == 0) { return ""; } List list = new List(); foreach (KeyValuePair> item in rules.OrderBy((KeyValuePair> x) => x.Key)) { if (item.Value == null) { continue; } foreach (KeyValuePair item2 in item.Value.OrderBy((KeyValuePair x) => x.Key)) { list.Add(SafeKey(item.Key) + ":" + Mathf.Max(1, item2.Key) + ":" + item2.Value); } } return string.Join(",", list.ToArray()); } private bool TryToggleInventoryRanking() { return false; } internal void EnsureInventoryRankingGui(InventoryGui gui) { DestroyInventoryRankingGui(); } private void DestroyInventoryRankingGui() { if ((Object)(object)_inventoryRankingButton != (Object)null) { try { Object.Destroy((Object)(object)_inventoryRankingButton); } catch { } _inventoryRankingButton = null; } if ((Object)(object)_inventoryRankingPanel != (Object)null) { try { Object.Destroy((Object)(object)_inventoryRankingPanel); } catch { } _inventoryRankingPanel = null; } _inventoryRankingOpen = false; } private void EnsureInventoryRankingGuiLegacy(InventoryGui gui) { if (!((Object)(object)gui == (Object)null) && (!((Object)(object)_inventoryRankingButton != (Object)null) || !((Object)(object)_inventoryRankingPanel != (Object)null))) { Transform root = (Transform)(((Object)(object)gui.m_player != (Object)null) ? ((object)gui.m_player) : ((object)((Component)gui).transform)); Transform transform = ((Component)gui).transform; _inventoryRankingButton = CreateInventoryRankingButton(gui, root); _inventoryRankingPanel = CreateInventoryRankingPanel(gui, transform); _inventoryRankingPanel.SetActive(false); UpdateInventoryRankingContent(force: true); } } internal void UpdateInventoryRankingGui(InventoryGui gui) { DestroyInventoryRankingGui(); } private void UpdateInventoryRankingGuiLegacy(InventoryGui gui) { if (!((Object)(object)gui == (Object)null)) { EnsureInventoryRankingGui(gui); bool flag = InventoryGui.IsVisible(); if ((Object)(object)_inventoryRankingButton != (Object)null && _inventoryRankingButton.activeSelf != flag) { _inventoryRankingButton.SetActive(flag); } if (!flag) { SetInventoryRankingOpen(open: false); } else if (_inventoryRankingOpen) { UpdateInventoryRankingContent(force: false); } } } private void SetInventoryRankingOpen(bool open) { _inventoryRankingOpen = open; if ((Object)(object)_inventoryRankingPanel != (Object)null) { if (_inventoryRankingPanel.activeSelf != open) { _inventoryRankingPanel.SetActive(open); } if (open) { _inventoryRankingPanel.transform.SetAsLastSibling(); } } if (!open) { return; } RequestSnapshotFromServer(); try { UpdateInventoryRankingContent(force: true); Canvas.ForceUpdateCanvases(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Glitnir Ranking] Falha ao atualizar conteudo do HUD de inventario: " + ex)); if ((Object)(object)_inventoryRankingBody != (Object)null) { _inventoryRankingBody.text = "GLITNIR RANKING\nFalha ao atualizar conteudo. Use Atualizar ou reabra o HUD."; } } } private GameObject CreateInventoryRankingButton(InventoryGui gui, Transform root) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown GameObject val = CloneNativeButton(gui, root, "GlitnirRanking_TabButton", "RANKING"); RectTransform component = val.GetComponent(); ((Transform)component).SetAsLastSibling(); component.anchorMin = new Vector2(1f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(1f, 1f); component.anchoredPosition = new Vector2(-18f, -20f); component.sizeDelta = new Vector2(112f, 30f); Button component2 = val.GetComponent