using System; using System.Collections.Generic; using UnityEngine; #if UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_STANDALONE_OSX || STEAMWORKS_WIN || STEAMWORKS_LIN_OSX using Steamworks; #endif public enum DlcEntitlementSource { None = 0, BuiltIn = 1, LocalFlag = 2, Steam = 3, RemoteCache = 4 } public readonly struct DlcEntitlementState { public readonly bool owned; public readonly bool installed; public readonly DlcEntitlementSource source; public readonly string dlcKey; public DlcEntitlementState(string key, bool ownedValue, bool installedValue, DlcEntitlementSource entitlementSource) { dlcKey = key ?? string.Empty; owned = ownedValue; installed = installedValue; source = entitlementSource; } } public static class DlcOwnershipService { private const string RemoteOwnershipPrefsPrefix = "dlc_remote_owned_"; private const string LocalOverridePrefsPrefix = "dlc_local_override_"; private static readonly Dictionary Cache = new Dictionary(StringComparer.OrdinalIgnoreCase); [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetState() { Cache.Clear(); } public static bool IsDlcOwned(dlcData dlc) { return GetEntitlement(dlc).owned; } public static bool IsDlcOwned(string dlcKey) { dlcData dlc = FindDlcByKey(dlcKey); if (dlc == null) { return true; } return IsDlcOwned(dlc); } public static DlcEntitlementState GetEntitlement(dlcData dlc) { if (dlc == null) { return new DlcEntitlementState(string.Empty, true, true, DlcEntitlementSource.BuiltIn); } string key = dlc.GetResolvedDlcKey(); if (Cache.TryGetValue(key, out DlcEntitlementState cached)) { return cached; } DlcEntitlementState resolved = ResolveEntitlement(dlc, key); Cache[key] = resolved; return resolved; } public static void SetRemoteOwned(string dlcKey, bool owned) { if (string.IsNullOrWhiteSpace(dlcKey)) { return; } string safeKey = dlcKey.Trim(); PlayerPrefs.SetInt(RemoteOwnershipPrefsPrefix + safeKey, owned ? 1 : 0); PlayerPrefs.Save(); Cache.Remove(safeKey); } public static void SetLocalOwnershipOverride(string dlcKey, bool? owned) { if (string.IsNullOrWhiteSpace(dlcKey)) { return; } string safeKey = dlcKey.Trim(); string prefsKey = LocalOverridePrefsPrefix + safeKey; if (owned.HasValue) { PlayerPrefs.SetInt(prefsKey, owned.Value ? 1 : 0); } else { PlayerPrefs.DeleteKey(prefsKey); } PlayerPrefs.Save(); Cache.Remove(safeKey); } public static void InvalidateCache() { Cache.Clear(); } public static void ClearLocalPersistence() { dlcData[] allDlcs = RuntimeResourcesCache.LoadAllDlcs(); if (allDlcs != null) { for (int i = 0; i < allDlcs.Length; i++) { dlcData dlc = allDlcs[i]; if (dlc == null) { continue; } string key = dlc.GetResolvedDlcKey(); if (string.IsNullOrWhiteSpace(key)) { continue; } PlayerPrefs.DeleteKey(RemoteOwnershipPrefsPrefix + key); PlayerPrefs.DeleteKey(LocalOverridePrefsPrefix + key); } } PlayerPrefs.Save(); Cache.Clear(); } private static DlcEntitlementState ResolveEntitlement(dlcData dlc, string key) { if (!dlc.ShouldEnforceEntitlement()) { return new DlcEntitlementState(key, true, dlc.builtInContent, DlcEntitlementSource.BuiltIn); } bool? localOverride = ReadOverride(key); if (localOverride.HasValue) { bool overrideValue = localOverride.Value; return new DlcEntitlementState(key, overrideValue, overrideValue, DlcEntitlementSource.LocalFlag); } if (dlc.dlcIsUnlocked) { return new DlcEntitlementState(key, true, true, DlcEntitlementSource.LocalFlag); } #if UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_STANDALONE_OSX || STEAMWORKS_WIN || STEAMWORKS_LIN_OSX if (dlc.steamAppId > 0 && SteamManager.Initialized) { try { AppId_t appId = new AppId_t((uint)Mathf.Max(0, dlc.steamAppId)); bool installed = SteamApps.BIsDlcInstalled(appId); if (installed) { return new DlcEntitlementState(key, true, true, DlcEntitlementSource.Steam); } } catch (Exception ex) { Debug.LogWarning("[DLC] Steam entitlement check failed for '" + key + "': " + ex.Message); } } #endif bool remoteOwned = PlayerPrefs.GetInt(RemoteOwnershipPrefsPrefix + key, 0) == 1; if (remoteOwned) { return new DlcEntitlementState(key, true, dlc.builtInContent, DlcEntitlementSource.RemoteCache); } return new DlcEntitlementState(key, false, false, DlcEntitlementSource.None); } private static bool? ReadOverride(string key) { string prefsKey = LocalOverridePrefsPrefix + key; if (!PlayerPrefs.HasKey(prefsKey)) { return null; } return PlayerPrefs.GetInt(prefsKey, 0) == 1; } private static dlcData FindDlcByKey(string dlcKey) { if (string.IsNullOrWhiteSpace(dlcKey)) { return null; } string safeKey = dlcKey.Trim(); dlcData[] allDlcs = RuntimeResourcesCache.LoadAllDlcs(); for (int i = 0; i < allDlcs.Length; i++) { dlcData dlc = allDlcs[i]; if (dlc == null) { continue; } if (string.Equals(dlc.GetResolvedDlcKey(), safeKey, StringComparison.OrdinalIgnoreCase)) { return dlc; } } return null; } }