├── Docs
├── DarkConfig.png
├── migrating_from_v1.md
├── tests.md
├── docnode.md
├── sources.md
└── validation.md
├── demo
├── Assets
│ ├── Demo
│ │ ├── Resources
│ │ │ ├── Configs
│ │ │ │ ├── enemy_spawning.bytes
│ │ │ │ ├── guns.bytes.meta
│ │ │ │ ├── index.bytes.meta
│ │ │ │ ├── player.bytes.meta
│ │ │ │ ├── Planes
│ │ │ │ │ ├── Whip.bytes.meta
│ │ │ │ │ ├── SmallPlanes.bytes.meta
│ │ │ │ │ ├── Whip.bytes
│ │ │ │ │ └── SmallPlanes.bytes
│ │ │ │ ├── Planes.meta
│ │ │ │ ├── enemy_spawning.bytes.meta
│ │ │ │ ├── player.bytes
│ │ │ │ ├── index.bytes
│ │ │ │ └── guns.bytes
│ │ │ └── Configs.meta
│ │ ├── Art
│ │ │ ├── plus.png
│ │ │ ├── puff.png
│ │ │ ├── wing.png
│ │ │ ├── Bullet.png
│ │ │ ├── circle.png
│ │ │ ├── fuselage.png
│ │ │ ├── City_Repeat.jpg
│ │ │ ├── stabilizer.png
│ │ │ ├── Clouds_Repeat.png
│ │ │ ├── plane.svg.meta
│ │ │ ├── puff.png.meta
│ │ │ ├── City_Repeat.jpg.meta
│ │ │ ├── Clouds_Repeat.png.meta
│ │ │ ├── wing.png.meta
│ │ │ ├── circle.png.meta
│ │ │ ├── plus.png.meta
│ │ │ ├── fuselage.png.meta
│ │ │ ├── Bullet.png.meta
│ │ │ └── stabilizer.png.meta
│ │ ├── Scenes
│ │ │ ├── Loader.unity.meta
│ │ │ └── PlaneDemo.unity.meta
│ │ ├── Art.meta
│ │ ├── Material
│ │ │ ├── City.mat.meta
│ │ │ ├── Clouds.mat.meta
│ │ │ ├── Clouds2.mat.meta
│ │ │ ├── Puff.mat.meta
│ │ │ ├── ShotFireParticle.mat.meta
│ │ │ ├── City.mat
│ │ │ ├── Clouds.mat
│ │ │ ├── Clouds2.mat
│ │ │ ├── Puff.mat
│ │ │ └── ShotFireParticle.mat
│ │ ├── Prefabs
│ │ │ ├── Enemy.prefab.meta
│ │ │ ├── Bullet.prefab.meta
│ │ │ ├── Pickup.prefab.meta
│ │ │ ├── PlaneView.prefab.meta
│ │ │ ├── Player.prefab.meta
│ │ │ ├── BulletHitFX.prefab.meta
│ │ │ ├── Title.prefab.meta
│ │ │ ├── Explosion.prefab.meta
│ │ │ ├── ShotFireFX.prefab.meta
│ │ │ ├── Player.prefab
│ │ │ ├── Enemy.prefab
│ │ │ ├── Bullet.prefab
│ │ │ └── Title.prefab
│ │ ├── Resources.meta
│ │ ├── Scripts
│ │ │ ├── Bullet.cs.meta
│ │ │ ├── GunCard.cs.meta
│ │ │ ├── LoadGame.cs.meta
│ │ │ ├── Parallax.cs.meta
│ │ │ ├── Pickup.cs.meta
│ │ │ ├── PlaneCard.cs.meta
│ │ │ ├── PlaneView.cs.meta
│ │ │ ├── AIController.cs.meta
│ │ │ ├── CameraFollow.cs.meta
│ │ │ ├── EnemySpawner.cs.meta
│ │ │ ├── PlaneController.cs.meta
│ │ │ ├── PlayerController.cs.meta
│ │ │ ├── Editor.meta
│ │ │ ├── Editor
│ │ │ │ ├── DemoEditorMenus.cs
│ │ │ │ └── DemoEditorMenus.cs.meta
│ │ │ ├── Location.cs.meta
│ │ │ ├── MetaGame.cs.meta
│ │ │ ├── CameraFollow.cs
│ │ │ ├── Pickup.cs
│ │ │ ├── Bullet.cs
│ │ │ ├── GunCard.cs
│ │ │ ├── Location.cs
│ │ │ ├── Parallax.cs
│ │ │ ├── PlayerController.cs
│ │ │ ├── LoadGame.cs
│ │ │ ├── AIController.cs
│ │ │ ├── PlaneView.cs
│ │ │ ├── EnemySpawner.cs
│ │ │ ├── PlaneCard.cs
│ │ │ ├── MetaGame.cs
│ │ │ └── PlaneController.cs
│ │ ├── generate_random_configs.py.meta
│ │ ├── Material.meta
│ │ ├── Prefabs.meta
│ │ ├── Scenes.meta
│ │ ├── Scripts.meta
│ │ └── generate_random_configs.py
│ ├── Editor
│ │ ├── Tests
│ │ │ └── UnitySpecificTests.cs.meta
│ │ └── Tests.meta
│ ├── Demo.meta
│ ├── Editor.meta
│ ├── DarkConfig.meta
│ ├── DarkConfig
│ │ ├── Editor.meta
│ │ ├── UnityTypeReifiers.cs.meta
│ │ ├── Editor
│ │ │ ├── EditorUtils.cs.meta
│ │ │ └── EditorUtils.cs
│ │ ├── UnityPlatform.cs.meta
│ │ ├── ResourcesSource.cs.meta
│ │ ├── UnityPlatform.cs
│ │ └── ResourcesSource.cs
│ └── Plugins.meta
├── ProjectSettings
│ ├── ProjectVersion.txt
│ ├── ClusterInputManager.asset
│ ├── PresetManager.asset
│ ├── NetworkManager.asset
│ ├── XRSettings.asset
│ ├── TimeManager.asset
│ ├── VersionControlSettings.asset
│ ├── AudioManager.asset
│ ├── EditorBuildSettings.asset
│ ├── VFXManager.asset
│ ├── Physics2DSettings.asset
│ ├── DynamicsManager.asset
│ ├── TagManager.asset
│ ├── UnityConnectSettings.asset
│ ├── PackageManagerSettings.asset
│ ├── EditorSettings.asset
│ ├── MemorySettings.asset
│ ├── GraphicsSettings.asset
│ ├── NavMeshAreas.asset
│ ├── NavMeshLayers.asset
│ └── QualitySettings.asset
└── Packages
│ └── manifest.json
├── .github
└── workflows
│ ├── test.yaml
│ └── build.yaml
├── src
└── DarkConfig
│ ├── ConfigProcessor.cs
│ ├── Internal
│ ├── BuiltInTypeReifiers.cs
│ ├── StringExtensions.cs
│ ├── RegexUtils.cs
│ ├── MultiCaseDictionary.cs
│ └── ChecksumUtils.cs
│ ├── DarkConfig.csproj
│ ├── ConfigFileInfo.cs
│ ├── ConfigSource.cs
│ ├── Settings.cs
│ ├── Exceptions.cs
│ └── Attributes.cs
├── .gitignore
├── test
├── DarkConfig.Tests.csproj
├── MissingFilesTests.cs
├── YamlParseTests.cs
├── DictComposingTests.cs
├── GlobMatchTests.cs
├── PostDocTests.cs
├── ListComposingTests.cs
├── DocNodeExtensionTests.cs
├── ConfigKeyTests.cs
└── FromDocTests.cs
├── THANKS.md
├── DarkConfig.sln
├── LICENSE.txt
├── .editorconfig
└── CONTRIBUTING.md
/Docs/DarkConfig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/Docs/DarkConfig.png
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/enemy_spawning.bytes:
--------------------------------------------------------------------------------
1 | NumEnemies: 5
2 | SpawnDistanceFromPlayer: 30
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/plus.png
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/puff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/puff.png
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/wing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/wing.png
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/Bullet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/Bullet.png
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/circle.png
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/fuselage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/fuselage.png
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/City_Repeat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/City_Repeat.jpg
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/stabilizer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/stabilizer.png
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/Clouds_Repeat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpryFox/DarkConfig/HEAD/demo/Assets/Demo/Art/Clouds_Repeat.png
--------------------------------------------------------------------------------
/demo/ProjectSettings/ProjectVersion.txt:
--------------------------------------------------------------------------------
1 | m_EditorVersion: 2022.3.40f1
2 | m_EditorVersionWithRevision: 2022.3.40f1 (cbdda657d2f0)
3 |
--------------------------------------------------------------------------------
/demo/Assets/Editor/Tests/UnitySpecificTests.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1f758bbe1fd24432800783013864594c
3 | timeCreated: 1639167736
--------------------------------------------------------------------------------
/demo/Assets/Demo.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 68805e3e4576d45b1bfbd6c9baa37348
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scenes/Loader.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a55d93e2b22ab4e3cbefce36b8389251
3 | DefaultImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b7927d779e32e4ae3a88a9082013d029
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/City.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0d7080b5bd26a4eb9ae1b366ea572675
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/Clouds.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: da99b5f5a9d4f43089b7baa8e11ffd6b
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/Clouds2.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: fc6a0f788423249f1921dfcc8ef8e08e
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/Puff.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9c85f01521d4342ff879d9d991cc48e6
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Enemy.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8527b6abad74f496383a24951dacb910
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scenes/PlaneDemo.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 65f06b13702854bbe9b379035e179cf8
3 | DefaultImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3c0bb187dcde8a0488bb192dffc2a1d7
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ca58de63c989f9646843644faba40d84
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Bullet.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8303d936d7f9b42f58a0f2e360412a31
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Pickup.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 95671f6452fc34383a438f7ea4d1448f
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/PlaneView.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 753a9c9756a404422b47e71325b95822
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Player.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5c37ec5de83b64fe8a6082ccc59ab1b3
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d549bfae05a2543bd9433913f767fc78
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/Editor/Tests.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 46950cd94de97e94cafd75184c515ea2
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b2c17f306770a443082f271fb23f74ea
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/BulletHitFX.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e0efc93d67d144caba51a4eca4d6ea12
3 | NativeFormatImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/guns.bytes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f5fe4ce5a50524c0ba527d3c1bc08113
3 | TextScriptImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/index.bytes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 373f8dc58fcb640dfb824976659aa9cf
3 | TextScriptImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/player.bytes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 89c7dacda5da649569b2e0bc0960217a
3 | TextScriptImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3c804828e85cb480693b08e35843e44d
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/Planes/Whip.bytes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6687ca752e5664b93a4db13255548d84
3 | TextScriptImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/Planes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bdb7faf752d144a53b6ab7484e0e336b
3 | folderAsset: yes
4 | DefaultImporter:
5 | userData:
6 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/Planes/SmallPlanes.bytes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 64f34bd932d7a48df87989f43dbd7eaa
3 | TextScriptImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/enemy_spawning.bytes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 531f54898f56f45e285cdfcdd555936a
3 | TextScriptImporter:
4 | userData:
5 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/ClusterInputManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!236 &1
4 | ClusterInputManager:
5 | m_ObjectHideFlags: 0
6 | m_Inputs: []
7 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/player.bytes:
--------------------------------------------------------------------------------
1 | StartingCard: Whip
2 | Keyboard:
3 | Left: LeftArrow
4 | Right: RightArrow
5 | Boost: UpArrow
6 | Slow: DownArrow
7 | Fire: Space
--------------------------------------------------------------------------------
/demo/ProjectSettings/PresetManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1386491679 &1
4 | PresetManager:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | m_DefaultPresets: {}
8 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/index.bytes:
--------------------------------------------------------------------------------
1 | # automatically generated DarkConfig index file
2 | #
3 | ---
4 | - enemy_spawning
5 | - guns
6 | - index
7 | - player
8 | - Planes/SmallPlanes
9 | - Planes/Whip
10 |
--------------------------------------------------------------------------------
/demo/Assets/Plugins.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0ab8149a2d2694eabb19a91f88f48302
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/NetworkManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!149 &1
4 | NetworkManager:
5 | m_ObjectHideFlags: 0
6 | m_DebugLevel: 0
7 | m_Sendrate: 15
8 | m_AssetToPrefab: {}
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/plane.svg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c5016da2ebde449b4be5490d7df02f6a
3 | timeCreated: 1487532903
4 | licenseType: Free
5 | DefaultImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/XRSettings.asset:
--------------------------------------------------------------------------------
1 | {
2 | "m_SettingKeys": [
3 | "VR Device Disabled",
4 | "VR Device User Alert"
5 | ],
6 | "m_SettingValues": [
7 | "False",
8 | "False"
9 | ]
10 | }
--------------------------------------------------------------------------------
/demo/ProjectSettings/TimeManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!5 &1
4 | TimeManager:
5 | m_ObjectHideFlags: 0
6 | Fixed Timestep: .0199999996
7 | Maximum Allowed Timestep: .333333343
8 | m_TimeScale: 1
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Title.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 20e534b0792bb4f08b0484e334d25fab
3 | timeCreated: 1452465365
4 | licenseType: Pro
5 | NativeFormatImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Bullet.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 75c0ac8114f2e4a4f8f0565b350c5183
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/GunCard.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2aace6748d2d74811804d972224d4300
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/LoadGame.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 922c3748c48304a54a7f4630ec12471c
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Parallax.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3b9914a9ae2d943959bd096fc9693f2d
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Pickup.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ae9b4960a1fef4f3a9f837a0cca1816c
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/PlaneCard.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1f1dd201afcfb40bcbc6ab907018bacf
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/PlaneView.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e44d58f6a31b44603b3c1ffbd9674cc6
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig/UnityTypeReifiers.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 509811cd1f99c4529a81cbec8e44483b
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Explosion.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a61f344ddc6c9416a97212247cfe3afa
3 | timeCreated: 1452410711
4 | licenseType: Pro
5 | NativeFormatImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/ShotFireFX.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cd082a60c9ec5414eb443362175370a9
3 | timeCreated: 1452474579
4 | licenseType: Pro
5 | NativeFormatImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/AIController.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e746dc99bf5984be5abc08aec0e62d4c
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/CameraFollow.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 05c24164f4871438fac069bc9648a92e
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/EnemySpawner.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 34d9b84e171d2450b8bf84a020c56e1e
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/PlaneController.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d792d93ca589c474196eb88a2f1ad3fd
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/generate_random_configs.py.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 822521391086c444ea914bc603be5bb7
3 | timeCreated: 1453367792
4 | licenseType: Pro
5 | DefaultImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig/Editor/EditorUtils.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 392929c4109b341d0a2c5fc1b4f340b4
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/ShotFireParticle.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f121850699ab842d7a75e4153c5da46f
3 | timeCreated: 1452474782
4 | licenseType: Pro
5 | NativeFormatImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/PlayerController.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 74880b4d8c9c547a59e921a550dbe9ea
3 | MonoImporter:
4 | serializedVersion: 2
5 | defaultReferences: []
6 | executionOrder: 0
7 | icon: {instanceID: 0}
8 | userData:
9 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8c61b9626853e4b0e98efe1f852c25ac
3 | folderAsset: yes
4 | timeCreated: 1452660066
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3e99c20d66d1b4ed0aa763991b030643
3 | folderAsset: yes
4 | timeCreated: 1452659954
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scenes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b87f3c31c0e454cf18bbffd16e7aab3b
3 | folderAsset: yes
4 | timeCreated: 1452659925
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c190061a7c4804e2e991c01d2d5ed8dc
3 | folderAsset: yes
4 | timeCreated: 1452660030
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: da1c6e5fc861c45b1b4e015a721ac209
3 | folderAsset: yes
4 | timeCreated: 1462951262
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/VersionControlSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!890905787 &1
4 | VersionControlSettings:
5 | m_ObjectHideFlags: 0
6 | m_Mode: Visible Meta Files
7 | m_CollabEditorSettings:
8 | inProgressEnabled: 1
9 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/AudioManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!11 &1
4 | AudioManager:
5 | m_ObjectHideFlags: 0
6 | m_Volume: 1
7 | Rolloff Scale: 1
8 | m_SpeedOfSound: 347
9 | Doppler Factor: 1
10 | Default Speaker Mode: 2
11 | m_DSPBufferSize: 0
12 | m_DisableAudio: 0
13 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/EditorBuildSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1045 &1
4 | EditorBuildSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | m_Scenes:
8 | - enabled: 1
9 | path: Assets/Demo/Scenes/Loader.unity
10 | - enabled: 1
11 | path: Assets/Demo/Scenes/PlaneDemo.unity
12 |
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig/UnityPlatform.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0d338fa5147d441c5a15d75d36674b3f
3 | timeCreated: 1475697181
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Editor/DemoEditorMenus.cs:
--------------------------------------------------------------------------------
1 | using UnityEditor;
2 | using DarkConfig;
3 |
4 | public static class DemoEditorMenus {
5 | [MenuItem("Assets/DarkConfig/Autogenerate Index")]
6 | static void MenuGenerateIndex() {
7 | EditorUtils.GenerateIndex("/Demo/Resources/Configs");
8 | AssetDatabase.Refresh();
9 | }
10 | }
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Location.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f90b18ca67c9a433289c14f4db6acdc7
3 | timeCreated: 1487538571
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/MetaGame.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0d90a3fe7400542ffa852129d357882e
3 | timeCreated: 1452464780
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig/ResourcesSource.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bdbdcb43d4a8749758059c0f19ada288
3 | timeCreated: 1462915515
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Editor/DemoEditorMenus.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 843a05b79c0254e329c9e8b287e67063
3 | timeCreated: 1462951276
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/VFXManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!937362698 &1
4 | VFXManager:
5 | m_ObjectHideFlags: 0
6 | m_IndirectShader: {fileID: 0}
7 | m_CopyBufferShader: {fileID: 0}
8 | m_SortShader: {fileID: 0}
9 | m_StripUpdateShader: {fileID: 0}
10 | m_RenderPipeSettingsPath:
11 | m_FixedTimeStep: 0.016666668
12 | m_MaxDeltaTime: 0.05
13 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/CameraFollow.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | public class CameraFollow : MonoBehaviour {
4 | public Transform Target;
5 |
6 | public Vector3 RelativePosition = new Vector3(0, 0, -10);
7 |
8 | void LateUpdate() {
9 | if (Target == null) {
10 | return;
11 | }
12 | transform.position = Target.position + RelativePosition;
13 | }
14 | }
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/Planes/Whip.bytes:
--------------------------------------------------------------------------------
1 | Whip:
2 | RotationRate: 200
3 | Speed: 15
4 | HitPoints: 10
5 |
6 | AIRange: 10
7 |
8 | Fuselage: [0, 0]
9 | Wing: [0.2, -0.2]
10 | Stabilizer:
11 | Pos: [0, -1.2]
12 | Size: [0.9, 0.9]
13 | GunMounts:
14 | - Name: MG1
15 | Location: [0, 2.5]
16 |
17 | LootTable:
18 | - Weight: 1
19 | Health: 5
20 | - Weight: 1
21 | CardName: Whip
22 | - Weight: 5 # drop nothing
23 |
--------------------------------------------------------------------------------
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | name: Run Tests
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | test:
9 | runs-on: windows-latest
10 |
11 | strategy:
12 | matrix:
13 | dotnet-version: ['8.0']
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 |
18 | - name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
19 | uses: actions/setup-dotnet@v2
20 | with:
21 | dotnet-version: ${{ matrix.dotnet-version }}
22 |
23 | - name: Run Tests
24 | run: dotnet test
--------------------------------------------------------------------------------
/demo/ProjectSettings/Physics2DSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!19 &1
4 | Physics2DSettings:
5 | m_ObjectHideFlags: 0
6 | m_Gravity: {x: 0, y: -9.81000042}
7 | m_DefaultMaterial: {fileID: 0}
8 | m_VelocityIterations: 8
9 | m_PositionIterations: 3
10 | m_RaycastsHitTriggers: 1
11 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
12 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 | runs-on: windows-latest
10 |
11 | strategy:
12 | matrix:
13 | dotnet-version: ['8.0']
14 | configuration:
15 | - Debug
16 | - Release
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 |
21 | - name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
22 | uses: actions/setup-dotnet@v2
23 | with:
24 | dotnet-version: ${{ matrix.dotnet-version }}
25 |
26 | - name: Build
27 | run: dotnet build --configuration ${{ matrix.configuration }}
--------------------------------------------------------------------------------
/src/DarkConfig/ConfigProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace DarkConfig {
2 | /// Processors can modify DocNodes after being parsed from YAML, but before used by the rest of the systems
3 | /// Note: this is ONLY run on the YAML -> DocNode path, any other DocNode creation is not affected
4 | public interface ConfigProcessor {
5 | /// Does this processor potentially mutate data
6 | /// Will make sure ComposedDocNode.MakeMutable() is run at least once before running this processor
7 | public bool CanMutate { get; }
8 |
9 | /// Process the DocNode here
10 | public void Process(string filename, ref DocNode doc);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # intermediate directories
2 | obj
3 | bin
4 | test/bin
5 | test/obj
6 | packages/
7 |
8 | # demo Unity files
9 | demo/Library
10 | demo/Temp
11 | demo/UserSettings
12 | demo/Logs
13 | demo/**/*.sln
14 | demo/**/*.csproj
15 | demo/**/*.unityproj
16 | demo/**/Builds
17 | demo/**/UnitTestResults.xml*
18 | demo/**/*-TestResults.xml*
19 |
20 | # these are copied here as a post-build step in the normal csproj
21 | demo/Assets/Plugins/DarkConfig.dll
22 | demo/Assets/Plugins/DarkConfig.dll.meta
23 | demo/Assets/Plugins/YamlDotNet.dll
24 | demo/Assets/Plugins/YamlDotNet.dll.meta
25 |
26 | .idea
27 | .vscode
28 | *.userprefs
29 | *.suo
30 | *.user
31 | *.pidb
32 | *.userprefs
33 | *.userprefs.meta
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/guns.bytes:
--------------------------------------------------------------------------------
1 | MG1:
2 | BulletSize: [1, 1]
3 | BulletDamage: 2
4 | BulletSpeed: 20
5 | BulletRange: 30
6 | RPS: 6
7 |
8 | MG2:
9 | BulletSize: [1, 1]
10 | BulletDamage: 2
11 | BulletSpeed: 25
12 | BulletRange: 35
13 | RPS: 8
14 |
15 | MG3:
16 | BulletSize: [1, 1]
17 | BulletDamage: 2
18 | BulletSpeed: 30
19 | BulletRange: 40
20 | RPS: 10
21 |
22 | Long Gun:
23 | BulletSize: [2, 1]
24 | BulletDamage: 4
25 | BulletSpeed: 10
26 | BulletRange: 60
27 | RPS: 1
28 |
29 | Piddler:
30 | BulletSize: [0.5, 1]
31 | BulletDamage: 1
32 | BulletSpeed: 18
33 | BulletRange: 40
34 | RPS: 0.1
--------------------------------------------------------------------------------
/src/DarkConfig/Internal/BuiltInTypeReifiers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace DarkConfig.Internal {
4 | static class BuiltInTypeReifiers {
5 | internal static object FromDateTime(object? existing, DocNode doc) {
6 | return DateTime.Parse(doc.StringValue, System.Globalization.CultureInfo.InvariantCulture);
7 | }
8 |
9 | internal static object FromTimeSpan(object? existing, DocNode doc) {
10 | bool isSuccess = TimeSpan.TryParse(doc.StringValue, out var newSpan);
11 | if (!isSuccess) {
12 | throw new ParseException(doc, "expected parseable timespan string " + doc.StringValue);
13 | }
14 |
15 | return newSpan;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/DarkConfig/Internal/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace DarkConfig.Internal {
2 | static class StringExtensions {
3 | /// Returns the lowercase version of the key if ignoreCase, otherwise return the key itself
4 | internal static string CanonicalizeKey(this string key, bool ignoreCase) {
5 | return ignoreCase ? key.ToLowerInvariant() : key;
6 | }
7 |
8 | /// Returns the hash of the lowercase version of the key if ignoreCase, otherwise return the hash of the key itself
9 | internal static int GetCanonicalHashCode(this string key, bool ignoreCase) {
10 | return ignoreCase ? key.ToLowerInvariant().GetHashCode() : key.GetHashCode();
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Docs/migrating_from_v1.md:
--------------------------------------------------------------------------------
1 | # Migrating from DarkConfig v1
2 |
3 | DarkConfig v2 introduces some breaking API changes. This is a quick guide on migrating from v1
4 |
5 | - `DarkConfig.Config` is now `DarkConfig.Configs`
6 | - `Config.Load()` is now `Configs.ParseFile()`
7 | - `ConfigReifier.Reify()` is now `Configs.Reify()`
8 | - 2D arrays are transposed compared to pre-migration. See: https://github.com/SpryFox/DarkConfig/issues/28
9 | - `DocNode` methods `AsInt()`, `AsFloat()`, `AsString()`, `AsBool()` have been removed. You can either add your own extension methods or change to use `As<>()`
10 | - `index.bytes` is not needed for `FileSource`
11 | - `ConfigFileInfo.Size` is now 64-bits (`long`)
12 | - Logs require the define flag `DC_LOGGING_ENABLED`
13 |
14 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/DynamicsManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!55 &1
4 | PhysicsManager:
5 | m_ObjectHideFlags: 0
6 | m_Gravity: {x: 0, y: -9.81000042, z: 0}
7 | m_DefaultMaterial: {fileID: 0}
8 | m_BounceThreshold: 2
9 | m_SleepVelocity: .150000006
10 | m_SleepAngularVelocity: .140000001
11 | m_MaxAngularVelocity: 7
12 | m_MinPenetrationForPenalty: .00999999978
13 | m_SolverIterationCount: 6
14 | m_RaycastsHitTriggers: 1
15 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
16 |
--------------------------------------------------------------------------------
/Docs/tests.md:
--------------------------------------------------------------------------------
1 | # Tests
2 |
3 | DarkConfig has a number of unit tests and integration tests. These are divided into general DarkConfig tests and Unity-specific tests.
4 |
5 | There is also a demo game, Dark Skies, which can be used to test behavior. To run the game, open and run the "Loader" scene in Unity. Changes to the Whip.bytes card (or any config) should hotload and be visible in the player plane's appearance or behavior.
6 |
7 | ## General Unit Tests
8 |
9 | The unit tests are located in the `test/` directory and are part of the `test/DarkConfig.Tests.csproj` project. They use the nunit framework and you can run them through Rider or Visual Studio.
10 |
11 | ## Unity specific tests
12 |
13 | From within Unity, run them using `Window > Tests Runner`.
--------------------------------------------------------------------------------
/test/DarkConfig.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | disable
5 | disable
6 |
7 | false
8 |
9 | false
10 | ..\bin\$(Configuration)_Test
11 | 9
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Pickup.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | public class Pickup : MonoBehaviour {
4 | // these two fields are used if it's a health pickup
5 | public int Health;
6 | public SpriteRenderer PlusSprite;
7 |
8 | ////////////////////////////////////////////
9 |
10 | void Start() {
11 | PlusSprite.color = Color.green;
12 | }
13 |
14 | void OnTriggerEnter2D(Collider2D c) {
15 | var topParent = c.transform;
16 |
17 | while (topParent.parent != null) {
18 | topParent = topParent.parent;
19 | }
20 |
21 | if (!topParent.CompareTag("Player")) {
22 | return;
23 | }
24 |
25 | var planeController = topParent.GetComponent();
26 | if (planeController != null) {
27 | if (Health > 0) {
28 | planeController.Heal(Health);
29 | }
30 |
31 | Destroy(gameObject);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/DarkConfig/DarkConfig.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | 9
6 |
7 | disable
8 | enable
9 | false
10 | ..\..\bin\$(Configuration)
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/TagManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!78 &1
4 | TagManager:
5 | tags:
6 | -
7 | Builtin Layer 0: Default
8 | Builtin Layer 1: TransparentFX
9 | Builtin Layer 2: Ignore Raycast
10 | Builtin Layer 3:
11 | Builtin Layer 4: Water
12 | Builtin Layer 5: UI
13 | Builtin Layer 6:
14 | Builtin Layer 7:
15 | User Layer 8:
16 | User Layer 9:
17 | User Layer 10:
18 | User Layer 11:
19 | User Layer 12:
20 | User Layer 13:
21 | User Layer 14:
22 | User Layer 15:
23 | User Layer 16:
24 | User Layer 17:
25 | User Layer 18:
26 | User Layer 19:
27 | User Layer 20:
28 | User Layer 21:
29 | User Layer 22:
30 | User Layer 23:
31 | User Layer 24:
32 | User Layer 25:
33 | User Layer 26:
34 | User Layer 27:
35 | User Layer 28:
36 | User Layer 29:
37 | User Layer 30:
38 | User Layer 31:
39 | m_SortingLayers:
40 | - name: Default
41 | userID: 0
42 | uniqueID: 0
43 | locked: 0
44 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Bullet.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | public class Bullet : MonoBehaviour {
4 | public int Damage;
5 | public float Speed;
6 | public GameObject HitPrefab;
7 |
8 | [HideInInspector]
9 | public Transform Firer;
10 |
11 | ////////////////////////////////////////////
12 |
13 | void OnTriggerEnter2D(Collider2D c) {
14 | var trf = TopParent(c.transform);
15 | if (trf == Firer) return;
16 | var planeController = trf.GetComponent();
17 | if (planeController == null) return;
18 | planeController.TakeDamage(Damage);
19 |
20 | var obj = Instantiate(HitPrefab, transform.position, transform.rotation);
21 | obj.transform.localScale = Vector3.one * Mathf.Sqrt(Damage);
22 | Destroy(obj, 2);
23 | Destroy(gameObject);
24 | }
25 |
26 | Transform TopParent(Transform t) {
27 | while (t.parent != null) {
28 | t = t.parent;
29 | }
30 |
31 | return t;
32 | }
33 | }
--------------------------------------------------------------------------------
/demo/ProjectSettings/UnityConnectSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!310 &1
4 | UnityConnectSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 1
7 | m_Enabled: 0
8 | m_TestMode: 0
9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events
10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events
11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com
12 | m_TestInitMode: 0
13 | CrashReportingSettings:
14 | m_EventUrl: https://perf-events.cloud.unity3d.com
15 | m_Enabled: 0
16 | m_LogBufferSize: 10
17 | m_CaptureEditorExceptions: 1
18 | UnityPurchasingSettings:
19 | m_Enabled: 0
20 | m_TestMode: 0
21 | UnityAnalyticsSettings:
22 | m_Enabled: 0
23 | m_TestMode: 0
24 | m_InitializeOnStartup: 1
25 | UnityAdsSettings:
26 | m_Enabled: 0
27 | m_InitializeOnStartup: 1
28 | m_TestMode: 0
29 | m_IosGameId:
30 | m_AndroidGameId:
31 | m_GameIds: {}
32 | m_GameId:
33 | PerformanceReportingSettings:
34 | m_Enabled: 0
35 |
--------------------------------------------------------------------------------
/THANKS.md:
--------------------------------------------------------------------------------
1 | THANKS
2 | =======
3 |
4 | This file contains the list of folks who have helped with the development of DarkConfig in one way or another. This list is not ordered in any way except perhaps loosely chronologically.
5 |
6 | - Spry Fox - the cradle that nutured this creation
7 | - Alex Holkner - created a system that read google spreadsheets into Unity games, which was the seed idea that inspired the creation of DarkConfig
8 | - Alex Jaffe - many illuminating conversations about the design of DarkConfig in the early stages
9 | - Daniel Cook - enthusiastic champion and power designer, DarkConfig is designed to make him happy
10 | - Andrew Fray - contributing bug fixes and feedback
11 | - David Edery - supporting the project the whole way
12 | - Rafael Cobra Teske - many bug fixes, contributions to console support
13 | - JJ Richards - lots of super detailed feedback
14 | - Cristian Soulos - contributing bug fixes and feedback
15 |
16 | If you are missing from the list and think you should be on it, please be assured it's merely an oversight! Let me know.
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/GunCard.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 | using DarkConfig;
4 |
5 | /// This is a "config data class", which mostly has public fields and not much else.
6 | public class GunCard {
7 | public Vector2 BulletSize = new Vector2(1, 1);
8 | public int BulletDamage;
9 | public float BulletSpeed;
10 | public float BulletRange;
11 |
12 | public float RPS;
13 |
14 | public float FireInterval => 1f / RPS;
15 |
16 | ////////////////////////////////////////////
17 |
18 | // This static collection allows us to manage the configs for the GunCard
19 | // completely within the GunCard class. Just have to be a little careful
20 | // to not access Cards before the preload finishes.
21 | [ConfigIgnore]
22 | static Dictionary _Cards;
23 |
24 | public static Dictionary Cards {
25 | get {
26 | if (_Cards == null) {
27 | Configs.Apply("guns", ref _Cards);
28 | }
29 | return _Cards;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/demo/ProjectSettings/PackageManagerSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &1
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 53
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0}
13 | m_Name:
14 | m_EditorClassIdentifier:
15 | m_EnablePreReleasePackages: 0
16 | m_AdvancedSettingsExpanded: 1
17 | m_ScopedRegistriesSettingsExpanded: 1
18 | m_SeeAllPackageVersions: 0
19 | m_DismissPreviewPackagesInUse: 0
20 | oneTimeWarningShown: 0
21 | m_Registries:
22 | - m_Id: main
23 | m_Name:
24 | m_Url: https://packages.unity.com
25 | m_Scopes: []
26 | m_IsDefault: 1
27 | m_Capabilities: 7
28 | m_ConfigSource: 0
29 | m_UserSelectedRegistryName:
30 | m_UserAddingNewScopedRegistry: 0
31 | m_RegistryInfoDraft:
32 | m_Modified: 0
33 | m_ErrorMessage:
34 | m_UserModificationsInstanceId: -846
35 | m_OriginalInstanceId: -848
36 | m_LoadAssets: 0
37 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/City.mat:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!21 &2100000
4 | Material:
5 | serializedVersion: 3
6 | m_ObjectHideFlags: 0
7 | m_PrefabParentObject: {fileID: 0}
8 | m_PrefabInternal: {fileID: 0}
9 | m_Name: City
10 | m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
11 | m_ShaderKeywords: []
12 | m_CustomRenderQueue: -1
13 | m_SavedProperties:
14 | serializedVersion: 2
15 | m_TexEnvs:
16 | data:
17 | first:
18 | name: _MainTex
19 | second:
20 | m_Texture: {fileID: 2800000, guid: ba92ef487ba9f4c868f20245eda29556, type: 3}
21 | m_Scale: {x: 4, y: 4}
22 | m_Offset: {x: 0, y: 0}
23 | data:
24 | first:
25 | name: _Illum
26 | second:
27 | m_Texture: {fileID: 0}
28 | m_Scale: {x: 1, y: 1}
29 | m_Offset: {x: 0, y: 0}
30 | m_Floats:
31 | data:
32 | first:
33 | name: _EmissionLM
34 | second: 0
35 | m_Colors:
36 | data:
37 | first:
38 | name: _Color
39 | second: {r: 1, g: 1, b: 1, a: 1}
40 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/Clouds.mat:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!21 &2100000
4 | Material:
5 | serializedVersion: 3
6 | m_ObjectHideFlags: 0
7 | m_PrefabParentObject: {fileID: 0}
8 | m_PrefabInternal: {fileID: 0}
9 | m_Name: Clouds
10 | m_Shader: {fileID: 30, guid: 0000000000000000f000000000000000, type: 0}
11 | m_ShaderKeywords: []
12 | m_CustomRenderQueue: -1
13 | m_SavedProperties:
14 | serializedVersion: 2
15 | m_TexEnvs:
16 | data:
17 | first:
18 | name: _MainTex
19 | second:
20 | m_Texture: {fileID: 2800000, guid: 7d176ee0e33a94f348fa3da0133ad559, type: 3}
21 | m_Scale: {x: 7, y: 7}
22 | m_Offset: {x: 0, y: 0}
23 | data:
24 | first:
25 | name: _Illum
26 | second:
27 | m_Texture: {fileID: 0}
28 | m_Scale: {x: 1, y: 1}
29 | m_Offset: {x: 0, y: 0}
30 | m_Floats:
31 | data:
32 | first:
33 | name: _EmissionLM
34 | second: 0
35 | m_Colors:
36 | data:
37 | first:
38 | name: _Color
39 | second: {r: .33088237, g: .324002981, b: .231131077, a: 1}
40 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/Clouds2.mat:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!21 &2100000
4 | Material:
5 | serializedVersion: 3
6 | m_ObjectHideFlags: 0
7 | m_PrefabParentObject: {fileID: 0}
8 | m_PrefabInternal: {fileID: 0}
9 | m_Name: Clouds2
10 | m_Shader: {fileID: 30, guid: 0000000000000000f000000000000000, type: 0}
11 | m_ShaderKeywords: []
12 | m_CustomRenderQueue: -1
13 | m_SavedProperties:
14 | serializedVersion: 2
15 | m_TexEnvs:
16 | data:
17 | first:
18 | name: _MainTex
19 | second:
20 | m_Texture: {fileID: 2800000, guid: 7d176ee0e33a94f348fa3da0133ad559, type: 3}
21 | m_Scale: {x: 5, y: 5}
22 | m_Offset: {x: 0, y: 0}
23 | data:
24 | first:
25 | name: _Illum
26 | second:
27 | m_Texture: {fileID: 0}
28 | m_Scale: {x: 1, y: 1}
29 | m_Offset: {x: 0, y: 0}
30 | m_Floats:
31 | data:
32 | first:
33 | name: _EmissionLM
34 | second: 0
35 | m_Colors:
36 | data:
37 | first:
38 | name: _Color
39 | second: {r: .20588237, g: .20588237, b: .20588237, a: 1}
40 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/puff.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 83779aa4fabb540a68af6e9981c5a89c
3 | TextureImporter:
4 | fileIDToRecycleName: {}
5 | serializedVersion: 2
6 | mipmaps:
7 | mipMapMode: 0
8 | enableMipMap: 1
9 | linearTexture: 0
10 | correctGamma: 0
11 | fadeOut: 0
12 | borderMipMap: 0
13 | mipMapFadeDistanceStart: 1
14 | mipMapFadeDistanceEnd: 3
15 | bumpmap:
16 | convertToNormalMap: 0
17 | externalNormalMap: 0
18 | heightScale: .25
19 | normalMapFilter: 0
20 | isReadable: 0
21 | grayScaleToAlpha: 0
22 | generateCubemap: 0
23 | seamlessCubemap: 0
24 | textureFormat: -1
25 | maxTextureSize: 1024
26 | textureSettings:
27 | filterMode: -1
28 | aniso: -1
29 | mipBias: -1
30 | wrapMode: -1
31 | nPOTScale: 1
32 | lightmap: 0
33 | compressionQuality: 50
34 | spriteMode: 0
35 | spriteExtrude: 1
36 | spriteMeshType: 1
37 | alignment: 0
38 | spritePivot: {x: .5, y: .5}
39 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
40 | spritePixelsToUnits: 100
41 | alphaIsTransparency: 1
42 | textureType: 0
43 | buildTargetSettings: []
44 | spriteSheet:
45 | sprites: []
46 | spritePackingTag:
47 | userData:
48 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/City_Repeat.jpg.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ba92ef487ba9f4c868f20245eda29556
3 | TextureImporter:
4 | fileIDToRecycleName: {}
5 | serializedVersion: 2
6 | mipmaps:
7 | mipMapMode: 0
8 | enableMipMap: 1
9 | linearTexture: 0
10 | correctGamma: 0
11 | fadeOut: 0
12 | borderMipMap: 0
13 | mipMapFadeDistanceStart: 1
14 | mipMapFadeDistanceEnd: 3
15 | bumpmap:
16 | convertToNormalMap: 0
17 | externalNormalMap: 0
18 | heightScale: .25
19 | normalMapFilter: 0
20 | isReadable: 0
21 | grayScaleToAlpha: 0
22 | generateCubemap: 0
23 | seamlessCubemap: 0
24 | textureFormat: -1
25 | maxTextureSize: 1024
26 | textureSettings:
27 | filterMode: -1
28 | aniso: -1
29 | mipBias: -1
30 | wrapMode: -1
31 | nPOTScale: 1
32 | lightmap: 0
33 | compressionQuality: 50
34 | spriteMode: 0
35 | spriteExtrude: 1
36 | spriteMeshType: 1
37 | alignment: 0
38 | spritePivot: {x: .5, y: .5}
39 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
40 | spritePixelsToUnits: 100
41 | alphaIsTransparency: 0
42 | textureType: 0
43 | buildTargetSettings: []
44 | spriteSheet:
45 | sprites: []
46 | spritePackingTag:
47 | userData:
48 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/Clouds_Repeat.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7d176ee0e33a94f348fa3da0133ad559
3 | TextureImporter:
4 | fileIDToRecycleName: {}
5 | serializedVersion: 2
6 | mipmaps:
7 | mipMapMode: 0
8 | enableMipMap: 1
9 | linearTexture: 0
10 | correctGamma: 0
11 | fadeOut: 0
12 | borderMipMap: 0
13 | mipMapFadeDistanceStart: 1
14 | mipMapFadeDistanceEnd: 3
15 | bumpmap:
16 | convertToNormalMap: 0
17 | externalNormalMap: 0
18 | heightScale: .25
19 | normalMapFilter: 0
20 | isReadable: 0
21 | grayScaleToAlpha: 0
22 | generateCubemap: 0
23 | seamlessCubemap: 0
24 | textureFormat: -1
25 | maxTextureSize: 1024
26 | textureSettings:
27 | filterMode: -1
28 | aniso: -1
29 | mipBias: -1
30 | wrapMode: -1
31 | nPOTScale: 1
32 | lightmap: 0
33 | compressionQuality: 50
34 | spriteMode: 0
35 | spriteExtrude: 1
36 | spriteMeshType: 1
37 | alignment: 0
38 | spritePivot: {x: .5, y: .5}
39 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
40 | spritePixelsToUnits: 100
41 | alphaIsTransparency: 1
42 | textureType: 0
43 | buildTargetSettings: []
44 | spriteSheet:
45 | sprites: []
46 | spritePackingTag:
47 | userData:
48 |
--------------------------------------------------------------------------------
/src/DarkConfig/ConfigFileInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace DarkConfig {
4 | public class ConfigFileInfo {
5 | /// The identifier we use to refer to this file.
6 | /// Usually the relative config file path without the file extension.
7 | /// "Weapons/Swords/Rapier" for the file "./Weapons/Swords/Rapier.yaml"
8 | public readonly string Name;
9 |
10 | // File size. Used as a quick, coarse-grained checksum for hotloading.
11 | public long Size;
12 |
13 | /// Checksum of file contents. Used in hotloading.
14 | public int Checksum;
15 |
16 | /// Last modified time of the file when it was loaded. Used to detect changes when hotloading.
17 | public DateTime Modified;
18 |
19 | /// Parsed file contents.
20 | public DocNode Parsed;
21 |
22 | public ConfigFileInfo(string name, int checksum, long size, DateTime modified, DocNode parsed) {
23 | Name = name;
24 | Checksum = checksum;
25 | Size = size;
26 | Modified = modified;
27 | Parsed = parsed;
28 | }
29 |
30 | public override string ToString() {
31 | return $"[{Name} {Checksum:X16}]";
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/EditorSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!159 &1
4 | EditorSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 9
7 | m_ExternalVersionControlSupport: Visible Meta Files
8 | m_SerializationMode: 2
9 | m_LineEndingsForNewScripts: 1
10 | m_DefaultBehaviorMode: 1
11 | m_PrefabRegularEnvironment: {fileID: 0}
12 | m_PrefabUIEnvironment: {fileID: 0}
13 | m_SpritePackerMode: 0
14 | m_SpritePackerPaddingPower: 1
15 | m_EtcTextureCompressorBehavior: 0
16 | m_EtcTextureFastCompressor: 2
17 | m_EtcTextureNormalCompressor: 2
18 | m_EtcTextureBestCompressor: 5
19 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp;asmref
20 | m_ProjectGenerationRootNamespace:
21 | m_CollabEditorSettings:
22 | inProgressEnabled: 1
23 | m_EnableTextureStreamingInEditMode: 1
24 | m_EnableTextureStreamingInPlayMode: 1
25 | m_AsyncShaderCompilation: 1
26 | m_EnterPlayModeOptionsEnabled: 0
27 | m_EnterPlayModeOptions: 3
28 | m_ShowLightmapResolutionOverlay: 1
29 | m_UseLegacyProbeSampleCount: 1
30 | m_AssetPipelineMode: 1
31 | m_CacheServerMode: 0
32 | m_CacheServerEndpoint:
33 | m_CacheServerNamespacePrefix: default
34 | m_CacheServerEnableDownload: 1
35 | m_CacheServerEnableUpload: 1
36 |
--------------------------------------------------------------------------------
/DarkConfig.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DarkConfig", "src\DarkConfig\DarkConfig.csproj", "{14D768D9-A27F-4DDA-BED3-D034F1887219}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DarkConfig.Tests", "test\DarkConfig.Tests.csproj", "{629730F0-AE4C-4D13-ACB6-DDB04019158E}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|Any CPU = Debug|Any CPU
10 | Release|Any CPU = Release|Any CPU
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {14D768D9-A27F-4DDA-BED3-D034F1887219}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14 | {14D768D9-A27F-4DDA-BED3-D034F1887219}.Debug|Any CPU.Build.0 = Debug|Any CPU
15 | {14D768D9-A27F-4DDA-BED3-D034F1887219}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | {14D768D9-A27F-4DDA-BED3-D034F1887219}.Release|Any CPU.Build.0 = Release|Any CPU
17 | {629730F0-AE4C-4D13-ACB6-DDB04019158E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {629730F0-AE4C-4D13-ACB6-DDB04019158E}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {629730F0-AE4C-4D13-ACB6-DDB04019158E}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {629730F0-AE4C-4D13-ACB6-DDB04019158E}.Release|Any CPU.Build.0 = Release|Any CPU
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Location.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using DarkConfig;
3 |
4 | public class Location {
5 | public Vector2 Pos;
6 |
7 | [ConfigAllowMissing]
8 | public Vector2 Size = new Vector2(1, 1);
9 |
10 | public Location(Vector2 pos, Vector2 size) {
11 | Pos = pos;
12 | Size = size;
13 | }
14 |
15 | // let's take over the parsing of Location, because we want to be able to
16 | // have a short form for the common case of specifying only the position
17 | public static Location FromDoc(Location existing, DocNode doc) {
18 | if (existing == null) {
19 | // want to modify an existing Location if possible, but if it's
20 | // null we need to instantiate it
21 | existing = new Location(default, default);
22 | }
23 |
24 | if (doc.Type == DocNodeType.List) {
25 | // it's a list, so we treat it as a position only, and size is 1,1
26 | Configs.Reify(ref existing.Pos, doc);
27 | existing.Size = new Vector2(1, 1);
28 | } else {
29 | // Do the default parsing for an object. Note that calling
30 | // Config.Reify on the same type would trigger infinite recursion.
31 | Configs.SetFieldsOnObject(ref existing, doc);
32 | }
33 |
34 | return existing;
35 | }
36 | }
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/Puff.mat:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!21 &2100000
4 | Material:
5 | serializedVersion: 3
6 | m_ObjectHideFlags: 0
7 | m_PrefabParentObject: {fileID: 0}
8 | m_PrefabInternal: {fileID: 0}
9 | m_Name: Puff
10 | m_Shader: {fileID: 203, guid: 0000000000000000f000000000000000, type: 0}
11 | m_ShaderKeywords: []
12 | m_CustomRenderQueue: -1
13 | m_SavedProperties:
14 | serializedVersion: 2
15 | m_TexEnvs:
16 | data:
17 | first:
18 | name: _MainTex
19 | second:
20 | m_Texture: {fileID: 2800000, guid: 83779aa4fabb540a68af6e9981c5a89c, type: 3}
21 | m_Scale: {x: 1, y: 1}
22 | m_Offset: {x: 0, y: 0}
23 | data:
24 | first:
25 | name: _Illum
26 | second:
27 | m_Texture: {fileID: 0}
28 | m_Scale: {x: 1, y: 1}
29 | m_Offset: {x: 0, y: 0}
30 | m_Floats:
31 | data:
32 | first:
33 | name: _EmissionLM
34 | second: 0
35 | data:
36 | first:
37 | name: _InvFade
38 | second: 1
39 | m_Colors:
40 | data:
41 | first:
42 | name: _Color
43 | second: {r: 1, g: 1, b: 1, a: 1}
44 | data:
45 | first:
46 | name: _TintColor
47 | second: {r: 1, g: 1, b: 1, a: .501960814}
48 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/MemorySettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!387306366 &1
4 | MemorySettings:
5 | m_ObjectHideFlags: 0
6 | m_EditorMemorySettings:
7 | m_MainAllocatorBlockSize: -1
8 | m_ThreadAllocatorBlockSize: -1
9 | m_MainGfxBlockSize: -1
10 | m_ThreadGfxBlockSize: -1
11 | m_CacheBlockSize: -1
12 | m_TypetreeBlockSize: -1
13 | m_ProfilerBlockSize: -1
14 | m_ProfilerEditorBlockSize: -1
15 | m_BucketAllocatorGranularity: -1
16 | m_BucketAllocatorBucketsCount: -1
17 | m_BucketAllocatorBlockSize: -1
18 | m_BucketAllocatorBlockCount: -1
19 | m_ProfilerBucketAllocatorGranularity: -1
20 | m_ProfilerBucketAllocatorBucketsCount: -1
21 | m_ProfilerBucketAllocatorBlockSize: -1
22 | m_ProfilerBucketAllocatorBlockCount: -1
23 | m_TempAllocatorSizeMain: -1
24 | m_JobTempAllocatorBlockSize: -1
25 | m_BackgroundJobTempAllocatorBlockSize: -1
26 | m_JobTempAllocatorReducedBlockSize: -1
27 | m_TempAllocatorSizeGIBakingWorker: -1
28 | m_TempAllocatorSizeNavMeshWorker: -1
29 | m_TempAllocatorSizeAudioWorker: -1
30 | m_TempAllocatorSizeCloudWorker: -1
31 | m_TempAllocatorSizeGfx: -1
32 | m_TempAllocatorSizeJobWorker: -1
33 | m_TempAllocatorSizeBackgroundWorker: -1
34 | m_TempAllocatorSizePreloadManager: -1
35 | m_PlatformMemorySettings: {}
36 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/generate_random_configs.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import random
4 |
5 | num_configs = int(sys.argv[1])
6 | start_value = int(sys.argv[2])
7 |
8 | class ArtPos(object):
9 | def __init__(self, pos, size):
10 | self.Pos = pos
11 | self.Size = size
12 |
13 | def __str__(self):
14 | return "\n Pos: [%f, %f]\n Size: [%f, %f]\n" % (
15 | self.Pos[0], self.Pos[1], self.Size[0], self.Size[1])
16 |
17 | for i in xrange(num_configs):
18 | body = dict()
19 | body["RotationRate"] = random.randint(40, 70)
20 | body["Speed"] = random.randint(5, 20)
21 | body["HitPoints"] = random.randint(3, 7)
22 | body["AIRange"] = 30
23 | body["Fuselage"] = ArtPos([0, 0], [random.uniform(0.8, 1.5), random.uniform(1, 2)])
24 | body["Wing"] = ArtPos([(body["Fuselage"].Size[0] - 0.8) * 0.5, random.uniform(-0.5, 0.7)],
25 | [random.uniform(0.4, 1.5), random.uniform(0.6, 2)])
26 | body["Stabilizer"] = ArtPos([0, -body["Fuselage"].Size[1] * random.uniform(0.3, 1)],
27 | [random.uniform(0.5, 2), random.uniform(0.5, 2)])
28 | body["GunMounts"] = "[{Name: Piddler, Location: [0, 0]}]"
29 | body["LootTable"] = "[{Weight: 2}, {Weight: 1, Health: 2}]"
30 |
31 | print "Generated%s:" % (i + start_value)
32 | for k, v in body.iteritems():
33 | print " %s: %s" % (k, v)
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/Parallax.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | public class Parallax : MonoBehaviour {
4 | public bool drift;
5 | public Transform ViewpointObject;
6 |
7 | /////////////////////////////////////////////////
8 |
9 | Vector3 currentPos;
10 | Vector3 startPos;
11 | Vector3 viewpointStartPos;
12 |
13 | /////////////////////////////////////////////////
14 |
15 | void Awake() {
16 | startPos = transform.position;
17 | currentPos = startPos;
18 | }
19 |
20 | void LateUpdate() {
21 | if (ViewpointObject != null) {
22 | var pos = ViewpointObject.transform.position - viewpointStartPos;
23 | float depth = Mathf.Max(1, transform.position.z);
24 | pos *= 1 - 1 / depth;
25 | var newPos = startPos + pos;
26 | newPos.z = depth;
27 | currentPos = newPos;
28 | }
29 |
30 | if (drift) {
31 | transform.position = currentPos + new Vector3(
32 | Mathf.Sin(Time.time * 0.152f + (name.GetHashCode() & 0xFF)) * 10,
33 | Mathf.Cos(Time.time * 0.1193f + ((name.GetHashCode() & 0xFF00) >> 8)) * 10,
34 | 0);
35 | } else {
36 | transform.position = currentPos;
37 | }
38 | }
39 |
40 | void Init(Transform obj) {
41 | ViewpointObject = obj;
42 | viewpointStartPos = ViewpointObject.position;
43 | }
44 | }
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig/UnityPlatform.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace DarkConfig {
5 | public static class UnityPlatform {
6 | public static void Setup(bool hotloading = false) {
7 | UnityTypeReifiers.RegisterAll();
8 | Configs.LogCallback = Log;
9 |
10 | if (hotloading) {
11 | SetupHotloadingManager();
12 | }
13 | }
14 |
15 | static void Log(LogVerbosity verbosity, string message) {
16 | switch (verbosity) {
17 | case LogVerbosity.Warn: Debug.LogWarning(message); break;
18 | case LogVerbosity.Info: Debug.Log(message); break;
19 | default: throw new ArgumentOutOfRangeException(nameof(verbosity), verbosity, null);
20 | }
21 | }
22 |
23 | private static GameObject hotloadingManagerInstance = null;
24 | static void SetupHotloadingManager() {
25 | if (hotloadingManagerInstance != null) {
26 | return;
27 | }
28 |
29 | hotloadingManagerInstance = new GameObject("DarkConfigHotloadingManager");
30 | hotloadingManagerInstance.AddComponent();
31 | }
32 |
33 | class HotloadingManager : MonoBehaviour {
34 | void Awake() {
35 | DontDestroyOnLoad(gameObject);
36 | }
37 |
38 | void Update() {
39 | Configs.Update(Time.deltaTime);
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/DarkConfig/ConfigSource.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace DarkConfig {
6 | /// A source of config files to load.
7 | /// e.g. a folder on disk, a binary file, a web server, etc.
8 | public abstract class ConfigSource {
9 | /// Does this config source support hotloading config files?
10 | public abstract bool CanHotload { get; }
11 |
12 | /// Generator function that finds and load all configs that this source knows about.
13 | /// Loads one file at a time, separated by a yield return null.
14 | /// Used for both blocking and time-sliced config loading.
15 | public abstract IEnumerable StepPreload();
16 |
17 | /// Try to hotload config files. Adds names of changed files to the list.
18 | public virtual void Hotload(List changedFiles) { }
19 |
20 | ///
21 | /// Enumerates all file lists (aka keys in AllFiles) in sorted order.
22 | ///
23 | /// An enumeration of all the filename keys in sorted order
24 | public IEnumerable GetSortedFilenames() {
25 | foreach (string fileName in AllFiles.Keys.OrderBy(it => it)) {
26 | yield return fileName;
27 | }
28 | }
29 |
30 | /////////////////////////////////////////////////
31 |
32 | /// All the currently loaded config file data.
33 | public Dictionary AllFiles = new();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2017, Spry Fox LLC
2 |
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 |
5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6 |
7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 |
9 | If the redistribution is a game, the game must include "Thanks to Spry Fox for DarkConfig" (or very similar language) within the game's credits.
10 |
11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Resources/Configs/Planes/SmallPlanes.bytes:
--------------------------------------------------------------------------------
1 | Crackerjack:
2 | RotationRate: 90
3 | Speed: 10
4 | HitPoints: 6
5 |
6 | AIRange: 20
7 |
8 | Fuselage: [0, 0]
9 | Wing:
10 | Pos: [0.2, 0]
11 | Size: [1.4, 1]
12 | Stabilizer: [0, -1.2]
13 | GunMounts:
14 | - Name: Piddler
15 | Location: [1, 1.5]
16 | - Name: Piddler
17 | Location: [-1, 1.5]
18 | - Name: Piddler
19 | Location: [2, 1.5]
20 | - Name: Piddler
21 | Location: [-2, 1.5]
22 |
23 | LootTable:
24 | - Weight: 2
25 | Health: 5
26 | - Weight: 1
27 |
28 |
29 | Swoop:
30 | RotationRate: 50
31 | Speed: 20
32 | HitPoints: 4
33 |
34 | AIRange: 30
35 |
36 | Fuselage:
37 | Pos: [0, 0]
38 | Size: [1.4, 1]
39 | Wing:
40 | Pos: [0.2, 0.2]
41 | Size: [0.6, 1]
42 | Stabilizer: [0, -1.2]
43 | GunMounts:
44 | - Name: Long Gun
45 | Location: [0, 3]
46 |
47 | LootTable:
48 | - Weight: 2
49 | Health: 6
50 | - Weight: 2
51 |
52 |
53 | Boomer:
54 | RotationRate: 70
55 | Speed: 6
56 | HitPoints: 20
57 |
58 | AIRange: 20
59 |
60 | Fuselage:
61 | Pos: [0, 0]
62 | Size: [2, 2]
63 | Wing:
64 | Pos: [0.4, 0.2]
65 | Size: [0.6, 2]
66 | Stabilizer:
67 | Pos: [0.2, -2.5]
68 | Size: [1, 1]
69 | GunMounts:
70 | - Name: MG2
71 | Location: [-0.25, 3.5]
72 | - Name: MG2
73 | Location: [0.25, 3.5]
74 |
75 | LootTable:
76 | - Weight: 2
77 | Health: 10
78 | - Weight: 6
79 |
--------------------------------------------------------------------------------
/src/DarkConfig/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace DarkConfig {
4 | [Flags]
5 | public enum ReificationOptions {
6 | None = 0,
7 |
8 | /// fields present in the YAML document but not on the object are allowed
9 | AllowExtraFields = 1 << 0,
10 |
11 | /// fields present on the object but not in the YAML document are allowed
12 | AllowMissingFields = 1 << 1,
13 |
14 | /// both missing and extra fields are allowed
15 | AllowMissingExtraFields = AllowExtraFields | AllowMissingFields,
16 |
17 | /// properties care about case
18 | CaseSensitive = 1 << 2
19 | }
20 |
21 | public enum LogVerbosity {
22 | Warn,
23 | Info
24 | }
25 |
26 | public class Settings {
27 | /// Default options for reification. Change this if you want to change
28 | /// DarkConfig behavior without passing in parameters to each call.
29 | public ReificationOptions DefaultReifierOptions = ReificationOptions.AllowMissingExtraFields | ReificationOptions.CaseSensitive;
30 |
31 | /// How aggressively DarkConfig logs
32 | public LogVerbosity LogLevel = LogVerbosity.Info;
33 |
34 | /// If enabled DarkConfig will scan files for changes every HotloadCheckInterval seconds.
35 | /// Setting it to false stops hotloading. Useful during production when configs are under rapid iteration.
36 | public bool EnableHotloading {
37 | get => Configs.FileManager.IsHotloadingFiles;
38 | set => Configs.FileManager.IsHotloadingFiles = value;
39 | }
40 |
41 | /// How often, in seconds, to scan files for changes.
42 | public float HotloadCheckFrequencySeconds = 2f;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/demo/Packages/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "com.unity.ai.navigation": "1.1.5",
4 | "com.unity.ide.rider": "3.0.31",
5 | "com.unity.ide.visualstudio": "2.0.22",
6 | "com.unity.ide.vscode": "1.2.5",
7 | "com.unity.test-framework": "1.1.33",
8 | "com.unity.ugui": "1.0.0",
9 | "com.unity.modules.ai": "1.0.0",
10 | "com.unity.modules.androidjni": "1.0.0",
11 | "com.unity.modules.animation": "1.0.0",
12 | "com.unity.modules.assetbundle": "1.0.0",
13 | "com.unity.modules.audio": "1.0.0",
14 | "com.unity.modules.cloth": "1.0.0",
15 | "com.unity.modules.director": "1.0.0",
16 | "com.unity.modules.imageconversion": "1.0.0",
17 | "com.unity.modules.imgui": "1.0.0",
18 | "com.unity.modules.jsonserialize": "1.0.0",
19 | "com.unity.modules.particlesystem": "1.0.0",
20 | "com.unity.modules.physics": "1.0.0",
21 | "com.unity.modules.physics2d": "1.0.0",
22 | "com.unity.modules.screencapture": "1.0.0",
23 | "com.unity.modules.terrain": "1.0.0",
24 | "com.unity.modules.terrainphysics": "1.0.0",
25 | "com.unity.modules.tilemap": "1.0.0",
26 | "com.unity.modules.ui": "1.0.0",
27 | "com.unity.modules.uielements": "1.0.0",
28 | "com.unity.modules.umbra": "1.0.0",
29 | "com.unity.modules.unityanalytics": "1.0.0",
30 | "com.unity.modules.unitywebrequest": "1.0.0",
31 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0",
32 | "com.unity.modules.unitywebrequestaudio": "1.0.0",
33 | "com.unity.modules.unitywebrequesttexture": "1.0.0",
34 | "com.unity.modules.unitywebrequestwww": "1.0.0",
35 | "com.unity.modules.vehicles": "1.0.0",
36 | "com.unity.modules.video": "1.0.0",
37 | "com.unity.modules.vr": "1.0.0",
38 | "com.unity.modules.wind": "1.0.0",
39 | "com.unity.modules.xr": "1.0.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/PlayerController.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections.Generic;
3 | using DarkConfig;
4 |
5 | public class PlayerController : MonoBehaviour {
6 | public static string StartingCard = null;
7 |
8 | public Dictionary Keyboard;
9 |
10 | [ConfigIgnore]
11 | public PlaneController Controller;
12 |
13 | ////////////////////////////////////////////
14 |
15 | void Start() {
16 | // TODO (graham): temp fix for demo.
17 | var reifierOptions = Configs.Settings.DefaultReifierOptions;
18 | Configs.Settings.DefaultReifierOptions = reifierOptions | ReificationOptions.AllowExtraFields;
19 |
20 | // Get keyboard bindings from the config, and also automatically
21 | // hotload them.
22 | Configs.ApplyThis("player", this);
23 | // The previous call to ApplyThis won't have touched the StartingCard
24 | // field, so for that we call ApplyStatic.
25 | Configs.ApplyStatic("player");
26 |
27 | Configs.Settings.DefaultReifierOptions = reifierOptions;
28 |
29 | Controller.Setup(PlaneCard.Cards[StartingCard]);
30 | }
31 |
32 | void Update() {
33 | float rotation = 0;
34 | if (Input.GetKey(Keyboard["Left"])) {
35 | rotation += 1;
36 | }
37 |
38 | if (Input.GetKey(Keyboard["Right"])) {
39 | rotation -= 1;
40 | }
41 |
42 | if (Input.GetKey(Keyboard["Slow"])) {
43 | Controller.Throttle = 0.6f;
44 | }
45 |
46 | if (Input.GetKey(Keyboard["Boost"])) {
47 | Controller.Throttle = 1.4f;
48 | }
49 |
50 | Controller.RotationCommand = rotation;
51 |
52 | Controller.IsFiring = Input.GetKey(Keyboard["Fire"]);
53 | }
54 |
55 | void Killed() {
56 | MetaGame.Instance.PlayerKilled();
57 | }
58 | }
--------------------------------------------------------------------------------
/test/MissingFilesTests.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using NUnit.Framework;
3 | using DarkConfig;
4 | using System.Text.RegularExpressions;
5 |
6 | [TestFixture]
7 | class MissingFilesTests {
8 | string tempDirPath;
9 |
10 | void CreateFile(string filename, string contents) {
11 | var fullPath = Path.Combine(tempDirPath, filename);
12 | using (var sw = new StreamWriter(fullPath, false, new System.Text.UTF8Encoding())) {
13 | sw.Write(contents);
14 | }
15 | }
16 |
17 | [SetUp]
18 | public void SetUp() {
19 | tempDirPath = Path.Combine(Path.GetTempPath(), "ListComposingTests");
20 | Directory.CreateDirectory(tempDirPath);
21 |
22 | Configs.Settings.EnableHotloading = true;
23 | Configs.Settings.HotloadCheckFrequencySeconds = 0.1f;
24 | Configs.AddConfigSource(new FileSource(tempDirPath, hotload: true));
25 | }
26 |
27 | [TearDown]
28 | public void TearDown() {
29 | Directory.Delete(tempDirPath, true);
30 | Configs.Clear();
31 | }
32 |
33 | [Test]
34 | public void MissingFiles() {
35 | CreateFile("spinner.yaml", "key: ok");
36 |
37 | Configs.Preload();
38 |
39 | // check the index after preload
40 | var filenames = Configs.GetFilenamesMatchingRegex(new Regex(".*"));
41 | Assert.Greater(filenames.Count, 0);
42 |
43 | Assert.IsTrue(filenames.Contains("spinner"));
44 |
45 | // check that we can load existing files
46 | var spinnerDoc = Configs.ParseFile("spinner");
47 |
48 | // this file should be present so this should pass
49 | Assert.IsTrue(spinnerDoc.ContainsKey("key"));
50 |
51 | Assert.Throws(() => {
52 | Configs.ParseFile("nonexistent", (d) => {
53 | Assert.Fail("Callback shouldn't be called");
54 | return false;
55 | });
56 | });
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/DarkConfig/Internal/RegexUtils.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace DarkConfig.Internal {
5 | public static class RegexUtils {
6 | /// Returns a list of all the strings in the given list that match the given regex.
7 | public static void FilterMatching(Regex pattern, IEnumerable strings, List results) {
8 | foreach (string str in strings) {
9 | if (pattern.IsMatch(str)) {
10 | results.Add(str);
11 | }
12 | }
13 | }
14 |
15 | public static void FilterMatching(Regex pattern, IEnumerable strings, HashSet results) {
16 | foreach (string str in strings) {
17 | if (pattern.IsMatch(str)) {
18 | results.Add(str);
19 | }
20 | }
21 | }
22 |
23 | public static void FilterMatchingGlob(string glob, IEnumerable strings, List results) {
24 | FilterMatching(GlobToRegex(glob), strings, results);
25 | }
26 |
27 | public static void FilterMatchingGlob(string glob, IEnumerable strings, HashSet results) {
28 | FilterMatching(GlobToRegex(glob), strings, results);
29 | }
30 |
31 | /// Converts a glob-style expression into a file path regex
32 | /// '*' matches any sequence of characters, but stops at slashes
33 | /// '?' matches a single character, except a slash
34 | /// '**' matches any sequence of characters, including slashes
35 | public static Regex GlobToRegex(string glob) {
36 | var regexString = Regex.Escape(glob)
37 | .Replace(@"\*\*", @".*")
38 | .Replace(@"\*", @"[^/]*")
39 | .Replace(@"\?", @"[^/]");
40 | var regex = new Regex("^" + regexString + "$", RegexOptions.IgnoreCase | RegexOptions.Singleline);
41 | return regex;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/DarkConfig/Internal/MultiCaseDictionary.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Diagnostics.CodeAnalysis;
5 |
6 | namespace DarkConfig.Internal {
7 | ///
8 | /// Dictionary that supports both case-sensitive and case-insensitive lookups while retaining the case of the key
9 | ///
10 | /// Type of values in the dictionary (keys are strings)
11 | internal class MultiCaseDictionary : IEnumerable<(string, TValueType)> {
12 | readonly Dictionary _dictionary;
13 |
14 | public MultiCaseDictionary() : this(0) { }
15 |
16 | public MultiCaseDictionary(int capacity) {
17 | _dictionary = new Dictionary(capacity, StringComparer.OrdinalIgnoreCase);
18 | }
19 |
20 | public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValueType value, bool ignoreCase) {
21 | if (_dictionary.TryGetValue(key, out var match)) {
22 | if (ignoreCase) {
23 | value = match.Value;
24 | return true;
25 | }
26 |
27 | if (key.Equals(match.Key)) {
28 | value = match.Value;
29 | return true;
30 | }
31 | }
32 |
33 | value = default;
34 | return false;
35 | }
36 |
37 | public void Add(string key, TValueType value) {
38 | _dictionary.Add(key, (key, value));
39 | }
40 |
41 | public bool TryAdd(string key, TValueType value) {
42 | return _dictionary.TryAdd(key, (key, value));
43 | }
44 |
45 | public IEnumerator<(string, TValueType)> GetEnumerator() {
46 | return _dictionary.Values.GetEnumerator();
47 | }
48 |
49 | IEnumerator IEnumerable.GetEnumerator() {
50 | return ((IEnumerable) _dictionary.Values).GetEnumerator();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/LoadGame.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using UnityEngine;
3 | using DarkConfig;
4 | using System.Diagnostics;
5 | using UnityEngine.SceneManagement;
6 |
7 | // This is the main loading class for the game. It's expected to run inside
8 | // its own scene, which then could display a loading indicator.
9 | public class LoadGame : MonoBehaviour {
10 | Stopwatch stopwatch;
11 |
12 | /////////////////////////////////////////////////
13 |
14 | void Awake() {
15 | #if DEBUG
16 | // Be strict in debug mode so that content creators will be quickly
17 | // notified of any mistakes. It will warn for any missing fields
18 | // (which haven't been annotated with ConfigAllowMissing) and for any
19 | // extra fields.
20 | Configs.Settings.DefaultReifierOptions = ReificationOptions.None;
21 | #else
22 | // In production mode, ignore missing/extra checks. This makes
23 | // loading faster. ConfigMandatory fields are still checked.
24 | Configs.Settings.DefaultReifierOptions = ConfigOptions.AllowMissingExtraFields;
25 | #endif
26 |
27 | UnityPlatform.Setup();
28 | Configs.AddConfigSource(new FileSource(Application.dataPath + "/Demo/Resources/Configs", ".bytes", hotload: true));
29 | StartCoroutine(StartGame());
30 | }
31 |
32 | IEnumerator StartGame() {
33 | stopwatch = Stopwatch.StartNew();
34 |
35 | // comment to disable periodic hotloading of files, it'll have to be manual
36 | Configs.Settings.EnableHotloading = true;
37 |
38 | // Preload will call StartGame when it's finished
39 | foreach (object _ in Configs.StepPreload()) {
40 | yield return null;
41 | }
42 |
43 | stopwatch.Stop();
44 | UnityEngine.Debug.Log("Config parsing ms: " + stopwatch.ElapsedMilliseconds);
45 |
46 | // PlaneCards are loaded on first access so this call to LoadConfigs is
47 | // functionally unnecessary, but since we're taking a framerate hit with
48 | // LoadLevel, might as well make it a tiny bit longer and load the cards
49 | // at the same time
50 | PlaneCard.LoadConfigs();
51 | SceneManager.LoadScene("PlaneDemo");
52 | }
53 | }
--------------------------------------------------------------------------------
/test/YamlParseTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using NUnit.Framework;
3 | using System.IO;
4 | using YamlDotNet.RepresentationModel;
5 | using DarkConfig;
6 |
7 | [TestFixture]
8 | public class YamlParseTests {
9 | static YamlNode ParseYamlNode(string str) {
10 | var yaml = new YamlStream();
11 | yaml.Load(new StringReader(str));
12 | return yaml.Documents[0].RootNode;
13 | }
14 |
15 | [TestFixture]
16 | public class YamlDocParser {
17 | [Test]
18 | public void JsonSubset_TraversedByDocNode() {
19 | string testStr = "{\"test_key\":\"test_value\"}";
20 | var dn = (DocNode) new YamlDocNode(ParseYamlNode(testStr), "testfilename");
21 | Assert.Multiple(() => {
22 | Assert.That(dn, Has.Count.EqualTo(1));
23 | Assert.That(dn["test_key"].StringValue, Is.EqualTo("test_value"));
24 | Assert.That(dn.ContainsKey("test_key"), Is.True);
25 | });
26 | foreach (var entry in dn.Pairs) {
27 | Assert.Multiple(() => {
28 | Assert.That(entry.Key, Is.EqualTo("test_key"));
29 | Assert.That(entry.Value.StringValue, Is.EqualTo("test_value"));
30 | });
31 | }
32 | }
33 |
34 | [Test]
35 | public void DocNodeDictionaryAccess_ThrowsWhenIndexed() {
36 | string testStr = @"---
37 | # interrupting comment
38 | key:
39 | inner_key: value
40 | ";
41 | var dn = (DocNode) new YamlDocNode(ParseYamlNode(testStr), "testfilename");
42 |
43 | var exception = Assert.Throws(() => {
44 | var x = dn["key"][3];
45 | });
46 |
47 | Assert.Multiple(() => {
48 | Assert.That(exception, Is.Not.Null);
49 |
50 | // verify that there's a line number in the exception
51 | Assert.That(exception.Message.IndexOf("Line: 4", StringComparison.Ordinal), Is.GreaterThanOrEqualTo(0));
52 |
53 | // verify that the dummy filename that we passed in shows up in the exception message
54 | Assert.That(exception.Message.IndexOf("testfilename", StringComparison.Ordinal), Is.GreaterThanOrEqualTo(0));
55 | });
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/test/DictComposingTests.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using NUnit.Framework;
3 | using DarkConfig;
4 |
5 | [TestFixture]
6 | class DictComposingTests {
7 | string tempDirPath;
8 | FileSource fileSource;
9 |
10 | void CreateFile(string filename, string contents) {
11 | File.WriteAllText(Path.Combine(tempDirPath, filename), contents);
12 | }
13 |
14 | [SetUp]
15 | public void SetUp() {
16 | tempDirPath = Path.Combine(Path.GetTempPath(), "DictComposingTests");
17 | Directory.CreateDirectory(tempDirPath);
18 |
19 | Configs.Settings.EnableHotloading = true;
20 | Configs.Settings.HotloadCheckFrequencySeconds = 0.1f;
21 | fileSource = new FileSource(tempDirPath, hotload: true);
22 | Configs.AddConfigSource(fileSource);
23 | }
24 |
25 | [TearDown]
26 | public void TearDown() {
27 | Directory.Delete(tempDirPath, true);
28 | Configs.Clear();
29 | }
30 |
31 | [Test]
32 | public void MergedDict() {
33 | CreateFile("globals.yaml", "Beetles: 12\nBirdName: Shabazz\nVersion: 1.2");
34 | CreateFile("items.yaml", "Treehouse: true");
35 | CreateFile("rooms.yaml", "Version: 1.3\nrooms:\n - Overthorax\n - Chirpinghouse\n - Antennagate\n - Subchitin");
36 |
37 | Configs.Preload();
38 |
39 | DocNode MixedDict = null;
40 |
41 | // load all files from the DictDir into one dict
42 | Configs.ParseFilesAsMergedDict("*", d => {
43 | MixedDict = d;
44 | return true;
45 | });
46 |
47 | Assert.Multiple(() => {
48 | Assert.That(MixedDict, Is.Not.Null);
49 | Assert.That(MixedDict, Has.Count.EqualTo(5));
50 | Assert.That(MixedDict["Beetles"].As(), Is.EqualTo(12));
51 | Assert.That(MixedDict["Version"].As(), Is.EqualTo(1.3f));
52 | Assert.That(MixedDict["Treehouse"].As(), Is.True);
53 | });
54 |
55 | // Overwrite file contents
56 | CreateFile("items.yaml", "Chitin: 1000");
57 |
58 | // force a reload
59 | Configs.Update(1.0f);
60 |
61 | Assert.Multiple(() => {
62 | Assert.That(MixedDict, Has.Count.EqualTo(5));
63 | Assert.That(MixedDict.ContainsKey("Treehouse"), Is.False);
64 | Assert.That(MixedDict["Chitin"].As(), Is.EqualTo(1000));
65 | });
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/test/GlobMatchTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using System.Collections.Generic;
3 | using DarkConfig.Internal;
4 |
5 | [TestFixture]
6 | class GlobMatchTests {
7 | static readonly List AllFiles = new List {
8 | "Folder/1File",
9 | "Folder/2File",
10 | "Folder/3File",
11 | "Folder/Thumbs",
12 | "Uggabo",
13 | "Buggabo",
14 | "Parent/Child/Grandchild/a",
15 | "Parent/Child/Grandchild/b",
16 | "Parent/Child/Grandchild/c",
17 | };
18 |
19 | [Test]
20 | public void MatchStar() {
21 | var matchingFiles = new List();
22 | RegexUtils.FilterMatchingGlob("Folder/*", AllFiles, matchingFiles);
23 | Assert.That(matchingFiles, Is.EqualTo(new List {"Folder/1File", "Folder/2File", "Folder/3File", "Folder/Thumbs"}));
24 | }
25 |
26 | [Test]
27 | public void MatchStarPostfix() {
28 | var matchingFiles = new List();
29 | RegexUtils.FilterMatchingGlob("Folder/*File", AllFiles, matchingFiles);
30 | Assert.That(matchingFiles, Is.EqualTo(new List {"Folder/1File", "Folder/2File", "Folder/3File"}));
31 | }
32 |
33 | [Test]
34 | public void MatchQuestionMark() {
35 | var matchingFiles = new List();
36 | RegexUtils.FilterMatchingGlob("Folder/?File", AllFiles, matchingFiles);
37 | Assert.That(matchingFiles, Is.EqualTo(new List {"Folder/1File", "Folder/2File", "Folder/3File"}));
38 | }
39 |
40 | [Test]
41 | public void MatchStarOnePathOnly() {
42 | var matchingFiles = new List();
43 | RegexUtils.FilterMatchingGlob("*", AllFiles, matchingFiles);
44 | Assert.That(matchingFiles, Is.EqualTo(new List {"Uggabo", "Buggabo"}));
45 | }
46 |
47 | [Test]
48 | public void MatchDoubleStar() {
49 | var matchingFiles = new List();
50 | RegexUtils.FilterMatchingGlob("Parent/**", AllFiles, matchingFiles);
51 | Assert.That(matchingFiles, Is.EqualTo(new List {"Parent/Child/Grandchild/a", "Parent/Child/Grandchild/b", "Parent/Child/Grandchild/c"}));
52 | }
53 |
54 | [Test]
55 | public void MatchDoubleStarCapped() {
56 | var matchingFiles = new List();
57 | RegexUtils.FilterMatchingGlob("Parent/**/b", AllFiles, matchingFiles);
58 | Assert.That(matchingFiles, Is.EqualTo(new List {"Parent/Child/Grandchild/b"}));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/wing.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: efdcfb877781c465ba7925e128bed459
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: -1
31 | maxTextureSize: 1024
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: 1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: 1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 5
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 1
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 1024
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | spriteSheet:
75 | serializedVersion: 2
76 | sprites: []
77 | outline: []
78 | physicsShape: []
79 | bones: []
80 | spriteID: 5e97eb03825dee720800000000000000
81 | internalID: 0
82 | vertices: []
83 | indices:
84 | edges: []
85 | weights: []
86 | secondaryTextures: []
87 | spritePackingTag:
88 | pSDRemoveMatte: 0
89 | pSDShowRemoveMatteOption: 0
90 | userData:
91 | assetBundleName:
92 | assetBundleVariant:
93 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/circle.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 699c214982c804fec985eb5e2dd904ba
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: -1
31 | maxTextureSize: 1024
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: 1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: 1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 1
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 1024
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | spriteSheet:
75 | serializedVersion: 2
76 | sprites: []
77 | outline: []
78 | physicsShape: []
79 | bones: []
80 | spriteID: 5e97eb03825dee720800000000000000
81 | internalID: 0
82 | vertices: []
83 | indices:
84 | edges: []
85 | weights: []
86 | secondaryTextures: []
87 | spritePackingTag:
88 | pSDRemoveMatte: 0
89 | pSDShowRemoveMatteOption: 0
90 | userData:
91 | assetBundleName:
92 | assetBundleVariant:
93 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/plus.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2c485a2c8da304368b3399fcce9ed746
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: -1
31 | maxTextureSize: 1024
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: -1
38 | wrapV: -1
39 | wrapW: -1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 1
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 1024
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | spriteSheet:
75 | serializedVersion: 2
76 | sprites: []
77 | outline: []
78 | physicsShape: []
79 | bones: []
80 | spriteID: 5e97eb03825dee720800000000000000
81 | internalID: 0
82 | vertices: []
83 | indices:
84 | edges: []
85 | weights: []
86 | secondaryTextures: []
87 | spritePackingTag:
88 | pSDRemoveMatte: 0
89 | pSDShowRemoveMatteOption: 0
90 | userData:
91 | assetBundleName:
92 | assetBundleVariant:
93 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/fuselage.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c50995914c97f40d9bfb8de748f41af5
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: -1
31 | maxTextureSize: 1024
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -100
37 | wrapU: -1
38 | wrapV: -1
39 | wrapW: -1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 1
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 1024
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | spriteSheet:
75 | serializedVersion: 2
76 | sprites: []
77 | outline: []
78 | physicsShape: []
79 | bones: []
80 | spriteID: 5e97eb03825dee720800000000000000
81 | internalID: 0
82 | vertices: []
83 | indices:
84 | edges: []
85 | weights: []
86 | secondaryTextures: []
87 | spritePackingTag:
88 | pSDRemoveMatte: 0
89 | pSDShowRemoveMatteOption: 0
90 | userData:
91 | assetBundleName:
92 | assetBundleVariant:
93 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/Bullet.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a67e64f9fbce445ab91e35154d482a51
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: -1
31 | maxTextureSize: 1024
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: 1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: 1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 9
47 | spritePivot: {x: 0.5104232, y: 0.6855326}
48 | spritePixelsToUnits: 25
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 1
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 1024
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | spriteSheet:
75 | serializedVersion: 2
76 | sprites: []
77 | outline: []
78 | physicsShape: []
79 | bones: []
80 | spriteID: 5e97eb03825dee720800000000000000
81 | internalID: 0
82 | vertices: []
83 | indices:
84 | edges: []
85 | weights: []
86 | secondaryTextures: []
87 | spritePackingTag:
88 | pSDRemoveMatte: 0
89 | pSDShowRemoveMatteOption: 0
90 | userData:
91 | assetBundleName:
92 | assetBundleVariant:
93 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Art/stabilizer.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 370489f73b8ca42b7aefad3ea4335cfa
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 0
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: -1
31 | maxTextureSize: 1024
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: 1
36 | mipBias: -100
37 | wrapU: 1
38 | wrapV: 1
39 | wrapW: 1
40 | nPOTScale: 0
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 1
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 9
47 | spritePivot: {x: 0.9724112, y: 0.4935096}
48 | spritePixelsToUnits: 100
49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
50 | spriteGenerateFallbackPhysicsShape: 1
51 | alphaUsage: 1
52 | alphaIsTransparency: 1
53 | spriteTessellationDetail: -1
54 | textureType: 8
55 | textureShape: 1
56 | singleChannelComponent: 0
57 | maxTextureSizeSet: 0
58 | compressionQualitySet: 0
59 | textureFormatSet: 0
60 | applyGammaDecoding: 1
61 | platformSettings:
62 | - serializedVersion: 3
63 | buildTarget: DefaultTexturePlatform
64 | maxTextureSize: 1024
65 | resizeAlgorithm: 0
66 | textureFormat: -1
67 | textureCompression: 1
68 | compressionQuality: 50
69 | crunchedCompression: 0
70 | allowsAlphaSplitting: 0
71 | overridden: 0
72 | androidETC2FallbackOverride: 0
73 | forceMaximumCompressionQuality_BC6H_BC7: 0
74 | spriteSheet:
75 | serializedVersion: 2
76 | sprites: []
77 | outline: []
78 | physicsShape: []
79 | bones: []
80 | spriteID: 5e97eb03825dee720800000000000000
81 | internalID: 0
82 | vertices: []
83 | indices:
84 | edges: []
85 | weights: []
86 | secondaryTextures: []
87 | spritePackingTag:
88 | pSDRemoveMatte: 0
89 | pSDShowRemoveMatteOption: 0
90 | userData:
91 | assetBundleName:
92 | assetBundleVariant:
93 |
--------------------------------------------------------------------------------
/test/PostDocTests.cs:
--------------------------------------------------------------------------------
1 | using DarkConfig;
2 | using NUnit.Framework;
3 |
4 | [TestFixture]
5 | class PostDocTests {
6 | const string FILENAME = "PostDocTests_TestFilename";
7 |
8 | class PostDocClass {
9 | public static PostDocClass PostDoc(PostDocClass existing) {
10 | existing.baseKey += 1;
11 | return existing;
12 | }
13 |
14 | public int baseKey;
15 | }
16 |
17 | class PostDocClass2 {
18 | public int baseKey = 0;
19 | }
20 |
21 | class PostDocClass3 {
22 | public static PostDocClass3 PostDoc(PostDocClass3 existing) {
23 | return new PostDocClass3 {
24 | baseKey = 99
25 | };
26 | }
27 |
28 | public int baseKey;
29 | }
30 |
31 | static T ReifyString(string str) where T : new() {
32 | var doc = Configs.ParseString(str, FILENAME);
33 | var result = default(T);
34 | Configs.Reify(ref result, doc);
35 | return result;
36 | }
37 |
38 | [Test]
39 | public void PostDoc_IsCalled() {
40 | var instance = ReifyString("baseKey: 10");
41 | Assert.That(instance.baseKey, Is.EqualTo(11));
42 | }
43 |
44 | [Test]
45 | public void PostDoc_DoesntExist() {
46 | var doc = Configs.ParseString("baseKey: 10", FILENAME);
47 | PostDocClass2 instance = null;
48 | Configs.Reify(ref instance, doc);
49 | Assert.Multiple(() => {
50 | Assert.That(instance, Is.Not.Null);
51 | Assert.That(instance.baseKey, Is.EqualTo(10));
52 | });
53 | }
54 |
55 | [Test]
56 | public void PostDoc_CanReplaceWithReturnValue() {
57 | var doc = Configs.ParseString("baseKey: 10", FILENAME);
58 | PostDocClass3 instance = null;
59 | Configs.Reify(ref instance, doc);
60 | Assert.Multiple(() => {
61 | Assert.That(instance, Is.Not.Null);
62 | Assert.That(instance.baseKey, Is.EqualTo(99));
63 | });
64 | }
65 |
66 | [Test]
67 | public void PostDoc_ManuallyRegistered() {
68 | var doc = Configs.ParseString("baseKey: 5", FILENAME);
69 | PostDocClass2 instance = null;
70 | Configs.RegisterPostDoc(instance => {
71 | return new PostDocClass2 {baseKey = 10};
72 | });
73 | Configs.Reify(ref instance, doc);
74 | Assert.Multiple(() => {
75 | Assert.That(instance, Is.Not.Null);
76 | Assert.That(instance.baseKey, Is.EqualTo(10));
77 | });
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/AIController.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | public class AIController : MonoBehaviour {
4 | public PlaneController Controller;
5 | public Transform PickupPrefab;
6 |
7 | ////////////////////////////////////////////
8 |
9 | public void Setup(PlaneCard card) {
10 | Controller.Setup(card);
11 | }
12 |
13 | ////////////////////////////////////////////
14 |
15 | void Update() {
16 | // try and target the player (which is a singleton because it's a single player game)
17 | var player = MetaGame.Instance.GetPlayer();
18 | if (player == null) {
19 | return;
20 | }
21 | var directionToPlayer = player.transform.position - transform.position;
22 | var angleToPlayer = (Mathf.Atan2(directionToPlayer.y, directionToPlayer.x) * Mathf.Rad2Deg - 90) % 360f;
23 | var myAngle = transform.eulerAngles.z % 360f;
24 |
25 | var turnAmount = Mathf.Clamp((angleToPlayer - myAngle) / 45, -1, 1);
26 |
27 | if (Controller == null) {
28 | return;
29 | }
30 | Controller.RotationCommand = turnAmount;
31 |
32 | var playerIsClose = directionToPlayer.magnitude < Controller.Card.AIRange;
33 | Controller.IsFiring = playerIsClose && Mathf.Abs(angleToPlayer - myAngle) < 20;
34 |
35 | Controller.Throttle = 1;
36 | }
37 |
38 | void Killed() {
39 | MetaGame.Instance.AIKilled();
40 |
41 | // spawn loot from the loot table
42 | var lootTable = Controller.Card.LootTable;
43 | if (lootTable == null) {
44 | Debug.LogError("null lootTable " + Controller.Card);
45 | return;
46 | }
47 |
48 | var totalWeight = 0f;
49 | foreach (var entry in lootTable) {
50 | if (entry == null) {
51 | Debug.Log("entry null");
52 | continue;
53 | }
54 |
55 | totalWeight += entry.Weight;
56 | }
57 |
58 | var rnd = totalWeight * Random.value;
59 |
60 | foreach (var lootTableEntry in lootTable) {
61 | if (rnd > lootTableEntry.Weight) {
62 | rnd -= lootTableEntry.Weight;
63 | } else {
64 | var loot = lootTableEntry;
65 | if (loot.Health == 0 && loot.CardName == null) return;
66 |
67 | var pickupObj = Instantiate(PickupPrefab, transform.position, Quaternion.identity);
68 | var pickup = pickupObj.GetComponent();
69 |
70 | pickup.Health = loot.Health;
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/test/ListComposingTests.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using NUnit.Framework;
3 | using DarkConfig;
4 | using System.Collections.Generic;
5 |
6 | [TestFixture]
7 | class ListComposingTests {
8 | class Character {
9 | public int Height = 0;
10 | public string Item = "";
11 | }
12 |
13 | string tempDirPath;
14 |
15 | void CreateFile(string filename, string contents) {
16 | string fullPath = Path.Combine(tempDirPath, filename);
17 | using var sw = new StreamWriter(fullPath, false, new System.Text.UTF8Encoding());
18 | sw.Write(contents);
19 | }
20 |
21 | [SetUp]
22 | public void SetUp() {
23 | tempDirPath = Path.Combine(Path.GetTempPath(), "ListComposingTests");
24 | Directory.CreateDirectory(tempDirPath);
25 |
26 | Configs.Settings.EnableHotloading = true;
27 | Configs.Settings.HotloadCheckFrequencySeconds = 0.1f;
28 | Configs.AddConfigSource(new FileSource(tempDirPath, hotload: true));
29 | }
30 |
31 | [TearDown]
32 | public void TearDown() {
33 | Directory.Delete(tempDirPath, true);
34 | Configs.Clear();
35 | }
36 |
37 | [Test]
38 | public void MergeList() {
39 | CreateFile("aragorn.yaml", "Height: 12\nItem: Anduril");
40 | CreateFile("arathorn.yaml", "Height: 11\nItem: Son");
41 | CreateFile("beorn.yaml", "Height: 18\nItem: Bear");
42 | CreateFile("celeborn.yaml", "Height: 14\nItem: Silver");
43 |
44 | Configs.Preload();
45 |
46 | var CharactersEndingInOrn = new List();
47 |
48 | // load all files from the ListDir into one list
49 | Configs.ParseFilesAsList("*", d => {
50 | Assert.That(d.Count, Is.EqualTo(4));
51 | Configs.Reify(ref CharactersEndingInOrn, d);
52 | return true;
53 | });
54 |
55 | Assert.Multiple(() => {
56 | Assert.That(CharactersEndingInOrn, Has.Count.EqualTo(4));
57 | Assert.That(CharactersEndingInOrn[0].Height, Is.EqualTo(12));
58 | Assert.That(CharactersEndingInOrn[0].Item, Is.EqualTo("Anduril"));
59 | });
60 |
61 | // change file contents
62 | CreateFile("aragorn.yaml", "Height: 12\nItem: Throne");
63 |
64 | // Force hotload
65 | Configs.Update(1.0f);
66 |
67 | Assert.Multiple(() => {
68 | Assert.That(CharactersEndingInOrn, Has.Count.EqualTo(4));
69 | Assert.That(CharactersEndingInOrn[0].Height, Is.EqualTo(12));
70 | Assert.That(CharactersEndingInOrn[0].Item, Is.EqualTo("Throne"));
71 | });
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/test/DocNodeExtensionTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using DarkConfig;
3 | using System.Collections.Generic;
4 |
5 | [TestFixture]
6 | class DocNodeExtensionTests {
7 | [Test]
8 | public void AsInt_Parses() {
9 | var doc = Configs.ParseString("10", "TestFilename");
10 | Assert.That(doc.As(), Is.EqualTo(10));
11 | }
12 |
13 | [Test]
14 | public void AsInt_Fails() {
15 | var doc = Configs.ParseString("not_an_int", "TestFilename");
16 | Assert.Throws(() => { doc.As(); });
17 | }
18 |
19 | [Test]
20 | public void AsFloat_Parses() {
21 | var doc = Configs.ParseString("1.45", "TestFilename");
22 | Assert.That(doc.As(), Is.EqualTo(1.45f));
23 | }
24 |
25 | [Test]
26 | public void AsString_Parses() {
27 | var doc = Configs.ParseString("derpy horse", "TestFilename");
28 | Assert.That(doc.As(), Is.EqualTo("derpy horse"));
29 | }
30 |
31 | [Test]
32 | public void AsBool_Parses() {
33 | var doc = Configs.ParseString("true", "TestFilename");
34 | Assert.That(doc.As(), Is.True);
35 | }
36 |
37 | [Test]
38 | public void NestedAccess_Dictionaries() {
39 | const string yaml = @"---
40 | key:
41 | nested:
42 | final: 123
43 | ";
44 | var doc = Configs.ParseString(yaml, "TestFilename");
45 | Assert.That(doc["key"]["nested"]["final"].As(), Is.EqualTo(123));
46 | }
47 |
48 | [Test]
49 | public void NestedAccess_Lists() {
50 | const string yaml = @"---
51 | key:
52 | - 9
53 | - 8.8
54 | - 7.1
55 | ";
56 | var doc = Configs.ParseString(yaml, "TestFilename");
57 | Assert.Multiple(() => {
58 | Assert.That(doc["key"][1].As(), Is.EqualTo(8.8f));
59 | Assert.That(doc["key"][2].As(), Is.EqualTo(7.1f));
60 | });
61 | }
62 |
63 | [Test]
64 | public void As_ListOfString() {
65 | const string yaml = @"---
66 | - the
67 | - quick
68 | - brown
69 | - fox
70 | ";
71 | var doc = Configs.ParseString(yaml, "TestFilename");
72 | var list = doc.As>();
73 | Assert.Multiple(() => {
74 | Assert.That(list[0], Is.EqualTo("the"));
75 | Assert.That(list[1], Is.EqualTo("quick"));
76 | Assert.That(list[2], Is.EqualTo("brown"));
77 | Assert.That(list[3], Is.EqualTo("fox"));
78 | });
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/GraphicsSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!30 &1
4 | GraphicsSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 7
7 | m_Deferred:
8 | m_Mode: 1
9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
10 | m_DeferredReflections:
11 | m_Mode: 1
12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}
13 | m_ScreenSpaceShadows:
14 | m_Mode: 1
15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}
16 | m_LegacyDeferred:
17 | m_Mode: 1
18 | m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0}
19 | m_DepthNormals:
20 | m_Mode: 1
21 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}
22 | m_MotionVectors:
23 | m_Mode: 1
24 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}
25 | m_LightHalo:
26 | m_Mode: 1
27 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}
28 | m_LensFlare:
29 | m_Mode: 1
30 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}
31 | m_AlwaysIncludedShaders:
32 | - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}
33 | - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0}
34 | - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0}
35 | - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0}
36 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
37 | - {fileID: 10782, guid: 0000000000000000f000000000000000, type: 0}
38 | m_PreloadedShaders: []
39 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,
40 | type: 0}
41 | m_ShaderSettings_Tier1:
42 | useCascadedShadowMaps: 1
43 | standardShaderQuality: 2
44 | useReflectionProbeBoxProjection: 1
45 | useReflectionProbeBlending: 1
46 | m_ShaderSettings_Tier2:
47 | useCascadedShadowMaps: 1
48 | standardShaderQuality: 2
49 | useReflectionProbeBoxProjection: 1
50 | useReflectionProbeBlending: 1
51 | m_ShaderSettings_Tier3:
52 | useCascadedShadowMaps: 1
53 | standardShaderQuality: 2
54 | useReflectionProbeBoxProjection: 1
55 | useReflectionProbeBlending: 1
56 | m_BuildTargetShaderSettings: []
57 | m_LightmapStripping: 0
58 | m_FogStripping: 0
59 | m_LightmapKeepPlain: 1
60 | m_LightmapKeepDirCombined: 1
61 | m_LightmapKeepDirSeparate: 1
62 | m_LightmapKeepDynamicPlain: 1
63 | m_LightmapKeepDynamicDirCombined: 1
64 | m_LightmapKeepDynamicDirSeparate: 1
65 | m_FogKeepLinear: 1
66 | m_FogKeepExp: 1
67 | m_FogKeepExp2: 1
68 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/NavMeshAreas.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!126 &1
4 | NavMeshLayers:
5 | m_ObjectHideFlags: 0
6 | Built-in Layer 0:
7 | name: Default
8 | cost: 1
9 | editType: 2
10 | Built-in Layer 1:
11 | name: Not Walkable
12 | cost: 1
13 | editType: 0
14 | Built-in Layer 2:
15 | name: Jump
16 | cost: 2
17 | editType: 2
18 | User Layer 0:
19 | name:
20 | cost: 1
21 | editType: 3
22 | User Layer 1:
23 | name:
24 | cost: 1
25 | editType: 3
26 | User Layer 2:
27 | name:
28 | cost: 1
29 | editType: 3
30 | User Layer 3:
31 | name:
32 | cost: 1
33 | editType: 3
34 | User Layer 4:
35 | name:
36 | cost: 1
37 | editType: 3
38 | User Layer 5:
39 | name:
40 | cost: 1
41 | editType: 3
42 | User Layer 6:
43 | name:
44 | cost: 1
45 | editType: 3
46 | User Layer 7:
47 | name:
48 | cost: 1
49 | editType: 3
50 | User Layer 8:
51 | name:
52 | cost: 1
53 | editType: 3
54 | User Layer 9:
55 | name:
56 | cost: 1
57 | editType: 3
58 | User Layer 10:
59 | name:
60 | cost: 1
61 | editType: 3
62 | User Layer 11:
63 | name:
64 | cost: 1
65 | editType: 3
66 | User Layer 12:
67 | name:
68 | cost: 1
69 | editType: 3
70 | User Layer 13:
71 | name:
72 | cost: 1
73 | editType: 3
74 | User Layer 14:
75 | name:
76 | cost: 1
77 | editType: 3
78 | User Layer 15:
79 | name:
80 | cost: 1
81 | editType: 3
82 | User Layer 16:
83 | name:
84 | cost: 1
85 | editType: 3
86 | User Layer 17:
87 | name:
88 | cost: 1
89 | editType: 3
90 | User Layer 18:
91 | name:
92 | cost: 1
93 | editType: 3
94 | User Layer 19:
95 | name:
96 | cost: 1
97 | editType: 3
98 | User Layer 20:
99 | name:
100 | cost: 1
101 | editType: 3
102 | User Layer 21:
103 | name:
104 | cost: 1
105 | editType: 3
106 | User Layer 22:
107 | name:
108 | cost: 1
109 | editType: 3
110 | User Layer 23:
111 | name:
112 | cost: 1
113 | editType: 3
114 | User Layer 24:
115 | name:
116 | cost: 1
117 | editType: 3
118 | User Layer 25:
119 | name:
120 | cost: 1
121 | editType: 3
122 | User Layer 26:
123 | name:
124 | cost: 1
125 | editType: 3
126 | User Layer 27:
127 | name:
128 | cost: 1
129 | editType: 3
130 | User Layer 28:
131 | name:
132 | cost: 1
133 | editType: 3
134 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/NavMeshLayers.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!126 &1
4 | NavMeshLayers:
5 | m_ObjectHideFlags: 0
6 | Built-in Layer 0:
7 | name: Default
8 | cost: 1
9 | editType: 2
10 | Built-in Layer 1:
11 | name: Not Walkable
12 | cost: 1
13 | editType: 0
14 | Built-in Layer 2:
15 | name: Jump
16 | cost: 2
17 | editType: 2
18 | User Layer 0:
19 | name:
20 | cost: 1
21 | editType: 3
22 | User Layer 1:
23 | name:
24 | cost: 1
25 | editType: 3
26 | User Layer 2:
27 | name:
28 | cost: 1
29 | editType: 3
30 | User Layer 3:
31 | name:
32 | cost: 1
33 | editType: 3
34 | User Layer 4:
35 | name:
36 | cost: 1
37 | editType: 3
38 | User Layer 5:
39 | name:
40 | cost: 1
41 | editType: 3
42 | User Layer 6:
43 | name:
44 | cost: 1
45 | editType: 3
46 | User Layer 7:
47 | name:
48 | cost: 1
49 | editType: 3
50 | User Layer 8:
51 | name:
52 | cost: 1
53 | editType: 3
54 | User Layer 9:
55 | name:
56 | cost: 1
57 | editType: 3
58 | User Layer 10:
59 | name:
60 | cost: 1
61 | editType: 3
62 | User Layer 11:
63 | name:
64 | cost: 1
65 | editType: 3
66 | User Layer 12:
67 | name:
68 | cost: 1
69 | editType: 3
70 | User Layer 13:
71 | name:
72 | cost: 1
73 | editType: 3
74 | User Layer 14:
75 | name:
76 | cost: 1
77 | editType: 3
78 | User Layer 15:
79 | name:
80 | cost: 1
81 | editType: 3
82 | User Layer 16:
83 | name:
84 | cost: 1
85 | editType: 3
86 | User Layer 17:
87 | name:
88 | cost: 1
89 | editType: 3
90 | User Layer 18:
91 | name:
92 | cost: 1
93 | editType: 3
94 | User Layer 19:
95 | name:
96 | cost: 1
97 | editType: 3
98 | User Layer 20:
99 | name:
100 | cost: 1
101 | editType: 3
102 | User Layer 21:
103 | name:
104 | cost: 1
105 | editType: 3
106 | User Layer 22:
107 | name:
108 | cost: 1
109 | editType: 3
110 | User Layer 23:
111 | name:
112 | cost: 1
113 | editType: 3
114 | User Layer 24:
115 | name:
116 | cost: 1
117 | editType: 3
118 | User Layer 25:
119 | name:
120 | cost: 1
121 | editType: 3
122 | User Layer 26:
123 | name:
124 | cost: 1
125 | editType: 3
126 | User Layer 27:
127 | name:
128 | cost: 1
129 | editType: 3
130 | User Layer 28:
131 | name:
132 | cost: 1
133 | editType: 3
134 |
--------------------------------------------------------------------------------
/Docs/docnode.md:
--------------------------------------------------------------------------------
1 | # DocNode
2 |
3 | DocNode is a union type. This means that though as far as C# the language is concerned there is only the one type, DocNode, under the hood it _behaves_ as though it is one of a few types. DocNodes can be Scalar, List, Dictionary, or (rarely) Invalid. Every DocNode has methods that behave like List or Dictionary accessors, but those will only work if the underlying type is right. So before you operate on a DocNode, it makes sense to check the type, using the `Type` property.
4 |
5 | Here's a summary of the DocNode interface.
6 |
7 | ## Type
8 | ```C#
9 | DocNodeType Type { get; }
10 | ```
11 |
12 | Type of the DocNode: Scalar, List, Dictionary, or Invalid
13 |
14 | ## As
15 | ```C#
16 | T As();
17 | ```
18 |
19 | Converts the DocNode to a type T, using the normal parsing rules. For example: `var arr = dn.As()`. Be careful not to call `As()` on the object passed in to the FromDoc function where T is also the registered type of the FromDoc; this will cause an infinite loop.
20 |
21 | ## List Accessor
22 | ```C#
23 | DocNode this[int index] { get; }
24 | ```
25 |
26 | Access the node as if it was a List. Will throw a DocNodeAccessException if Type is not List.
27 |
28 | ## Dictionary Accessor
29 | ```C#
30 | DocNode this[string key] { get; }
31 | ```
32 |
33 | Access the node as if it was a Dictionary. Will throw a DocNodeAccessException if Type is not Dictionary.
34 |
35 | ## Count
36 | ```C#
37 | int Count { get; }
38 | ```
39 |
40 | Number of items in the collection, for both List and Dictionary type. Will throw a DocNodeAccessException if Type is not List or Dictionary.
41 |
42 | ## StringValue
43 | ```C#
44 | string StringValue { get; }
45 | ```
46 |
47 | Value as a string. Will throw a DocNodeAccessException if Type is not Scalar.
48 |
49 | ## ContainsKey
50 | ```C#
51 | bool ContainsKey(string key);
52 | ```
53 |
54 | Returns true if the key is in the dictionary. Will throw a DocNodeAccessException if Type is not Dictionary.
55 |
56 | ## Values
57 | ```C#
58 | IEnumerable Values { get; }
59 | ```
60 |
61 | Iterates over the values of a list. Will throw a DocNodeAccessException if Type is not List.
62 |
63 | ## Pairs
64 | ```C#
65 | IEnumerable> Pairs { get; }
66 | ```
67 |
68 | Iterates over a the key/value pairs of a dictionary. Will throw a `DocNodeAccessException` if Type is not Dictionary.
69 |
70 | ## SourceInformation
71 | ```C#
72 | string SourceInformation { get; }
73 | ```
74 |
75 | String describing the position and context in the source format (e.g. line number). Used to print useful error messages when difficulties are encountered parsing.
76 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/PlaneView.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | [ExecuteInEditMode]
4 | public class PlaneView : MonoBehaviour {
5 | public SpriteRenderer Fuselage;
6 | public SpriteRenderer LeftWing;
7 | public SpriteRenderer RightWing;
8 | public SpriteRenderer LeftStabilizer;
9 | public SpriteRenderer RightStabilizer;
10 |
11 | public PlaneController Controller;
12 |
13 | public PlaneCard Card {
14 | get => _Card;
15 | set {
16 | // we hook up listeners to OnChanged so it gets called when the Card gets modified
17 | if (_Card != null) {
18 | _Card.OnChanged -= Refresh;
19 | }
20 | _Card = value;
21 | _Card.OnChanged += Refresh;
22 | Refresh(_Card);
23 | }
24 | }
25 |
26 | public void Refresh(PlaneCard card) {
27 | // Here, we're copying values from the card because we have to to interface with Unity.
28 | // However, we've arranged things so that this function gets called any time
29 | // the card gets modified.
30 | Fuselage.transform.localPosition = Card.Fuselage.Pos;
31 | Fuselage.transform.localScale = new Vector3(Card.Fuselage.Size.x, Card.Fuselage.Size.y, 1);
32 | LeftWing.transform.localPosition = new Vector2(-Card.Wing.Pos.x, Card.Wing.Pos.y);
33 | LeftWing.transform.localScale = new Vector3(Card.Wing.Size.x, Card.Wing.Size.y, 1);
34 | RightWing.transform.localPosition = Card.Wing.Pos;
35 | RightWing.transform.localScale = new Vector3(-Card.Wing.Size.x, Card.Wing.Size.y, 1);
36 | LeftStabilizer.transform.localPosition = new Vector2(-Card.Stabilizer.Pos.x, Card.Stabilizer.Pos.y);
37 | LeftStabilizer.transform.localScale = Card.Stabilizer.Size;
38 | RightStabilizer.transform.localPosition = Card.Stabilizer.Pos;
39 | RightStabilizer.transform.localScale = new Vector3(-Card.Stabilizer.Size.x, Card.Stabilizer.Size.y, 1);
40 |
41 | if (Controller == null) return;
42 | var healthPct = ((float) Controller.HitPoints) / Controller.MaxHitPoints;
43 | var color = Color.Lerp(Color.white, Color.Lerp(Color.red, Color.black, 0.2f), 1 - healthPct);
44 | Fuselage.color = color;
45 | LeftWing.color = color;
46 | RightWing.color = color;
47 | LeftStabilizer.color = color;
48 | RightStabilizer.color = color;
49 | }
50 |
51 | ////////////////////////////////////////////
52 |
53 | PlaneCard _Card;
54 |
55 | ////////////////////////////////////////////
56 |
57 | void OnDestroy() {
58 | // need to clean up this listener so it's not a memory leak
59 | if (Card != null) {
60 | Card.OnChanged -= Refresh;
61 | }
62 | }
63 |
64 | void Killed() { }
65 | }
--------------------------------------------------------------------------------
/Docs/sources.md:
--------------------------------------------------------------------------------
1 | # Config Sources
2 |
3 | `ConfigSource` instances provide DarkConfig with a means to find and load config files. DarkConfig provides two built-in source types out of the box that cover most cases, but the `ConfigSource` base class is designed to be extended to support custom data sources for your game. Config sources can optionally support hotloading of configs
4 |
5 | ## Built-In Source Types
6 |
7 | There are two built-in source types:
8 | - `ResourcesSource` loads files from Unity's "Resources" directories.
9 | - `FileSource` loads files from any directory on the filesystem.
10 |
11 | Before working with DarkConfig, you need to provide it with one or more config sources.
12 |
13 | ```C#
14 | // FileSource
15 |
16 | var configsDir = Path.Combine(Directory.GetCurrentDirectory(), "Configs");
17 |
18 | // load all files with the ".yaml" extension from the given path
19 | Configs.AddSource(new FileSource(configsDir));
20 |
21 | // load all files with the ".conf" extension from the given path.
22 | Configs.AddSource(new FileSource(configsDir, "conf"))
23 |
24 | // load all files with either the ".yml" or ".yaml" extension from the given path.
25 | Configs.AddSource(new FileSource(configsDir, new[]{"yml", "yaml"}))
26 |
27 | // same as above but also enable file hotloading (disabled by default)
28 | Configs.AddSource(new FileSource(configsDir, new[]{"yml", "yaml"}, hotload:true))
29 |
30 |
31 | // ResourcesSource (Unity only)
32 |
33 | // load files in the default path: Assets/Resources/Configs
34 | Configs.AddSource(new ResourcesSource());
35 |
36 | // load files in the path: Assets/Resources/Demo
37 | Configs.AddSource(new ResourcesSource("Demo"));
38 |
39 | // load files in the path: Assets/Resources/Demo with hotloading (disabled by default) enabled.
40 | Configs.AddSource(new ResourcesSource("Demo", hotload:true));
41 | ```
42 |
43 | ## Custom Sources
44 |
45 | To provide an additional means of loading config files, you can create a custom `ConfigSource` subclass. Refer to one of the built-in source types for an example of this process.
46 |
47 | There are a few methods and properties you need to provide:
48 |
49 | * `CanHotload`: (required) A bool indicating whether this config source supports hotloading files.
50 | * `StepPreload`: (required) A generator function that loads one config file before yielding null. This allows for both blocking and time-sliced loading.
51 | * `Hotload`: (optional) Called by DarkConfig to detect changes to files that it should hotload. Add the file names of changed configs to the `changedFiles` list parameter to indicate they should be hotloaded.
52 | * `AllFiles`: field that provides the loaded config file data to DarkConfig. `StepPreload` should populate this dictionary with instances of `ConfigFileInfo` mapped to by the config file name (without extension).
53 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Player.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1 &100000
4 | GameObject:
5 | m_ObjectHideFlags: 0
6 | m_PrefabParentObject: {fileID: 0}
7 | m_PrefabInternal: {fileID: 100100000}
8 | serializedVersion: 4
9 | m_Component:
10 | - 4: {fileID: 400000}
11 | - 114: {fileID: 11400000}
12 | - 114: {fileID: 11400002}
13 | m_Layer: 0
14 | m_Name: Player
15 | m_TagString: Player
16 | m_Icon: {fileID: 0}
17 | m_NavMeshLayer: 0
18 | m_StaticEditorFlags: 0
19 | m_IsActive: 1
20 | --- !u!4 &400000
21 | Transform:
22 | m_ObjectHideFlags: 1
23 | m_PrefabParentObject: {fileID: 0}
24 | m_PrefabInternal: {fileID: 100100000}
25 | m_GameObject: {fileID: 100000}
26 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
27 | m_LocalPosition: {x: 0, y: 0, z: 0}
28 | m_LocalScale: {x: 1, y: 1, z: 1}
29 | m_Children: []
30 | m_Father: {fileID: 0}
31 | m_RootOrder: 0
32 | --- !u!114 &11400000
33 | MonoBehaviour:
34 | m_ObjectHideFlags: 1
35 | m_PrefabParentObject: {fileID: 0}
36 | m_PrefabInternal: {fileID: 100100000}
37 | m_GameObject: {fileID: 100000}
38 | m_Enabled: 1
39 | m_EditorHideFlags: 0
40 | m_Script: {fileID: 11500000, guid: 74880b4d8c9c547a59e921a550dbe9ea, type: 3}
41 | m_Name:
42 | m_EditorClassIdentifier:
43 | StartingCard:
44 | Controller: {fileID: 11400002}
45 | --- !u!114 &11400002
46 | MonoBehaviour:
47 | m_ObjectHideFlags: 1
48 | m_PrefabParentObject: {fileID: 0}
49 | m_PrefabInternal: {fileID: 100100000}
50 | m_GameObject: {fileID: 100000}
51 | m_Enabled: 1
52 | m_EditorHideFlags: 0
53 | m_Script: {fileID: 11500000, guid: d792d93ca589c474196eb88a2f1ad3fd, type: 3}
54 | m_Name:
55 | m_EditorClassIdentifier:
56 | RotationCommand: 0
57 | IsFiring: 0
58 | Throttle: 1
59 | HitPoints: 0
60 | MaxHitPoints: 0
61 | BulletPrefab: {fileID: 400000, guid: 8303d936d7f9b42f58a0f2e360412a31, type: 2}
62 | ViewPrefab: {fileID: 400010, guid: 753a9c9756a404422b47e71325b95822, type: 2}
63 | ExplosionPrefab: {fileID: 150164, guid: a61f344ddc6c9416a97212247cfe3afa, type: 2}
64 | ShotFireFXPrefab: {fileID: 112258, guid: cd082a60c9ec5414eb443362175370a9, type: 2}
65 | View: {fileID: 0}
66 | --- !u!1001 &100100000
67 | Prefab:
68 | m_ObjectHideFlags: 1
69 | serializedVersion: 2
70 | m_Modification:
71 | m_TransformParent: {fileID: 0}
72 | m_Modifications:
73 | - target: {fileID: 0}
74 | propertyPath: ExplosionPrefab
75 | value:
76 | objectReference: {fileID: 150164, guid: a61f344ddc6c9416a97212247cfe3afa, type: 2}
77 | - target: {fileID: 0}
78 | propertyPath: m_TagString
79 | value: Player
80 | objectReference: {fileID: 0}
81 | - target: {fileID: 0}
82 | propertyPath: ShotFireFXPrefab
83 | value:
84 | objectReference: {fileID: 112258, guid: cd082a60c9ec5414eb443362175370a9, type: 2}
85 | m_RemovedComponents: []
86 | m_ParentPrefab: {fileID: 0}
87 | m_RootGameObject: {fileID: 100000}
88 | m_IsPrefabParent: 1
89 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/EnemySpawner.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections.Generic;
3 | using DarkConfig;
4 |
5 | public class EnemySpawner : MonoBehaviour {
6 | public GameObject EnemyPrefab;
7 |
8 | /////////////////////////////////////////////////
9 |
10 | // Set by config
11 | int numEnemies = 5;
12 | int spawnDistanceFromPlayer = 30;
13 |
14 | // Instance vars
15 | List enemies = new List();
16 | PlayerController player;
17 |
18 | /////////////////////////////////////////////////
19 |
20 | void Start() {
21 | // This is _not_ the main initialization code, that's in LoadGame.cs.
22 | // This code runs in the editor, in the PlaneDemo scene, if LoadGame
23 | // hasn't run yet.
24 | // What happens here is that we add the source and then call ApplyThis
25 | // without calling Configs.Preload. In the Unity editor (or as
26 | // determined by the Platform implementation), it will trigger an
27 | // "immediate preload" inside ApplyThis that will load/parse all the
28 | // config files within the function call (so it'll be slow and drop a
29 | // frame).
30 | if (Configs.NumConfigSources == 0) {
31 | Configs.AddConfigSource(new FileSource(Application.dataPath + "/Demo/Resources/Configs", ".bytes"));
32 | }
33 |
34 | Configs.ApplyThis("enemy_spawning", this);
35 | }
36 |
37 | void Update() {
38 | if (player == null) {
39 | player = FindObjectOfType();
40 | }
41 |
42 | if (player == null) {
43 | return;
44 | }
45 |
46 | // clean out destroyed enemies
47 | for (int i = 0; i < enemies.Count; i++) {
48 | if (enemies[i] == null) {
49 | enemies.RemoveAt(i);
50 | i--;
51 | }
52 | }
53 |
54 | if (enemies.Count < numEnemies) {
55 | // pick a card, any card (except the card the player currently has)
56 | PlaneCard chosenCard = null;
57 | while (chosenCard == null || chosenCard == player.Controller.Card) {
58 | var cardNames = new List(PlaneCard.Cards.Keys);
59 | var chosenName = cardNames[(int) (Random.value * PlaneCard.Cards.Count)];
60 | chosenCard = PlaneCard.Cards[chosenName];
61 | }
62 |
63 | // pick a location near the player
64 | var spawnPos = player.transform.position + (Vector3)Random.insideUnitCircle.normalized * spawnDistanceFromPlayer;
65 | var spawnRotation = Quaternion.AngleAxis(Random.value * 360, Vector3.forward);
66 |
67 | // set up the ai
68 | var enemyObj = Instantiate(EnemyPrefab, spawnPos, spawnRotation);
69 | var controller = enemyObj.GetComponent();
70 | controller.Setup(chosenCard);
71 |
72 | enemies.Add(controller);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/PlaneCard.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 | using DarkConfig;
4 |
5 | public class GunMount {
6 | public string Name;
7 | public Location Location;
8 |
9 | // Note that this is assuming that GunCard.Cards is loaded by the
10 | // time we get into this property accessor.
11 | public GunCard Card => GunCard.Cards[Name];
12 | }
13 |
14 | public class LootTableEntry {
15 | // Don't permit this field to be missing in the config, even if DarkConfig
16 | // isn't being strict.
17 | [ConfigMandatory]
18 | public float Weight;
19 |
20 | // Permit this field to be missing in the config, even if DarkConfig is
21 | // being strict.
22 | [ConfigAllowMissing]
23 | public int Health;
24 |
25 | [ConfigAllowMissing]
26 | public string CardName;
27 | }
28 |
29 | public class PlaneCard {
30 | // Fields that are expected to be set in configs.
31 | public Location Fuselage = new Location(new Vector2(0, 0), new Vector2(1, 1));
32 | public Location Wing = new Location(new Vector2(0, 0), new Vector2(1, 1));
33 | public Location Stabilizer = new Location(new Vector2(0, -1), new Vector2(1, 1));
34 |
35 | public float RotationRate = 100;
36 | public float Speed = 15;
37 | public int HitPoints = 5;
38 |
39 | public float AIRange = 5;
40 |
41 | public List GunMounts;
42 |
43 | [ConfigMandatory]
44 | public List LootTable;
45 |
46 | /////////////////////////////////////////////////////////
47 |
48 | // this field can't be set by DarkConfig because it's a function; it's
49 | // also ignored for clarity
50 | [ConfigIgnore]
51 | public System.Action OnChanged;
52 |
53 | // see LoadConfigs for where this is hooked up
54 | [ConfigIgnore]
55 | static Dictionary _Cards;
56 |
57 | // DarkConfig can't currently set properties
58 | public static Dictionary Cards {
59 | get {
60 | if (_Cards == null) {
61 | LoadConfigs();
62 | }
63 | return _Cards;
64 | }
65 | }
66 |
67 | public static void LoadConfigs() {
68 | // loads all config files in the Planes directory
69 | Configs.ParseFilesAsMergedDict("Planes/**", doc => {
70 | Configs.Reify(ref _Cards, doc);
71 | return true;
72 | });
73 | }
74 |
75 | // We have a few places in the code which need to be notified when their
76 | // PlaneCard is modified. These are places where we had no choice but to
77 | // copy some of the values from the PlaneCard into some other object, and
78 | // therefore need to re-copy the values when the PlaneCard gets hotloaded.
79 | //
80 | // To see those use cases, look for usage of OnChanged in PlaneView.cs.
81 | // See hotloading.md for more information on hotloading in general.
82 | public static PlaneCard PostDoc(PlaneCard existing) {
83 | existing.OnChanged?.Invoke(existing);
84 | return existing;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/DarkConfig/Exceptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace DarkConfig {
5 | public class YamlFileException : Exception {
6 | public YamlFileException(string filename, YamlDotNet.Core.YamlException inner)
7 | : base($"Encountered error parsing YAML file '{filename}': {inner.Message}", inner) {
8 | Filename = filename;
9 | YamlException = inner;
10 | }
11 |
12 | public string Filename { get; private set; }
13 | public YamlDotNet.Core.YamlException YamlException { get; private set; }
14 | }
15 |
16 | /// The reason for this strange structure is that when Unity prints
17 | /// exceptions raised in the course of Reifying, often the info you need
18 | /// (the line number) is buried in the second or Nth exception. Unity
19 | /// prints exceptions by printing the message and StackTrace starting from
20 | /// the innermost exception, and working outwards. So this instead makes it
21 | /// more readable by putting all the messages up top, and then all the
22 | /// stack traces in a big line, still from inner at the top to outer at the
23 | /// bottom. It's a bit more readable, and most importantly the line
24 | /// numbers in the config files are much more prominent.
25 | public class ParseException : Exception {
26 | public ParseException(DocNode? exceptionNode, string message, Exception? inner = null) : base((inner != null ? inner.Message + "\n" : "") + message) {
27 | Node = exceptionNode;
28 | wrappedException = inner;
29 | }
30 |
31 | public override string? StackTrace => wrappedException == null ? base.StackTrace : wrappedException.StackTrace + "\n-----\n" + base.StackTrace;
32 | public override string Message => base.Message + (Node != null ? $" from {Node.SourceInformation}" : "");
33 | public string RawMessage => base.Message;
34 | public bool HasNode => Node != null;
35 |
36 | public readonly DocNode? Node;
37 | readonly Exception? wrappedException;
38 | }
39 |
40 | public class TypedParseException : ParseException {
41 | public Type ParsedType;
42 | public TypedParseException(Type type, DocNode node, string message) : base(node, message) {
43 | ParsedType = type;
44 | }
45 | }
46 |
47 | public class MissingFieldsException : TypedParseException {
48 | public MissingFieldsException(Type type, DocNode node, string message) : base(type, node, message) { }
49 | }
50 |
51 | public class ExtraFieldsException : TypedParseException {
52 | public ExtraFieldsException(Type type, DocNode node, string message) : base(type, node, message) { }
53 | }
54 |
55 | public class ConfigFileNotFoundException : FileNotFoundException {
56 | public ConfigFileNotFoundException(string filename) : base("Couldn't find file " + filename + ". Perhaps it isn't in the index, or wasn't preloaded.", filename) { }
57 | }
58 |
59 | public class NotPreloadedException : InvalidOperationException {
60 | public NotPreloadedException(string message) : base(message) { }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Baseline
7 | [*]
8 | charset = utf-8
9 | tab_width = 4
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 |
13 | [*.csproj]
14 | indent_style = tab
15 | indent_size = 4
16 |
17 | [*.cs]
18 | indent_style = space
19 | indent_size = 4
20 | max_line_length = 120
21 |
22 | # Microsoft .NET properties
23 | csharp_indent_braces = false
24 | csharp_new_line_before_catch = false
25 | csharp_new_line_before_else = false
26 | csharp_new_line_before_open_brace = none
27 | csharp_preferred_modifier_order = public, private, override, virtual, file, new, abstract, internal, protected, sealed, static, readonly, extern, unsafe, volatile, async, required:suggestion
28 | csharp_preserve_single_line_blocks = true
29 | csharp_space_after_cast = true
30 | csharp_style_var_for_built_in_types = false:suggestion
31 | dotnet_sort_system_directives_first = false
32 | dotnet_style_require_accessibility_modifiers = never:suggestion
33 |
34 | # ReSharper properties
35 | resharper_align_multiline_binary_expressions_chain = false
36 | resharper_allow_comment_after_lbrace = true
37 | resharper_blank_lines_after_block_statements = 0
38 | resharper_blank_lines_around_auto_property = 0
39 | resharper_blank_lines_around_local_method = 0
40 | resharper_blank_lines_around_property = 0
41 | resharper_blank_lines_around_single_line_type = 1
42 | resharper_braces_for_for = required
43 | resharper_braces_for_foreach = required
44 | resharper_braces_for_ifelse = not_required
45 | resharper_braces_for_while = required
46 | resharper_braces_redundant = false
47 | resharper_csharp_blank_lines_around_field = 0
48 | resharper_csharp_blank_lines_around_type = 0
49 | resharper_csharp_blank_lines_inside_region = 0
50 | resharper_csharp_empty_block_style = together
51 | resharper_csharp_insert_final_newline = true
52 | resharper_csharp_max_line_length = 172
53 | resharper_csharp_remove_blank_lines_near_braces_in_declarations = false
54 | resharper_csharp_wrap_before_binary_opsign = true
55 | resharper_csharp_wrap_ternary_expr_style = wrap_if_long
56 | resharper_default_value_when_type_evident = default_expression
57 | resharper_indent_pars = outside
58 | resharper_instance_members_qualify_declared_in =
59 | resharper_keep_existing_attribute_arrangement = true
60 | resharper_keep_existing_declaration_block_arrangement = false
61 | resharper_keep_existing_enum_arrangement = false
62 | resharper_max_attribute_length_for_same_line = 20
63 | resharper_csharp_object_creation_when_type_evident = target_typed
64 | resharper_csharp_object_creation_when_type_not_evident = target_typed
65 | resharper_parentheses_non_obvious_operations = none, bitwise_and, bitwise_exclusive_or, bitwise_inclusive_or, bitwise
66 | resharper_parentheses_redundancy_style = remove
67 | resharper_place_accessorholder_attribute_on_same_line = false
68 | resharper_place_expr_accessor_on_single_line = true
69 | resharper_place_expr_method_on_single_line = true
70 | resharper_place_simple_initializer_on_single_line = false
71 | resharper_space_within_single_line_array_initializer_braces = false
72 | resharper_wrap_array_initializer_style = chop_if_long
73 |
--------------------------------------------------------------------------------
/demo/ProjectSettings/QualitySettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!47 &1
4 | QualitySettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 5
7 | m_CurrentQuality: 3
8 | m_QualitySettings:
9 | - serializedVersion: 2
10 | name: Fastest
11 | pixelLightCount: 0
12 | shadows: 0
13 | shadowResolution: 0
14 | shadowProjection: 1
15 | shadowCascades: 1
16 | shadowDistance: 15
17 | blendWeights: 1
18 | textureQuality: 1
19 | anisotropicTextures: 0
20 | antiAliasing: 0
21 | softParticles: 0
22 | softVegetation: 0
23 | vSyncCount: 0
24 | lodBias: .300000012
25 | maximumLODLevel: 0
26 | particleRaycastBudget: 4
27 | excludedTargetPlatforms: []
28 | - serializedVersion: 2
29 | name: Fast
30 | pixelLightCount: 0
31 | shadows: 0
32 | shadowResolution: 0
33 | shadowProjection: 1
34 | shadowCascades: 1
35 | shadowDistance: 20
36 | blendWeights: 2
37 | textureQuality: 0
38 | anisotropicTextures: 0
39 | antiAliasing: 0
40 | softParticles: 0
41 | softVegetation: 0
42 | vSyncCount: 0
43 | lodBias: .400000006
44 | maximumLODLevel: 0
45 | particleRaycastBudget: 16
46 | excludedTargetPlatforms: []
47 | - serializedVersion: 2
48 | name: Simple
49 | pixelLightCount: 1
50 | shadows: 1
51 | shadowResolution: 0
52 | shadowProjection: 1
53 | shadowCascades: 1
54 | shadowDistance: 20
55 | blendWeights: 2
56 | textureQuality: 0
57 | anisotropicTextures: 1
58 | antiAliasing: 0
59 | softParticles: 0
60 | softVegetation: 0
61 | vSyncCount: 0
62 | lodBias: .699999988
63 | maximumLODLevel: 0
64 | particleRaycastBudget: 64
65 | excludedTargetPlatforms: []
66 | - serializedVersion: 2
67 | name: Good
68 | pixelLightCount: 2
69 | shadows: 2
70 | shadowResolution: 1
71 | shadowProjection: 1
72 | shadowCascades: 2
73 | shadowDistance: 40
74 | blendWeights: 2
75 | textureQuality: 0
76 | anisotropicTextures: 1
77 | antiAliasing: 0
78 | softParticles: 0
79 | softVegetation: 1
80 | vSyncCount: 1
81 | lodBias: 1
82 | maximumLODLevel: 0
83 | particleRaycastBudget: 256
84 | excludedTargetPlatforms: []
85 | - serializedVersion: 2
86 | name: Beautiful
87 | pixelLightCount: 3
88 | shadows: 2
89 | shadowResolution: 2
90 | shadowProjection: 1
91 | shadowCascades: 2
92 | shadowDistance: 70
93 | blendWeights: 4
94 | textureQuality: 0
95 | anisotropicTextures: 2
96 | antiAliasing: 2
97 | softParticles: 1
98 | softVegetation: 1
99 | vSyncCount: 1
100 | lodBias: 1.5
101 | maximumLODLevel: 0
102 | particleRaycastBudget: 1024
103 | excludedTargetPlatforms: []
104 | - serializedVersion: 2
105 | name: Fantastic
106 | pixelLightCount: 4
107 | shadows: 2
108 | shadowResolution: 2
109 | shadowProjection: 1
110 | shadowCascades: 4
111 | shadowDistance: 150
112 | blendWeights: 4
113 | textureQuality: 0
114 | anisotropicTextures: 2
115 | antiAliasing: 2
116 | softParticles: 1
117 | softVegetation: 1
118 | vSyncCount: 1
119 | lodBias: 2
120 | maximumLODLevel: 0
121 | particleRaycastBudget: 4096
122 | excludedTargetPlatforms: []
123 | m_PerPlatformDefaultQuality: {}
124 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Enemy.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1 &100000
4 | GameObject:
5 | m_ObjectHideFlags: 0
6 | m_PrefabParentObject: {fileID: 0}
7 | m_PrefabInternal: {fileID: 100100000}
8 | serializedVersion: 4
9 | m_Component:
10 | - 4: {fileID: 400000}
11 | - 114: {fileID: 11400002}
12 | - 114: {fileID: 11400000}
13 | - 50: {fileID: 5000000}
14 | m_Layer: 0
15 | m_Name: Enemy
16 | m_TagString: Untagged
17 | m_Icon: {fileID: 0}
18 | m_NavMeshLayer: 0
19 | m_StaticEditorFlags: 0
20 | m_IsActive: 1
21 | --- !u!4 &400000
22 | Transform:
23 | m_ObjectHideFlags: 1
24 | m_PrefabParentObject: {fileID: 0}
25 | m_PrefabInternal: {fileID: 100100000}
26 | m_GameObject: {fileID: 100000}
27 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
28 | m_LocalPosition: {x: 17.6522179, y: 0, z: 0}
29 | m_LocalScale: {x: 1, y: 1, z: 1}
30 | m_Children: []
31 | m_Father: {fileID: 0}
32 | m_RootOrder: 0
33 | --- !u!50 &5000000
34 | Rigidbody2D:
35 | serializedVersion: 2
36 | m_ObjectHideFlags: 1
37 | m_PrefabParentObject: {fileID: 0}
38 | m_PrefabInternal: {fileID: 100100000}
39 | m_GameObject: {fileID: 100000}
40 | m_Mass: 1
41 | m_LinearDrag: 0
42 | m_AngularDrag: 0
43 | m_GravityScale: 0
44 | m_IsKinematic: 1
45 | m_Interpolate: 0
46 | m_SleepingMode: 1
47 | m_CollisionDetection: 0
48 | m_Constraints: 0
49 | --- !u!114 &11400000
50 | MonoBehaviour:
51 | m_ObjectHideFlags: 1
52 | m_PrefabParentObject: {fileID: 0}
53 | m_PrefabInternal: {fileID: 100100000}
54 | m_GameObject: {fileID: 100000}
55 | m_Enabled: 1
56 | m_EditorHideFlags: 0
57 | m_Script: {fileID: 11500000, guid: e746dc99bf5984be5abc08aec0e62d4c, type: 3}
58 | m_Name:
59 | m_EditorClassIdentifier:
60 | Controller: {fileID: 11400002}
61 | PickupPrefab: {fileID: 400000, guid: 95671f6452fc34383a438f7ea4d1448f, type: 2}
62 | --- !u!114 &11400002
63 | MonoBehaviour:
64 | m_ObjectHideFlags: 1
65 | m_PrefabParentObject: {fileID: 0}
66 | m_PrefabInternal: {fileID: 100100000}
67 | m_GameObject: {fileID: 100000}
68 | m_Enabled: 1
69 | m_EditorHideFlags: 0
70 | m_Script: {fileID: 11500000, guid: d792d93ca589c474196eb88a2f1ad3fd, type: 3}
71 | m_Name:
72 | m_EditorClassIdentifier:
73 | RotationCommand: 0
74 | IsFiring: 0
75 | Throttle: 1
76 | HitPoints: 0
77 | MaxHitPoints: 0
78 | BulletPrefab: {fileID: 400000, guid: 8303d936d7f9b42f58a0f2e360412a31, type: 2}
79 | ViewPrefab: {fileID: 400010, guid: 753a9c9756a404422b47e71325b95822, type: 2}
80 | ExplosionPrefab: {fileID: 150164, guid: a61f344ddc6c9416a97212247cfe3afa, type: 2}
81 | ShotFireFXPrefab: {fileID: 112258, guid: cd082a60c9ec5414eb443362175370a9, type: 2}
82 | View: {fileID: 0}
83 | --- !u!1001 &100100000
84 | Prefab:
85 | m_ObjectHideFlags: 1
86 | serializedVersion: 2
87 | m_Modification:
88 | m_TransformParent: {fileID: 0}
89 | m_Modifications:
90 | - target: {fileID: 0}
91 | propertyPath: ExplosionPrefab
92 | value:
93 | objectReference: {fileID: 150164, guid: a61f344ddc6c9416a97212247cfe3afa, type: 2}
94 | - target: {fileID: 0}
95 | propertyPath: ShotFireFXPrefab
96 | value:
97 | objectReference: {fileID: 112258, guid: cd082a60c9ec5414eb443362175370a9, type: 2}
98 | m_RemovedComponents: []
99 | m_ParentPrefab: {fileID: 0}
100 | m_RootGameObject: {fileID: 100000}
101 | m_IsPrefabParent: 1
102 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Bullet.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1 &100000
4 | GameObject:
5 | m_ObjectHideFlags: 0
6 | m_PrefabParentObject: {fileID: 0}
7 | m_PrefabInternal: {fileID: 100100000}
8 | serializedVersion: 4
9 | m_Component:
10 | - 4: {fileID: 400000}
11 | - 212: {fileID: 21200000}
12 | - 58: {fileID: 5800000}
13 | - 114: {fileID: 11400000}
14 | - 50: {fileID: 5000000}
15 | m_Layer: 0
16 | m_Name: Bullet
17 | m_TagString: Untagged
18 | m_Icon: {fileID: 0}
19 | m_NavMeshLayer: 0
20 | m_StaticEditorFlags: 0
21 | m_IsActive: 1
22 | --- !u!4 &400000
23 | Transform:
24 | m_ObjectHideFlags: 1
25 | m_PrefabParentObject: {fileID: 0}
26 | m_PrefabInternal: {fileID: 100100000}
27 | m_GameObject: {fileID: 100000}
28 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
29 | m_LocalPosition: {x: -24.0824509, y: 28.2398224, z: 0}
30 | m_LocalScale: {x: 1, y: 1, z: 1}
31 | m_Children: []
32 | m_Father: {fileID: 0}
33 | m_RootOrder: 0
34 | --- !u!50 &5000000
35 | Rigidbody2D:
36 | serializedVersion: 2
37 | m_ObjectHideFlags: 1
38 | m_PrefabParentObject: {fileID: 0}
39 | m_PrefabInternal: {fileID: 100100000}
40 | m_GameObject: {fileID: 100000}
41 | m_Mass: 1
42 | m_LinearDrag: 0
43 | m_AngularDrag: 0
44 | m_GravityScale: 0
45 | m_IsKinematic: 0
46 | m_Interpolate: 0
47 | m_SleepingMode: 1
48 | m_CollisionDetection: 1
49 | m_Constraints: 4
50 | --- !u!58 &5800000
51 | CircleCollider2D:
52 | m_ObjectHideFlags: 1
53 | m_PrefabParentObject: {fileID: 0}
54 | m_PrefabInternal: {fileID: 100100000}
55 | m_GameObject: {fileID: 100000}
56 | m_Enabled: 1
57 | m_Material: {fileID: 0}
58 | m_IsTrigger: 1
59 | m_UsedByEffector: 0
60 | m_Offset: {x: 0, y: 0}
61 | serializedVersion: 2
62 | m_Radius: .300000012
63 | --- !u!114 &11400000
64 | MonoBehaviour:
65 | m_ObjectHideFlags: 1
66 | m_PrefabParentObject: {fileID: 0}
67 | m_PrefabInternal: {fileID: 100100000}
68 | m_GameObject: {fileID: 100000}
69 | m_Enabled: 1
70 | m_EditorHideFlags: 0
71 | m_Script: {fileID: 11500000, guid: 75c0ac8114f2e4a4f8f0565b350c5183, type: 3}
72 | m_Name:
73 | m_EditorClassIdentifier:
74 | Damage: 0
75 | Speed: 0
76 | HitPrefab: {fileID: 100002, guid: e0efc93d67d144caba51a4eca4d6ea12, type: 2}
77 | Firer: {fileID: 0}
78 | --- !u!212 &21200000
79 | SpriteRenderer:
80 | m_ObjectHideFlags: 1
81 | m_PrefabParentObject: {fileID: 0}
82 | m_PrefabInternal: {fileID: 100100000}
83 | m_GameObject: {fileID: 100000}
84 | m_Enabled: 1
85 | m_CastShadows: 0
86 | m_ReceiveShadows: 0
87 | m_Materials:
88 | - {fileID: 10754, guid: 0000000000000000e000000000000000, type: 0}
89 | m_SubsetIndices:
90 | m_StaticBatchRoot: {fileID: 0}
91 | m_UseLightProbes: 0
92 | m_ReflectionProbeUsage: 0
93 | m_ProbeAnchor: {fileID: 0}
94 | m_ScaleInLightmap: 1
95 | m_PreserveUVs: 0
96 | m_IgnoreNormalsForChartDetection: 0
97 | m_ImportantGI: 0
98 | m_MinimumChartSize: 4
99 | m_AutoUVMaxDistance: .5
100 | m_AutoUVMaxAngle: 89
101 | m_LightmapParameters: {fileID: 0}
102 | m_SortingLayerID: 0
103 | m_SortingOrder: 0
104 | m_Sprite: {fileID: 21300000, guid: a67e64f9fbce445ab91e35154d482a51, type: 3}
105 | m_Color: {r: 1, g: 1, b: 1, a: 1}
106 | --- !u!1001 &100100000
107 | Prefab:
108 | m_ObjectHideFlags: 1
109 | serializedVersion: 2
110 | m_Modification:
111 | m_TransformParent: {fileID: 0}
112 | m_Modifications: []
113 | m_RemovedComponents: []
114 | m_ParentPrefab: {fileID: 0}
115 | m_RootGameObject: {fileID: 100000}
116 | m_IsPrefabParent: 1
117 |
--------------------------------------------------------------------------------
/test/ConfigKeyTests.cs:
--------------------------------------------------------------------------------
1 | using DarkConfig;
2 | using NUnit.Framework;
3 | using System;
4 |
5 | [TestFixture]
6 | class ConfigKeyTests {
7 |
8 | class TestType {
9 | [ConfigKey("level")]
10 | public int CurrentLevel = 1;
11 |
12 | [ConfigKey("StartingXP")]
13 | public int XP { get; set; } = 5;
14 | }
15 |
16 | [Test]
17 | public void ConfigKeyAttributeChangesParsing() {
18 | const string yaml = "{level: 42, StartingXP: 99}";
19 | var doc = Configs.ParseString(yaml, "ConfigKeyAttributeChangesParsing");
20 | var instance = new TestType();
21 | Configs.Reify(ref instance, doc);
22 |
23 | Assert.Multiple(() => {
24 | Assert.That(instance, Is.Not.Null);
25 | Assert.That(instance.CurrentLevel, Is.EqualTo(42));
26 | Assert.That(instance.XP, Is.EqualTo(99));
27 | });
28 | }
29 |
30 | class NullKeyClass {
31 | [ConfigKey(null)]
32 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
33 | public int fail;
34 | #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
35 | }
36 |
37 | [Test]
38 | public void SettingANullKeyNameThrows() {
39 | const string yaml = "{fail: 42}";
40 | var doc = Configs.ParseString(yaml, "SettingANullKeyNameThrows");
41 | var instance = new NullKeyClass();
42 |
43 | Assert.Throws(() => {
44 | Configs.Reify(ref instance, doc);
45 | });
46 | }
47 |
48 | class EmptyKeyClass {
49 | [ConfigKey("")]
50 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
51 | public int fail;
52 | #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
53 | }
54 |
55 | [Test]
56 | public void SettingAnEmptyKeyNameThrows() {
57 | const string yaml = "{fail: 42}";
58 | var doc = Configs.ParseString(yaml, "SettingAnEmptyKeyNameThrows");
59 | var instance = new EmptyKeyClass();
60 |
61 | Assert.Throws(() => {
62 | Configs.Reify(ref instance, doc);
63 | });
64 | }
65 |
66 | class WhitespaceKeyClass {
67 | [ConfigKey(" ")]
68 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
69 | public int fail;
70 | #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
71 | }
72 |
73 | [Test]
74 | public void SettingAnAllWhitespaceKeyNameThrows() {
75 | const string yaml = "{fail: 42}";
76 | var doc = Configs.ParseString(yaml, "SettingAnAllWhitespaceKeyNameThrows");
77 | var instance = new WhitespaceKeyClass();
78 |
79 | Assert.Throws(() => {
80 | Configs.Reify(ref instance, doc);
81 | });
82 | }
83 |
84 | class PaddedKeyClass {
85 | [ConfigKey(" after ")]
86 | #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
87 | public int before;
88 | #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
89 | }
90 |
91 | [Test]
92 | public void KeyNamesAreTrimmed() {
93 | const string yaml = "{after: 42}";
94 | var doc = Configs.ParseString(yaml, "SettingAnAllWhitespaceKeyNameThrows");
95 | var instance = new PaddedKeyClass();
96 | Configs.Reify(ref instance, doc);
97 |
98 | Assert.That(instance.before, Is.EqualTo(42));
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Material/ShotFireParticle.mat:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!21 &2100000
4 | Material:
5 | serializedVersion: 6
6 | m_ObjectHideFlags: 0
7 | m_PrefabParentObject: {fileID: 0}
8 | m_PrefabInternal: {fileID: 0}
9 | m_Name: ShotFireParticle
10 | m_Shader: {fileID: 200, guid: 0000000000000000f000000000000000, type: 0}
11 | m_ShaderKeywords:
12 | m_LightmapFlags: 5
13 | m_CustomRenderQueue: 3000
14 | stringTagMap: {}
15 | m_SavedProperties:
16 | serializedVersion: 2
17 | m_TexEnvs:
18 | data:
19 | first:
20 | name: _MainTex
21 | second:
22 | m_Texture: {fileID: 10300, guid: 0000000000000000f000000000000000, type: 0}
23 | m_Scale: {x: 1, y: 1}
24 | m_Offset: {x: 0, y: 0}
25 | data:
26 | first:
27 | name: _BumpMap
28 | second:
29 | m_Texture: {fileID: 0}
30 | m_Scale: {x: 1, y: 1}
31 | m_Offset: {x: 0, y: 0}
32 | data:
33 | first:
34 | name: _DetailNormalMap
35 | second:
36 | m_Texture: {fileID: 0}
37 | m_Scale: {x: 1, y: 1}
38 | m_Offset: {x: 0, y: 0}
39 | data:
40 | first:
41 | name: _ParallaxMap
42 | second:
43 | m_Texture: {fileID: 0}
44 | m_Scale: {x: 1, y: 1}
45 | m_Offset: {x: 0, y: 0}
46 | data:
47 | first:
48 | name: _OcclusionMap
49 | second:
50 | m_Texture: {fileID: 0}
51 | m_Scale: {x: 1, y: 1}
52 | m_Offset: {x: 0, y: 0}
53 | data:
54 | first:
55 | name: _EmissionMap
56 | second:
57 | m_Texture: {fileID: 0}
58 | m_Scale: {x: 1, y: 1}
59 | m_Offset: {x: 0, y: 0}
60 | data:
61 | first:
62 | name: _DetailMask
63 | second:
64 | m_Texture: {fileID: 0}
65 | m_Scale: {x: 1, y: 1}
66 | m_Offset: {x: 0, y: 0}
67 | data:
68 | first:
69 | name: _DetailAlbedoMap
70 | second:
71 | m_Texture: {fileID: 0}
72 | m_Scale: {x: 1, y: 1}
73 | m_Offset: {x: 0, y: 0}
74 | data:
75 | first:
76 | name: _MetallicGlossMap
77 | second:
78 | m_Texture: {fileID: 0}
79 | m_Scale: {x: 1, y: 1}
80 | m_Offset: {x: 0, y: 0}
81 | m_Floats:
82 | data:
83 | first:
84 | name: _SrcBlend
85 | second: 1
86 | data:
87 | first:
88 | name: _DstBlend
89 | second: 0
90 | data:
91 | first:
92 | name: _Cutoff
93 | second: .5
94 | data:
95 | first:
96 | name: _InvFade
97 | second: 1
98 | data:
99 | first:
100 | name: _Parallax
101 | second: .0199999996
102 | data:
103 | first:
104 | name: _ZWrite
105 | second: 1
106 | data:
107 | first:
108 | name: _Glossiness
109 | second: .5
110 | data:
111 | first:
112 | name: _BumpScale
113 | second: 1
114 | data:
115 | first:
116 | name: _OcclusionStrength
117 | second: 1
118 | data:
119 | first:
120 | name: _DetailNormalMapScale
121 | second: 1
122 | data:
123 | first:
124 | name: _UVSec
125 | second: 0
126 | data:
127 | first:
128 | name: _Mode
129 | second: 0
130 | data:
131 | first:
132 | name: _Metallic
133 | second: 0
134 | m_Colors:
135 | data:
136 | first:
137 | name: _EmissionColor
138 | second: {r: 0, g: 0, b: 0, a: 1}
139 | data:
140 | first:
141 | name: _Color
142 | second: {r: 1, g: 1, b: 1, a: 1}
143 | data:
144 | first:
145 | name: _TintColor
146 | second: {r: 1, g: 1, b: 1, a: 1}
147 |
--------------------------------------------------------------------------------
/Docs/validation.md:
--------------------------------------------------------------------------------
1 | # Validation
2 |
3 | So the designer you work with added a ton of new enemies, but sometimes accidentally forgot to set their color. So now you're wondering why sometimes the map has all these black dots on it. Whoops, might have been nice to have a big old error pointing this subtle bug out! That's what validation is for.
4 |
5 | DarkConfig does two very simple validations: checking for *missing* fields, and *extra* fields. A missing field is one that is in your class, but it's missing from the config file (as in our mainColor example). An extra field is present in the config file but not on the object -- maybe it's a misspelling like "mainColour", or maybe it's left over from a previous generation.
6 |
7 | ```C#
8 | // SampleObject.cs ----
9 | public class SampleObject {
10 | string field1;
11 | string field2;
12 | }
13 | ```
14 | ```yaml
15 | # sample_config.bytes ----
16 | field1: value1
17 | # field2 is missing
18 | field3: value3 # this field is extra
19 | ```
20 |
21 | When you call Config.Apply, DarkConfig will run these validations and throw ParseExceptions for violations, showing the file and line number. Then you can fix them!
22 |
23 | You can turn on and off validation at many different levels in DarkConfig.
24 |
25 | The precedence of validation is: Field Attribute > Class Attribute > Reify > Global
26 |
27 | ## Global Validation
28 |
29 | Control the default/global validation mode by setting the Config.ConfigOptions enum. This enum has several flags:
30 |
31 | * AllowExtraFields: If set, DarkConfig will not check whether extra fields are present in the config file.
32 | * AllowMissingFields: If set, DarkConfig will not check whether any fields are missing from the config file.
33 | * CaseSensitive: If set, DarkConfig will treat fields as distinct if they differ only in case.
34 | * None: None of the above. DarkConfig will be case-insensitive and will check for missing and extra fields.
35 |
36 | Example:
37 |
38 | // tell DarkConfig to not complain about anything
39 | Config.ConfigOptions = ConfigOptions.AllowExtraFields | ConfigOptions.AllowMissingFields;
40 |
41 | // we can skip some fields but say something if there's an extra field
42 | Config.ConfigOptions = ConfigOptions.AllowMissingFields;
43 |
44 | ## Field Validation
45 |
46 | Set per-field validation with attributes.
47 |
48 | * The `ConfigMandatory` attribute means the field cannot go missing. This overrides any global setting of `ConfigOptions.AllowMissingFields`.
49 | * `ConfigAllowMissing` makes its field skippable, it will not give an exception if not present in the config file. This overrides a global setting that lacks `ConfigOptions.AllowMissingFields`.
50 | * ConfigIgnore is special: it causes DarkConfig to ignore any validation on the field and _also_ never set it. It will be as though the field doesn't exist on the class. This is useful when you want to have strict validation on all other fields but have a few fields that are not set from config files.
51 |
52 |
53 | ```C#
54 | class AttributesClass {
55 | [ConfigMandatory]
56 | public int field1 = -1; // will complain if this field is not in config
57 |
58 | [ConfigAllowMissing]
59 | public string field2 = "initial"; // will happily tolerate this field being missing
60 |
61 | [ConfigIgnore]
62 | public bool field3 = false; // will not set this field
63 | }
64 | ```
65 |
66 | ## Class Validation
67 |
68 | Change DarkConfig's validation on a per-class basis with attributes.
69 |
70 | You can apply ConfigMandatory or ConfigAllowMissing to any class, where they apply to all fields.
71 |
72 | As a special case, classes derived from MonoBehaviour have an implicit ConfigAllowMissing. This is because the MonoBehaviour class has a ton of fields that you don't control and don't want to set.
73 |
74 |
75 | ## Reify Validation
76 |
77 | You can set ConfigOptions per call to Reify. This overrides the global setting for the duration of the call.
78 |
79 | Config.Reify("Configs/testconfig.bytes", ref obj, ConfigOptions.CaseSensitive | ConfigOptions.AllowExtraFields);
80 |
81 |
--------------------------------------------------------------------------------
/src/DarkConfig/Internal/ChecksumUtils.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace DarkConfig.Internal {
4 | public static class ChecksumUtils {
5 | /// Computes an integer checksum of a string.
6 | public static int Checksum(string body) {
7 | byte[] input = System.Text.Encoding.UTF8.GetBytes(body);
8 | using (MemoryStream stream = new MemoryStream(input)) {
9 | return MurMurHash3(stream);
10 | }
11 | }
12 |
13 | /// Computes an integer checksum of a stream.
14 | public static int Checksum(Stream stream) {
15 | return MurMurHash3(stream);
16 | }
17 |
18 | /////////////////////////////////////////////////
19 |
20 | /// MurMurHash3
21 | /// This code is public domain.
22 | ///
23 | /// The MurmurHash3 algorithm was created by Austin Appleby and put into the public domain.
24 | /// See http://code.google.com/p/smhasher/
25 | ///
26 | /// This C# variant was authored by
27 | /// Elliott B. Edwards and was placed into the public domain as a gist
28 | private static int MurMurHash3(Stream stream) {
29 | const uint seed = 144;
30 |
31 | uint rotl32(uint x, byte r) {
32 | return (x << r) | (x >> (32 - r));
33 | }
34 |
35 | uint fmix(uint h) {
36 | h ^= h >> 16;
37 | h *= 0x85ebca6b;
38 | h ^= h >> 13;
39 | h *= 0xc2b2ae35;
40 | h ^= h >> 16;
41 | return h;
42 | }
43 |
44 | const uint c1 = 0xcc9e2d51;
45 | const uint c2 = 0x1b873593;
46 |
47 | uint h1 = seed;
48 | uint k1 = 0;
49 | uint streamLength = 0;
50 |
51 | var reader = new BinaryReader(stream);
52 |
53 | byte[] chunk = reader.ReadBytes(4);
54 | while (chunk.Length > 0) {
55 | streamLength += (uint) chunk.Length;
56 | switch (chunk.Length) {
57 | case 4:
58 | /* Get four bytes from the input into an uint */
59 | k1 = (uint)
60 | (chunk[0]
61 | | chunk[1] << 8
62 | | chunk[2] << 16
63 | | chunk[3] << 24);
64 |
65 | /* bitmagic hash */
66 | k1 *= c1;
67 | k1 = rotl32(k1, 15);
68 | k1 *= c2;
69 |
70 | h1 ^= k1;
71 | h1 = rotl32(h1, 13);
72 | h1 = h1 * 5 + 0xe6546b64;
73 | break;
74 | case 3:
75 | k1 = (uint)
76 | (chunk[0]
77 | | chunk[1] << 8
78 | | chunk[2] << 16);
79 | k1 *= c1;
80 | k1 = rotl32(k1, 15);
81 | k1 *= c2;
82 | h1 ^= k1;
83 | break;
84 | case 2:
85 | k1 = (uint)
86 | (chunk[0]
87 | | chunk[1] << 8);
88 | k1 *= c1;
89 | k1 = rotl32(k1, 15);
90 | k1 *= c2;
91 | h1 ^= k1;
92 | break;
93 | case 1:
94 | k1 = (uint) (chunk[0]);
95 | k1 *= c1;
96 | k1 = rotl32(k1, 15);
97 | k1 *= c2;
98 | h1 ^= k1;
99 | break;
100 | }
101 |
102 | chunk = reader.ReadBytes(4);
103 | }
104 |
105 | // finalization, magic chants to wrap it all up
106 | h1 ^= streamLength;
107 | h1 = fmix(h1);
108 |
109 | unchecked { //ignore overflow
110 | return (int) h1;
111 | }
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig/ResourcesSource.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System;
3 | using System.Collections;
4 | using System.Collections.Generic;
5 |
6 | namespace DarkConfig {
7 | /// loads configs from a Unity Resources directory
8 | ///
9 | /// since we can't check the timestamp on the files, it has to read them in in their
10 | /// entirety to see whether to hotload them
11 | public class ResourcesSource : ConfigSource {
12 | const string INDEX_FILENAME = "index";
13 |
14 | public override bool CanHotload { get; }
15 |
16 | public ResourcesSource(string baseDir = "Configs", bool hotload = false) {
17 | this.baseDir = baseDir;
18 | CanHotload = hotload && Application.isEditor;
19 | }
20 |
21 | public override IEnumerable StepPreload() {
22 | AllFiles.Clear();
23 | filesList.Clear();
24 |
25 | // Load the index file.
26 | indexFile = ReadFile(INDEX_FILENAME);
27 | if (indexFile == null) {
28 | throw new($"Index file is missing at Resources path {INDEX_FILENAME}.");
29 | }
30 |
31 | // Load all the files.
32 | foreach (var nameNode in indexFile.Parsed.Values) {
33 | string filename = nameNode.StringValue;
34 | filesList.Add(filename);
35 | if (filename != "index") {
36 | AllFiles[filename] = ReadFile(filename);
37 | yield return null;
38 | }
39 | }
40 | }
41 |
42 | public override void Hotload(List changedFiles) {
43 | // First try to load the index in case any files were added or removed.
44 | var newIndex = ReadFile(INDEX_FILENAME);
45 | if (newIndex == null) {
46 | throw new($"Index file is missing at Resources path {INDEX_FILENAME}.");
47 | }
48 |
49 | if (newIndex.Checksum != indexFile.Checksum) {
50 | // Index has changed, possibly have added or removed files from the index.
51 | // TODO Smart update, don't just toss the whole list and start from scratch.
52 | foreach (object _ in StepPreload()) { }
53 | changedFiles.AddRange(filesList);
54 | } else {
55 | // Index hasn't changed. Check each file.
56 | foreach (string file in filesList) {
57 | var newFile = ReadFile(file);
58 | if (newFile.Checksum == AllFiles[file].Checksum) {
59 | continue;
60 | }
61 | AllFiles[file] = newFile;
62 | changedFiles.Add(file);
63 | }
64 | }
65 | }
66 |
67 | public override string ToString() {
68 | return $"ResourcesSource({baseDir})";
69 | }
70 |
71 | /////////////////////////////////////////////////
72 |
73 | ConfigFileInfo indexFile;
74 | readonly List filesList = new List();
75 | readonly string baseDir;
76 |
77 | /////////////////////////////////////////////////
78 |
79 | ConfigFileInfo ReadFile(string filename) {
80 | // Get the full resources path for the file.
81 | string path = baseDir + "/" + filename;
82 |
83 | // Remove extension if one is specified.
84 | path = System.IO.Path.ChangeExtension(path, null);
85 |
86 | var asset = Resources.Load(path);
87 | if (asset == null) {
88 | return null;
89 | }
90 |
91 | return new(
92 | name: filename,
93 | checksum: Internal.ChecksumUtils.Checksum(asset.text),
94 | size: asset.text.Length,
95 | // It's not easy to get a modified timestamp on a resources file, so just set it to the
96 | // default DateTime value. We'll instead rely on checksums to detect differences that need hotloading.
97 | modified: new(),
98 | parsed: Configs.ParseString(asset.text, filename));
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/MetaGame.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 | using DarkConfig;
4 |
5 | public class MetaGame : MonoBehaviour {
6 | [Header("Prefabs")]
7 | public GameObject PlayerPrefab;
8 | public GameObject TitlePrefab;
9 |
10 | [Header("UI")]
11 | public TextMesh Score;
12 |
13 | [Header("References")]
14 | public GameObject Background;
15 |
16 | // Singleton.
17 | public static MetaGame Instance;
18 |
19 | /////////////////////////////////////////////////
20 |
21 | public void PlayerKilled() {
22 | SetState(GameState.Postgame);
23 | }
24 |
25 | public void AIKilled() {
26 | score++;
27 | Score.text = string.Format("Score: {0}", score);
28 | }
29 |
30 | public PlayerController GetPlayer() {
31 | if (player == null) return null;
32 | return player;
33 | }
34 |
35 | /////////////////////////////////////////////////
36 |
37 | enum GameState {
38 | Title,
39 | Playing,
40 | Postgame
41 | }
42 |
43 | int score;
44 | float currentStateStartTime;
45 | Transform title;
46 | PlayerController player;
47 | GameState currentState;
48 |
49 | /////////////////////////////////////////////////
50 |
51 | void Awake() {
52 | Instance = this;
53 | }
54 |
55 | void Start() {
56 | SetState(GameState.Title);
57 | }
58 |
59 | void Update() {
60 | Configs.Update(Time.deltaTime);
61 |
62 | switch (currentState) {
63 | case GameState.Title:
64 | if (Input.GetKeyDown(KeyCode.Space)) {
65 | SetState(GameState.Playing);
66 | }
67 | break;
68 | case GameState.Playing:
69 | break;
70 | case GameState.Postgame:
71 | if (Time.time - currentStateStartTime > 10 || Input.GetKeyDown(KeyCode.Space)) {
72 | SetState(GameState.Title);
73 | }
74 | break;
75 | default:
76 | throw new ArgumentOutOfRangeException();
77 | }
78 |
79 | // Shift+H to hotload
80 | if (Input.GetKeyDown(KeyCode.H) && (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) {
81 | Debug.Log("Hotloading configs");
82 | Configs.DoImmediateHotload();
83 | }
84 |
85 | // Q toggles hotloading
86 | if (Input.GetKeyDown(KeyCode.Q)) {
87 | Configs.Settings.EnableHotloading = !Configs.Settings.EnableHotloading;
88 | Debug.Log("Setting auto hotloading to: " + Configs.Settings.EnableHotloading);
89 | }
90 | }
91 |
92 | void SetState(GameState newState) {
93 | if (currentState != newState) {
94 | ExitState(currentState);
95 | currentState = newState;
96 | currentStateStartTime = Time.time;
97 | }
98 |
99 | switch (newState) {
100 | case GameState.Title:
101 | Camera.main.transform.position = new Vector3(0, 0, -10);
102 | title = Instantiate(TitlePrefab).transform;
103 | break;
104 | case GameState.Playing:
105 | Score.gameObject.SetActive(true);
106 | score = 0;
107 | Score.text = "Score: 0";
108 |
109 | var playerTrf = Instantiate(PlayerPrefab).transform;
110 | player = playerTrf.GetComponent();
111 | FindObjectOfType().Target = playerTrf;
112 |
113 | Background.BroadcastMessage("Init", playerTrf);
114 |
115 | break;
116 | case GameState.Postgame:
117 | break;
118 | }
119 | }
120 |
121 | void ExitState(GameState state) {
122 | switch (state) {
123 | case GameState.Title:
124 | Destroy(title.gameObject);
125 | break;
126 | case GameState.Playing:
127 | break;
128 | case GameState.Postgame:
129 | Score.gameObject.SetActive(false);
130 | break;
131 | }
132 | }
133 | }
--------------------------------------------------------------------------------
/demo/Assets/Demo/Scripts/PlaneController.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections.Generic;
3 |
4 | public class PlaneController : MonoBehaviour {
5 | public float RotationCommand;
6 | public bool IsFiring;
7 | public float Throttle = 1;
8 |
9 | public int HitPoints;
10 |
11 | // note that this is a property rather than a field, so that hotloaded
12 | // changes to the card will affect the MaxHitPoints implicitly
13 | public int MaxHitPoints {
14 | get {
15 | if (Card == null) {
16 | return 1;
17 | }
18 | if (CompareTag("Player")) {
19 | // for gameplay reasons, players are more durable
20 | return Card.HitPoints * 2;
21 | }
22 |
23 | return Card.HitPoints;
24 | }
25 | }
26 |
27 | public PlaneCard Card;
28 |
29 | public Transform BulletPrefab;
30 | public Transform ViewPrefab;
31 |
32 | public GameObject ExplosionPrefab;
33 | public GameObject ShotFireFXPrefab;
34 |
35 | [HideInInspector]
36 | public PlaneView View;
37 |
38 | ////////////////////////////////////////////
39 |
40 | public void Setup(PlaneCard card) {
41 | var viewTrf = Instantiate(ViewPrefab, transform.position, transform.rotation);
42 | viewTrf.parent = transform;
43 | View = viewTrf.GetComponent();
44 | View.Controller = this;
45 |
46 | // here we take a reference to the card; the card object will get
47 | // updated automatically for us if the config hotloads, so we don't
48 | // have to have special hotloading logic in this class
49 | Card = card;
50 |
51 | // do this after assigning to Card b/c MaxHitPoints depends on Card
52 | HitPoints = MaxHitPoints;
53 | lastFiredTimes.Clear();
54 |
55 | // display the view; this has to be after HitPoints is assigned
56 | // or the view will show the wrong thing
57 | View.Card = card;
58 | }
59 |
60 | public void Heal(int points) {
61 | HitPoints += points;
62 | if (HitPoints > MaxHitPoints) HitPoints = MaxHitPoints;
63 | View.Refresh(Card);
64 | }
65 |
66 | public void TakeDamage(int points) {
67 | HitPoints -= points;
68 | View.Refresh(Card);
69 | if (HitPoints <= 0) {
70 | gameObject.BroadcastMessage("Killed");
71 | Destroy(gameObject);
72 | var explosion = Instantiate(ExplosionPrefab, transform.position, Quaternion.identity);
73 | Destroy(explosion, 1f);
74 | }
75 | }
76 |
77 | ////////////////////////////////////////////
78 |
79 | // this handles the rate-of-fire for the guns
80 | readonly Dictionary lastFiredTimes = new Dictionary();
81 |
82 | ////////////////////////////////////////////
83 |
84 | void FixedUpdate() {
85 | var eulers = transform.eulerAngles;
86 | eulers.z += RotationCommand * Card.RotationRate * Time.fixedDeltaTime;
87 | transform.eulerAngles = eulers;
88 |
89 | transform.position += transform.up * Card.Speed * Throttle * Time.fixedDeltaTime;
90 |
91 | var now = Time.fixedTime;
92 | if (IsFiring) {
93 | foreach (var mount in Card.GunMounts) {
94 | if (lastFiredTimes.ContainsKey(mount) && now - lastFiredTimes[mount] < mount.Card.FireInterval) {
95 | continue;
96 | }
97 |
98 | // When we construct a bullet, we copy all its attributes
99 | // from the configs; this means that hotloading won't affect
100 | // existing bullets. That's probably OK; bullets are very
101 | // ephemeral and it's quick to get more of them, so there isn't
102 | // much of a time cost to not hotloading them individually.
103 | // We do very much want hotloading to apply when they're being
104 | // shot, though, otherwise we'd have to restart the entire game
105 | // to see changes, which would be slow.
106 | var gunCard = mount.Card;
107 | var bulletTrf = Instantiate(BulletPrefab, transform.TransformPoint(mount.Location.Pos), transform.rotation);
108 | var bulletComponent = bulletTrf.GetComponent();
109 | bulletComponent.Damage = gunCard.BulletDamage;
110 | bulletComponent.Speed = Card.Speed * Throttle + gunCard.BulletSpeed;
111 | bulletTrf.GetComponent().velocity = bulletTrf.up * bulletComponent.Speed;
112 | bulletComponent.Firer = transform;
113 | bulletTrf.localScale = new Vector3(gunCard.BulletSize.x, gunCard.BulletSize.y, 1);
114 |
115 | float timeToFly = gunCard.BulletRange / bulletComponent.Speed;
116 |
117 | Destroy(bulletTrf.gameObject, timeToFly);
118 |
119 | lastFiredTimes[mount] = now;
120 |
121 | // display muzzle flash
122 | var fx = Instantiate(ShotFireFXPrefab, transform.TransformPoint(mount.Location.Pos), transform.rotation);
123 | Destroy(fx, 0.5f);
124 | }
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/demo/Assets/DarkConfig/Editor/EditorUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 | using UnityEditor;
4 | using System.Collections.Generic;
5 | using System.IO;
6 |
7 | namespace DarkConfig {
8 | public static class EditorMenus {
9 | [MenuItem ("Assets/DarkConfig/Autogenerate Index")]
10 | static void MenuGenerateIndex() {
11 | EditorUtils.GenerateIndex("Resources/Configs");
12 | AssetDatabase.Refresh();
13 | }
14 | }
15 |
16 | public class EditorUtils {
17 | static readonly string[] INDEX_FILE_HEADER = {
18 | "# automatically generated DarkConfig index file",
19 | "# DO NOT EDIT THIS MANUALLY",
20 | "# Instead, run Assets > DarkConfig > Autogenerate Index",
21 | "#",
22 | "---"
23 | };
24 |
25 | public static List FindConfigFiles(string baseDir = "/Resources/Configs") {
26 | var retval = new List();
27 | var absPath = new DirectoryInfo(Application.dataPath + baseDir);
28 | var absPathSlashed = absPath.FullName.Replace("\\", "/");
29 |
30 | var fileInfo = absPath.GetFiles("*.bytes", SearchOption.AllDirectories);
31 | foreach (var file in fileInfo) {
32 | var dirName = file.DirectoryName.Replace("\\", "/");
33 |
34 | var relativeToBase = dirName.Replace(absPathSlashed, "").Trim('/', '\\');
35 | var completePath = (relativeToBase + "/" + file.Name).Trim('/', '\\');
36 | retval.Add(completePath);
37 | }
38 |
39 | retval.Sort((a, b) => {
40 | var slashesA = CountCharacter('/', a);
41 | var slashesB = CountCharacter('/', b);
42 | if (slashesA != slashesB) {
43 | return slashesA.CompareTo(slashesB);
44 | }
45 |
46 | return string.Compare(a, b, StringComparison.Ordinal);
47 | });
48 |
49 | return retval;
50 | }
51 |
52 | ///
53 | /// Counts the instances of a character in a string
54 | ///
55 | /// character to count
56 | /// string to search within
57 | /// count of
58 | static int CountCharacter(char c, string s) {
59 | int count = 0;
60 | foreach (char t in s) {
61 | if (t == c) {
62 | count++;
63 | }
64 | }
65 |
66 | return count;
67 | }
68 |
69 | ///
70 | /// Writes the list of files to the index file.
71 | /// The files are expected to be listed relative to a Resources directory.
72 | /// The indexFile is specified relative to the Assets directory. It must also be in a resources directory.
73 | /// E.g. "Assets/Resources/Configs/index.bytes"
74 | ///
75 | ///
76 | ///
77 | ///
78 | public static int WriteIndexFile(List filesInIndex, string indexFile) {
79 | int resourcesIdx = indexFile.IndexOf("Resources/", StringComparison.Ordinal);
80 | if (resourcesIdx < 0) {
81 | Debug.LogError($"Index file {indexFile} should have Resources directory in its path");
82 | return 0;
83 | }
84 |
85 | string relToResources = indexFile.Substring(resourcesIdx + "Resources/".Length);
86 |
87 | string indexPath = Application.dataPath + "/" + indexFile;
88 |
89 | // create directory if necessary
90 | var indexDir = new FileInfo(indexPath).Directory;
91 | if (!indexDir.Exists) {
92 | indexDir.Create();
93 | }
94 |
95 | int totalWritten = 0;
96 |
97 | // write the header into the file
98 | using (var writer = new StreamWriter(indexPath, false)) {
99 | // Write file header
100 | foreach (string headerLine in INDEX_FILE_HEADER) {
101 | writer.WriteLine(headerLine);
102 | }
103 |
104 | // write all the index entries into the file
105 | foreach (string file in filesInIndex) {
106 | // skip over index file itself, it's likely to be in the list already
107 | if (file == relToResources) {
108 | continue;
109 | }
110 | writer.WriteLine("- " + file);
111 | totalWritten++;
112 | }
113 | }
114 |
115 | File.SetLastWriteTime(indexPath, DateTime.Now);
116 | return totalWritten;
117 | }
118 |
119 | public static void GenerateIndex(string baseDir) {
120 | var indexFilePath = baseDir + "/index.bytes";
121 | Debug.Log("Generating Index at " + indexFilePath + " using files in directory " + baseDir);
122 | var configs = FindConfigFiles(baseDir);
123 | // rename to short names
124 | for (int configIndex = 0; configIndex < configs.Count; configIndex++) {
125 | configs[configIndex] = configs[configIndex]
126 | .Replace(baseDir + "/", "")
127 | .Replace(".bytes", "");
128 | }
129 |
130 | var total = WriteIndexFile(configs, indexFilePath);
131 | Debug.Log("Wrote " + total + " configs to index");
132 | }
133 | }
134 | }
--------------------------------------------------------------------------------
/demo/Assets/Demo/Prefabs/Title.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1 &104874
4 | GameObject:
5 | m_ObjectHideFlags: 0
6 | m_PrefabParentObject: {fileID: 0}
7 | m_PrefabInternal: {fileID: 100100000}
8 | serializedVersion: 4
9 | m_Component:
10 | - 4: {fileID: 450926}
11 | m_Layer: 0
12 | m_Name: Title
13 | m_TagString: Untagged
14 | m_Icon: {fileID: 0}
15 | m_NavMeshLayer: 0
16 | m_StaticEditorFlags: 0
17 | m_IsActive: 1
18 | --- !u!1 &182930
19 | GameObject:
20 | m_ObjectHideFlags: 0
21 | m_PrefabParentObject: {fileID: 0}
22 | m_PrefabInternal: {fileID: 100100000}
23 | serializedVersion: 4
24 | m_Component:
25 | - 4: {fileID: 488126}
26 | - 23: {fileID: 2341492}
27 | - 102: {fileID: 10256288}
28 | m_Layer: 0
29 | m_Name: Title Text
30 | m_TagString: Untagged
31 | m_Icon: {fileID: 0}
32 | m_NavMeshLayer: 0
33 | m_StaticEditorFlags: 0
34 | m_IsActive: 1
35 | --- !u!1 &189376
36 | GameObject:
37 | m_ObjectHideFlags: 0
38 | m_PrefabParentObject: {fileID: 0}
39 | m_PrefabInternal: {fileID: 100100000}
40 | serializedVersion: 4
41 | m_Component:
42 | - 4: {fileID: 486094}
43 | - 23: {fileID: 2383948}
44 | - 102: {fileID: 10283204}
45 | m_Layer: 0
46 | m_Name: Subtitle
47 | m_TagString: Untagged
48 | m_Icon: {fileID: 0}
49 | m_NavMeshLayer: 0
50 | m_StaticEditorFlags: 0
51 | m_IsActive: 1
52 | --- !u!4 &450926
53 | Transform:
54 | m_ObjectHideFlags: 1
55 | m_PrefabParentObject: {fileID: 0}
56 | m_PrefabInternal: {fileID: 100100000}
57 | m_GameObject: {fileID: 104874}
58 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
59 | m_LocalPosition: {x: 0, y: 0, z: 0}
60 | m_LocalScale: {x: 1, y: 1, z: 1}
61 | m_Children:
62 | - {fileID: 488126}
63 | - {fileID: 486094}
64 | m_Father: {fileID: 0}
65 | m_RootOrder: 0
66 | --- !u!4 &486094
67 | Transform:
68 | m_ObjectHideFlags: 1
69 | m_PrefabParentObject: {fileID: 0}
70 | m_PrefabInternal: {fileID: 100100000}
71 | m_GameObject: {fileID: 189376}
72 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
73 | m_LocalPosition: {x: 0, y: -8.5, z: 0}
74 | m_LocalScale: {x: 1, y: 1, z: 1}
75 | m_Children: []
76 | m_Father: {fileID: 450926}
77 | m_RootOrder: 1
78 | --- !u!4 &488126
79 | Transform:
80 | m_ObjectHideFlags: 1
81 | m_PrefabParentObject: {fileID: 0}
82 | m_PrefabInternal: {fileID: 100100000}
83 | m_GameObject: {fileID: 182930}
84 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
85 | m_LocalPosition: {x: 0, y: 0, z: 0}
86 | m_LocalScale: {x: 1, y: 1, z: 1}
87 | m_Children: []
88 | m_Father: {fileID: 450926}
89 | m_RootOrder: 0
90 | --- !u!23 &2341492
91 | MeshRenderer:
92 | m_ObjectHideFlags: 1
93 | m_PrefabParentObject: {fileID: 0}
94 | m_PrefabInternal: {fileID: 100100000}
95 | m_GameObject: {fileID: 182930}
96 | m_Enabled: 1
97 | m_CastShadows: 1
98 | m_ReceiveShadows: 1
99 | m_Materials:
100 | - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
101 | m_SubsetIndices:
102 | m_StaticBatchRoot: {fileID: 0}
103 | m_UseLightProbes: 1
104 | m_ReflectionProbeUsage: 1
105 | m_ProbeAnchor: {fileID: 0}
106 | m_ScaleInLightmap: 1
107 | m_PreserveUVs: 0
108 | m_IgnoreNormalsForChartDetection: 0
109 | m_ImportantGI: 0
110 | m_MinimumChartSize: 4
111 | m_AutoUVMaxDistance: .5
112 | m_AutoUVMaxAngle: 89
113 | m_LightmapParameters: {fileID: 0}
114 | m_SortingLayerID: 0
115 | m_SortingOrder: 0
116 | --- !u!23 &2383948
117 | MeshRenderer:
118 | m_ObjectHideFlags: 1
119 | m_PrefabParentObject: {fileID: 0}
120 | m_PrefabInternal: {fileID: 100100000}
121 | m_GameObject: {fileID: 189376}
122 | m_Enabled: 1
123 | m_CastShadows: 1
124 | m_ReceiveShadows: 1
125 | m_Materials:
126 | - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
127 | m_SubsetIndices:
128 | m_StaticBatchRoot: {fileID: 0}
129 | m_UseLightProbes: 1
130 | m_ReflectionProbeUsage: 1
131 | m_ProbeAnchor: {fileID: 0}
132 | m_ScaleInLightmap: 1
133 | m_PreserveUVs: 0
134 | m_IgnoreNormalsForChartDetection: 0
135 | m_ImportantGI: 0
136 | m_MinimumChartSize: 4
137 | m_AutoUVMaxDistance: .5
138 | m_AutoUVMaxAngle: 89
139 | m_LightmapParameters: {fileID: 0}
140 | m_SortingLayerID: 0
141 | m_SortingOrder: 0
142 | --- !u!102 &10256288
143 | TextMesh:
144 | serializedVersion: 3
145 | m_ObjectHideFlags: 1
146 | m_PrefabParentObject: {fileID: 0}
147 | m_PrefabInternal: {fileID: 100100000}
148 | m_GameObject: {fileID: 182930}
149 | m_Text: Dark Skies
150 | m_OffsetZ: 0
151 | m_CharacterSize: 1
152 | m_LineSpacing: 1
153 | m_Anchor: 4
154 | m_Alignment: 1
155 | m_TabSize: 4
156 | m_FontSize: 120
157 | m_FontStyle: 3
158 | m_RichText: 1
159 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
160 | m_Color:
161 | serializedVersion: 2
162 | rgba: 4294967295
163 | --- !u!102 &10283204
164 | TextMesh:
165 | serializedVersion: 3
166 | m_ObjectHideFlags: 1
167 | m_PrefabParentObject: {fileID: 0}
168 | m_PrefabInternal: {fileID: 100100000}
169 | m_GameObject: {fileID: 189376}
170 | m_Text: Press Space to Start
171 | m_OffsetZ: 0
172 | m_CharacterSize: 1
173 | m_LineSpacing: 1
174 | m_Anchor: 4
175 | m_Alignment: 1
176 | m_TabSize: 4
177 | m_FontSize: 40
178 | m_FontStyle: 0
179 | m_RichText: 1
180 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
181 | m_Color:
182 | serializedVersion: 2
183 | rgba: 4294967295
184 | --- !u!1001 &100100000
185 | Prefab:
186 | m_ObjectHideFlags: 1
187 | serializedVersion: 2
188 | m_Modification:
189 | m_TransformParent: {fileID: 0}
190 | m_Modifications: []
191 | m_RemovedComponents: []
192 | m_ParentPrefab: {fileID: 0}
193 | m_RootGameObject: {fileID: 104874}
194 | m_IsPrefabParent: 1
195 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | CONTRIBUTING
2 | ==============
3 |
4 | Thanks for thinking about contributing to DarkConfig! There's a lot of ways that someone can contribute to an open source project, many of which can be done without programming experience. For general information check out [this article](https://blog.newrelic.com/2014/05/05/open-source_gettingstarted/), or [this one](https://guides.github.com/activities/contributing-to-open-source/).
5 |
6 | - Help organize issues. Detect and consolidate duplicates. Reply to issues that are just questions.
7 | - Tackle existing issues. Sometimes issues are stuck because they lack information or specificity. Sometimes they need special setup to reproduce. Sometimes they need decision making.
8 | - Improve documentation. Rewrite things that are confusing, fix typos. Ask questions about things you're confused about!
9 | - Report bugs clearly. Include a simple reproduction if possible. If not, be as deliberate and thorough in your description as you can, including code and context. DarkConfig is something of a "be everything" project, unfortunately, so it will have lots of cases where folks are simply using it differently than anyone else ever did and exposing fresh bugs.
10 | - Review pull requests. They generally need reviewing to make sure they fit in with the rest of the project. Not to mention testing that they work and achieve their goals!
11 | - Improve compliance with the style. We have a goal style but fail to meet it. Finding those places and tidying them up will help keep the project clean for the future!
12 | - Help broaden compatibility. We currently support a narrow set of Unity versions, but it would be nice to support more, and also to support other C# platforms.
13 | - Make Dark Skies better. When developers are learning about a project, looking at a real-world example is more instructive than simple contrived demos. The richer and more finished a game Dark Skies becomes, the more competent people will be at using DarkConfig based on its example. This can also provide a motivating impetus to add or change features.
14 |
15 | We'll be more likely to maintain DarkConfig if it's easy, so the more you can help us save time, the more likely we are to make forward progress!
16 |
17 | # Code Contributions
18 |
19 | We welcome code contributions! Please check through this section to make sure that your contribution is as smooth as it can be.
20 |
21 | - Keep in mind the scope of the project. DarkConfig is meant to be flexible and powerful, but it probably shouldn't grow to be a sprawling Django-like framework. It should remain focused on loading configuration files with minimum fuss.
22 | - Please only open one pull request per feature/fix. Omnibus pull requests can be hard to understand.
23 | - Make sure to follow the code style. Indent using 4 spaces, braces in OTBS style except one-line ifs lack newlines or braces, member variables prefixed with m_ and statics with s_, capitalize publics, no unnecessary properties.
24 | - Make sure the new feature is covered by tests. Let's shoot for 100% coverage, every contribution helps!
25 | - Try to maintain backwards compatibility. This just makes it smoother for a large group of people to keep contributing. There are some things that might require breakage, in which case we should create a branch and a DarkConfig 2.0 release.
26 | - Avoid major refactorings. Those can make the contirbution hard to read and understand.
27 | - Improve or maintain performance. DarkConfig ain't never gonna be the fastest config loader out there, but we do have projects that need to load dozens of megabytes of configs in thousands of files and those should remain workable.
28 | - When you contribute code to the project, you implicitly do so under the terms of the DarkConfig LICENSE. Ensure you have the the rights to the code you're contributing.
29 | - Always run all the tests before committing! They're there to check for simple mistakes. See the tests docmentation in the Docs directory for information on how to run them.
30 |
31 | # Areas of Development
32 |
33 | Some areas that currently need thought and/or work:
34 |
35 | More tests! This is *infrastructure* so we want it to be rock-solid. I typically find it difficult to test the runtime of the system; pretty much every method on ConfigFileManager is undertested. It's so annoying to set up integration tests in Unity! The new ConsolePlatform maybe provides a way to make unit tests because we can run coroutines manually. In general though, increasing test coverage, and simplifying test authoring, is always welcome.
36 |
37 | Singletons. Right now DarkConfig is designed as a singleton; you only have one Config. I'm not completely convinced that this is the best thing. Perhaps it should be refactored to be a constructable object, and if a game wants to treat it like a singleton, it's easy enough to just assign the object to a static variable.
38 |
39 | Removing friction. Here's a few rough edges:
40 | - The necessity of creating your own editor script for index generation. Maybe we should configure DarkConfig via a configuration file. Or maybe we can get rid of the index file. We maybe make the example auto-update the index file whenever files on disk change.
41 | - Preloading. It fixed a number of race condition bugs when introduced, so it's definitely good. But maybe it could be cleaner and less obtrusive.
42 | - PostDocs kinda suck in practice. I'd love to wedge a tiny bit more magic into DarkConfig to make my day authoring configs a little easier.
43 |
44 | Compatibility improvements. More Unity versions would be nice. I'm also _pretty_ sure that DarkConfig works on iOS with the .Net 2.0 subset, but it's been a while since I've actually tested it.
45 |
46 | Security. It would be nice if without doing a ton of work we were able to tighten up the security story for DarkConfig, making it suitable to load untrusted files. This will mostly involve the YAML parser.
47 |
--------------------------------------------------------------------------------
/test/FromDocTests.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using DarkConfig;
3 | using System.Collections.Generic;
4 | using System;
5 |
6 | [TestFixture]
7 | class FromDocTests {
8 | class TestClass {
9 | public int baseKey;
10 |
11 | public static TestClass FromDoc(TestClass existing, DocNode doc) {
12 | if (doc.Type != DocNodeType.List) {
13 | throw new System.ArgumentException("Not a list! " + doc.Type);
14 | }
15 |
16 | if (doc[0].StringValue == "Derived") {
17 | TestClassDerived derivedExisting;
18 | if (existing is TestClassDerived) {
19 | derivedExisting = (TestClassDerived) existing;
20 | } else {
21 | derivedExisting = new TestClassDerived();
22 | }
23 |
24 | derivedExisting.derivedKey =
25 | Convert.ToInt32(doc[1].StringValue, System.Globalization.CultureInfo.InvariantCulture);
26 | return derivedExisting;
27 | } else {
28 | if (!(existing is TestClass)) {
29 | existing = new TestClass();
30 | }
31 |
32 | existing.baseKey =
33 | Convert.ToInt32(doc[1].StringValue, System.Globalization.CultureInfo.InvariantCulture);
34 | return existing;
35 | }
36 | }
37 | }
38 |
39 | class TestClassDerived : TestClass {
40 | public int derivedKey;
41 | }
42 |
43 | const string FILENAME = "FromDocTests_TestFileName";
44 |
45 | T ReifyString(string str) where T : new() {
46 | var doc = Configs.ParseString(str, "FromDocTests_ReifyString_TestFileName");
47 | var instance = default(T);
48 | Configs.Reify(ref instance, doc);
49 | return instance;
50 | }
51 |
52 | [Test]
53 | public void FromDoc_CalledToReify() {
54 | var tc = ReifyString("[\"Base\", 12]");
55 | Assert.That(tc.baseKey, Is.EqualTo(12));
56 | }
57 |
58 | [Test]
59 | public void FromDoc_SpawnsDerivedClass() {
60 | var tc = ReifyString("[\"Derived\", 12]");
61 | Assert.Multiple(() => {
62 | Assert.That(tc.baseKey, Is.EqualTo(0));
63 | Assert.That(tc, Is.InstanceOf());
64 | });
65 | }
66 |
67 | [Test]
68 | public void FromDoc_UpdatesTestClass() {
69 | var tc = new TestClass {baseKey = 15};
70 | var saved = tc;
71 | var doc = Configs.ParseString("[\"Base\", 99]", FILENAME);
72 | Configs.Reify(ref tc, doc);
73 | Assert.Multiple(() => {
74 | Assert.That(saved, Is.SameAs(tc));
75 | Assert.That(tc.baseKey, Is.EqualTo(99));
76 | });
77 | }
78 |
79 | [Test]
80 | public void FromDoc_UpdatesDerived() {
81 | TestClass tc = new TestClassDerived {baseKey = 1, derivedKey = 2};
82 | var saved = tc;
83 | var doc = Configs.ParseString("[\"Derived\", 66]", FILENAME);
84 | Configs.Reify(ref tc, doc);
85 | Assert.Multiple(() => {
86 | Assert.That(saved, Is.SameAs(tc));
87 | Assert.That(tc.baseKey, Is.EqualTo(1));
88 | Assert.That(((TestClassDerived) tc).derivedKey, Is.EqualTo(66));
89 | });
90 | }
91 |
92 | [Test]
93 | public void FromDoc_UpdatesDerived_AsBase() {
94 | TestClass tc = new TestClassDerived {baseKey = 4, derivedKey = 5};
95 | var saved = tc;
96 | var doc = Configs.ParseString("[\"Base\", 123]", FILENAME);
97 | Configs.Reify(ref tc, doc);
98 | Assert.Multiple(() => {
99 | Assert.That(saved, Is.SameAs(tc));
100 | Assert.That(tc.baseKey, Is.EqualTo(123));
101 | Assert.That(((TestClassDerived) tc).derivedKey, Is.EqualTo(5));
102 | });
103 | }
104 |
105 | [Test]
106 | public void FromDoc_OverwritesBase_WithDerived() {
107 | var tc = new TestClass {baseKey = 19};
108 | var saved = tc;
109 | var doc = Configs.ParseString("[\"Derived\", 321]", FILENAME);
110 | Configs.Reify(ref tc, doc);
111 | Assert.Multiple(() => {
112 | Assert.That(ReferenceEquals(tc, saved), Is.False);
113 | Assert.That(tc, Is.InstanceOf());
114 | Assert.That(((TestClassDerived) tc).derivedKey, Is.EqualTo(321));
115 | });
116 | }
117 |
118 | [Test]
119 | [Ignore("It's not completely clear how we should call parent class FromDocs when reifying a derived object")]
120 | public void FromDoc_SpawnsDerivedClass_WhenCastAsDerived() {
121 | var tc = ReifyString("[\"Derived\", 12]");
122 | Assert.That(tc.baseKey, Is.EqualTo(0));
123 | }
124 |
125 | [Test]
126 | public void FromDoc_WrapsExceptions() {
127 | Assert.Throws(() => { ReifyString("{\"wrong\": \"structure\"}"); });
128 | }
129 |
130 | [Test]
131 | public void FromDoc_CalledWhenReifyingNullClass() {
132 | TestClass tc = null;
133 | var doc = Configs.ParseString("[\"Base\", 451]", FILENAME);
134 | Configs.Reify(ref tc, doc);
135 | Assert.Multiple(() => {
136 | Assert.That(tc, Is.Not.Null);
137 | Assert.That(tc.baseKey, Is.EqualTo(451));
138 | });
139 | }
140 |
141 | [Test]
142 | public void FromDoc_CalledWhenReifyingEmptyList() {
143 | var lst = new List();
144 | var doc = Configs.ParseString("[[\"Base\", 451]]", FILENAME);
145 | Configs.Reify(ref lst, doc);
146 | Assert.Multiple(() => {
147 | Assert.That(lst, Has.Count.EqualTo(1));
148 | Assert.That(lst[0].baseKey, Is.EqualTo(451));
149 | });
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/DarkConfig/Attributes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace DarkConfig {
4 | /// If the field annotated with a Mandatory, or any field on a Mandatory class,
5 | /// is not present in the YAML, DarkConfig will complain, regardless of other
6 | /// settings.
7 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct)]
8 | public class ConfigMandatoryAttribute : Attribute { }
9 |
10 | /// If an AllowMissing field, or any field on an AllowMissing class, is not
11 | /// present in the YAML, DarkConfig will not complain, regardless of other
12 | /// settings.
13 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct)]
14 | public class ConfigAllowMissingAttribute : Attribute { }
15 |
16 | /// If a field has the Ignore attribute, it will be completely ignored by
17 | /// DarkConfig; not set, not checked, it's as if it wasn't on the class in the
18 | /// first place.
19 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
20 | public class ConfigIgnoreAttribute : Attribute { }
21 |
22 | /// If a field has the SourceInformation attribute then the field is
23 | /// automatically populated with DocNode.SourceInformation by SetFieldsOnObject()
24 | /// Useful if you do validation or want better error reporting after reification
25 | /// Use #if flags to remove this in production code where it's not needed
26 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
27 | public class ConfigSourceInformationAttribute : Attribute { }
28 |
29 | /// Specifies a specific named value that should be read from the yaml
30 | /// and assigned to this field or property. Useful when you'd prefer to use
31 | /// different names in C# and yaml.
32 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
33 | public class ConfigKeyAttribute : Attribute {
34 | public string Key;
35 |
36 | ///
37 | /// Read the value corresponding to when
38 | /// reifying this value, rather than the value associated with the
39 | /// key that matches this field/property value's name.
40 | ///
41 | /// The substitute key
42 | public ConfigKeyAttribute(string key) {
43 | if (string.IsNullOrWhiteSpace(key)) {
44 | throw new ArgumentNullException(nameof(key));
45 | }
46 |
47 | Key = key.Trim();
48 | }
49 | }
50 |
51 | ///
52 | /// Marks this type as a polymorphic union of its parent type and indicates the key whose presence implies this type
53 | ///
54 | [AttributeUsage(AttributeTargets.Class)]
55 | public class ConfigUnionAttribute : Attribute {
56 | public readonly string Key;
57 |
58 | ///
59 | /// When parsing the parent type, if the key is then this type will be
60 | /// parsed instead.
61 | ///
62 | /// The substitute key
63 | public ConfigUnionAttribute(string key) {
64 | if (string.IsNullOrWhiteSpace(key)) {
65 | throw new ArgumentNullException(nameof(key));
66 | }
67 | Key = key.Trim();
68 | }
69 | }
70 |
71 | ///
72 | /// Marks this type as a polymorphic union of its parent type and indicates the key whose presence implies this type
73 | /// The key for this union is in the same doc as it's properties. The key is always the first property specified.
74 | ///
75 | [AttributeUsage(AttributeTargets.Class)]
76 | public class ConfigUnionInlineAttribute : Attribute {
77 | public readonly string Key;
78 |
79 | ///
80 | /// When parsing the parent type, if the key is then this type will be
81 | /// parsed instead.
82 | ///
83 | /// The substitute key
84 | public ConfigUnionInlineAttribute(string key) {
85 | if (string.IsNullOrWhiteSpace(key)) {
86 | throw new ArgumentNullException(nameof(key));
87 | }
88 | Key = key.Trim();
89 | }
90 | }
91 |
92 | /// If the field annotated with inline then we will look for it's properties in the same doc as the parent
93 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
94 | public class ConfigInlineAttribute : Attribute { }
95 |
96 | /// Name of this type in documentation. For generic types, "<0>" indicates the first template parameter, "<1>" the second, and so on.
97 | [AttributeUsage(AttributeTargets.Class)]
98 | public class ConfigDocumentationNameAttribute : Attribute {
99 | public readonly string Value;
100 |
101 | public ConfigDocumentationNameAttribute(string value) {
102 | Value = value;
103 | }
104 | }
105 |
106 | /// Description of this type or field
107 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property, Inherited = false)]
108 | public class ConfigDocumentationDescriptionAttribute : Attribute {
109 | public readonly string Value;
110 |
111 | public ConfigDocumentationDescriptionAttribute(string value) {
112 | Value = value;
113 | }
114 | }
115 |
116 | /// Example yaml of this type
117 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
118 | public class ConfigDocumentationExampleAttribute : Attribute {
119 | public readonly string Value;
120 |
121 | public ConfigDocumentationExampleAttribute(string value) {
122 | Value = value;
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------