Initial Push
This commit is contained in:
107
.gitignore
vendored
Normal file
107
.gitignore
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# This .gitignore file should be placed at the root of your Unity project directory
|
||||||
|
#
|
||||||
|
# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore
|
||||||
|
#
|
||||||
|
# Recommended: add any editor/OS/tool-specific ignore rules from the Global/ templates as needed.
|
||||||
|
# See: https://github.com/github/gitignore/tree/main/Global
|
||||||
|
#
|
||||||
|
.utmp/
|
||||||
|
/[Ll]ibrary/
|
||||||
|
/[Tt]emp/
|
||||||
|
/[Oo]bj/
|
||||||
|
/[Bb]uild/
|
||||||
|
/[Bb]uilds/
|
||||||
|
/[Ll]ogs/
|
||||||
|
/[Uu]ser[Ss]ettings/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# By default unity supports Blender asset imports, *.blend1 blender files do not need to be commited to version control.
|
||||||
|
*.blend1
|
||||||
|
*.blend1.meta
|
||||||
|
|
||||||
|
# MemoryCaptures can get excessive in size.
|
||||||
|
# They also could contain extremely sensitive data
|
||||||
|
/[Mm]emoryCaptures/
|
||||||
|
|
||||||
|
# Recordings can get excessive in size
|
||||||
|
/[Rr]ecordings/
|
||||||
|
|
||||||
|
# Uncomment this line if you wish to ignore the asset store tools plugin
|
||||||
|
# /[Aa]ssets/AssetStoreTools*
|
||||||
|
|
||||||
|
# Autogenerated Jetbrains Rider plugin
|
||||||
|
/[Aa]ssets/Plugins/Editor/JetBrains*
|
||||||
|
# Jetbrains Rider personal-layer settings
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# Visual Studio cache directory
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Gradle cache directory
|
||||||
|
.gradle/
|
||||||
|
|
||||||
|
# Autogenerated VS/MD/Consulo solution and project files
|
||||||
|
ExportedObj/
|
||||||
|
.consulo/
|
||||||
|
*.csproj
|
||||||
|
*.unityproj
|
||||||
|
*.sln
|
||||||
|
*.slnx
|
||||||
|
*.suo
|
||||||
|
*.tmp
|
||||||
|
*.user
|
||||||
|
*.userprefs
|
||||||
|
*.pidb
|
||||||
|
*.booproj
|
||||||
|
*.svd
|
||||||
|
*.pdb
|
||||||
|
*.mdb
|
||||||
|
*.opendb
|
||||||
|
*.VC.db
|
||||||
|
|
||||||
|
# Unity3D generated meta files
|
||||||
|
*.pidb.meta
|
||||||
|
*.pdb.meta
|
||||||
|
*.mdb.meta
|
||||||
|
|
||||||
|
# Unity3D generated file on crash reports
|
||||||
|
sysinfo.txt
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Builds
|
||||||
|
*.apk
|
||||||
|
*.aab
|
||||||
|
*.unitypackage
|
||||||
|
*.unitypackage.meta
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# Crashlytics generated file
|
||||||
|
crashlytics-build.properties
|
||||||
|
|
||||||
|
# TestRunner generated files
|
||||||
|
InitTestScene*.unity*
|
||||||
|
|
||||||
|
# Addressables default ignores, before user customizations
|
||||||
|
/ServerData
|
||||||
|
/[Aa]ssets/StreamingAssets/aa*
|
||||||
|
/[Aa]ssets/AddressableAssetsData/link.xml*
|
||||||
|
/[Aa]ssets/Addressables_Temp*
|
||||||
|
# By default, Addressables content builds will generate addressables_content_state.bin
|
||||||
|
# files in platform-specific subfolders, for example:
|
||||||
|
# /Assets/AddressableAssetsData/OSX/addressables_content_state.bin
|
||||||
|
/[Aa]ssets/AddressableAssetsData/*/*.bin*
|
||||||
|
|
||||||
|
# Visual Scripting auto-generated files
|
||||||
|
/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db
|
||||||
|
/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db.meta
|
||||||
|
/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers
|
||||||
|
/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers.meta
|
||||||
|
|
||||||
|
# Auto-generated scenes by play mode tests
|
||||||
|
/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
|
||||||
|
|
||||||
|
# Auto-generated cache in Assets folder
|
||||||
|
/[Aa]ssets/[Ss]ceneDependencyCache*
|
||||||
|
|
||||||
15
.idea/.idea.Colorbook/.idea/.gitignore
generated
vendored
Normal file
15
.idea/.idea.Colorbook/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/contentModel.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/modules.xml
|
||||||
|
/.idea.Colorbook.iml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
7
.idea/.idea.Colorbook/.idea/encodings.xml
generated
Normal file
7
.idea/.idea.Colorbook/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
8
.idea/.idea.Colorbook/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.Colorbook/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
BIN
Assets/.DS_Store
vendored
Normal file
BIN
Assets/.DS_Store
vendored
Normal file
Binary file not shown.
8
Assets/Darkmatter.meta
Normal file
8
Assets/Darkmatter.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: db8662bfd07624a4a89c80b7441bae0e
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Darkmatter/.DS_Store
vendored
Normal file
BIN
Assets/Darkmatter/.DS_Store
vendored
Normal file
Binary file not shown.
8
Assets/Darkmatter/Code.meta
Normal file
8
Assets/Darkmatter/Code.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 77eb0c56d882f4031a235f0ea6202922
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Darkmatter/Code/.DS_Store
vendored
Normal file
BIN
Assets/Darkmatter/Code/.DS_Store
vendored
Normal file
Binary file not shown.
8
Assets/Darkmatter/Code/Core.meta
Normal file
8
Assets/Darkmatter/Code/Core.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 04fd8e2c965eb4fb49f4deca22ee7c1d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Core/Compatibility.meta
Normal file
8
Assets/Darkmatter/Code/Core/Compatibility.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1275b0c93b187472ab525938a5f6308a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#if !NET5_0_OR_GREATER
|
||||||
|
namespace System.Runtime.CompilerServices
|
||||||
|
{
|
||||||
|
// Enables C# 9 records/init-only members on older Unity/.NET profiles.
|
||||||
|
internal static class IsExternalInit
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 30b275c4b60da48f7a836b26c9576f6c
|
||||||
8
Assets/Darkmatter/Code/Core/Contracts.meta
Normal file
8
Assets/Darkmatter/Code/Core/Contracts.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bca93034b2c4d4539936e716adb298a9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Core/Contracts/Services.meta
Normal file
8
Assets/Darkmatter/Code/Core/Contracts/Services.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 100aa93d3292d46cda004ce163762e13
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7d0243ec32cd1418f96bd1a3930eadc3
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Darkmatter.Core.Contracts.Services.Assets
|
||||||
|
{
|
||||||
|
public interface IAssetProviderService
|
||||||
|
{
|
||||||
|
UniTask<GameObject> InstantiateAsync(
|
||||||
|
string assetId,
|
||||||
|
Vector3 position,
|
||||||
|
Quaternion rotation,
|
||||||
|
IProgress<float> progress,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
UniTask<GameObject> InstantiateAsync(
|
||||||
|
string assetId,
|
||||||
|
IProgress<float> progress,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
UniTask<T> LoadAssetAsync<T>(
|
||||||
|
string assetId,
|
||||||
|
IProgress<float> progress,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
UniTask<IReadOnlyList<T>> LoadAssetsAsync<T>(
|
||||||
|
IProgress<float> progress,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
UniTask<IReadOnlyList<T>> LoadAssetsAsync<T>(
|
||||||
|
object key,
|
||||||
|
IProgress<float> progress,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
void UnloadAsset(string assetId);
|
||||||
|
|
||||||
|
UniTask LoadExternalCatalogAsync(
|
||||||
|
string catalogUrl,
|
||||||
|
IProgress<float> progress,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
void ReleaseInstance(GameObject instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d3f25dd3dcb494f9dabbf23a0114b562
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9ecc165686aa0429daf34d55396c1bfa
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Darkmatter.Core.Data.Dynamic.Services.Audio;
|
||||||
|
using Darkmatter.Core.Enums.Services.Audio;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Darkmatter.Core.Contracts.Services.Audio
|
||||||
|
{
|
||||||
|
public interface IAudioService
|
||||||
|
{
|
||||||
|
AudioHandle Play(in AudioRequest request);
|
||||||
|
UniTask InitializeAsync(CancellationToken cancellationToken);
|
||||||
|
UniTask<AudioClip> LoadClipFromPath(string path, CancellationToken cancellationToken);
|
||||||
|
void Stop(AudioHandle handle, float fadeOutSeconds = 0f);
|
||||||
|
void SetPitch(AudioHandle handle, float pitch);
|
||||||
|
void SetVolume(AudioHandle handle, float volume01);
|
||||||
|
bool IsPlaying(AudioHandle handle);
|
||||||
|
void SetChannelPaused(AudioChannel channel, bool paused);
|
||||||
|
void SetChannelVolume(AudioChannel channel, float volume01);
|
||||||
|
void SetSnapshotWeights(params float[] weights);
|
||||||
|
void TransitionSnapshotWeights(float duration, params float[] weights);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4a49dff8ccb645b9b282b303b718fb5c
|
||||||
|
timeCreated: 1770634613
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Darkmatter.Core.Data.Dynamic.Services.Audio;
|
||||||
|
using Darkmatter.Core.Enums.Services.Audio;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Darkmatter.Core.Contracts.Services.Audio
|
||||||
|
{
|
||||||
|
public interface ISfxPlayer
|
||||||
|
{
|
||||||
|
AudioHandle Play(SfxId id, Transform follow = null);
|
||||||
|
AudioHandle PlayLoop(SfxId id, Transform follow = null);
|
||||||
|
void Stop(AudioHandle handle, float fadeOutSeconds = 0f);
|
||||||
|
bool IsPlaying(AudioHandle handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b2cab46f32d7466b9decc9c7ff94eb2
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1903ca430080472fbe36c5c1577bc502
|
||||||
|
timeCreated: 1770642710
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Darkmatter.Core.Enums.Services.Scenes;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace Darkmatter.Core.Contracts.Services.Scenes
|
||||||
|
{
|
||||||
|
public interface ISceneService
|
||||||
|
{
|
||||||
|
UniTask LoadSceneAsync(GameScene scene, IProgress<float> progress, CancellationToken cancellationToken);
|
||||||
|
UniTask UnloadSceneAsync(GameScene scene, IProgress<float> progress, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
UniTask LoadSceneAsync(
|
||||||
|
string sceneKey,
|
||||||
|
IProgress<float> progress,
|
||||||
|
CancellationToken cancellationToken,
|
||||||
|
bool setActiveScene = true);
|
||||||
|
|
||||||
|
UniTask UnloadSceneAsync(
|
||||||
|
string sceneKey,
|
||||||
|
IProgress<float> progress,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
bool TryGetLoadedScene(string sceneKey, out Scene scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c3ea399e8d284f538e256460c1def877
|
||||||
|
timeCreated: 1770642720
|
||||||
16
Assets/Darkmatter/Code/Core/Core.asmdef
Normal file
16
Assets/Darkmatter/Code/Core/Core.asmdef
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "Core",
|
||||||
|
"rootNamespace": "Darkmatter.Core",
|
||||||
|
"references": [
|
||||||
|
"GUID:f51ebe6a0ceec4240a699833d6309b23"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
Assets/Darkmatter/Code/Core/Core.asmdef.meta
Normal file
7
Assets/Darkmatter/Code/Core/Core.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6a0a834eb41764f12ba55c3fb04a40cb
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Core/Data.meta
Normal file
8
Assets/Darkmatter/Code/Core/Data.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5b2cc64aeeed544509ce5b2191154d78
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Core/Data/Dynamic.meta
Normal file
8
Assets/Darkmatter/Code/Core/Data/Dynamic.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e087f5b8739414488be6e94f4cf8f3ce
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Core/Data/Dynamic/Services.meta
Normal file
8
Assets/Darkmatter/Code/Core/Data/Dynamic/Services.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 56cb41057044542528a751d0be05d961
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e5b0eb514db9e488ea6cf383b759a2e4
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Darkmatter.Core.Data.Dynamic.Services.Audio
|
||||||
|
{
|
||||||
|
public readonly struct AudioHandle
|
||||||
|
{
|
||||||
|
public static readonly AudioHandle Invalid = new(0, 0);
|
||||||
|
|
||||||
|
public readonly int Id;
|
||||||
|
public readonly ushort Generation;
|
||||||
|
public bool IsValid => Id > 0;
|
||||||
|
|
||||||
|
public AudioHandle(int id, ushort generation)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Generation = generation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a062321bebaa428d84c9e6ce14edc4d9
|
||||||
|
timeCreated: 1770636619
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Darkmatter.Core.Enums.Services.Audio;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Darkmatter.Core.Data.Dynamic.Services.Audio
|
||||||
|
{
|
||||||
|
public readonly struct AudioRequest
|
||||||
|
{
|
||||||
|
public readonly AudioClip Clip;
|
||||||
|
public readonly AudioChannel Channel;
|
||||||
|
public readonly AudioPlayMode PlayMode;
|
||||||
|
public readonly bool StopChannelBeforePlay;
|
||||||
|
public readonly float Volume01;
|
||||||
|
public readonly float Pitch;
|
||||||
|
public readonly bool Spatial3D;
|
||||||
|
public readonly Transform FollowTarget;
|
||||||
|
public readonly float MinDistance;
|
||||||
|
public readonly float MaxDistance;
|
||||||
|
|
||||||
|
public AudioRequest(AudioClip clip, AudioChannel channel, AudioPlayMode mode,
|
||||||
|
bool stopChannelBeforePlay = false,
|
||||||
|
float volume01 = 1f, float pitch = 1f, bool spatial3D = false, Transform followTarget = null,
|
||||||
|
float minDistance = 1f, float maxDistance = 500f)
|
||||||
|
{
|
||||||
|
Clip = clip;
|
||||||
|
Channel = channel;
|
||||||
|
PlayMode = mode;
|
||||||
|
StopChannelBeforePlay = stopChannelBeforePlay;
|
||||||
|
Volume01 = volume01;
|
||||||
|
Pitch = pitch;
|
||||||
|
Spatial3D = spatial3D;
|
||||||
|
FollowTarget = followTarget;
|
||||||
|
MinDistance = minDistance;
|
||||||
|
MaxDistance = maxDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0d94a32bb4b841a88971f46d7fc3984b
|
||||||
|
timeCreated: 1770636723
|
||||||
8
Assets/Darkmatter/Code/Core/Data/Static.meta
Normal file
8
Assets/Darkmatter/Code/Core/Data/Static.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7e90b7ea022ae4de5962d79e5c36846d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Core/Data/Static/Services.meta
Normal file
8
Assets/Darkmatter/Code/Core/Data/Static/Services.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4e05cfff249d84f78a2dc81dc36772c2
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3346e766e438c4a46a5f2c70019c93da
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Darkmatter.Core.Enums.Services.Audio;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Darkmatter.Core.Data.Static.Services.Audio
|
||||||
|
{
|
||||||
|
[CreateAssetMenu(fileName = "SfxCatalog", menuName = "Darkmatter/Audio/Sfx Catalog")]
|
||||||
|
public class SfxCatalogSO : ScriptableObject
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class Entry
|
||||||
|
{
|
||||||
|
public SfxId Id;
|
||||||
|
public AudioClip Clip;
|
||||||
|
public AudioChannel Channel = AudioChannel.Sfx;
|
||||||
|
[Range(0f, 1f)] public float Volume = 1f;
|
||||||
|
[Range(0.1f, 3f)] public float Pitch = 1f;
|
||||||
|
public bool Loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField] private List<Entry> entries = new();
|
||||||
|
|
||||||
|
private Dictionary<SfxId, Entry> _index;
|
||||||
|
|
||||||
|
public bool TryGet(SfxId id, out Entry entry)
|
||||||
|
{
|
||||||
|
if (_index == null) BuildIndex();
|
||||||
|
return _index.TryGetValue(id, out entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildIndex()
|
||||||
|
{
|
||||||
|
_index = new Dictionary<SfxId, Entry>(entries.Count);
|
||||||
|
foreach (var e in entries)
|
||||||
|
{
|
||||||
|
if (e != null && e.Id != SfxId.None && e.Clip != null)
|
||||||
|
_index[e.Id] = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnValidate() => _index = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8dc05770caf7f49e9a59dab81283a278
|
||||||
8
Assets/Darkmatter/Code/Core/Enums.meta
Normal file
8
Assets/Darkmatter/Code/Core/Enums.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 393a0b77d95da48f3ba6251f868bcf96
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Core/Enums/Services.meta
Normal file
8
Assets/Darkmatter/Code/Core/Enums/Services.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e97b83c35ee0e4b0d91e6ab4a333abe5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Core/Enums/Services/Audio.meta
Normal file
8
Assets/Darkmatter/Code/Core/Enums/Services/Audio.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2a7ce1f001dba4bc680c52517e4b5c68
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Darkmatter.Core.Enums.Services.Audio
|
||||||
|
{
|
||||||
|
public enum AudioChannel
|
||||||
|
{
|
||||||
|
Sfx,
|
||||||
|
VehicleSfx,
|
||||||
|
AISfx,
|
||||||
|
Engine,
|
||||||
|
VehicleBrake,
|
||||||
|
PassengerChatter,
|
||||||
|
Music,
|
||||||
|
Ambience,
|
||||||
|
Weather,
|
||||||
|
BGM,
|
||||||
|
UI,
|
||||||
|
Radio
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d2031508f5c44c969e2a03200a867b36
|
||||||
|
timeCreated: 1770636948
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Darkmatter.Core.Enums.Services.Audio
|
||||||
|
{
|
||||||
|
public enum AudioPlayMode
|
||||||
|
{
|
||||||
|
OneShot,
|
||||||
|
Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 63f44faaf2da4474ba7c3850f21dd02f
|
||||||
|
timeCreated: 1770636914
|
||||||
16
Assets/Darkmatter/Code/Core/Enums/Services/Audio/SfxId.cs
Normal file
16
Assets/Darkmatter/Code/Core/Enums/Services/Audio/SfxId.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Darkmatter.Core.Enums.Services.Audio
|
||||||
|
{
|
||||||
|
public enum SfxId
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
WiperUp = 100,
|
||||||
|
WiperDown = 101,
|
||||||
|
|
||||||
|
BlinkerTick = 200,
|
||||||
|
|
||||||
|
GearShift = 300,
|
||||||
|
|
||||||
|
ReverseBeep = 400,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9a490eff4a4c14cbc9ee02b9fcd543b8
|
||||||
8
Assets/Darkmatter/Code/Core/Enums/Services/Scenes.meta
Normal file
8
Assets/Darkmatter/Code/Core/Enums/Services/Scenes.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 92344a3224a2f4e88ad7438669c9dfb9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace Darkmatter.Core.Enums.Services.Scenes
|
||||||
|
{
|
||||||
|
public enum GameScene
|
||||||
|
{
|
||||||
|
Boot,
|
||||||
|
MainMenu,
|
||||||
|
Gameplay,
|
||||||
|
TestTraffic,
|
||||||
|
VehicleTest,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2ae040ecfda14b5e9f4082294637b020
|
||||||
|
timeCreated: 1770642840
|
||||||
8
Assets/Darkmatter/Code/Libs.meta
Normal file
8
Assets/Darkmatter/Code/Libs.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea36b1548e1bb42feafed8de99668386
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Libs/FSM.meta
Normal file
8
Assets/Darkmatter/Code/Libs/FSM.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a60fee6d8a3034037ac8cc01c45baf11
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Libs/FSM/Docs.meta
Normal file
8
Assets/Darkmatter/Code/Libs/FSM/Docs.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 395d10529759141ddabeab8039459e38
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
19
Assets/Darkmatter/Code/Libs/FSM/Docs/FSMLib.md
Normal file
19
Assets/Darkmatter/Code/Libs/FSM/Docs/FSMLib.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# FSM Lib
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
`Darkmatter.Libs.FSM` holds reusable finite-state-machine primitives intended for feature-local orchestration without creating cross-feature dependencies.
|
||||||
|
|
||||||
|
## Public Entry Points
|
||||||
|
|
||||||
|
- FSM runtime types under `Assets/Darkmatter/Code/Libs/FSM`
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Intended to remain generic and feature-agnostic
|
||||||
|
- May be referenced by any feature that needs local state transitions
|
||||||
|
|
||||||
|
## Extension Notes
|
||||||
|
|
||||||
|
- Keep the API deterministic and side-effect free.
|
||||||
|
- Domain-specific states, transitions, or scene references belong in the consuming feature or service slice.
|
||||||
7
Assets/Darkmatter/Code/Libs/FSM/Docs/FSMLib.md.meta
Normal file
7
Assets/Darkmatter/Code/Libs/FSM/Docs/FSMLib.md.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fa62a43934add4beb92f51d9effd9d84
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
9
Assets/Darkmatter/Code/Libs/FSM/IState.cs
Normal file
9
Assets/Darkmatter/Code/Libs/FSM/IState.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Darkmatter.Libs.FSM
|
||||||
|
{
|
||||||
|
public interface IState
|
||||||
|
{
|
||||||
|
void Enter();
|
||||||
|
void Tick();
|
||||||
|
void Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Darkmatter/Code/Libs/FSM/IState.cs.meta
Normal file
2
Assets/Darkmatter/Code/Libs/FSM/IState.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2f814fe27db8b43349a93943bf71fcd5
|
||||||
14
Assets/Darkmatter/Code/Libs/FSM/Libs.FSM.asmdef
Normal file
14
Assets/Darkmatter/Code/Libs/FSM/Libs.FSM.asmdef
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "Libs.FSM",
|
||||||
|
"rootNamespace": "Darkmatter.Libs.FSM",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
Assets/Darkmatter/Code/Libs/FSM/Libs.FSM.asmdef.meta
Normal file
7
Assets/Darkmatter/Code/Libs/FSM/Libs.FSM.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c176ee863a5e74e88a6517f9f102cf92
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
15
Assets/Darkmatter/Code/Libs/FSM/State.cs
Normal file
15
Assets/Darkmatter/Code/Libs/FSM/State.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace Darkmatter.Libs.FSM
|
||||||
|
{
|
||||||
|
public abstract class State<T> : IState
|
||||||
|
{
|
||||||
|
protected readonly T runner;
|
||||||
|
protected State(T runner)
|
||||||
|
{
|
||||||
|
this.runner = runner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Enter() { }
|
||||||
|
public virtual void Tick() { }
|
||||||
|
public virtual void Exit() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Darkmatter/Code/Libs/FSM/State.cs.meta
Normal file
2
Assets/Darkmatter/Code/Libs/FSM/State.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e5d044f466d7a46379be2ebb74292a74
|
||||||
26
Assets/Darkmatter/Code/Libs/FSM/StateMachine.cs
Normal file
26
Assets/Darkmatter/Code/Libs/FSM/StateMachine.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
namespace Darkmatter.Libs.FSM
|
||||||
|
{
|
||||||
|
public abstract class StateMachine
|
||||||
|
{
|
||||||
|
public IState CurrentState { get; private set; }
|
||||||
|
public IState PreviousState { get; private set; }
|
||||||
|
|
||||||
|
public void ChangeState(IState newState)
|
||||||
|
{
|
||||||
|
if (newState == null) return;
|
||||||
|
if (CurrentState == newState) return;
|
||||||
|
|
||||||
|
CurrentState?.Exit();
|
||||||
|
|
||||||
|
PreviousState = CurrentState;
|
||||||
|
CurrentState = newState;
|
||||||
|
|
||||||
|
CurrentState.Enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Tick()
|
||||||
|
{
|
||||||
|
CurrentState?.Tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Darkmatter/Code/Libs/FSM/StateMachine.cs.meta
Normal file
2
Assets/Darkmatter/Code/Libs/FSM/StateMachine.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c9c75568b26bd4667ad07404934fc0c9
|
||||||
8
Assets/Darkmatter/Code/Libs/Installers.meta
Normal file
8
Assets/Darkmatter/Code/Libs/Installers.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d669ee184e2754ff6af32edd4c87fdbc
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Libs/Installers/Docs.meta
Normal file
8
Assets/Darkmatter/Code/Libs/Installers/Docs.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6e5889f574d4c4d1ca18fb324f33938d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
19
Assets/Darkmatter/Code/Libs/Installers/Docs/InstallersLib.md
Normal file
19
Assets/Darkmatter/Code/Libs/Installers/Docs/InstallersLib.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Installers Lib
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
`Darkmatter.Libs.Installers` provides the shared installer contract used by App and scene scopes to register feature/service modules through explicit DI composition.
|
||||||
|
|
||||||
|
## Public Entry Points
|
||||||
|
|
||||||
|
- `IServiceModule`
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Depends only on `VContainer` so modules can register against `IContainerBuilder`.
|
||||||
|
- Is consumed by App scopes (`RootLifetimeScope`, `GameplayLifetimeScope`, `MainMenuLifetimeScope`) and scene/entity scopes that register `MonoBehaviour` modules through explicit inspector wiring.
|
||||||
|
|
||||||
|
## Extension Notes
|
||||||
|
|
||||||
|
- Keep this lib minimal and contract-only. It should stay reusable by both feature and service slices without pulling in higher-layer code.
|
||||||
|
- New reusable installer helpers belong here only if they remain generic and do not encode game-specific behavior.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6c035b34d3ec341d58cdb799f2326799
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
9
Assets/Darkmatter/Code/Libs/Installers/IServiceModule.cs
Normal file
9
Assets/Darkmatter/Code/Libs/Installers/IServiceModule.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using VContainer;
|
||||||
|
|
||||||
|
namespace Darkmatter.Libs.Installers
|
||||||
|
{
|
||||||
|
public interface IServiceModule
|
||||||
|
{
|
||||||
|
void Register(IContainerBuilder builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a4fccd9c5f4934143929c601592e9616
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "Libs.Installers",
|
||||||
|
"rootNamespace": "Darkmatter.Libs.Installers",
|
||||||
|
"references": [
|
||||||
|
"GUID:b0214a6008ed146ff8f122a6a9c2f6cc"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c1c03c0e5b2f4412b9f2be1c20d6a9b1
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Libs/Observer.meta
Normal file
8
Assets/Darkmatter/Code/Libs/Observer.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e8fea732420104e728b3290bc8e9bfc1
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Libs/Observer/Docs.meta
Normal file
8
Assets/Darkmatter/Code/Libs/Observer/Docs.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 62a255738299947f5aa75ed233adb93a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
20
Assets/Darkmatter/Code/Libs/Observer/Docs/ObserverLib.md
Normal file
20
Assets/Darkmatter/Code/Libs/Observer/Docs/ObserverLib.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Observer Lib
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
`Darkmatter.Libs.Observer` provides the shared event-bus/observer primitives used for fire-and-forget notifications across slices.
|
||||||
|
|
||||||
|
## Public Entry Points
|
||||||
|
|
||||||
|
- `IEventBus`
|
||||||
|
- `EventBus`
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Used broadly by App, Features, and Services for cross-slice notifications
|
||||||
|
- Should remain infrastructure-only; business rules stay in the caller/subscriber
|
||||||
|
|
||||||
|
## Extension Notes
|
||||||
|
|
||||||
|
- Use this lib for notifications, not request/response flows.
|
||||||
|
- Preserve disposable subscription semantics so lifetime scopes can cleanly unsubscribe on teardown.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aeb1895d9f8424537b1a062bba8f6f14
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
75
Assets/Darkmatter/Code/Libs/Observer/EventBus.cs
Normal file
75
Assets/Darkmatter/Code/Libs/Observer/EventBus.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using VContainer;
|
||||||
|
|
||||||
|
namespace Darkmatter.Libs.Observer
|
||||||
|
{
|
||||||
|
public class EventBus : IEventBus
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
public EventBus() { }
|
||||||
|
|
||||||
|
private readonly Dictionary<Type, object> _map = new();
|
||||||
|
public void Publish<T>(T evt) where T : struct
|
||||||
|
{
|
||||||
|
if (_map.TryGetValue(typeof(T), out var handlerObj))
|
||||||
|
{
|
||||||
|
var action = (Action<T>)handlerObj;
|
||||||
|
action?.Invoke(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable Subscribe<T>(Action<T> handler) where T : struct
|
||||||
|
{
|
||||||
|
var t = typeof(T);
|
||||||
|
|
||||||
|
if (_map.TryGetValue(t, out var existingHandlers))
|
||||||
|
{
|
||||||
|
var action = (Action<T>)existingHandlers;
|
||||||
|
_map[t] = action + handler;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_map[t] = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Unsub<T>(this, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Unsubscribe<T>(Action<T> handler) where T : struct
|
||||||
|
{
|
||||||
|
var t = typeof(T);
|
||||||
|
if (_map.TryGetValue(t, out var existingHandlers))
|
||||||
|
{
|
||||||
|
var action = (Action<T>)existingHandlers;
|
||||||
|
action -= handler;
|
||||||
|
|
||||||
|
if (action == null)
|
||||||
|
_map.Remove(t);
|
||||||
|
else
|
||||||
|
_map[t] = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Unsub<T> : IDisposable where T : struct
|
||||||
|
{
|
||||||
|
private readonly EventBus _bus;
|
||||||
|
private Action<T> _handler;
|
||||||
|
|
||||||
|
public Unsub(EventBus bus, Action<T> handler)
|
||||||
|
{
|
||||||
|
_bus = bus;
|
||||||
|
_handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_handler != null)
|
||||||
|
{
|
||||||
|
_bus.Unsubscribe(_handler);
|
||||||
|
_handler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Darkmatter/Code/Libs/Observer/EventBus.cs.meta
Normal file
2
Assets/Darkmatter/Code/Libs/Observer/EventBus.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3360f02303be544ecbe8c21dc0c4e33e
|
||||||
18
Assets/Darkmatter/Code/Libs/Observer/IEventBus.cs
Normal file
18
Assets/Darkmatter/Code/Libs/Observer/IEventBus.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Darkmatter.Libs.Observer
|
||||||
|
{
|
||||||
|
public interface IEventBus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Publish an event of type T to all subscribers.
|
||||||
|
/// </summary>
|
||||||
|
void Publish<T>(T evt) where T : struct;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribe to an event of type T.
|
||||||
|
/// Returns IDisposable – calling Dispose() unsubscribes.
|
||||||
|
/// </summary>
|
||||||
|
IDisposable Subscribe<T>(Action<T> handler) where T : struct;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Darkmatter/Code/Libs/Observer/IEventBus.cs.meta
Normal file
2
Assets/Darkmatter/Code/Libs/Observer/IEventBus.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 22922a82d6e00458f8e34b78a9599a66
|
||||||
16
Assets/Darkmatter/Code/Libs/Observer/Libs.Observer.asmdef
Normal file
16
Assets/Darkmatter/Code/Libs/Observer/Libs.Observer.asmdef
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "Libs.Observer",
|
||||||
|
"rootNamespace": "Darkmatter.Libs.Observer",
|
||||||
|
"references": [
|
||||||
|
"VContainer"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b4c9f7fbf1e144933a1797dc208ece5f
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Libs/PlayerPrefs.meta
Normal file
8
Assets/Darkmatter/Code/Libs/PlayerPrefs.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f4c1e3acaf8f7448a847fd93afeb9123
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Libs/PlayerPrefs/Docs.meta
Normal file
8
Assets/Darkmatter/Code/Libs/PlayerPrefs/Docs.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 85a19104921a942afae4ed40c49ef826
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
# Protected PlayerPrefs Toolkit
|
||||||
|
|
||||||
|
Protected PlayerPrefs Toolkit is a lightweight Unity package for project-local data protection, key management, and editor-side inspection of stored values. It wraps Unity `PlayerPrefs`, hashes keys, encrypts values, and adds a guided setup flow so the package can be configured before it ships.
|
||||||
|
|
||||||
|
[[SCREENSHOT: Package Overview - Add a screenshot of the package files and folders inside the Unity Project window.]]
|
||||||
|
|
||||||
|
## What Is Included
|
||||||
|
|
||||||
|
- `ProtectedPlayerPrefs`: runtime API for string, int, float, and bool values.
|
||||||
|
- `ProtectedPlayerPrefsSettings`: project settings asset that stores the SHA-256 hash derived from your password.
|
||||||
|
- Getting Started window: first-run setup that appears automatically when the hash password has not been configured.
|
||||||
|
- `PlayerPrefsKeyRegistry`: registry asset for known keys and descriptions.
|
||||||
|
- `PlayerPrefs Editor`: editor window for registered keys, raw lookup, delete actions, and key-code generation.
|
||||||
|
- `PlayerPrefsKeys`: generated constants class that removes string literals from gameplay and service code.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
- Import the package into your project.
|
||||||
|
- If no hash password is configured, Unity creates the settings asset and opens `Tools/Darkmatter/Protected PlayerPrefs/Getting Started`.
|
||||||
|
- Enter a strong password and save it. The package stores only the SHA-256 hash, not the raw password.
|
||||||
|
- Keep the original password in a secure team password manager.
|
||||||
|
- Use `ProtectedPlayerPrefs` from runtime code and `Tools/Darkmatter/PlayerPrefs Editor` from the editor.
|
||||||
|
|
||||||
|
[[SCREENSHOT: Getting Started Window - Add a screenshot of the first-run setup window with the password fields and action buttons.]]
|
||||||
|
|
||||||
|
## Settings Asset and Hash Password
|
||||||
|
|
||||||
|
The package creates or reuses this asset path by default:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Assets/Darkmatter/Data/Settings/Persistance/Resources/ProtectedPlayerPrefsSettings.asset
|
||||||
|
```
|
||||||
|
|
||||||
|
Important behavior:
|
||||||
|
|
||||||
|
- The runtime loads the settings asset through `Resources`, so the configured hash is available in builds.
|
||||||
|
- If no configured hash exists, the runtime falls back to the built-in default passphrase for backward compatibility.
|
||||||
|
- Changing the password later changes the derived encryption key and IV. Existing protected values will no longer decrypt.
|
||||||
|
- The package stores only the SHA-256 hash of the password. The original password cannot be recovered from the asset.
|
||||||
|
|
||||||
|
[[SCREENSHOT: Settings Asset Inspector - Add a screenshot of the generated ProtectedPlayerPrefsSettings asset in the Inspector.]]
|
||||||
|
|
||||||
|
## Runtime API
|
||||||
|
|
||||||
|
Use `ProtectedPlayerPrefs` exactly like a narrow, protected subset of Unity `PlayerPrefs`.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Darkmatter.Libs.PlayerPrefs;
|
||||||
|
|
||||||
|
ProtectedPlayerPrefs.SetString(PlayerPrefsKeys.Accounts.SavedAuthRequest, json);
|
||||||
|
ProtectedPlayerPrefs.SetInt("Profile.Level", 12);
|
||||||
|
ProtectedPlayerPrefs.SetFloat("Audio.MasterVolume", 0.8f);
|
||||||
|
ProtectedPlayerPrefs.SetBool("Onboarding.Completed", true);
|
||||||
|
ProtectedPlayerPrefs.Save();
|
||||||
|
```
|
||||||
|
|
||||||
|
Reading values:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var savedAuth = ProtectedPlayerPrefs.GetString(PlayerPrefsKeys.Accounts.SavedAuthRequest, string.Empty);
|
||||||
|
var level = ProtectedPlayerPrefs.GetInt("Profile.Level", 1);
|
||||||
|
var volume = ProtectedPlayerPrefs.GetFloat("Audio.MasterVolume", 1f);
|
||||||
|
var onboardingDone = ProtectedPlayerPrefs.GetBool("Onboarding.Completed", false);
|
||||||
|
```
|
||||||
|
|
||||||
|
Available API surface:
|
||||||
|
|
||||||
|
- `Init(string passphrase)`: optional manual initialization from a raw password.
|
||||||
|
- `InitWithHash(string hashedPassphrase)`: optional manual initialization from a SHA-256 hash.
|
||||||
|
- `ComputePassphraseHash(string passphrase)`: helper used by the setup flow.
|
||||||
|
- `SetString`, `GetString`
|
||||||
|
- `SetInt`, `GetInt`
|
||||||
|
- `SetFloat`, `GetFloat`
|
||||||
|
- `SetBool`, `GetBool`
|
||||||
|
- `HasKey`, `DeleteKey`, `DeleteAll`, `Save`
|
||||||
|
|
||||||
|
## Key Registry and Code Generation
|
||||||
|
|
||||||
|
Use `PlayerPrefsKeyRegistry` to maintain a curated list of known keys, types, and descriptions. The editor window can generate a strongly named `PlayerPrefsKeys` class from the registry.
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
- Central place to document keys.
|
||||||
|
- Safer refactoring than raw string literals.
|
||||||
|
- Cleaner service and feature code.
|
||||||
|
- Easier QA and debugging in the editor window.
|
||||||
|
|
||||||
|
Example generated structure:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public static class PlayerPrefsKeys
|
||||||
|
{
|
||||||
|
public static class Accounts
|
||||||
|
{
|
||||||
|
public const string SavedAuthRequest = "Accounts.SavedAuthRequest";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SaveGame
|
||||||
|
{
|
||||||
|
public const string Session = "SaveGame.Session";
|
||||||
|
public const string Vehicle = "SaveGame.Vehicle";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## PlayerPrefs Editor
|
||||||
|
|
||||||
|
Open the tool from:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Tools/Darkmatter/PlayerPrefs Editor
|
||||||
|
```
|
||||||
|
|
||||||
|
The editor window includes three areas:
|
||||||
|
|
||||||
|
- Registered Keys: edit keys defined in the registry and save protected values.
|
||||||
|
- Raw Lookup: inspect or delete unprotected third-party or legacy keys by name.
|
||||||
|
- Settings: manage registry generation output and open the Protected PlayerPrefs setup flow.
|
||||||
|
|
||||||
|
[[SCREENSHOT: Registered Keys Tab - Add a screenshot of the Registered Keys tab with a few example entries.]]
|
||||||
|
|
||||||
|
[[SCREENSHOT: Raw Lookup Tab - Add a screenshot of the Raw Lookup tab showing a direct key lookup.]]
|
||||||
|
|
||||||
|
## Integration Notes
|
||||||
|
|
||||||
|
- Treat this package as protection against casual local tampering, not as a replacement for a trusted backend.
|
||||||
|
- Use generated keys for feature and service code whenever the key is known ahead of time.
|
||||||
|
- Save immediately after write operations that must survive app termination.
|
||||||
|
- For sensitive gameplay state, pair local protection with server validation when the product design requires trust.
|
||||||
|
|
||||||
|
## Recommended Workflow for Shipping
|
||||||
|
|
||||||
|
- Configure the hash password on first import.
|
||||||
|
- Register the keys that belong to your project.
|
||||||
|
- Generate `PlayerPrefsKeys.cs`.
|
||||||
|
- Replace raw string literals with generated constants.
|
||||||
|
- Run a smoke test with a fresh install and with existing saved data.
|
||||||
|
- Capture the screenshots marked in this documentation before publishing the package.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
Problem: protected values return defaults after you change the password.
|
||||||
|
|
||||||
|
- Cause: the encryption key changed because the stored hash changed.
|
||||||
|
- Fix: restore the original password or clear and rebuild the protected local cache.
|
||||||
|
|
||||||
|
Problem: the setup window keeps appearing.
|
||||||
|
|
||||||
|
- Cause: the settings asset exists, but its hash password is still blank.
|
||||||
|
- Fix: open the Getting Started window and save a password.
|
||||||
|
|
||||||
|
Problem: a key is visible in PlayerPrefs but not in the Registered Keys tab.
|
||||||
|
|
||||||
|
- Cause: the value exists, but the key is not in `PlayerPrefsKeyRegistry`.
|
||||||
|
- Fix: add the key to the registry or inspect it from Raw Lookup.
|
||||||
|
|
||||||
|
## Package Files
|
||||||
|
|
||||||
|
Core files:
|
||||||
|
|
||||||
|
- `Assets/Darkmatter/Code/Libs/PlayerPrefs/Runtime/ProtectedPlayerPrefs.cs`
|
||||||
|
- `Assets/Darkmatter/Code/Libs/PlayerPrefs/Runtime/ProtectedPlayerPrefsSettings.cs`
|
||||||
|
- `Assets/Darkmatter/Code/Libs/PlayerPrefs/Runtime/PlayerPrefsKeyRegistry.cs`
|
||||||
|
- `Assets/Darkmatter/Code/Libs/PlayerPrefs/Runtime/PlayerPrefsKeys.cs`
|
||||||
|
- `Assets/Darkmatter/Code/Libs/PlayerPrefs/Editor/PlayerPrefsEditorWindow.cs`
|
||||||
|
- `Assets/Darkmatter/Code/Libs/PlayerPrefs/Editor/ProtectedPlayerPrefsGettingStartedWindow.cs`
|
||||||
|
- `Assets/Darkmatter/Code/Libs/PlayerPrefs/Editor/ProtectedPlayerPrefsSetupBootstrap.cs`
|
||||||
|
|
||||||
|
## Final Checklist Before Asset Store Submission
|
||||||
|
|
||||||
|
- Verify the configured hash password is set.
|
||||||
|
- Remove project-specific sample keys that should not ship.
|
||||||
|
- Replace every screenshot placeholder in this PDF.
|
||||||
|
- Confirm the generated documentation opens correctly on a clean machine.
|
||||||
|
- Test the package in a fresh Unity project before export.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3c99b2d93e5a945328d6fc323c99142d
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/F1 2 0 R /F2 3 0 R /F3 4 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 13 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
9 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
10 0 obj
|
||||||
|
<<
|
||||||
|
/PageMode /UseNone /Pages 12 0 R /Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
11 0 obj
|
||||||
|
<<
|
||||||
|
/Author (Codex) /CreationDate (D:20260412135420+05'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260412135420+05'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||||
|
/Subject (\(unspecified\)) /Title (Protected PlayerPrefs Toolkit Documentation) /Trapped /False
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
12 0 obj
|
||||||
|
<<
|
||||||
|
/Count 5 /Kids [ 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R ] /Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
13 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1830
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gau`T9lo&I&A@sBlr!.@1r4+mQ;0X/8S@>,b4Leh!0`M_L]eOpZDrE3"\;7i/DKeb;qj*o9`iu'[r:.V9Ye\e@Xf.&^i+;(r'5c80OFg:]EmYL_#*rHNsdV!ZRgpJbOUIN$p=-D-Lg-W"c&IiILmh,Ufm&H4NX:J3/Y?kFro1Qj":d^0?IZfEgH,8RAHM4)t\1npObu!..n78b7!WI!4d7#pTiXIHqaB]%M%TW(Pa`bdD4gH$I&q&9";%gNlc(Y((01jeE]a,!qYk7s4&+M7;1&g(aHC)kMX>!"0sj[+>]nBgP;)i3;(@T=^%okP/04WC`:;NeWo'Q3G3Pt735XT+Gd]J@4(pR\=5H>5%?qQTr(2U@H*D#a#of44c(65VqlZ4^^P&%!uk*l+f$hanbp*?$/=hG-L9`,2hPLWoAocoN=KCPo/-r8Zc$n4ZICfcH[^QRWJRu,-A_j&%3p;@F/TdLW2o#PDjgD-?[8Jld.Z^-#RRUIhsl5"/IGZK8MrAdbaFj8.BM3?Lk!iLNk7u6Va,M6XK8dj1KB\K.^%KC%u:B;I#]i8d<`]p#?MhPP.%ALD\'MY&GG'W*7+L`6faF[XJf;WAc,BR]dJaTEn>'V>Njk9ASK"eDU>!g'[rNe(:DeH&ti<B+*Bk0gu=+IMe]dWIblhJ!b9TM<`Q35HTXE]_BaF_`Q2lAP.RLS,LqOknh#FV+5C(pE0rGUN-IXM+Z=?(q5Ktg3d,T7X]tBV'*cF#TPG%P8/e-,Tm%c@]<b5rh%Z#,Y#[k.$5IA'lB4_?.tZV[BYX$MlRn,:XltpZ9Kt5!!$RB-5q)`FA+Hd3>F<7]&lT&#i!&\l:"Q,>1&iT:$gX39>VhU#M`O11G8u?S`CmJlVf2-[[3d)&M1on!rl#%eAbRc+Nbfe236$:pW6K0X.>;jb6&+#&pW5V0+A,"f%5/GK"ORLADr@2a[A"SB'O:1&hB:4<G;"gaf)(+@pD&&tVEp0?[D$&Wm"fuS;J_Ql2bDIgUcKeLN;bdKf:[-B9/Q&CTm-Z[_faK`G^FjMKhZ8an`:J<JoZQ+5^V%m7;X$/B`rZ+58SPi+-3"'oa!_!jp^b80WbqX'/9.Y_!%\%!EA$]=>WQ1/*k=nd*;#tB;d+WScPmt\jIKD8C?d^D^RHq?#]O=aDqF3Tp_YSGGJNF5G$9Sj!6V4B@AU2fO4tCRS)%l4$@nBC*T"8+[Q".AAP'Yb:'A.h.Y3o^kD*ahSFBS\-:Ld42>M.!cI+VmH:LkTnA>U2`Vhij&NlPqU6p5@.,JOqneX6<qu*9%eO[ciYj7JF0XhMKWke[1)0Yd28dQR#m`oh)T3'+)(!(d%H#p>m3-B'1tQg"_U]mPb_0NI09C8`[1MXu4%0]rVAW2loCWb6Y*p*"A:'%`)/%B(#"IMjjQS2QlrlP"cH+BHlIl>lLgom(9f^M'7?(".1dSE:gnDT.]aH337%`&Q$7dZAA*02MKj'o?WF1`%P\^cJ=o.ch'#a%f7[;;hW`XIS35W#kCtTo6:Ti+8l5uQ2s/W"3;VrF!EV'ghotdf3NldB4[V>+T<T$)&Y2-:50mD'H"nnsk#)36TTj:Ci\K/_.XG(Pb31;c7a$knRq;Af)?So5rFnq'?2>0bGONWiSi6.m5HsDUTE^@!4R%.HW5Kh1C*8rW].kLoKZ-S/)&:.ul]H59;`qC(;i&HT6O*OYcJXE@4&%l;U,KX`:R%+],:Q2\>E$;8nIrcTSbmh=e5[&ePZoPe`r`^Sei"NcC*Q,;]dj>#oZ%rA1)0'NZ3nnUr1JQ)6oD#8>('!&SA"02UguPT,D8rBg\Tg9C+EeO9`IAVRQsK~>endstream
|
||||||
|
endobj
|
||||||
|
14 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1690
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gb"/&>Ar7S'RnB33%-Jr*>1ik?/(Qi&(Y/qicYjW*b^tIfuTmT;K;[(G=]i]in0*C'^Np^+?%0UF8<Ok'*SpHW;E2?!+(ZDr#Gt+_mf[bOq>VTiU-F$p4T5=Ree1h1^ba,J_7.OL<K<='h":2ILuC<U_0;'SA>9$0I2+sHW7j^?SohpS46i;'C?iD;<"tf^!+Ck5\M,a?4:Rlik4n,F&%$,D4SVc[#J_a:_o6CLbOhm$q<i-Sf9kI;9@BoN>+$&N28(DKR!maY;#t/.O$#SoJ"8r/OgV>aEg*5dhG\q)SLQ43K+S;GKdAjL<V]jOa4XuQ!m/'Wb8JYgJ:1gQ;[2s&g>K+<IhRWhMja^O=o%Y%5mi7ii_>MbL_:^OKU;lKR`%<.FMXIKIJ4]T*@QUJ9Zp;J`959QUZ^S0n'bY6UNtqI/MOTiBapE!htO4e=q.DL`5)-TW;=;?Ko!WQAM-XANAkPc[amk$eu!%0c,G?`rPe&;1FYK(b"bhHd@Z`eI8Sh;jR<q6oC5[3.p13$4QRX(Kl9VF!u^ik"O;?BQ!)]XI9MI@HR!*ag>AG2+9f;:,T6_p#ME<R+0-s8s;stK1iqc!TH89@3WI'[1o?E-0f5cUZ]UL(D7&$ojf5Q7]k_mo*;e'*VXmaU4i6(j3cST>uP_<lR7cFg:h4I*aCRQC)C[gaG'YFkrF]+>b6*tMXGi_=GB!.VW6O0gL0@bBi%2/RGf'[_A'.1rc5M)kNA!Onrkq)H,8dor,RI[6.iaDPAM(5/XX?o)DT3+4=>U88b<G_^%!n/PdinCWH6R_V9e$QFlThWr+XgZ#0l`O='B:TWgo\gE\2'lF*tR`=hU-/\O'P/:\BEtqVMbKMoARDFO=^bSD=LNNdrk.[LT[p]A'pe4@oAUleH%u:77tpm<TMXMS>rZEZjY#mP0%"-aY4nD1IC]Bi5AoJIr8"V.<Nl$<V5hTLgdm=0eFB_)Z,\DtFN#.bfT8_8.R6knN6b.`ADhfUM$3Nbl_l0i>5Ja[+RV,-]<3d07B8rE_b<m!,=Q<fbQ[4PH94"]Y$n,5`AqL-B`nD29TB=^k%Cap6DRBQ5e?#Zt<iY#-k8MJ8<pQ=+9*CtkQd\QiN@&/.nJZT\S#5Pu,gp8gFshq)pMc0R:"2onVXRL4(^`gc&/E@gMhIU;,N]l^UJL9Fmd^\CL\XX1oc=`u=G*@kp<]Fne4'Qt=[&p5\RkO]_pEfbQ2!9ClEB7nt]8_W)Va,*c\CBs4VLZ'+'6qq!s$cdu,!qrKj@b0rO/Nl+p.^p8Q<6OADa1CL(/X0L"-*@UrK5^$<^+'7Wr4%:Gj-gf3rr7o=rC(d?0=n+S&k`<Gcf%lF`[e3ZHR!fHcF4Gk4.Eq4l2Ar;9]&=o'#.LuVGTQ6T!`-9[XETa\p2pb)YX\*Dq]]XENqUDpT_%dcZ)S9>6't7.;l@0TjgX9eS23U,1r2OjSi)3'Bc'S_oe$J-hSJ_iP4)Ij7L^0>&2mM\(`D.-mkDT>l8a)P"WW>#SBbg/#,qeF[*=lpf`kiSkET2AJi^\@;Y.nr"]Nn3JFY?<nAudo`nK(<9AMr!`'2$Eq5@MnaaXJ<pDD`hr,VhD\)05OQ:V_0uD8B`:n=PJWlrj7`d/09@WK%23GCL@$p"4DA4Be8^q3Kc(ER=d9dE1@Y`k/\*Q;&Mne>sHJ=JHh$kE#rW*!X[:'~>endstream
|
||||||
|
endobj
|
||||||
|
15 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1760
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gau`UD/\/e&H;*)E<n86*AU*K7(otk#Id:RU)f]R?M>aQ(n%\;d#q%!cFfkNI7IF@A0lEL<JV;2[Uo#9*o9&(a&'#0Q^3,r"i./FS@tU]D4&7,?/i`^"'eM(?O%ia6"%;iK$^>N!+K8^%mF>1;IG_k[c?'!#E7$V$Xj([Fc"?]O!K[R9:_/h!OLS$de])/8utXY'jA*h#k_H[nj(G(V3NrlV3)N".O4I<Xo,^g..ddd)#CLfiK_QmLpWPdQ/l_SY*=``Qt2Vt:%U6/qur9kN?I5T6483`;'QI"0Ok-c<Ds0^C:*].As`=H[aam3QHo](B2PoFWTH7`dY>?QDEDeDC'qagKH\RH&qITb`f<i>+LS9Lel<V")ME?qK3sRMR9FQhc<bM0#?JB]j1ogc[D64h2Cd#/3"V2".nYe"_\H77o9G]'$9a02:h/+-Na)oSega7(f>GVgpL9\W;&!S1WcKrP$Mc6e(*_A.OI'1m`F>guf?[c>r@1f>YnHVX\p(-f*hM<!E?YS^?rini!c<iuAJ1\#+Qna.\</TtVFa7+kIDAn9=`0#Mqdg.oHrU5MIrJnkomU@0UP]'a*KmoSLp.*i(=\'F.%r@m!VCg@JEelM4&"%pu@i3fOQ%72SNrIV?QDM+NngXZaW';d>B<7haddCX*9XA.cuk<^14t=-MXtT<Qqr$NDSp82?Vs'R>T4mad0A@XGl+RlMGq`p?=r8?Q\fTj,jYuNJIC^J`43t_;j?>)D$0*0B\33lq8;c;&VWRCh`I&93C\j^m>25-<qntP^*V@aqhcYn[h]E?)FKp"+9G@n?H<Yq_QXk[lg;70[B3@Tp))RF8U4RS'ATD@M^_h,Co%N#,YgZ6Ap3KG#1=AS9#j?6:$`DdCNJ5SdWVn/s['lm&g_<oE`gkhOkg+$VVM-;lH:(V$o#$Q6$0_Khi3d$i:&1b?u\c[d+F@E<N:SrDEO'WofiP\R0V!OsA=4l]8d#Rms1e,5&gjJ/j,l=aq-EN/M!+p[L3ZVH*MEV'FXoA^^^)JCq$\IIql1[jaEr*Be'CD4mIT`O<pd&06GM@[:C.=p%Qun2d$G\.ko=mGVT:Z*%a.3?(B#EB(>CiV\gr#E\>5'3e\q:kR&td7A5P8lBIUU&rT[67Hn/A22l:fj+ro30'M\?7uJr(h]`jO&T"8.QN(%]KVgYQmOjmE0mBLljb52_JFj]5j;rIQHnP0;SGCBLFpdnQl8OIoU?'+<,kpKe=hW/Y2.\dn-\!.':j=a!*n9-nr.MNr)lg&j.NNY!F8<fG>+N#X6I6TLG#N08+>F$c2TMZZ#@Qoj:R-RBf,K&j4TAG<T>MAS9])sm=9m(4?XGhSNe,&;K,<>m$uTU_.YMJ&.>WK,p5P:l[jY2"9cBo&@gYDJcL0:<<RPFb[r1+O>[OcmI4Eu1mi]q<&m^'QN,4SrCK`%;\Ql6ABq=[M:'-7iB&dZ\GIntQGnhW,.%?NA5k;[A%Z_pahRL1^n8YjDQh-)(@H'UClPZ&l##js0@eu,SF!q/4*e(Uo@ruf60!oc9.Y2ba#K4EB%TVC[SfZhXC]O<$"(V5M58dd:d*RrbjaAYFLmM:k4I1g26W&ob1u4mMN6.e:4-<(:#9XOh92+Y9.J^mjdf>dhNnN1c9+<QkN]JiU<^K*a<UD3/t*H,5p(VNg)Pjc(<HUs4bQB5A:,\JQ_qfA)"hE%n+VXVVGe6;H.&qEo+I@pj[%IYP8'WjS?JC[^U`TaAc;(['phH2m6Uf*rr<&AA%M~>endstream
|
||||||
|
endobj
|
||||||
|
16 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1656
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gb"/'9on$e&A@P9R,bVG'N#s<S6N`@'Ffq1f0p3a&%"aZN$V=EWV-,FkE1q@b'ldu)VIc#^c&!^L@aQPS"U^RGVfA&e3lMSkg^VA/>C+@=:umdGXoM#c9@j(H(*nn6:!e2(s=@I?eLAjhOZThZ65+c=[ZDa#&HJm;b^th"Aq4k<70g[1nFrY4:RL:l1'om%Doqi3"N9c!]E]WE(5/%k'%]jgi"qVnmR`lb_C@[2Dh$0Bq-B-Ra0f+AOU#r8ae1D(](f\.mj]@Wi`Wmd,G]jIWQ)-&i^:3@fh_US:]8XJ/qoOb+3>7p9*R(blW(3/MFP[/a-89FL>d5[o,(.GGE\cScnPI;j7J8?tR:YYW<Z(pje#;6d6+7-@*Dt3V%FIj_]ML`o8OA.>U-qaCQH#*u@"Z+n]1A@9N;Sc]$]Q$&XBl]J>hYSg#_FnE\B[4Y!%8@8A\g^%2WGUN]=I]Cn,(`jA8N.8?Y;KGDuom4im0,B`gfjkB-n74$Ntf[Emf%5%>(n6,s%mJD@>U1Z_A3Z2+f_HPWoc"j>)j632U=rOI9ZkeYJSmnKVgNJm-;j(la"5j=<?NWdMY,,-pG#P&Da"SRET0.EaS[#tO]>rl]@Y:n4S5Ji1es=YlpP^A15nrE8Yqus-/4;)nPi?1ko/NC<0^d$9#\]Mmclh\8`G,?gQds8G$dk?$Z4=$(CfFCA4dLt=E#/<e^-S#D*D+53[ced!1Lb1]m)#YsB]S:d8*0t^dS'G9/;]S$fn`i-7,GA\Yhhs`JalA!86kbtXTUq.ZkVYi<d8=a,d*#$0C$,UjIWlE%Bl]Ne8"&a.96dHRr2A%A&,/P,Y=,m6^$ms8EPsP`6cjbaeJfLEf287U&5ZnD@@OGE,,^#J8$I?;cb@o<W;uAB*Z:<k4X7V1`n+Fd95Cf<^mQTTG`Gi.t;K==:^Si-e-I9fO'j_h*8/-YkdAZ0_RQ@6/^=B*Fb]G[s];.8`TU!bJ/[]Y;uH\V1P0HRB9G>:CG\a)Dl_*%<@@A*7a(0iC4VMWc?#C%an8s=gNb`d?IQB+.`nnl$[dBoVmiE0-^rY(<Yru%Yr.5@!AONf5$Am<t[AiW)Cu["61o*I1<`E6U%r<g&_55i2*VW3GDrBZf-+`DX@#;1CLr1,jSWu'&Zc:i[aXSg1cBE%g,F2U?H,,&@Vk]TIE,=Mg,U9'=ro$fkCL$V;X3EnC[&j$ht0EAY?<V3XF:RCJfh51,qLS(T;&ILRK76Zr_%`5N$k,.p;SGk=FeC#h-)3oU!G9Fb@j9gn>N("nC@ap(Vb8n8\De=ElV"^(>u"CRPk>pUART2l?GG]A1K9KiWVK^MJ?S3_Z`JZ.#W%c7c2#agG%aQc9In)_f'pS^NU]#0g;G>\)Y?4XGM-HO`$@e%p#@ILiG>LrN=l$58G"Zr`fAD#]1*Qib@njPiB(ns*D6NOk2D-ME3<U[tr^9-WiseXLecF$;YH\RRs&R).eOeFP-Y`6SH[HqM-FA$*aAN)R#$K.D$abq%7Yk"SK%d"3feXI4YJHOa<5$$#2JGBBe6(;_7<+bp^VR_m)C%:aZ`q4Ygc&D*jA#G+430+$Hg:I&WU_;r5&1j8_+n`?l98FOg>a&(%R(,3h,+1'/\s*KN2;;3ZQBf6GXhgkT$Hie'Y."Dh@FS5ldq*o?~>endstream
|
||||||
|
endobj
|
||||||
|
17 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1554
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gb!$H9ip@5'ScA[MRuQ/>0$28*-MK4ZR?2"dWX*!@On]1[cNW^+THnn'ZW0A0EsX2i>h?LmC^$VrK+0K^r9Yis!@ob`<+%8!lT`1$:s(XYdfE`MmKU":)q9Y(P!/p"%$I9beH^Po?V%qKZ%jn5Or1pLCT_A;^`as!lk[16TNqF+8)O,'SgSQH*eSIO#h/'kY>E2*ukms'/p!'5-qTf?tq(R3cQrI*%$Y.Nt[sO:?[IH7ffA(.5X[S9Aq4q>:+86>:EY[O`.c/"VtHQnZ2I.-U9s6D%'5qS:VEC)3@mf5]!69[biS?iPZm8A8/?CCBdiaW=)aAWjsN>Eh0\Q3,h\C"tt'b`(ZU1^ph5,)h4PK>#Qeq;Zk>hnJ(q8h"<-[d^n8[d(goh1!+Kr7AGPNoeL/0'12;2IA$MN[WeAbiQoGUmm;_]*aZOUNT'$f_TchjP>JhMLT:$u*R/As5VcWg&lQ04FflJ+Q"CZ;U4sqg,pLbTK8M.$04aA?R'bUC\oI&bLiNdk-XI(=DfK7UmfMLJ-)#J1Iq1<[_+>=2^#hgN?.)o]F)*T_'=k5==M]`/)]U:X=^mu-O!lS*%V8UNqtiMf7Ifm[FYl=?BZ;.>0l3rYQ@b3GbtFM7@=f_cjGZ^(AVuO$CS%H95\e1i(C@g<cm3+LOA4Hn5CYm;5#n\:0fpkpk@H.20a_$:#uXc:)('<pC&9:I[/VqF!C<*IL#snu"STWr<klJVf"eP*fF2HLcP:$rcZ<4N[+t<46r(OS%%@j'@\lNdH%u`E23fN:S*2T+T]dBuqNc.?\ZZ6e3*]e!JPU-r6RU`1`Gl0.H*F^rVjB`>@:SbrXYCZ[.>32VXTD4`4U_^nj1Dt!q=PUL3t@<V?H.8P)Km8ea9XTU+Da$jR>kj.o?!a;:1UdX.(@H"Hi6e`N55fS#/b$_:"'lG(A:Wa7*J9?93'r>JTV/t;A:g_pM__&VQTdbnUHWbmU5q(rJl!>EKg.WR0CK>m\O'C=%;nrL[GH]08SJ'X++gK+(kIKj!uT<"k?^u`g7l\j(lA.j%`X7kB3bL_8^>=d^eTfIJi-E"*#O6rh(<$qh)ai5E"$.Zg>/h2X6"A*=CUA6Hji_\Ud*6fj`egaSbi.UcOsB-.OXZrTFB?U8&t0P]CT=\jO)lJ*l[/+"9E6I`:T.*j&o==Zo]fRa!eZ^"\EU2W8SJkdi.7paGugTQk8+h?tKKbA3n"Xa9O/VXCKH/h_7#g2YC(WP`;eP"gro(G=@_m"?niCqn3eD7qL1;MSn;0?lTrb4GQ^9h4fM]>/o`jK#E=4t!(0nTEZZ9c\?<9_[0uHeo'2G_<8gZU3I#j/+IHgV93^K!0EN%<tb3[aW#U25;'h-L*_$D48UlT8pc-X?O:Jb^7LM94Y&m-J5NY0l4$m+rMmCJd+<,M*,?if@aZ8gg&*M&0^T6M0(R=hE>;QaMK:LR[$QEF)$n^^K7Xp3ssG/8dqU;gX6_54lS#dTLm/H5)5LN3WZ-l0MW"QFRs@bY0l>8_FcR70RoEoDtiK#b?Z-#&!`6)M@BkR9*Uo7")?2C`;~>endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 18
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000061 00000 n
|
||||||
|
0000000112 00000 n
|
||||||
|
0000000219 00000 n
|
||||||
|
0000000331 00000 n
|
||||||
|
0000000436 00000 n
|
||||||
|
0000000641 00000 n
|
||||||
|
0000000846 00000 n
|
||||||
|
0000001051 00000 n
|
||||||
|
0000001256 00000 n
|
||||||
|
0000001461 00000 n
|
||||||
|
0000001531 00000 n
|
||||||
|
0000001834 00000 n
|
||||||
|
0000001918 00000 n
|
||||||
|
0000003840 00000 n
|
||||||
|
0000005622 00000 n
|
||||||
|
0000007474 00000 n
|
||||||
|
0000009222 00000 n
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/ID
|
||||||
|
[<eece393ebfb26a539f7b54ebcb7e7fe0><eece393ebfb26a539f7b54ebcb7e7fe0>]
|
||||||
|
% ReportLab generated PDF document -- digest (opensource)
|
||||||
|
|
||||||
|
/Info 11 0 R
|
||||||
|
/Root 10 0 R
|
||||||
|
/Size 18
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
10868
|
||||||
|
%%EOF
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fad8e38235e6e45a3b3d7fecc3323ea7
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Darkmatter/Code/Libs/PlayerPrefs/Editor.meta
Normal file
8
Assets/Darkmatter/Code/Libs/PlayerPrefs/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af3bb097acf624c119d24f77a7c70eb3
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "Libs.PlayerPrefs.Editor",
|
||||||
|
"rootNamespace": "Darkmatter.Libs.PlayerPrefs.Editor",
|
||||||
|
"references": [
|
||||||
|
"Libs.PlayerPrefs"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3f4f71cd22f5047c78946bd1928ea7b1
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,559 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Darkmatter.Libs.PlayerPrefs.Editor
|
||||||
|
{
|
||||||
|
public class PlayerPrefsEditorWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private PlayerPrefsKeyRegistry _registry;
|
||||||
|
private Vector2 _scrollPos;
|
||||||
|
private Dictionary<string, string> _editValues = new();
|
||||||
|
private Dictionary<string, bool> _dirty = new();
|
||||||
|
|
||||||
|
// Add-key fields
|
||||||
|
private string _newKey = "";
|
||||||
|
private PlayerPrefType _newType = PlayerPrefType.String;
|
||||||
|
private string _newDescription = "";
|
||||||
|
|
||||||
|
private enum Tab { Registered, Raw, Settings }
|
||||||
|
private Tab _currentTab = Tab.Registered;
|
||||||
|
|
||||||
|
[MenuItem("Tools/Darkmatter/PlayerPrefs Editor")]
|
||||||
|
public static void Open()
|
||||||
|
{
|
||||||
|
var window = GetWindow<PlayerPrefsEditorWindow>("PlayerPrefs Editor");
|
||||||
|
window.minSize = new Vector2(450, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
FindRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FindRegistry()
|
||||||
|
{
|
||||||
|
var guids = AssetDatabase.FindAssets("t:PlayerPrefsKeyRegistry");
|
||||||
|
if (guids.Length > 0)
|
||||||
|
{
|
||||||
|
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||||
|
_registry = AssetDatabase.LoadAssetAtPath<PlayerPrefsKeyRegistry>(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
DrawToolbar();
|
||||||
|
|
||||||
|
switch (_currentTab)
|
||||||
|
{
|
||||||
|
case Tab.Registered:
|
||||||
|
DrawRegisteredKeys();
|
||||||
|
break;
|
||||||
|
case Tab.Raw:
|
||||||
|
DrawRawSection();
|
||||||
|
break;
|
||||||
|
case Tab.Settings:
|
||||||
|
DrawSettings();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Toolbar ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void DrawToolbar()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||||
|
|
||||||
|
if (GUILayout.Toggle(_currentTab == Tab.Registered, "Registered Keys", EditorStyles.toolbarButton))
|
||||||
|
_currentTab = Tab.Registered;
|
||||||
|
if (GUILayout.Toggle(_currentTab == Tab.Raw, "Raw Lookup", EditorStyles.toolbarButton))
|
||||||
|
_currentTab = Tab.Raw;
|
||||||
|
if (GUILayout.Toggle(_currentTab == Tab.Settings, "Settings", EditorStyles.toolbarButton))
|
||||||
|
_currentTab = Tab.Settings;
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
GUI.backgroundColor = new Color(1f, 0.4f, 0.4f);
|
||||||
|
if (GUILayout.Button("Delete All PlayerPrefs", EditorStyles.toolbarButton))
|
||||||
|
{
|
||||||
|
if (EditorUtility.DisplayDialog(
|
||||||
|
"Delete All PlayerPrefs",
|
||||||
|
"This will permanently delete ALL PlayerPrefs (both regular and protected). Are you sure?",
|
||||||
|
"Delete All", "Cancel"))
|
||||||
|
{
|
||||||
|
UnityEngine.PlayerPrefs.DeleteAll();
|
||||||
|
UnityEngine.PlayerPrefs.Save();
|
||||||
|
_editValues.Clear();
|
||||||
|
_dirty.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GUI.backgroundColor = Color.white;
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Registered Keys Tab ─────────────────────────────────
|
||||||
|
|
||||||
|
private void DrawRegisteredKeys()
|
||||||
|
{
|
||||||
|
// Registry selector
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
_registry = (PlayerPrefsKeyRegistry)EditorGUILayout.ObjectField(
|
||||||
|
"Key Registry", _registry, typeof(PlayerPrefsKeyRegistry), false);
|
||||||
|
if (_registry == null && GUILayout.Button("Create", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
CreateRegistry();
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
if (_registry == null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
"Assign or create a PlayerPrefsKeyRegistry asset to manage keys.",
|
||||||
|
MessageType.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(4);
|
||||||
|
|
||||||
|
// Add new key
|
||||||
|
DrawAddKeySection();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(4);
|
||||||
|
|
||||||
|
// Key list
|
||||||
|
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
|
||||||
|
|
||||||
|
for (int i = _registry.Entries.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var entry = _registry.Entries[i];
|
||||||
|
DrawKeyEntry(entry, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawAddKeySection()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Register New Key", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
|
||||||
|
_newKey = EditorGUILayout.TextField("Key", _newKey);
|
||||||
|
_newType = (PlayerPrefType)EditorGUILayout.EnumPopup("Type", _newType);
|
||||||
|
_newDescription = EditorGUILayout.TextField("Description", _newDescription);
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup(string.IsNullOrWhiteSpace(_newKey));
|
||||||
|
if (GUILayout.Button("Register Key"))
|
||||||
|
{
|
||||||
|
Undo.RecordObject(_registry, "Register PlayerPref Key");
|
||||||
|
_registry.AddKey(_newKey, _newType, _newDescription);
|
||||||
|
EditorUtility.SetDirty(_registry);
|
||||||
|
_newKey = "";
|
||||||
|
_newDescription = "";
|
||||||
|
GenerateKeysClass(_generatedOutputPath);
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawKeyEntry(PlayerPrefKeyEntry entry, int index)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
|
||||||
|
// Header row
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField(entry.Key, EditorStyles.boldLabel);
|
||||||
|
GUILayout.Label($"[{entry.Type}]", GUILayout.Width(50));
|
||||||
|
|
||||||
|
bool hasProtected = ProtectedPlayerPrefs.HasKey(entry.Key);
|
||||||
|
bool hasRaw = UnityEngine.PlayerPrefs.HasKey(entry.Key);
|
||||||
|
string source = hasProtected ? "Protected" : hasRaw ? "Raw" : "Not Set";
|
||||||
|
GUILayout.Label(source, EditorStyles.miniLabel, GUILayout.Width(70));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(entry.Description))
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField(entry.Description, EditorStyles.miniLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value display/edit
|
||||||
|
string currentValue = GetCurrentValue(entry);
|
||||||
|
string editKey = $"{entry.Key}_{index}";
|
||||||
|
|
||||||
|
if (!_editValues.ContainsKey(editKey))
|
||||||
|
_editValues[editKey] = currentValue;
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
|
string newVal = entry.Type switch
|
||||||
|
{
|
||||||
|
PlayerPrefType.Bool => EditorGUILayout.Toggle("Value",
|
||||||
|
_editValues[editKey] == "1" || _editValues[editKey].Equals("true", StringComparison.OrdinalIgnoreCase))
|
||||||
|
? "1" : "0",
|
||||||
|
_ => EditorGUILayout.TextField("Value", _editValues[editKey])
|
||||||
|
};
|
||||||
|
|
||||||
|
if (newVal != _editValues[editKey])
|
||||||
|
{
|
||||||
|
_editValues[editKey] = newVal;
|
||||||
|
_dirty[editKey] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
// Action buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup(!_dirty.ContainsKey(editKey) || !_dirty[editKey]);
|
||||||
|
if (GUILayout.Button("Save", GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
SaveValue(entry, _editValues[editKey]);
|
||||||
|
_dirty[editKey] = false;
|
||||||
|
GenerateKeysClass(_generatedOutputPath);
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Refresh", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
_editValues[editKey] = GetCurrentValue(entry);
|
||||||
|
_dirty[editKey] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.backgroundColor = new Color(1f, 0.6f, 0.4f);
|
||||||
|
if (GUILayout.Button("Delete Value", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
ProtectedPlayerPrefs.DeleteKey(entry.Key);
|
||||||
|
UnityEngine.PlayerPrefs.DeleteKey(entry.Key);
|
||||||
|
UnityEngine.PlayerPrefs.Save();
|
||||||
|
_editValues[editKey] = "";
|
||||||
|
_dirty[editKey] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Unregister", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Undo.RecordObject(_registry, "Unregister PlayerPref Key");
|
||||||
|
_registry.RemoveKey(entry.Key);
|
||||||
|
EditorUtility.SetDirty(_registry);
|
||||||
|
_editValues.Remove(editKey);
|
||||||
|
_dirty.Remove(editKey);
|
||||||
|
GenerateKeysClass(_generatedOutputPath);
|
||||||
|
}
|
||||||
|
GUI.backgroundColor = Color.white;
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetCurrentValue(PlayerPrefKeyEntry entry)
|
||||||
|
{
|
||||||
|
// Try protected first, then raw
|
||||||
|
if (ProtectedPlayerPrefs.HasKey(entry.Key))
|
||||||
|
{
|
||||||
|
return entry.Type switch
|
||||||
|
{
|
||||||
|
PlayerPrefType.String => ProtectedPlayerPrefs.GetString(entry.Key),
|
||||||
|
PlayerPrefType.Int => ProtectedPlayerPrefs.GetInt(entry.Key).ToString(),
|
||||||
|
PlayerPrefType.Float => ProtectedPlayerPrefs.GetFloat(entry.Key).ToString(),
|
||||||
|
PlayerPrefType.Bool => ProtectedPlayerPrefs.GetBool(entry.Key) ? "1" : "0",
|
||||||
|
_ => ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UnityEngine.PlayerPrefs.HasKey(entry.Key))
|
||||||
|
{
|
||||||
|
return entry.Type switch
|
||||||
|
{
|
||||||
|
PlayerPrefType.String => UnityEngine.PlayerPrefs.GetString(entry.Key),
|
||||||
|
PlayerPrefType.Int => UnityEngine.PlayerPrefs.GetInt(entry.Key).ToString(),
|
||||||
|
PlayerPrefType.Float => UnityEngine.PlayerPrefs.GetFloat(entry.Key).ToString(),
|
||||||
|
PlayerPrefType.Bool => UnityEngine.PlayerPrefs.GetInt(entry.Key) == 1 ? "1" : "0",
|
||||||
|
_ => ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveValue(PlayerPrefKeyEntry entry, string value)
|
||||||
|
{
|
||||||
|
switch (entry.Type)
|
||||||
|
{
|
||||||
|
case PlayerPrefType.String:
|
||||||
|
ProtectedPlayerPrefs.SetString(entry.Key, value);
|
||||||
|
break;
|
||||||
|
case PlayerPrefType.Int:
|
||||||
|
if (int.TryParse(value, out var intVal))
|
||||||
|
ProtectedPlayerPrefs.SetInt(entry.Key, intVal);
|
||||||
|
break;
|
||||||
|
case PlayerPrefType.Float:
|
||||||
|
if (float.TryParse(value, out var floatVal))
|
||||||
|
ProtectedPlayerPrefs.SetFloat(entry.Key, floatVal);
|
||||||
|
break;
|
||||||
|
case PlayerPrefType.Bool:
|
||||||
|
ProtectedPlayerPrefs.SetBool(entry.Key, value == "1");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ProtectedPlayerPrefs.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Raw Lookup Tab ──────────────────────────────────────
|
||||||
|
|
||||||
|
private string _rawKey = "";
|
||||||
|
private string _rawValue = "";
|
||||||
|
private string _rawType = "String";
|
||||||
|
|
||||||
|
private void DrawRawSection()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Raw PlayerPrefs Lookup", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
"Look up any PlayerPrefs key directly (unprotected). Useful for debugging third-party or legacy keys.",
|
||||||
|
MessageType.Info);
|
||||||
|
|
||||||
|
_rawKey = EditorGUILayout.TextField("Key", _rawKey);
|
||||||
|
_rawType = EditorGUILayout.TextField("Type (String/Int/Float)", _rawType);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Get"))
|
||||||
|
{
|
||||||
|
_rawValue = _rawType.ToLower() switch
|
||||||
|
{
|
||||||
|
"int" => UnityEngine.PlayerPrefs.GetInt(_rawKey).ToString(),
|
||||||
|
"float" => UnityEngine.PlayerPrefs.GetFloat(_rawKey).ToString(),
|
||||||
|
_ => UnityEngine.PlayerPrefs.GetString(_rawKey)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Has Key?"))
|
||||||
|
{
|
||||||
|
_rawValue = UnityEngine.PlayerPrefs.HasKey(_rawKey)
|
||||||
|
? "Key EXISTS"
|
||||||
|
: "Key NOT found";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Delete"))
|
||||||
|
{
|
||||||
|
UnityEngine.PlayerPrefs.DeleteKey(_rawKey);
|
||||||
|
UnityEngine.PlayerPrefs.Save();
|
||||||
|
_rawValue = "(deleted)";
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(4);
|
||||||
|
EditorGUILayout.LabelField("Result:", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.SelectableLabel(_rawValue, EditorStyles.textArea, GUILayout.MinHeight(40));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Settings Tab ────────────────────────────────────────
|
||||||
|
|
||||||
|
private string _generatedOutputPath = "Assets/Darkmatter/Code/Libs/PlayerPrefs/Runtime/PlayerPrefsKeys.cs";
|
||||||
|
|
||||||
|
private void DrawSettings()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("Registry Asset", EditorStyles.boldLabel);
|
||||||
|
_registry = (PlayerPrefsKeyRegistry)EditorGUILayout.ObjectField(
|
||||||
|
"Active Registry", _registry, typeof(PlayerPrefsKeyRegistry), false);
|
||||||
|
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
DrawProtectionSettings();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
EditorGUILayout.LabelField("Code Generation", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
"Generates a static PlayerPrefsKeys class so you can use PlayerPrefsKeys.Career.Level instead of string literals.",
|
||||||
|
MessageType.Info);
|
||||||
|
_generatedOutputPath = EditorGUILayout.TextField("Output Path", _generatedOutputPath);
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup(_registry == null || _registry.Entries.Count == 0);
|
||||||
|
if (GUILayout.Button("Generate PlayerPrefsKeys.cs"))
|
||||||
|
{
|
||||||
|
GenerateKeysClass(_generatedOutputPath);
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
EditorGUILayout.LabelField("Danger Zone", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
GUI.backgroundColor = new Color(1f, 0.3f, 0.3f);
|
||||||
|
if (GUILayout.Button("Delete ALL PlayerPrefs"))
|
||||||
|
{
|
||||||
|
if (EditorUtility.DisplayDialog(
|
||||||
|
"Delete All PlayerPrefs",
|
||||||
|
"This will permanently delete ALL PlayerPrefs. This cannot be undone.",
|
||||||
|
"Delete All", "Cancel"))
|
||||||
|
{
|
||||||
|
UnityEngine.PlayerPrefs.DeleteAll();
|
||||||
|
UnityEngine.PlayerPrefs.Save();
|
||||||
|
_editValues.Clear();
|
||||||
|
_dirty.Clear();
|
||||||
|
Debug.Log("[PlayerPrefs Editor] All PlayerPrefs deleted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GUI.backgroundColor = Color.white;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawProtectionSettings()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Protection Setup", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
var settingsAsset = global::Darkmatter.Libs.PlayerPrefs.Editor.ProtectedPlayerPrefsSettingsUtility.FindSettingsAsset();
|
||||||
|
using (new EditorGUI.DisabledScope(true))
|
||||||
|
{
|
||||||
|
EditorGUILayout.ObjectField(
|
||||||
|
"Settings Asset",
|
||||||
|
settingsAsset,
|
||||||
|
typeof(ProtectedPlayerPrefsSettings),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = settingsAsset != null && settingsAsset.HasConfiguredHash
|
||||||
|
? "Configured"
|
||||||
|
: "Missing";
|
||||||
|
EditorGUILayout.LabelField("Hash Password", status);
|
||||||
|
EditorGUILayout.LabelField("Runtime Source", ProtectedPlayerPrefs.InitializationSource);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Open Getting Started"))
|
||||||
|
{
|
||||||
|
global::Darkmatter.Libs.PlayerPrefs.Editor.ProtectedPlayerPrefsGettingStartedWindow.OpenWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup(settingsAsset == null);
|
||||||
|
if (GUILayout.Button("Ping Asset"))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(settingsAsset);
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Open Docs"))
|
||||||
|
{
|
||||||
|
global::Darkmatter.Libs.PlayerPrefs.Editor.ProtectedPlayerPrefsSettingsUtility.OpenDocumentationPdf();
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Code Generation ─────────────────────────────────────
|
||||||
|
|
||||||
|
private void GenerateKeysClass(string outputPath)
|
||||||
|
{
|
||||||
|
// Keys with a dot → nested class: "career.level" → PlayerPrefsKeys.Career.Level
|
||||||
|
// Keys without a dot → flat const on PlayerPrefsKeys directly: "volume" → PlayerPrefsKeys.Volume
|
||||||
|
var groups = new SortedDictionary<string, SortedDictionary<string, PlayerPrefKeyEntry>>();
|
||||||
|
var flat = new SortedDictionary<string, PlayerPrefKeyEntry>();
|
||||||
|
|
||||||
|
foreach (var entry in _registry.Entries)
|
||||||
|
{
|
||||||
|
var dotIndex = entry.Key.IndexOf('.');
|
||||||
|
if (dotIndex > 0)
|
||||||
|
{
|
||||||
|
var group = ToClassName(entry.Key.Substring(0, dotIndex));
|
||||||
|
var member = ToConstName(entry.Key.Substring(dotIndex + 1));
|
||||||
|
if (!groups.ContainsKey(group))
|
||||||
|
groups[group] = new SortedDictionary<string, PlayerPrefKeyEntry>();
|
||||||
|
groups[group][member] = entry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flat[ToConstName(entry.Key)] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("// <auto-generated>");
|
||||||
|
sb.AppendLine("// Generated by PlayerPrefs Editor — do not edit by hand.");
|
||||||
|
sb.AppendLine("// Re-generate via Tools > Darkmatter > PlayerPrefs Editor > Settings.");
|
||||||
|
sb.AppendLine("// </auto-generated>");
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLine("namespace Darkmatter.Libs.PlayerPrefs");
|
||||||
|
sb.AppendLine("{");
|
||||||
|
sb.AppendLine(" public static class PlayerPrefsKeys");
|
||||||
|
sb.AppendLine(" {");
|
||||||
|
|
||||||
|
foreach (var (constName, entry) in flat)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(entry.Description))
|
||||||
|
sb.AppendLine($" /// <summary>{entry.Description} ({entry.Type})</summary>");
|
||||||
|
sb.AppendLine($" public const string {constName} = \"{entry.Key}\";");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flat.Count > 0 && groups.Count > 0)
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
foreach (var (groupName, members) in groups)
|
||||||
|
{
|
||||||
|
sb.AppendLine($" public static class {groupName}");
|
||||||
|
sb.AppendLine(" {");
|
||||||
|
foreach (var (constName, entry) in members)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(entry.Description))
|
||||||
|
sb.AppendLine($" /// <summary>{entry.Description} ({entry.Type})</summary>");
|
||||||
|
sb.AppendLine($" public const string {constName} = \"{entry.Key}\";");
|
||||||
|
}
|
||||||
|
sb.AppendLine(" }");
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine(" }");
|
||||||
|
sb.AppendLine("}");
|
||||||
|
|
||||||
|
var fullPath = Path.Combine(
|
||||||
|
Path.GetDirectoryName(Application.dataPath),
|
||||||
|
outputPath.TrimStart('/'));
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
|
||||||
|
File.WriteAllText(fullPath, sb.ToString(), Encoding.UTF8);
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
|
||||||
|
Debug.Log($"[PlayerPrefs Editor] Generated {outputPath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToClassName(string segment)
|
||||||
|
{
|
||||||
|
var clean = Regex.Replace(segment, @"[^a-zA-Z0-9_]", "_");
|
||||||
|
return char.ToUpper(clean[0]) + clean.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToConstName(string segment)
|
||||||
|
{
|
||||||
|
// "some.nested.key" → "SomeNestedKey"
|
||||||
|
var parts = segment.Split('.');
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var part in parts)
|
||||||
|
{
|
||||||
|
var clean = Regex.Replace(part, @"[^a-zA-Z0-9_]", "_");
|
||||||
|
if (clean.Length == 0) continue;
|
||||||
|
sb.Append(char.ToUpper(clean[0]));
|
||||||
|
sb.Append(clean.Substring(1));
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helpers ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void CreateRegistry()
|
||||||
|
{
|
||||||
|
var path = EditorUtility.SaveFilePanelInProject(
|
||||||
|
"Create PlayerPrefs Key Registry",
|
||||||
|
"PlayerPrefsKeyRegistry",
|
||||||
|
"asset",
|
||||||
|
"Choose where to save the registry asset.");
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(path)) return;
|
||||||
|
|
||||||
|
var asset = CreateInstance<PlayerPrefsKeyRegistry>();
|
||||||
|
AssetDatabase.CreateAsset(asset, path);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
_registry = asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b322d8950c37b4852a2ce171c9b96e5b
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Darkmatter.Libs.PlayerPrefs.Editor
|
||||||
|
{
|
||||||
|
public class ProtectedPlayerPrefsGettingStartedWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private string _passphrase = string.Empty;
|
||||||
|
private string _confirmPassphrase = string.Empty;
|
||||||
|
private Vector2 _scrollPosition;
|
||||||
|
|
||||||
|
[MenuItem("Tools/Darkmatter/Protected PlayerPrefs/Getting Started")]
|
||||||
|
public static void OpenWindow()
|
||||||
|
{
|
||||||
|
var window = GetWindow<ProtectedPlayerPrefsGettingStartedWindow>(
|
||||||
|
true,
|
||||||
|
"Protected PlayerPrefs Setup",
|
||||||
|
true);
|
||||||
|
window.minSize = new Vector2(520f, 520f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("Protected PlayerPrefs Setup", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.Space(4);
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
"Set a project-specific hash password before shipping the package. The raw password is not stored - only its SHA-256 hash is saved in the settings asset.",
|
||||||
|
MessageType.Info);
|
||||||
|
|
||||||
|
DrawChecklist();
|
||||||
|
DrawSettingsSummary();
|
||||||
|
DrawPassphraseForm();
|
||||||
|
DrawActions();
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawChecklist()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Before You Save", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.LabelField("1. Use a unique project-specific password.");
|
||||||
|
EditorGUILayout.LabelField("2. Store it in your team password manager.");
|
||||||
|
EditorGUILayout.LabelField("3. Changing it later will make existing protected values unreadable.");
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawSettingsSummary()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Settings Asset", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
var settingsAsset = ProtectedPlayerPrefsSettingsUtility.GetOrCreateSettingsAsset();
|
||||||
|
using (new EditorGUI.DisabledScope(true))
|
||||||
|
{
|
||||||
|
EditorGUILayout.ObjectField(
|
||||||
|
"Asset",
|
||||||
|
settingsAsset,
|
||||||
|
typeof(ProtectedPlayerPrefsSettings),
|
||||||
|
false);
|
||||||
|
EditorGUILayout.TextField("Path", ProtectedPlayerPrefsSettingsUtility.GetSettingsPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = settingsAsset.HasConfiguredHash
|
||||||
|
? "Configured"
|
||||||
|
: "Hash password missing";
|
||||||
|
EditorGUILayout.LabelField("Status", status);
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawPassphraseForm()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Hash Password", EditorStyles.boldLabel);
|
||||||
|
_passphrase = EditorGUILayout.PasswordField("Password", _passphrase);
|
||||||
|
_confirmPassphrase = EditorGUILayout.PasswordField("Confirm", _confirmPassphrase);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Generate Strong Password"))
|
||||||
|
{
|
||||||
|
_passphrase = ProtectedPlayerPrefsSettingsUtility.GenerateRandomPassphrase();
|
||||||
|
_confirmPassphrase = _passphrase;
|
||||||
|
GUI.FocusControl(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Clear"))
|
||||||
|
{
|
||||||
|
_passphrase = string.Empty;
|
||||||
|
_confirmPassphrase = string.Empty;
|
||||||
|
GUI.FocusControl(null);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawActions()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Save Hash Password", GUILayout.Height(32f)))
|
||||||
|
{
|
||||||
|
SaveHashPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Open PlayerPrefs Editor", GUILayout.Height(32f)))
|
||||||
|
{
|
||||||
|
PlayerPrefsEditorWindow.Open();
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Ping Settings Asset"))
|
||||||
|
{
|
||||||
|
var settingsAsset = ProtectedPlayerPrefsSettingsUtility.GetOrCreateSettingsAsset();
|
||||||
|
EditorGUIUtility.PingObject(settingsAsset);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup(!ProtectedPlayerPrefsSettingsUtility.HasDocumentationPdf());
|
||||||
|
if (GUILayout.Button("Open Package Docs"))
|
||||||
|
{
|
||||||
|
ProtectedPlayerPrefsSettingsUtility.OpenDocumentationPdf();
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveHashPassword()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_passphrase))
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog(
|
||||||
|
"Hash Password Required",
|
||||||
|
"Enter a password before saving the Protected PlayerPrefs settings.",
|
||||||
|
"OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_passphrase.Length < 12)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog(
|
||||||
|
"Password Too Short",
|
||||||
|
"Use at least 12 characters so the generated hash is project-specific and hard to guess.",
|
||||||
|
"OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_passphrase != _confirmPassphrase)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog(
|
||||||
|
"Passwords Do Not Match",
|
||||||
|
"The confirmation value does not match the password.",
|
||||||
|
"OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtectedPlayerPrefsSettingsUtility.SavePassphrase(_passphrase);
|
||||||
|
ShowNotification(new GUIContent("Protected PlayerPrefs hash saved."));
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b13eca0e3647841f58589a5146434751
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Darkmatter.Libs.PlayerPrefs.Editor
|
||||||
|
{
|
||||||
|
internal static class ProtectedPlayerPrefsSettingsUtility
|
||||||
|
{
|
||||||
|
internal const string DefaultSettingsDirectory = "Assets/Darkmatter/Data/Settings/Persistance/Resources";
|
||||||
|
internal const string DefaultSettingsPath = DefaultSettingsDirectory + "/ProtectedPlayerPrefsSettings.asset";
|
||||||
|
internal const string DocumentationPdfPath =
|
||||||
|
"Assets/Darkmatter/Code/Libs/PlayerPrefs/Docs/ProtectedPlayerPrefsToolkit_Documentation.pdf";
|
||||||
|
|
||||||
|
internal static ProtectedPlayerPrefsSettings FindSettingsAsset()
|
||||||
|
{
|
||||||
|
var guids = AssetDatabase.FindAssets("t:ProtectedPlayerPrefsSettings");
|
||||||
|
if (guids.Length == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||||
|
return AssetDatabase.LoadAssetAtPath<ProtectedPlayerPrefsSettings>(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ProtectedPlayerPrefsSettings GetOrCreateSettingsAsset()
|
||||||
|
{
|
||||||
|
var existing = FindSettingsAsset();
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(DefaultSettingsDirectory);
|
||||||
|
|
||||||
|
var asset = ScriptableObject.CreateInstance<ProtectedPlayerPrefsSettings>();
|
||||||
|
AssetDatabase.CreateAsset(asset, DefaultSettingsPath);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool NeedsSetup()
|
||||||
|
{
|
||||||
|
var settings = GetOrCreateSettingsAsset();
|
||||||
|
return !settings.HasConfiguredHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SavePassphrase(string passphrase)
|
||||||
|
{
|
||||||
|
var settings = GetOrCreateSettingsAsset();
|
||||||
|
var hashedPassphrase = ProtectedPlayerPrefs.ComputePassphraseHash(passphrase);
|
||||||
|
settings.SetHashedPassphrase(hashedPassphrase);
|
||||||
|
EditorUtility.SetDirty(settings);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
ProtectedPlayerPrefs.InitWithHash(hashedPassphrase);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetSettingsPath()
|
||||||
|
{
|
||||||
|
var settings = FindSettingsAsset();
|
||||||
|
return settings == null ? DefaultSettingsPath : AssetDatabase.GetAssetPath(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool HasDocumentationPdf()
|
||||||
|
{
|
||||||
|
return File.Exists(Path.GetFullPath(DocumentationPdfPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void OpenDocumentationPdf()
|
||||||
|
{
|
||||||
|
var absolutePath = Path.GetFullPath(DocumentationPdfPath);
|
||||||
|
if (!File.Exists(absolutePath))
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog(
|
||||||
|
"Documentation Missing",
|
||||||
|
"The package PDF has not been generated yet.",
|
||||||
|
"OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorUtility.OpenWithDefaultApp(absolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GenerateRandomPassphrase()
|
||||||
|
{
|
||||||
|
return $"{System.Guid.NewGuid():N}{System.Guid.NewGuid():N}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e0a53d97fb18e40cf9c86a04e8ba50a8
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user