Pin AetheriaStores routing with tests; make PlayerSettings readable
Cut 8b. tests/Aetheria.Shared.Tests (xunit, net10.0) builds a temp-directory catalog through AetheriaStores.Open(catalogWritable: true) per test: - OpenThenFlushLeavesEveryFileByteIdentical: the catalog, plus run and player stores seeded with their globals, a SavedZone and an InputLayout. - WeaponWrittenThroughGearHandleReloadsAsWeapon - CatalogRefusesWrites: bytes unchanged too. - SavedZoneNeverLandsInCatalog: no home without a run store; the snapshot schemas show where it landed. - FactionIsASingletonInstance: through a ref, a ref-keyed map key, and the name index. - MissingCatalogGlobalIsLoud: uses a test-only [CultGlobal] routed to the catalog. The game has no catalog globals yet. The first test found a real defect. PlayerInputSettings.InputActionMap was keyed by (string, int), and CultLib's untrusted-data MessagePack security has no hash-resistant comparer for a tuple, so a player store could never be read back. It is now action name to binding index to path; its two Unity readers change in the Unity commit. Each test was checked to fail with the rule it pins removed; see the report. Builds: Aetheria.Shared, AetherDb, Aetheria.Shared.Tests (6/6 pass). Unity does not build yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
1d15c6780d
commit
c2026142be
39 changed files with 151 additions and 2079 deletions
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2501605fca062c84d963e811df551698
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class AetheriaDatabaseView : DatabaseListView
|
||||
{
|
||||
// Singleton to avoid multiple instances of window.
|
||||
private static DatabaseListView _instance;
|
||||
public static DatabaseListView Instance => _instance ? _instance : GetWindow<AetheriaDatabaseView>();
|
||||
[MenuItem("Window/Aetheria/Database Tools")]
|
||||
static void Init() => Instance.Show();
|
||||
private void Awake()
|
||||
{
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
protected override string FilePath => new DirectoryInfo(Application.dataPath).Parent.CreateSubdirectory("GameData").FullName;
|
||||
|
||||
protected override DatabaseEntryGroup[] Groupers => new DatabaseEntryGroup[]
|
||||
{
|
||||
new DatabaseEntryGroup<SimpleCommodityData,SimpleCommodityCategory>(
|
||||
data => data.Category,
|
||||
category => Enum.GetName(typeof(SimpleCommodityCategory), category),
|
||||
(data, category) => data.Category = category ),
|
||||
new DatabaseEntryGroup<CompoundCommodityData, CompoundCommodityCategory>(
|
||||
data => data.Category,
|
||||
category => Enum.GetName(typeof(CompoundCommodityCategory), category),
|
||||
(data, category) => data.Category = category ),
|
||||
new DatabaseEntryGroup<GearData, HardpointType>(
|
||||
data => data.HardpointType,
|
||||
type => Enum.GetName(typeof(HardpointType), type),
|
||||
(data, type) => data.Hardpoint = type),
|
||||
new DatabaseEntryGroup<GearData, Guid>(
|
||||
data => data.Manufacturer,
|
||||
factionID => factionID == Guid.Empty ? "Default" : cultCache.Get<Faction>(factionID).ShortName,
|
||||
(data, faction) => data.Manufacturer = faction),
|
||||
new DatabaseEntryGroup<HullData, HullType>(
|
||||
data => data.HullType,
|
||||
type => Enum.GetName(typeof(HullType), type),
|
||||
(data, type) => data.HullType = type)
|
||||
};
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4016d25951e246a184441b2aae75eba0
|
||||
timeCreated: 1617151686
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
using System;
|
||||
|
||||
public abstract class BaseInspector<T>
|
||||
{
|
||||
protected const int width = 150;
|
||||
protected const int labelWidth = 20;
|
||||
protected const int toggleWidth = 18;
|
||||
protected const int arrowWidth = 22;
|
||||
|
||||
public abstract T Inspect(string label, T value, object parent, DatabaseInspector inspectorWindow);
|
||||
}
|
||||
|
||||
public abstract class BaseInspector<T, A> where A : Attribute
|
||||
{
|
||||
protected const int width = 150;
|
||||
protected const int labelWidth = 20;
|
||||
protected const int toggleWidth = 18;
|
||||
protected const int arrowWidth = 22;
|
||||
|
||||
public abstract T Inspect(string label, T value, object parent, DatabaseInspector inspectorWindow, A attribute);
|
||||
}
|
||||
|
||||
public abstract class BaseInspector<T, A, B> where A : Attribute where B : Attribute
|
||||
{
|
||||
protected const int width = 150;
|
||||
protected const int labelWidth = 20;
|
||||
protected const int toggleWidth = 18;
|
||||
protected const int arrowWidth = 22;
|
||||
|
||||
public abstract T Inspect(string label, T value, object parent, DatabaseInspector inspectorWindow, A attributeA, B attributeB);
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f3cde78766994141a3da6ab88d09118f
|
||||
timeCreated: 1617052142
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
using CultMath;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static CultMath.math;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
using int2 = CultMath.int2;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
public class DatabaseInspector : EditorWindow
|
||||
{
|
||||
public static CultCache CultCache;
|
||||
public DatabaseEntry entry;
|
||||
|
||||
// Singleton to avoid multiple instances of window.
|
||||
private static DatabaseInspector _instance;
|
||||
public static DatabaseInspector Instance => _instance ? _instance : GetWindow<DatabaseInspector>();
|
||||
private static bool _listItemStyle;
|
||||
public static GUIStyle ListItemStyle => (_listItemStyle = !_listItemStyle) ? DatabaseListView.ListStyleEven : DatabaseListView.ListStyleOdd;
|
||||
|
||||
public const int Width = 150;
|
||||
|
||||
private Vector2 _view;
|
||||
private GUIStyle _warning;
|
||||
private List<(Type targetType, HashSet<Type> attributes, Type inspectorType, object instance)> _inspectors =
|
||||
new List<(Type targetType, HashSet<Type> attributes, Type inspectorType, object instance)>();
|
||||
|
||||
private List<(Type targetType, HashSet<Type> attributes, Type inspectorType, Dictionary<Type, object> instances)> _genericInspectors =
|
||||
new List<(Type targetType, HashSet<Type> attributes, Type inspectorType, Dictionary<Type, object> instances)>();
|
||||
private HashSet<object> _foldouts = new HashSet<object>();
|
||||
|
||||
public Color LabelColor => EditorGUIUtility.isProSkin ? Color.white : Color.black;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
_instance = this;
|
||||
CultCache.OnUpdate += (_,__) => EditorDispatcher.Dispatch(Repaint);
|
||||
wantsMouseMove = true;
|
||||
_warning = new GUIStyle(EditorStyles.boldLabel);
|
||||
_warning.normal.textColor = Color.red;
|
||||
|
||||
var inspectorTypes = typeof(BaseInspector<>).GetAllGenericChildClasses()
|
||||
.Concat(typeof(BaseInspector<,>).GetAllGenericChildClasses())
|
||||
.Concat(typeof(BaseInspector<,,>).GetAllGenericChildClasses());
|
||||
foreach (var type in inspectorTypes)
|
||||
{
|
||||
var genericArguments = type.BaseType.GetGenericArguments();
|
||||
if(genericArguments.Any())
|
||||
{
|
||||
if (genericArguments[0].ContainsGenericParameters)
|
||||
{
|
||||
_genericInspectors.Add((
|
||||
genericArguments[0].GetGenericTypeDefinition(),
|
||||
new HashSet<Type>(genericArguments.Skip(1)),
|
||||
type,
|
||||
new Dictionary<Type, object>()
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
_inspectors.Add((
|
||||
genericArguments[0],
|
||||
new HashSet<Type>(genericArguments.Skip(1)),
|
||||
type,
|
||||
Activator.CreateInstance(type)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Inspect(object obj, object parent = null)
|
||||
{
|
||||
foreach (var field in obj.GetType().GetFields().OrderBy(f=>f.GetCustomAttribute<KeyAttribute>()?.IntKey ?? 0))
|
||||
{
|
||||
Inspect(obj, field, parent);
|
||||
}
|
||||
}
|
||||
|
||||
public void Inspect(object obj, FieldInfo field, object parent = null)
|
||||
{
|
||||
var inspectable = field.GetCustomAttribute<InspectableAttribute>();
|
||||
if (inspectable == null) return;
|
||||
|
||||
var attributes = field.GetCustomAttributes(false);
|
||||
|
||||
var type = field.FieldType;
|
||||
var value = field.GetValue(obj);
|
||||
|
||||
field.SetValue(obj, InspectMember(field.Name.SplitCamelCase(), value, parent ?? obj, type, false, attributes));
|
||||
}
|
||||
|
||||
public object InspectMember(string label, object value, object parent, Type type, bool suppressContainer = false, params object[] attributes)
|
||||
{
|
||||
var style = _listItemStyle;
|
||||
object GetAttribute(Type attributeType)
|
||||
{
|
||||
return attributes.FirstOrDefault(attributeType.IsInstanceOfType);
|
||||
}
|
||||
|
||||
if (value == null && type != typeof(string) && !(type.IsInterface || type.IsAbstract)) value = Activator.CreateInstance(type);
|
||||
|
||||
var potentialInspectors = _inspectors
|
||||
.Where(pi =>
|
||||
pi.targetType == type &&
|
||||
pi.attributes.All(attributeType => GetAttribute(attributeType) != null));
|
||||
if (potentialInspectors.Any())
|
||||
{
|
||||
var preferredInspector = potentialInspectors.MaxBy(i => i.attributes.Count);
|
||||
var parameters = new[] {label, value, parent, this}
|
||||
.Concat(preferredInspector.attributes.Select(GetAttribute))
|
||||
.ToArray();
|
||||
value = preferredInspector.inspectorType.GetMethod("Inspect").Invoke(preferredInspector.instance, parameters);
|
||||
_listItemStyle = style;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
var isflags = type.GetCustomAttributes<FlagsAttribute>().Any();
|
||||
var names = Enum.GetNames(type);
|
||||
value = InspectEnum(label, (int) value, isflags ? names.Skip(1).ToArray() : names, isflags);
|
||||
_listItemStyle = style;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (type.GetCustomAttribute<InspectableAttribute>() != null)
|
||||
{
|
||||
if(!suppressContainer)
|
||||
GUILayout.BeginVertical(GUI.skin.box);
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
if(!suppressContainer)
|
||||
{
|
||||
if (Foldout(_foldouts.Contains(value), label, true, EditorStyles.boldLabel))
|
||||
_foldouts.Add(value);
|
||||
else _foldouts.Remove(value);
|
||||
}
|
||||
if (type.IsInterface || type.IsAbstract)
|
||||
{
|
||||
var subTypes = type.GetAllChildClasses().Where(t=>!(t.IsInterface || t.IsAbstract)).ToArray();
|
||||
var selected = value != null ? Array.IndexOf(subTypes, value.GetType()) + 1 : 0;
|
||||
var newSelection = Popup(selected,
|
||||
subTypes.Select(t => t.Name.SplitCamelCase()).Prepend("None").ToArray());
|
||||
if (newSelection != selected)
|
||||
value = newSelection == 0 ? null : Activator.CreateInstance(subTypes[newSelection - 1]);
|
||||
}
|
||||
else if (value == null) value = Activator.CreateInstance(type);
|
||||
}
|
||||
|
||||
if (value != null && (_foldouts.Contains(value) || suppressContainer)) Inspect(value, parent);
|
||||
if(!suppressContainer)
|
||||
GUILayout.EndVertical();
|
||||
_listItemStyle = style;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genericTypeDefinition = type.GetGenericTypeDefinition();
|
||||
var potentialGenericInspectors = _genericInspectors
|
||||
.Where(pi =>
|
||||
pi.targetType == genericTypeDefinition &&
|
||||
pi.attributes.All(attributeType => GetAttribute(attributeType) != null));
|
||||
if (potentialGenericInspectors.Any())
|
||||
{
|
||||
var genericTypeArguments = type.GenericTypeArguments;
|
||||
var preferredInspector = potentialGenericInspectors.MaxBy(i => i.attributes.Count);
|
||||
var genericInspectorType = preferredInspector.inspectorType.MakeGenericType(genericTypeArguments);
|
||||
if (!preferredInspector.instances.ContainsKey(genericInspectorType))
|
||||
preferredInspector.instances[genericInspectorType] =
|
||||
Activator.CreateInstance(genericInspectorType);
|
||||
var parameters = new[] {label, value, parent, this};
|
||||
value = genericInspectorType.GetMethod("Inspect").Invoke(preferredInspector.instances[genericInspectorType], parameters);
|
||||
_listItemStyle = style;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"Field \"{label}\" has unknown type {type.Name}. No inspector was generated.");
|
||||
|
||||
_listItemStyle = style;
|
||||
return value;
|
||||
}
|
||||
|
||||
public int InspectEnum(string label, int value, string[] enumOptions, bool flags)
|
||||
{
|
||||
if (flags)
|
||||
{
|
||||
Space();
|
||||
LabelField(label, EditorStyles.boldLabel);
|
||||
using (new VerticalScope(GUI.skin.box))
|
||||
{
|
||||
for (var i = 0; i < enumOptions.Length; i++)
|
||||
{
|
||||
using (var h = new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(enumOptions[i], GUILayout.Width(Width));
|
||||
var set = Toggle(1 << i == (1 << i & value));
|
||||
if (set)
|
||||
value |= 1 << i;
|
||||
else value &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
using (var h = new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(Width));
|
||||
return Popup(value, enumOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
if (entry==null)
|
||||
{
|
||||
LabelField("No Entry Selected!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(entry is INamedEntry namedEntry)
|
||||
GUILayout.Label(namedEntry.EntryName, EditorStyles.boldLabel);
|
||||
|
||||
_view = BeginScrollView(
|
||||
_view,
|
||||
false,
|
||||
false,
|
||||
GUIStyle.none,
|
||||
GUI.skin.verticalScrollbar,
|
||||
GUI.skin.scrollView,
|
||||
GUILayout.Width(EditorGUIUtility.currentViewWidth),
|
||||
GUILayout.ExpandHeight(true));
|
||||
|
||||
// Serialize inspected object so we can check whether it has actually changed
|
||||
RegisterResolver.Register();
|
||||
var previousBytes = MessagePackSerializer.Serialize<DatabaseEntry>(entry);
|
||||
var previousHash = previousBytes.GetHashSHA1();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var dataEntry = entry;
|
||||
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
// if(GUILayout.Button("Print JSON"))
|
||||
// Debug.Log(entry.ToJson());
|
||||
if (GUILayout.Button("Delete Entry"))
|
||||
{
|
||||
CultCache.Remove(entry);
|
||||
entry = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Clone Entry"))
|
||||
{
|
||||
var copy = MessagePackSerializer.Deserialize<DatabaseEntry>(previousBytes);
|
||||
copy.ID = Guid.NewGuid();
|
||||
CultCache.Add(copy);
|
||||
}
|
||||
}
|
||||
|
||||
using (var h = new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("ID");
|
||||
LabelField(dataEntry.ID.ToString());
|
||||
}
|
||||
|
||||
_listItemStyle = false;
|
||||
Inspect(entry);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if(previousHash != MessagePackSerializer.Serialize(entry).GetHashSHA1())
|
||||
CultCache.Add(entry);
|
||||
}
|
||||
|
||||
EndScrollView();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 30b612dc279f724418b8a7a5426287c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,319 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public abstract class DatabaseListView : EditorWindow
|
||||
{
|
||||
public static GUIStyle ListStyleOdd;
|
||||
public static GUIStyle ListStyleEven;
|
||||
|
||||
public Guid SelectedItem;
|
||||
public GUIStyle SelectedStyle;
|
||||
|
||||
protected CultCache cultCache;
|
||||
private static DatabaseInspector _inspector;
|
||||
private Dictionary<string, (bool foldout, (Type entryType, bool foldout)[] types)> _tables;
|
||||
private Type[] _globalTypes;
|
||||
private Vector2 _view;
|
||||
|
||||
private HashSet<int> _groupFoldouts = new HashSet<int>();
|
||||
|
||||
protected abstract string FilePath { get; }
|
||||
protected virtual DatabaseEntryGroup[] Groupers => new DatabaseEntryGroup[] { };
|
||||
|
||||
protected class DatabaseEntryGroup<T, K> : DatabaseEntryGroup where T : DatabaseEntry
|
||||
{
|
||||
public Func<T,K> GroupSelector { get; }
|
||||
public Func<K,string> GroupLabel { get; }
|
||||
public Action<T, K> Activator { get; }
|
||||
|
||||
public DatabaseEntryGroup(Func<T, K> groupSelector, Func<K, string> groupLabel, Action<T, K> activator)
|
||||
{
|
||||
GroupSelector = groupSelector;
|
||||
GroupLabel = groupLabel;
|
||||
Activator = activator;
|
||||
}
|
||||
|
||||
public override bool CanGroup(Type type)
|
||||
{
|
||||
return typeof(T) == type;
|
||||
}
|
||||
|
||||
public override object SelectGroup(DatabaseEntry o)
|
||||
{
|
||||
if (o is T t) return GroupSelector(t);
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string GetGroupName(object o)
|
||||
{
|
||||
if (o is K k) return GroupLabel(k);
|
||||
return "ERROR";
|
||||
}
|
||||
|
||||
public override int HashGroup(object o)
|
||||
{
|
||||
return o.GetHashCode();
|
||||
if (o is K k) return k.GetHashCode();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override void Activate(DatabaseEntry o, object g)
|
||||
{
|
||||
if(o is T t && g is K k) Activator(t, k);
|
||||
throw new ArgumentException(
|
||||
$"Entry group activation failed! Expected: object type {typeof(T)}, group type {typeof(K)}; received: object type {o.GetType()}, group type {g.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract class DatabaseEntryGroup
|
||||
{
|
||||
public abstract bool CanGroup(Type type);
|
||||
public abstract object SelectGroup(DatabaseEntry o);
|
||||
public abstract string GetGroupName(object o);
|
||||
public abstract int HashGroup(object o);
|
||||
public abstract void Activate(DatabaseEntry o, object g);
|
||||
}
|
||||
|
||||
public Color LabelColor => EditorGUIUtility.isProSkin ? Color.white : Color.black;
|
||||
|
||||
private bool _listItemStyle;
|
||||
private SingleFileMessagePackBackingStore _msgPackStore;
|
||||
public GUIStyle ListItemStyle => (_listItemStyle = !_listItemStyle) ? ListStyleEven : ListStyleOdd;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
ListStyleOdd = new GUIStyle
|
||||
{
|
||||
normal = {background = EditorGUIUtility.isProSkin ? XKCDColors.DarkGrey.ToTexture() : XKCDColors.LightGrey.ToTexture()},
|
||||
margin = new RectOffset(0, 0, 0, 0)
|
||||
};
|
||||
ListStyleEven = new GUIStyle
|
||||
{
|
||||
normal = {background = EditorGUIUtility.isProSkin ? Color.Lerp(XKCDColors.DarkGrey,Color.black,.5f).ToTexture() : Color.Lerp(XKCDColors.LightGrey,Color.white,.5f).ToTexture()},
|
||||
margin = new RectOffset(0, 0, 0, 0)
|
||||
};
|
||||
|
||||
_msgPackStore = new SingleFileMessagePackBackingStore(Path.Combine(FilePath, "AetherDB.msgpack"));
|
||||
cultCache = new CultCache();
|
||||
cultCache.AddBackingStore(_msgPackStore);
|
||||
cultCache.AddBackingStore(new MultiFileMessagePackBackingStore(FilePath), typeof(NameFile));
|
||||
cultCache.PullAllBackingStores();
|
||||
|
||||
cultCache.OnUpdate += (_,__) => EditorDispatcher.Dispatch(Repaint);
|
||||
DatabaseInspector.CultCache = cultCache;
|
||||
_inspector = DatabaseInspector.Instance;
|
||||
_inspector.Show();
|
||||
|
||||
_globalTypes = typeof(DatabaseEntry).GetAllChildClasses()
|
||||
.Where(t => t.GetCustomAttribute<InspectableAttribute>() != null && t.GetCustomAttribute<GlobalSettingsAttribute>() != null).ToArray();
|
||||
_tables = typeof(DatabaseEntry).GetAllChildClasses()
|
||||
.Where(t => t.GetCustomAttribute<InspectableAttribute>() != null && t.GetCustomAttribute<GlobalSettingsAttribute>() == null)
|
||||
.GroupBy(t=>t.GetCustomAttribute<DatabaseCategoryAttribute>()?.Category ?? "Default")
|
||||
.ToDictionary(group=>group.Key, group=> (false, group.Select(t=>(t, false)).ToArray()));
|
||||
|
||||
InitStyles();
|
||||
}
|
||||
|
||||
private void InitStyles()
|
||||
{
|
||||
SelectedStyle = new GUIStyle
|
||||
{
|
||||
normal = {background = EditorGUIUtility.isProSkin ? XKCDColors.DarkGreyBlue.ToTexture() : XKCDColors.Greyblue.ToTexture()},
|
||||
margin = new RectOffset(0, 0, 0, 0)
|
||||
};
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
Event currentEvent = Event.current;
|
||||
EventType currentEventType = currentEvent.type;
|
||||
|
||||
if (currentEventType == EventType.DragUpdated)
|
||||
// Indicate that we don't accept drags ourselves
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
|
||||
|
||||
if (GUILayout.Button("Save"))
|
||||
{
|
||||
_msgPackStore.PushAll();
|
||||
Debug.Log("Saved database to AetherDB.msgpack");
|
||||
}
|
||||
GUILayout.Space(5);
|
||||
|
||||
_view = BeginScrollView(
|
||||
_view,
|
||||
false,
|
||||
false,
|
||||
GUIStyle.none,
|
||||
GUI.skin.verticalScrollbar,
|
||||
GUI.skin.scrollView,
|
||||
GUILayout.Width(EditorGUIUtility.currentViewWidth),
|
||||
GUILayout.ExpandHeight(true));
|
||||
|
||||
foreach (var globalType in _globalTypes)
|
||||
{
|
||||
var globalEntry = cultCache.GetGlobal(globalType);
|
||||
var style = ListItemStyle;
|
||||
var selected = SelectedItem == globalEntry.ID;
|
||||
using (new HorizontalScope(selected?SelectedStyle:style))
|
||||
{
|
||||
using (var h = new HorizontalScope())
|
||||
{
|
||||
if (h.rect.Contains(currentEvent.mousePosition))
|
||||
{
|
||||
if (currentEventType == EventType.MouseUp)
|
||||
Select(globalEntry);
|
||||
}
|
||||
|
||||
GUILayout.Label(globalType.Name.SplitCamelCase(), selected ? EditorStyles.whiteLabel : GUI.skin.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGroup(Type entryType, IEnumerable<DatabaseEntry> entries, IEnumerable<DatabaseEntryGroup> nestedGroups, int indent = 2, Action<DatabaseEntry> activatePreviousGroups = null, string previousGroupNames = "")
|
||||
{
|
||||
var grouper = nestedGroups.FirstOrDefault();
|
||||
if (grouper != null)
|
||||
{
|
||||
var groups = entries.GroupBy(o => grouper.SelectGroup(o));
|
||||
foreach (var group in groups)
|
||||
{
|
||||
//var groupHash = grouper.HashGroup(group.Key);
|
||||
var groupName = grouper.GetGroupName(group.Key);
|
||||
var groupHash = groupName.GetHashCode();
|
||||
using (new HorizontalScope(ListItemStyle))
|
||||
{
|
||||
GUILayout.Space(indent*10);
|
||||
if (Foldout(_groupFoldouts.Contains(groupHash), FormatTypeName(groupName), true))
|
||||
_groupFoldouts.Add(groupHash);
|
||||
else _groupFoldouts.Remove(groupHash);
|
||||
}
|
||||
|
||||
if (_groupFoldouts.Contains(groupHash))
|
||||
{
|
||||
Action<DatabaseEntry> activate = entry =>
|
||||
{
|
||||
activatePreviousGroups?.Invoke(entry);
|
||||
grouper.Activate(entry, group.Key);
|
||||
};
|
||||
|
||||
DisplayGroup(entryType, group, nestedGroups.Skip(1), indent + 1, activate, $"{groupName} {previousGroupNames}");
|
||||
|
||||
using (var h = new HorizontalScope(ListItemStyle))
|
||||
{
|
||||
if (GUI.Button(h.rect, GUIContent.none, GUIStyle.none))
|
||||
CreateItem(entryType, activate);
|
||||
GUILayout.Space((indent+1)*10);
|
||||
GUILayout.Label($"Create {groupName} {previousGroupNames}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = 0;
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
index++;
|
||||
var style = ListItemStyle;
|
||||
var selected = SelectedItem == entry.ID;
|
||||
using (var h = new HorizontalScope(selected ? SelectedStyle : style))
|
||||
{
|
||||
if (h.rect.Contains(currentEvent.mousePosition))
|
||||
{
|
||||
if (currentEventType == EventType.MouseDrag)
|
||||
{
|
||||
DragAndDrop.PrepareStartDrag();
|
||||
DragAndDrop.SetGenericData("Item", entry.ID);
|
||||
DragAndDrop.StartDrag("Database Item");
|
||||
}
|
||||
else if (currentEventType == EventType.MouseUp)
|
||||
Select(entry);
|
||||
}
|
||||
|
||||
GUILayout.Space(indent*10);
|
||||
GUILayout.Label((entry as INamedEntry)?.EntryName ?? index.ToString(),
|
||||
selected ? EditorStyles.whiteLabel : GUI.skin.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var table in _tables.Keys.ToArray())
|
||||
{
|
||||
var (tableFoldout, _) = _tables[table];
|
||||
using (new HorizontalScope(ListItemStyle))
|
||||
{
|
||||
_tables[table] = (Foldout(tableFoldout = _tables[table].foldout, table, true), _tables[table].types);
|
||||
}
|
||||
|
||||
if (tableFoldout)
|
||||
{
|
||||
for (var i = 0; i < _tables[table].types.Length; i++)
|
||||
{
|
||||
var (entryType, typeFoldout) = _tables[table].types[i];
|
||||
using (new HorizontalScope(ListItemStyle))
|
||||
{
|
||||
GUILayout.Space(10);
|
||||
_tables[table].types[i] = (entryType, typeFoldout = Foldout(typeFoldout,
|
||||
((NameAttribute) entryType.GetCustomAttribute(typeof(NameAttribute)))?.Name ?? FormatTypeName(entryType.Name),
|
||||
true));
|
||||
}
|
||||
|
||||
if (typeFoldout)
|
||||
{
|
||||
var entries = cultCache.GetAll(entryType)
|
||||
.OrderBy(entry => entry is INamedEntry namedEntry ? namedEntry.EntryName : entry.ID.ToString());
|
||||
var groupers = Groupers.Where(g => g.CanGroup(entryType));
|
||||
DisplayGroup(entryType, entries, groupers);
|
||||
|
||||
using (var h = new HorizontalScope(ListItemStyle))
|
||||
{
|
||||
if (GUI.Button(h.rect, GUIContent.none, GUIStyle.none))
|
||||
CreateItem(entryType);
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Label($"Create {FormatTypeName(entryType.Name)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EndScrollView();
|
||||
}
|
||||
|
||||
public static string FormatTypeName(string typeName)
|
||||
{
|
||||
return (typeName.EndsWith("Data")
|
||||
? typeName.Substring(0, typeName.Length - 4)
|
||||
: typeName).SplitCamelCase();
|
||||
}
|
||||
|
||||
void Select(DatabaseEntry item)
|
||||
{
|
||||
SelectedItem = item.ID;
|
||||
Repaint();
|
||||
_inspector.entry = cultCache.Get(SelectedItem);
|
||||
_inspector.Repaint();
|
||||
}
|
||||
|
||||
private DatabaseEntry CreateItem(Type type, Action<DatabaseEntry> onCreate = null)
|
||||
{
|
||||
var newEntry = (DatabaseEntry) Activator.CreateInstance(type);
|
||||
if(newEntry is INamedEntry entry)
|
||||
entry.EntryName = $"New {FormatTypeName(type.Name)}";
|
||||
onCreate?.Invoke(newEntry);
|
||||
cultCache.Add(newEntry);
|
||||
Select(newEntry);
|
||||
return newEntry;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b514fcab25f3b914b89824efbe244591
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public static class EditorDispatcher
|
||||
{
|
||||
private static readonly Queue<Action> dispatchQueue = new Queue<Action>();
|
||||
private static double timeSliceLimit = 10.0; // in miliseconds
|
||||
private static Stopwatch timer;
|
||||
|
||||
static EditorDispatcher()
|
||||
{
|
||||
EditorApplication.update += Update;
|
||||
timer = new Stopwatch();
|
||||
}
|
||||
|
||||
private static void Update ()
|
||||
{
|
||||
lock (dispatchQueue)
|
||||
{
|
||||
int dispatchCount = 0;
|
||||
|
||||
timer.Reset();
|
||||
timer.Start();
|
||||
|
||||
while (dispatchQueue.Count > 0 && (timer.Elapsed.TotalMilliseconds <= timeSliceLimit))
|
||||
{
|
||||
dispatchQueue.Dequeue().Invoke();
|
||||
|
||||
dispatchCount++;
|
||||
}
|
||||
|
||||
timer.Stop();
|
||||
|
||||
// if (dispatchCount > 0)
|
||||
// UnityEngine.Debug.Log(string.Format("[EditorDispatcher] Dispatched {0} calls in {1}ms", dispatchCount, timer.Elapsed.TotalMilliseconds));
|
||||
|
||||
// todo some logic for disconnecting update when the queue is empty
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send an Action Delegate to be run on the main thread. See EditorDispatchActions for some common usecases.
|
||||
/// </summary>
|
||||
/// <param name="task">An action delegate to run on the main thread</param>
|
||||
/// <returns>An AsyncDispatch that can be used to track if the dispatch has completed.</returns>
|
||||
public static AsyncDispatch Dispatch(Action task)
|
||||
{
|
||||
lock (dispatchQueue)
|
||||
{
|
||||
AsyncDispatch dispatch = new AsyncDispatch();
|
||||
|
||||
// enqueue a new task that runs the supplied task and completes the dispatcher
|
||||
dispatchQueue.Enqueue(() => { task(); dispatch.FinishedDispatch(); });
|
||||
|
||||
return dispatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the progress of the dispatched action. Can be yielded to in a coroutine.
|
||||
/// If not using coroutines, look at the IsDone property to find out when its okay to proceed.
|
||||
/// </summary>
|
||||
public class AsyncDispatch : CustomYieldInstruction
|
||||
{
|
||||
public bool IsDone { get; private set; }
|
||||
public override bool keepWaiting { get { return !IsDone; } }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Flags this dispatch as completed.
|
||||
/// </summary>
|
||||
internal void FinishedDispatch()
|
||||
{
|
||||
IsDone = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7269ba738c469a4ea5b50811ef07f11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b62c0242f5704a96829889e1d4c44900
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CultMath;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
using static CultMath.math;
|
||||
|
||||
public class StatReferenceInspector : BaseInspector<StatReference>
|
||||
{
|
||||
public override StatReference Inspect(string label, StatReference value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
|
||||
using (new VerticalScope())
|
||||
{
|
||||
var targetObjects = typeof(BehaviorData).GetAllChildClasses()
|
||||
.Concat(typeof(EquippableItemData).GetAllChildClasses()).ToArray();
|
||||
var objectNames = targetObjects.Select(b => b.Name).ToArray();
|
||||
var selectedIndex = Array.IndexOf(objectNames, value.Target);
|
||||
if (selectedIndex == -1)
|
||||
{
|
||||
selectedIndex = 0;
|
||||
GUI.changed = true;
|
||||
}
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("Target", GUILayout.Width(width));
|
||||
var newSelection = Popup(selectedIndex, objectNames);
|
||||
if (newSelection != selectedIndex)
|
||||
GUI.changed = true;
|
||||
value.Target = objectNames[newSelection];
|
||||
}
|
||||
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("Stat", GUILayout.Width(width));
|
||||
var stats = targetObjects[selectedIndex].GetFields()
|
||||
.Where(f => f.FieldType == typeof(PerformanceStat)).ToArray();
|
||||
if(stats.Length==0)
|
||||
GUILayout.Label("No Stats!");
|
||||
else
|
||||
{
|
||||
var statNames = stats.Select(s => s.Name).ToArray();
|
||||
var selectedStatIndex = Array.IndexOf(statNames, value.Stat);
|
||||
if (selectedStatIndex == -1)
|
||||
{
|
||||
selectedStatIndex = 0;
|
||||
GUI.changed = true;
|
||||
}
|
||||
var newSelection = Popup(selectedStatIndex, statNames);
|
||||
if (newSelection != selectedStatIndex)
|
||||
GUI.changed = true;
|
||||
value.Stat = statNames[newSelection];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class PerformanceStatInspector : BaseInspector<PerformanceStat>
|
||||
{
|
||||
public override PerformanceStat Inspect(string label, PerformanceStat value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
if(value == null)
|
||||
value = new PerformanceStat();
|
||||
using (new VerticalScope())
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("Min", EditorStyles.miniLabel, GUILayout.Width(labelWidth));
|
||||
value.Min = DelayedFloatField(value.Min);
|
||||
GUILayout.Label("Max", EditorStyles.miniLabel, GUILayout.Width(labelWidth + 5));
|
||||
value.Max = DelayedFloatField(value.Max);
|
||||
}
|
||||
|
||||
// Only display heat, durability and quality effects if the stat actually varies
|
||||
if (Math.Abs(value.Min - value.Max) > .0001f)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("", GUILayout.Width(width));
|
||||
GUILayout.Label("H", EditorStyles.miniLabel, GUILayout.Width(labelWidth));
|
||||
value.HeatExponentMultiplier = DelayedFloatField(value.HeatExponentMultiplier);
|
||||
|
||||
GUILayout.Label("D", EditorStyles.miniLabel, GUILayout.Width(labelWidth));
|
||||
value.DurabilityExponentMultiplier = DelayedFloatField(value.DurabilityExponentMultiplier);
|
||||
|
||||
GUILayout.Label("Q", EditorStyles.miniLabel, GUILayout.Width(labelWidth));
|
||||
value.QualityExponent = DelayedFloatField(value.QualityExponent);
|
||||
}
|
||||
}
|
||||
|
||||
Space();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class ShapeInspector : BaseInspector<Shape>
|
||||
{
|
||||
private Material _schematicMat = new Material(Shader.Find("Legacy Shaders/Particles/Additive"));
|
||||
|
||||
public override Shape Inspect(string label, Shape value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
value = new Shape(1, 1);
|
||||
value[int2(0)] = true;
|
||||
}
|
||||
using (new VerticalScope())
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("Size", GUILayout.ExpandWidth(false));
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.Width = DelayedIntField(value.Width);
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
value.Height = DelayedIntField(value.Height);
|
||||
}
|
||||
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("", GUILayout.Width(width));
|
||||
using (new VerticalScope())
|
||||
{
|
||||
for (var y = value.Height - 1; y >= 0; y--)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
for (var x = 0; x < value.Width; x++)
|
||||
{
|
||||
var oldColor = GUI.backgroundColor;
|
||||
value[int2(x, y)] = Toggle(value[int2(x, y)], GUILayout.Width(toggleWidth));
|
||||
GUI.backgroundColor = oldColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class ShapeSchematicInspector : BaseInspector<Shape, InspectableSchematicShapeAttribute>
|
||||
{
|
||||
private Material _schematicMat = new Material(Shader.Find("Legacy Shaders/Particles/Additive"));
|
||||
|
||||
public override Shape Inspect(string label, Shape value, object parent, DatabaseInspector inspectorWindow, InspectableSchematicShapeAttribute attribute)
|
||||
{
|
||||
var item = parent as EquippableItemData;
|
||||
if(value == null)
|
||||
{
|
||||
value = new Shape(1, 1);
|
||||
value[int2(0)] = true;
|
||||
}
|
||||
using (new VerticalScope())
|
||||
{
|
||||
Texture2D schematic = null;
|
||||
if(item != null && !string.IsNullOrEmpty(item.Schematic))
|
||||
schematic = AssetDatabase.LoadAssetAtPath<Texture2D>(item.Schematic);
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("Size", GUILayout.ExpandWidth(false));
|
||||
if(schematic == null)
|
||||
{
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.Width = DelayedIntField(value.Width);
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
value.Height = DelayedIntField(value.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.Width = DelayedIntField(value.Width);
|
||||
value.Height = Mathf.RoundToInt(value.Width * ((float)schematic.height / schematic.width));
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
LabelField($"{value.Height}");
|
||||
}
|
||||
}
|
||||
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label("", GUILayout.Width(width));
|
||||
using (var a = new VerticalScope())
|
||||
{
|
||||
if(schematic != null)
|
||||
{
|
||||
EditorGUI.DrawPreviewTexture(a.rect, schematic, _schematicMat);
|
||||
}
|
||||
for (var y = value.Height - 1; y >= 0; y--)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
for (var x = 0; x < value.Width; x++)
|
||||
{
|
||||
var oldColor = GUI.backgroundColor;
|
||||
if(item is HullData hull)
|
||||
{
|
||||
var hardpoint = hull.Hardpoints
|
||||
.FirstOrDefault(hp =>
|
||||
hp.Shape.Coordinates
|
||||
.Select(v => v + hp.Position)
|
||||
.Any(v => v.x == x && v.y == y));
|
||||
if (hardpoint != null)
|
||||
GUI.backgroundColor = hardpoint.TintColor.ToColor();
|
||||
}
|
||||
value[int2(x, y)] = Toggle(value[int2(x, y)], GUILayout.Width(toggleWidth));
|
||||
GUI.backgroundColor = oldColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 57d0bc090768458b9428efd7a8a33741
|
||||
timeCreated: 1617069994
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
using System.Linq;
|
||||
using CultMath;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
using static CultMath.math;
|
||||
|
||||
public class AnimationCurveFloat4Inspector : BaseInspector<float4[]>
|
||||
{
|
||||
public override float4[] Inspect(string label, float4[] value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
var val = value != null && value.Length > 0
|
||||
? value.ToCurve()
|
||||
: new AnimationCurve();
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
val = CurveField(val, Color.yellow, new Rect(0, 0, 1, 1));
|
||||
}
|
||||
|
||||
return val.keys.Select(k => float4(k.time, k.value, k.inTangent, k.outTangent)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class BezierCurveInspector : BaseInspector<BezierCurve>
|
||||
{
|
||||
public override BezierCurve Inspect(string label, BezierCurve value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
var val = value != null && value.Keys != null && value.Keys.Length > 0
|
||||
? value.ToCurve()
|
||||
: new AnimationCurve();
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
val = CurveField(val, Color.yellow, new Rect(0, 0, 1, 1));
|
||||
}
|
||||
|
||||
value.Keys = val.keys.Select(k => float4(k.time, k.value, k.inTangent, k.outTangent)).ToArray();
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimationCurveInspect : BaseInspector<AnimationCurve>
|
||||
{
|
||||
public override AnimationCurve Inspect(string label, AnimationCurve value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (var h = new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
if (value == null)
|
||||
value = new AnimationCurve();
|
||||
value = CurveField(value, Color.yellow, new Rect(0, 0, 1, 1));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c89e0e7eb9f243cbbce28df59f6fb1c9
|
||||
timeCreated: 1617066399
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class DatabaseLinkInspector : BaseInspector<Guid, InspectableDatabaseLinkAttribute>
|
||||
{
|
||||
public override Guid Inspect(string label, Guid value, object parent, DatabaseInspector inspectorWindow, InspectableDatabaseLinkAttribute link)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
var valueEntry = DatabaseInspector.CultCache.Get(value);
|
||||
using (var h = new HorizontalScope(GUI.skin.box))
|
||||
{
|
||||
GUILayout.Label((valueEntry as INamedEntry)?.EntryName ?? valueEntry?.ID.ToString() ?? $"Drag and drop: {DatabaseListView.FormatTypeName(link.EntryType.Name)}");
|
||||
|
||||
if (h.rect.Contains(Event.current.mousePosition) && (Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform))
|
||||
{
|
||||
var guid = (Guid) DragAndDrop.GetGenericData("Item");
|
||||
var dragEntry = DatabaseInspector.CultCache.Get(guid);
|
||||
var dragValid = dragEntry != null && link.EntryType.IsInstanceOfType(dragEntry) && dragEntry != valueEntry;
|
||||
if(Event.current.type == EventType.DragUpdated)
|
||||
DragAndDrop.visualMode = dragValid ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
|
||||
else if(Event.current.type == EventType.DragPerform)
|
||||
{
|
||||
if (dragValid)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
GUI.changed = true;
|
||||
return guid;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GUILayout.Button("-", GUILayout.Width((EditorGUIUtility.singleLineHeight - 3)*2), GUILayout.Height(EditorGUIUtility.singleLineHeight-3)))
|
||||
{
|
||||
return Guid.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class DatabaseLinkObjectInspector<T> : BaseInspector<DatabaseLink<T>> where T : DatabaseEntry
|
||||
{
|
||||
public override DatabaseLink<T> Inspect(string label, DatabaseLink<T> value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
if (value == null)
|
||||
value = new DatabaseLink<T>();
|
||||
|
||||
if(!string.IsNullOrEmpty(label))
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
}
|
||||
|
||||
var valueEntry = value.Value;
|
||||
using (var h = string.IsNullOrEmpty(label) ? new HorizontalScope() : new HorizontalScope(GUI.skin.box))
|
||||
{
|
||||
GUILayout.Label((valueEntry as INamedEntry)?.EntryName ?? valueEntry?.ID.ToString() ?? $"Drag and drop: {DatabaseListView.FormatTypeName(typeof(T).Name)}");
|
||||
|
||||
if (h.rect.Contains(Event.current.mousePosition) && (Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform))
|
||||
{
|
||||
var guid = (Guid) DragAndDrop.GetGenericData("Item");
|
||||
var dragEntry = DatabaseInspector.CultCache.Get(guid);
|
||||
var dragValid = dragEntry is T && dragEntry != valueEntry;
|
||||
if(Event.current.type == EventType.DragUpdated)
|
||||
DragAndDrop.visualMode = dragValid ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
|
||||
else if(Event.current.type == EventType.DragPerform)
|
||||
{
|
||||
if (dragValid)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
value.LinkID = guid;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(value.LinkID != Guid.Empty)
|
||||
{
|
||||
if (GUILayout.Button("-",
|
||||
GUILayout.Width((EditorGUIUtility.singleLineHeight - 3) * 2),
|
||||
GUILayout.Height(EditorGUIUtility.singleLineHeight - 3)))
|
||||
{
|
||||
value.LinkID = Guid.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(label))
|
||||
{
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class DatabaseLinkListInspector : BaseInspector<List<Guid>, InspectableDatabaseLinkAttribute>
|
||||
{
|
||||
private bool _listItemStyle;
|
||||
public GUIStyle ListItemStyle =>
|
||||
(_listItemStyle = !_listItemStyle) ? DatabaseListView.ListStyleEven : DatabaseListView.ListStyleOdd;
|
||||
|
||||
public override List<Guid> Inspect(string label,
|
||||
List<Guid> value,
|
||||
object parent,
|
||||
DatabaseInspector inspectorWindow,
|
||||
InspectableDatabaseLinkAttribute link)
|
||||
{
|
||||
using (var v = new VerticalScope(GUI.skin.box))
|
||||
{
|
||||
GUILayout.Label(label, EditorStyles.boldLabel);
|
||||
if (value.Count == 0)
|
||||
{
|
||||
using (new HorizontalScope(ListItemStyle))
|
||||
{
|
||||
GUILayout.Label($"Drag from list to add {link.EntryType.Name}");
|
||||
}
|
||||
}
|
||||
foreach (var guid in value.ToArray())
|
||||
{
|
||||
using (new HorizontalScope(ListItemStyle))
|
||||
{
|
||||
var entry = DatabaseInspector.CultCache.Get(guid);
|
||||
if (entry == null)
|
||||
{
|
||||
value.Remove(guid);
|
||||
GUI.changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label((entry as INamedEntry)?.EntryName ?? entry.ID.ToString());
|
||||
|
||||
// var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight));
|
||||
// GUI.DrawTexture(rect, Icons.Instance.minus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0);
|
||||
if (GUILayout.Button("-", GUILayout.Width((EditorGUIUtility.singleLineHeight - 3)*2), GUILayout.Height(EditorGUIUtility.singleLineHeight-3)))
|
||||
{
|
||||
value.Remove(guid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (v.rect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
var guid = DragAndDrop.GetGenericData("Item");
|
||||
var dragObj = DragAndDrop.GetGenericData("Item") is Guid ? DatabaseInspector.CultCache.Get((Guid) guid) : null;
|
||||
var dragValid = dragObj != null && link.EntryType.IsInstanceOfType(dragObj) && !value.Contains(dragObj.ID);
|
||||
if(Event.current.type == EventType.DragUpdated)
|
||||
DragAndDrop.visualMode = dragValid ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
|
||||
else if(Event.current.type == EventType.DragPerform)
|
||||
{
|
||||
if (dragValid)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
value.Add((Guid) guid);
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5e08712ea4854536b130f82ad61db7a8
|
||||
timeCreated: 1617069551
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class FloatDictionaryInspector : BaseInspector<Dictionary<Guid, float>, InspectableDatabaseLinkAttribute>
|
||||
{
|
||||
public override Dictionary<Guid, float> Inspect(string label,
|
||||
Dictionary<Guid, float> value,
|
||||
object parent,
|
||||
DatabaseInspector inspectorWindow,
|
||||
InspectableDatabaseLinkAttribute attribute)
|
||||
{
|
||||
Space();
|
||||
LabelField(label, EditorStyles.boldLabel);
|
||||
|
||||
using (var v = new VerticalScope(GUI.skin.box))
|
||||
{
|
||||
if (value.Count == 0)
|
||||
{
|
||||
using (new HorizontalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
GUILayout.Label("Drag from list to add item");
|
||||
}
|
||||
}
|
||||
foreach (var ingredient in value.ToArray())
|
||||
{
|
||||
using (new HorizontalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
var entry = DatabaseInspector.CultCache.Get(ingredient.Key);
|
||||
if (entry == null)
|
||||
{
|
||||
value.Remove(ingredient.Key);
|
||||
GUI.changed = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
if(entry is INamedEntry named)
|
||||
GUILayout.Label(named.EntryName);
|
||||
else GUILayout.Label(entry.ID.ToString());
|
||||
value[ingredient.Key] = DelayedFloatField(value[ingredient.Key], GUILayout.Width(50));
|
||||
|
||||
// var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight));
|
||||
// GUI.DrawTexture(rect, Icons.Instance.minus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0);
|
||||
if (GUILayout.Button("-", GUILayout.Width((EditorGUIUtility.singleLineHeight - 3)*2), GUILayout.Height(EditorGUIUtility.singleLineHeight-3)))
|
||||
value.Remove(ingredient.Key);
|
||||
}
|
||||
}
|
||||
|
||||
if (v.rect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
var dragData = DragAndDrop.GetGenericData("Item");
|
||||
var isId = dragData is Guid;
|
||||
var dragEntry = isId ? DatabaseInspector.CultCache.Get((Guid)dragData) : null;
|
||||
var correctType = attribute.EntryType.IsInstanceOfType(dragEntry);
|
||||
if(Event.current.type == EventType.DragUpdated)
|
||||
DragAndDrop.visualMode = correctType ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
|
||||
else if(Event.current.type == EventType.DragPerform)
|
||||
{
|
||||
if (isId && correctType)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
value[(Guid)dragData] = 1;
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class RangedFloatDictionaryInspector : BaseInspector<Dictionary<Guid, float>, InspectableDatabaseLinkAttribute, RangedFloatAttribute>
|
||||
{
|
||||
public override Dictionary<Guid, float> Inspect(string label,
|
||||
Dictionary<Guid, float> value,
|
||||
object parent,
|
||||
DatabaseInspector inspectorWindow,
|
||||
InspectableDatabaseLinkAttribute link,
|
||||
RangedFloatAttribute range)
|
||||
{
|
||||
Space();
|
||||
LabelField(label, EditorStyles.boldLabel);
|
||||
|
||||
using (var v = new VerticalScope(GUI.skin.box))
|
||||
{
|
||||
if (value.Count == 0)
|
||||
{
|
||||
using (new HorizontalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
GUILayout.Label("Drag from list to add item");
|
||||
}
|
||||
}
|
||||
foreach (var ingredient in value.ToArray())
|
||||
{
|
||||
using (new HorizontalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
var entry = DatabaseInspector.CultCache.Get(ingredient.Key);
|
||||
if (entry == null)
|
||||
{
|
||||
value.Remove(ingredient.Key);
|
||||
GUI.changed = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
if(entry is INamedEntry named)
|
||||
GUILayout.Label(named.EntryName);
|
||||
else GUILayout.Label(entry.ID.ToString());
|
||||
value[ingredient.Key] = Slider(value[ingredient.Key], range.Min, range.Max);
|
||||
|
||||
// var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight));
|
||||
// GUI.DrawTexture(rect, Icons.Instance.minus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0);
|
||||
if (GUILayout.Button("-", GUILayout.Width((EditorGUIUtility.singleLineHeight - 3)*2), GUILayout.Height(EditorGUIUtility.singleLineHeight-3)))
|
||||
value.Remove(ingredient.Key);
|
||||
}
|
||||
}
|
||||
|
||||
if (v.rect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
var dragData = DragAndDrop.GetGenericData("Item");
|
||||
var isId = dragData is Guid;
|
||||
var dragEntry = isId ? DatabaseInspector.CultCache.Get((Guid)dragData) : null;
|
||||
var correctType = link.EntryType.IsInstanceOfType(dragEntry);
|
||||
if(Event.current.type == EventType.DragUpdated)
|
||||
DragAndDrop.visualMode = correctType ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
|
||||
else if(Event.current.type == EventType.DragPerform)
|
||||
{
|
||||
if (isId && correctType)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
value[(Guid)dragData] = range.Max;
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public class IntDictionaryInspector : BaseInspector<Dictionary<Guid, int>, InspectableDatabaseLinkAttribute>
|
||||
{
|
||||
public override Dictionary<Guid, int> Inspect(string label,
|
||||
Dictionary<Guid, int> value,
|
||||
object parent,
|
||||
DatabaseInspector inspectorWindow,
|
||||
InspectableDatabaseLinkAttribute attribute)
|
||||
{
|
||||
Space();
|
||||
LabelField(label, EditorStyles.boldLabel);
|
||||
|
||||
using (var v = new VerticalScope(GUI.skin.box))
|
||||
{
|
||||
if (value.Count == 0)
|
||||
{
|
||||
using (var h = new HorizontalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
GUILayout.Label("Drag from list to add item");
|
||||
}
|
||||
}
|
||||
foreach (var ingredient in value.ToArray())
|
||||
{
|
||||
using (var h = new HorizontalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
var entry = DatabaseInspector.CultCache.Get(ingredient.Key);
|
||||
if (entry == null)
|
||||
{
|
||||
value.Remove(ingredient.Key);
|
||||
GUI.changed = true;
|
||||
return value;
|
||||
}
|
||||
if(entry is INamedEntry named)
|
||||
GUILayout.Label(named.EntryName);
|
||||
else GUILayout.Label(entry.ID.ToString());
|
||||
|
||||
value[ingredient.Key] = DelayedIntField(value[ingredient.Key], GUILayout.Width(50));
|
||||
|
||||
// var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight));
|
||||
// GUI.DrawTexture(rect, Icons.Instance.minus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0);
|
||||
if (GUILayout.Button("-", GUILayout.Width((EditorGUIUtility.singleLineHeight - 3)*2), GUILayout.Height(EditorGUIUtility.singleLineHeight-3)) || value[ingredient.Key]==0)
|
||||
value.Remove(ingredient.Key);
|
||||
}
|
||||
}
|
||||
|
||||
if (v.rect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
var dragData = DragAndDrop.GetGenericData("Item");
|
||||
var isId = dragData is Guid;
|
||||
var dragEntry = isId ? DatabaseInspector.CultCache.Get((Guid)dragData) : null;
|
||||
var correctType = attribute.EntryType.IsInstanceOfType(dragEntry);
|
||||
if(Event.current.type == EventType.DragUpdated)
|
||||
DragAndDrop.visualMode = correctType ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
|
||||
else if(Event.current.type == EventType.DragPerform)
|
||||
{
|
||||
if (isId && correctType)
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
value[(Guid)dragData] = 1;
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a42620fd406045889578e2ad8d5fd8de
|
||||
timeCreated: 1617067689
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static CultMath.math;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class EnumInspector<T> : BaseInspector<List<T>, InspectableEnumValuesAttribute>
|
||||
{
|
||||
public override List<T> Inspect(string label, List<T> list, object parent, DatabaseInspector inspectorWindow, InspectableEnumValuesAttribute attribute)
|
||||
{
|
||||
var names = Enum.GetNames(attribute.EnumType);
|
||||
list ??= new List<T>();
|
||||
|
||||
while(list.Count < names.Length) list.Add(Activator.CreateInstance<T>());
|
||||
while(list.Count > names.Length) list.RemoveAt(list.Count-1);
|
||||
|
||||
for (var i = 0; i < names.Length; i++)
|
||||
inspectorWindow.InspectMember(names[i], list[i], parent, typeof(T));
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public class EnumDictionaryInspector<E, T> : BaseInspector<EnumDictionary<E, T>> where E : Enum
|
||||
{
|
||||
private static HashSet<object> _foldouts = new HashSet<object>();
|
||||
|
||||
public override EnumDictionary<E, T> Inspect(string label, EnumDictionary<E, T> dict, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
var names = Enum.GetNames(typeof(E));
|
||||
if (dict == null || dict.Values.Length != names.Length)
|
||||
{
|
||||
var newValue = new T[names.Length];
|
||||
if(dict != null && dict.Values.Length > 0)
|
||||
Array.Copy(dict.Values, newValue, min(dict.Values.Length, newValue.Length));
|
||||
dict.Values = newValue;
|
||||
}
|
||||
|
||||
using (new VerticalScope(GUI.skin.box))
|
||||
{
|
||||
if (Foldout(_foldouts.Contains(dict), label, true, EditorStyles.boldLabel))
|
||||
_foldouts.Add(dict);
|
||||
else _foldouts.Remove(dict);
|
||||
if(_foldouts.Contains(dict))
|
||||
{
|
||||
for (var i = 0; i < names.Length; i++)
|
||||
{
|
||||
using (new VerticalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
dict.Values[i] = (T) inspectorWindow.InspectMember(names[i], dict.Values[i], parent, typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: da1b15e5564747958639c8f959226c74
|
||||
timeCreated: 1617335525
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class FloatInspector : BaseInspector<float>
|
||||
{
|
||||
public override float Inspect(string label, float value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return DelayedFloatField(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RangedFloatInspector : BaseInspector<float, InspectableRangedFloatAttribute>
|
||||
{
|
||||
public override float Inspect(string label,
|
||||
float value,
|
||||
object parent,
|
||||
DatabaseInspector inspectorWindow,
|
||||
InspectableRangedFloatAttribute attribute)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return Slider(value, attribute.Min, attribute.Max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TemperatureFloatInspector : BaseInspector<float, InspectableTemperatureAttribute>
|
||||
{
|
||||
public override float Inspect(string label,
|
||||
float value,
|
||||
object parent,
|
||||
DatabaseInspector inspectorWindow,
|
||||
InspectableTemperatureAttribute attribute)
|
||||
{
|
||||
using (var h = new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("°K", EditorStyles.miniLabel, GUILayout.Width(labelWidth));
|
||||
value = DelayedFloatField(value);
|
||||
GUILayout.Label("°C", EditorStyles.miniLabel, GUILayout.Width(labelWidth));
|
||||
value = DelayedFloatField(value - 273.15f) + 273.15f;
|
||||
GUILayout.Label("°F", EditorStyles.miniLabel, GUILayout.Width(labelWidth));
|
||||
value = (DelayedFloatField((value - 273.15f) * 1.8f + 32) - 32) / 1.8f + 273.15f;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 47b8e5bc90714055881eb31baf2ab879
|
||||
timeCreated: 1617065074
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
using static CultMath.math;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class IntInspector : BaseInspector<int>
|
||||
{
|
||||
public override int Inspect(string label, int value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return DelayedIntField(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UintInspector : BaseInspector<uint>
|
||||
{
|
||||
public override uint Inspect(string label, uint value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return (uint) max(DelayedIntField((int)value), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RangedIntInspector : BaseInspector<int, InspectableRangedIntAttribute>
|
||||
{
|
||||
public override int Inspect(string label, int value, object parent, DatabaseInspector inspectorWindow, InspectableRangedIntAttribute attribute)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return IntSlider(value, attribute.Min, attribute.Max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ac7d3b8651964ce1a732a002447d6f69
|
||||
timeCreated: 1617065365
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class ListInspector<T> : BaseInspector<List<T>>
|
||||
{
|
||||
private static HashSet<object> _foldouts = new HashSet<object>();
|
||||
|
||||
public override List<T> Inspect(string label, List<T> list, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
var listType = typeof(T);
|
||||
var inspectable = listType.GetCustomAttribute<InspectableAttribute>();
|
||||
// if (listType.GetCustomAttribute<InspectableAttribute>() != null)
|
||||
// {
|
||||
//Space();
|
||||
using (new VerticalScope(GUI.skin.box))
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
list = new List<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var sorted = true;
|
||||
var order = int.MinValue;
|
||||
foreach(var element in list)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
list.Remove(element);
|
||||
return list;
|
||||
}
|
||||
var elementType = element.GetType();
|
||||
var elementOrder = elementType.GetCustomAttribute<OrderAttribute>()?.Order ?? 0;
|
||||
if (elementOrder < order)
|
||||
sorted = false;
|
||||
order = elementOrder;
|
||||
}
|
||||
|
||||
if (!sorted)
|
||||
{
|
||||
list.Sort((x, y) => (x.GetType().GetCustomAttribute<OrderAttribute>()?.Order ?? 0).CompareTo(y.GetType().GetCustomAttribute<OrderAttribute>()?.Order ?? 0));
|
||||
}
|
||||
}
|
||||
if (Foldout(_foldouts.Contains(list), label, true, EditorStyles.boldLabel))
|
||||
_foldouts.Add(list);
|
||||
else _foldouts.Remove(list);
|
||||
if (_foldouts.Contains(list))
|
||||
{
|
||||
foreach (var o in list)
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
list.Remove(o);
|
||||
break;
|
||||
}
|
||||
bool _tinted = false;
|
||||
var originalColor = GUI.backgroundColor;
|
||||
if (o is ITintInspector tint)
|
||||
{
|
||||
_tinted = true;
|
||||
GUI.backgroundColor = tint.TintColor.ToColor();
|
||||
}
|
||||
|
||||
using (new VerticalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
if (inspectable != null)
|
||||
{
|
||||
if (Foldout(_foldouts.Contains(o), o.ToString(), true, EditorStyles.boldLabel))
|
||||
_foldouts.Add(o);
|
||||
else
|
||||
_foldouts.Remove(o);
|
||||
}
|
||||
else inspectorWindow.InspectMember("", o, parent, listType, true);
|
||||
|
||||
if (GUILayout.Button("-", GUILayout.Width((EditorGUIUtility.singleLineHeight - 3)*2), GUILayout.Height(EditorGUIUtility.singleLineHeight-3)))
|
||||
{
|
||||
list.Remove(o);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(inspectable != null && _foldouts.Contains(o))
|
||||
inspectorWindow.InspectMember("", o, parent, o.GetType(), true);
|
||||
}
|
||||
|
||||
if(_tinted)
|
||||
GUI.backgroundColor = originalColor;
|
||||
}
|
||||
using (new HorizontalScope(DatabaseInspector.ListItemStyle))
|
||||
{
|
||||
GUILayout.Label($"Add new {listType.GetFullName()}", GUILayout.ExpandWidth(true));
|
||||
// var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight));
|
||||
// GUI.DrawTexture(rect, Icons.Instance.plus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0);
|
||||
if (GUILayout.Button("+", GUILayout.Width((EditorGUIUtility.singleLineHeight - 3)*2), GUILayout.Height(EditorGUIUtility.singleLineHeight-3)))
|
||||
{
|
||||
if (listType.IsAbstract || listType.IsInterface)
|
||||
{
|
||||
var menu = new GenericMenu();
|
||||
foreach (var childType in listType.GetAllChildClasses().Where(t=>!t.IsAbstract && !t.IsInterface))
|
||||
{
|
||||
menu.AddItem(new GUIContent(childType.Name), false, () =>
|
||||
{
|
||||
var o = (T) Activator.CreateInstance(childType);
|
||||
_foldouts.Add(o);
|
||||
list.Add(o);
|
||||
});
|
||||
}
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
else
|
||||
{
|
||||
var o = Activator.CreateInstance<T>();
|
||||
_foldouts.Add(o);
|
||||
list.Add(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
// else Debug.Log($"Field \"{label}\" is a list of non-Inspectable type {listType.Name}. No inspector was generated.");
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 551335721057411d942b3317ae028546
|
||||
timeCreated: 1617145746
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
using CultMath;
|
||||
using CultMath.UnityBridge;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class Float2Inspector : BaseInspector<float2>
|
||||
{
|
||||
public override float2 Inspect(string label, float2 value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.x = DelayedFloatField(value.x);
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
value.y = DelayedFloatField(value.y);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Float3Inspector : BaseInspector<float3>
|
||||
{
|
||||
public override float3 Inspect(string label, float3 value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.x = DelayedFloatField(value.x);
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
value.y = DelayedFloatField(value.y);
|
||||
GUILayout.Label("Z", GUILayout.Width(labelWidth));
|
||||
value.z = DelayedFloatField(value.z);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Float3ColorInspector : BaseInspector<float3, InspectableColorAttribute>
|
||||
{
|
||||
public override float3 Inspect(string label, float3 value, object parent, DatabaseInspector inspectorWindow, InspectableColorAttribute attribute)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return ColorField(value.ToColor()).ToFloat3();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Float4Inspector : BaseInspector<float4>
|
||||
{
|
||||
public override float4 Inspect(string label, float4 value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.x = DelayedFloatField(value.x);
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
value.y = DelayedFloatField(value.y);
|
||||
GUILayout.Label("Z", GUILayout.Width(labelWidth));
|
||||
value.z = DelayedFloatField(value.z);
|
||||
GUILayout.Label("W", GUILayout.Width(labelWidth));
|
||||
value.w = DelayedFloatField(value.w);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Float4ColorInspector : BaseInspector<float4, InspectableColorAttribute>
|
||||
{
|
||||
public override float4 Inspect(string label, float4 value, object parent, DatabaseInspector inspectorWindow, InspectableColorAttribute attribute)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return ColorField(value.ToColor()).ToCultMath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Int2Inspector : BaseInspector<int2>
|
||||
{
|
||||
public override int2 Inspect(string label, int2 value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.x = DelayedIntField(value.x);
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
value.y = DelayedIntField(value.y);
|
||||
if (GUILayout.Button("\u2190", GUILayout.Width(arrowWidth)))
|
||||
value.x--;
|
||||
if (GUILayout.Button("\u2192", GUILayout.Width(arrowWidth)))
|
||||
value.x++;
|
||||
if (GUILayout.Button("\u2191", GUILayout.Width(arrowWidth)))
|
||||
value.y++;
|
||||
if (GUILayout.Button("\u2193", GUILayout.Width(arrowWidth)))
|
||||
value.y--;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Int3Inspector : BaseInspector<int3>
|
||||
{
|
||||
public override int3 Inspect(string label, int3 value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.x = DelayedIntField(value.x);
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
value.y = DelayedIntField(value.y);
|
||||
GUILayout.Label("Z", GUILayout.Width(labelWidth));
|
||||
value.z = DelayedIntField(value.z);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Int4Inspector : BaseInspector<int4>
|
||||
{
|
||||
public override int4 Inspect(string label, int4 value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
GUILayout.Label("X", GUILayout.Width(labelWidth));
|
||||
value.x = DelayedIntField(value.x);
|
||||
GUILayout.Label("Y", GUILayout.Width(labelWidth));
|
||||
value.y = DelayedIntField(value.y);
|
||||
GUILayout.Label("Z", GUILayout.Width(labelWidth));
|
||||
value.z = DelayedIntField(value.z);
|
||||
GUILayout.Label("W", GUILayout.Width(labelWidth));
|
||||
value.w = DelayedIntField(value.w);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ae5bba94d1f1a7144944aebf833497b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class BoolInspector : BaseInspector<bool>
|
||||
{
|
||||
public override bool Inspect(string label, bool value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (var h = new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return Toggle(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class StringInspector : BaseInspector<string>
|
||||
{
|
||||
public override string Inspect(string label, string value, object parent, DatabaseInspector inspectorWindow)
|
||||
{
|
||||
using (var h = new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return DelayedTextField(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class StringAreaInspector : BaseInspector<string, InspectableTextAttribute>
|
||||
{
|
||||
public override string Inspect(string label, string value, object parent, DatabaseInspector inspectorWindow, InspectableTextAttribute attribute)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
EditorStyles.textArea.wordWrap = true;
|
||||
return DelayedTextField(value, EditorStyles.textArea, GUILayout.Height(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 91dcbfa1679e4be1944d6856ca63bf13
|
||||
timeCreated: 1617065181
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class TypeInspector : BaseInspector<Type, InspectableTypeAttribute>
|
||||
{
|
||||
public override Type Inspect(string label, Type value, object parent, DatabaseInspector inspectorWindow, InspectableTypeAttribute attribute)
|
||||
{
|
||||
var types = attribute.Type.GetAllChildClasses();
|
||||
var enumOptions = new[] {"None"}.Concat(types.Select(t => t.Name)).ToArray();
|
||||
var index = Array.IndexOf(types, value) + 1;
|
||||
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
index = Popup(index, enumOptions);
|
||||
}
|
||||
|
||||
return index == 0 ? null : types[index - 1];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4762384a61dc4d08a1ba4e7564a20081
|
||||
timeCreated: 1617066802
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static UnityEditor.EditorGUILayout;
|
||||
|
||||
public class TextureInspector : BaseInspector<string, InspectableTextureAttribute>
|
||||
{
|
||||
public override string Inspect(string label,
|
||||
string value,
|
||||
object parent,
|
||||
DatabaseInspector inspectorWindow,
|
||||
InspectableTextureAttribute attribute)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return AssetDatabase.GetAssetPath(ObjectField(AssetDatabase.LoadAssetAtPath<Texture2D>(value), typeof(Texture2D), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GameObjectInspector : BaseInspector<string, InspectablePrefabAttribute>
|
||||
{
|
||||
public override string Inspect(string label, string value, object parent, DatabaseInspector inspectorWindow, InspectablePrefabAttribute attribute)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return AssetDatabase.GetAssetPath(ObjectField(AssetDatabase.LoadAssetAtPath<GameObject>(value), typeof(GameObject), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TextAssetInspector : BaseInspector<string, InspectableTextAssetAttribute>
|
||||
{
|
||||
public override string Inspect(string label,
|
||||
string value,
|
||||
object parent,
|
||||
DatabaseInspector inspectorWindow,
|
||||
InspectableTextAssetAttribute attribute)
|
||||
{
|
||||
using (new HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(label, GUILayout.Width(width));
|
||||
return AssetDatabase.GetAssetPath(ObjectField(AssetDatabase.LoadAssetAtPath<TextAsset>(value), typeof(TextAsset), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8519fa7d4f06468bb9be151f053d1fcb
|
||||
timeCreated: 1617067005
|
||||
|
|
@ -68,7 +68,9 @@ public class PlayerGraphicsSettings
|
|||
[MessagePackObject]
|
||||
public class PlayerInputSettings
|
||||
{
|
||||
[Key(0)] public Dictionary<(string action, int binding), string> InputActionMap = new Dictionary<(string action, int binding), string>();
|
||||
// Binding override paths by action name, then binding index. A tuple key has no hash-resistant comparer, so
|
||||
// MessagePack's untrusted-data security refuses to read it back.
|
||||
[Key(0)] public Dictionary<string, Dictionary<int, string>> InputActionMap = new Dictionary<string, Dictionary<int, string>>();
|
||||
[Key(1)] public List<string> ActionBarInputs = new List<string>();
|
||||
}
|
||||
|
||||
|
|
|
|||
15
tests/Aetheria.Shared.Tests/Aetheria.Shared.Tests.csproj
Normal file
15
tests/Aetheria.Shared.Tests/Aetheria.Shared.Tests.csproj
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Pins AetheriaStores' routing and the document model against CultLib's CultCache, headless. -->
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Aetheria.Shared\Aetheria.Shared.csproj" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
133
tests/Aetheria.Shared.Tests/AetheriaStoresTests.cs
Normal file
133
tests/Aetheria.Shared.Tests/AetheriaStoresTests.cs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using CultMath;
|
||||
using GameCult.Caching;
|
||||
using GameCult.Caching.MessagePack;
|
||||
using MessagePack;
|
||||
using Xunit;
|
||||
|
||||
// A [CultGlobal] routed to the catalog (PersonalityAttribute is a catalog home). The game has none yet, so this is
|
||||
// the only way to prove a read-only catalog refuses to open without one.
|
||||
[CultDocument("aetheria.tests.catalogglobal", "1"), CultGlobal, MessagePackObject]
|
||||
public class TestCatalogGlobal : PersonalityAttribute { }
|
||||
|
||||
// Each test gets its own temp directory holding a catalog built through Open(catalogWritable: true).
|
||||
public sealed class AetheriaStoresTests : IDisposable
|
||||
{
|
||||
private readonly string _root = Path.Combine(Path.GetTempPath(), "aetheria-stores-" + Guid.NewGuid().ToString("N"));
|
||||
private string Catalog => Path.Combine(_root, "Aetheria.cc");
|
||||
private string Run => Path.Combine(_root, "run.cc");
|
||||
private string Player => Path.Combine(_root, "player.cc");
|
||||
|
||||
public AetheriaStoresTests()
|
||||
{
|
||||
Directory.CreateDirectory(_root);
|
||||
using var cache = AetheriaStores.Open(Catalog, catalogWritable: true);
|
||||
cache.Upsert(new TestCatalogGlobal { Name = "Temperament" });
|
||||
var faction = new Faction { Name = "Adrasteia", ShortName = "ADR", PrimaryColor = new float3(1, .5f, .25f) };
|
||||
faction.Allegiance[cache.Upsert(faction)] = 1;
|
||||
cache.Upsert(faction);
|
||||
cache.Upsert(new WeaponItemData { Name = "Lance", Manufacturer = cache.RefOf(faction) });
|
||||
cache.FlushAsync().Wait();
|
||||
}
|
||||
|
||||
public void Dispose() => Directory.Delete(_root, true);
|
||||
|
||||
[Fact]
|
||||
public void OpenThenFlushLeavesEveryFileByteIdentical()
|
||||
{
|
||||
using (var cache = AetheriaStores.Open(Catalog, Run, Player))
|
||||
{
|
||||
var zone = cache.Upsert(new SavedZone { Name = "Entrance", Position = new float2(1, 2), Contents = new ZonePack() });
|
||||
cache.Upsert(new SavedGame { Zones = new[] { zone }, Factions = new[] { cache.RefOf(cache.GetAll<Faction>().Single()) } });
|
||||
cache.Upsert(new PlayerSettings());
|
||||
cache.Upsert(new InputLayout { Rows = new InputLayoutRow[] { new InputLayoutRowSpacer { Height = 1 } } });
|
||||
cache.FlushAsync().Wait();
|
||||
}
|
||||
|
||||
var before = new[] { Catalog, Run, Player }.Select(Hash).ToArray();
|
||||
using (var cache = AetheriaStores.Open(Catalog, Run, Player))
|
||||
{
|
||||
Assert.NotNull(cache.GetGlobal<SavedGame>());
|
||||
Assert.NotNull(cache.GetGlobal<PlayerSettings>());
|
||||
cache.FlushAsync().Wait();
|
||||
}
|
||||
|
||||
Assert.Equal(before, new[] { Catalog, Run, Player }.Select(Hash).ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WeaponWrittenThroughGearHandleReloadsAsWeapon()
|
||||
{
|
||||
CultRecordHandle<GearData> handle;
|
||||
using (var cache = AetheriaStores.Open(Catalog, catalogWritable: true))
|
||||
{
|
||||
handle = cache.UpsertAsync<GearData>(new WeaponItemData { Name = "Pike" }).Result;
|
||||
cache.FlushAsync().Wait();
|
||||
}
|
||||
|
||||
using (var cache = AetheriaStores.Open(Catalog))
|
||||
Assert.Equal("Pike", Assert.IsType<WeaponItemData>(cache.Get(handle.Key)).Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CatalogRefusesWrites()
|
||||
{
|
||||
var before = Hash(Catalog);
|
||||
using (var cache = AetheriaStores.Open(Catalog))
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => cache.Upsert(new Faction { Name = "Usurper" }));
|
||||
cache.FlushAsync().Wait();
|
||||
}
|
||||
|
||||
Assert.Equal(before, Hash(Catalog));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SavedZoneNeverLandsInCatalog()
|
||||
{
|
||||
using (var cache = AetheriaStores.Open(Catalog, catalogWritable: true))
|
||||
Assert.Throws<InvalidOperationException>(() => cache.Upsert(new SavedZone { Name = "Stray" }));
|
||||
|
||||
using (var cache = AetheriaStores.Open(Catalog, Run, catalogWritable: true))
|
||||
{
|
||||
cache.Upsert(new SavedZone { Name = "Home" });
|
||||
cache.FlushAsync().Wait();
|
||||
}
|
||||
|
||||
Assert.DoesNotContain(SchemaNames(Catalog), name => name == "aetheria.savedzone");
|
||||
Assert.Contains("aetheria.savedzone", SchemaNames(Run));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FactionIsASingletonInstance()
|
||||
{
|
||||
using var cache = AetheriaStores.Open(Catalog);
|
||||
var faction = cache.GetAll<Faction>().Single();
|
||||
Assert.Same(faction, cache.Get(cache.GetAll<WeaponItemData>().Single().Manufacturer));
|
||||
Assert.Same(faction, cache.Get(faction.Allegiance.Keys.Single()));
|
||||
Assert.Same(faction, cache.GetByName<Faction>("Adrasteia"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MissingCatalogGlobalIsLoud()
|
||||
{
|
||||
var empty = Path.Combine(_root, "empty.cc");
|
||||
using (AetheriaStores.Open(empty, catalogWritable: true)) { }
|
||||
var error = Assert.Throws<InvalidOperationException>(() => AetheriaStores.Open(empty));
|
||||
Assert.Contains("aetheria.tests.catalogglobal", error.Message);
|
||||
using (AetheriaStores.Open(Catalog)) { }
|
||||
}
|
||||
|
||||
private static string Hash(string path) => Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(path)));
|
||||
|
||||
private static string[] SchemaNames(string path)
|
||||
{
|
||||
var snapshot = CultDocumentMessagePackSerialization.DeserializeSnapshot(File.ReadAllBytes(path));
|
||||
return snapshot.Records
|
||||
.Select(record => snapshot.SchemaCatalog.Single(entry => entry.SchemaId == record.SchemaId).SchemaName)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue