From 9735f64a17d5ce7b93c7e4250ac71f21593fd1e7 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Tue, 28 Jun 2022 14:13:26 -0700 Subject: [PATCH 01/10] Ecsact runtime --- .../Editor/EcsactPackagesPostprocessor.cs | 33 +- .../Editor/EcsactSettings.cs | 25 +- .../Editor/EcsactSettings.uxml | 5 +- .../Editor/Importer/EcsactPackage.cs.meta | 2 +- .../Editor/ecsact-logo-128.png | Bin 0 -> 32184 bytes .../Editor/ecsact-logo-128.png.meta | 122 +++ .../Editor/ecsact-logo-256.png | Bin 0 -> 99354 bytes .../Editor/ecsact-logo-256.png.meta | 122 +++ .../Runtime/EcsactRuntime.cs | 779 ++++++++++++++++++ .../Runtime/EcsactRuntime.cs.meta | 11 + .../Runtime/EcsactRuntimeSettings.cs | 38 + .../Runtime/EcsactRuntimeSettings.cs.meta | 11 + packages/com.seaube.ecsact/package.json | 4 +- 13 files changed, 1136 insertions(+), 16 deletions(-) create mode 100644 packages/com.seaube.ecsact/Editor/ecsact-logo-128.png create mode 100644 packages/com.seaube.ecsact/Editor/ecsact-logo-128.png.meta create mode 100644 packages/com.seaube.ecsact/Editor/ecsact-logo-256.png create mode 100644 packages/com.seaube.ecsact/Editor/ecsact-logo-256.png.meta create mode 100644 packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs create mode 100644 packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs create mode 100644 packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs.meta diff --git a/packages/com.seaube.ecsact/Editor/EcsactPackagesPostprocessor.cs b/packages/com.seaube.ecsact/Editor/EcsactPackagesPostprocessor.cs index 25d6775..6d6a9a6 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactPackagesPostprocessor.cs +++ b/packages/com.seaube.ecsact/Editor/EcsactPackagesPostprocessor.cs @@ -3,6 +3,7 @@ using System.IO; using System.Diagnostics; using System.Collections.Generic; +using System.Linq; public class EcsactPackagesPostprocessor : AssetPostprocessor { @@ -59,6 +60,26 @@ static void OnPostprocessAllAssets } } + static void TryImportAssets + ( List assets + ) + { + EditorApplication.delayCall += () => { + if(!Progress.running) { + try { + AssetDatabase.StartAssetEditing(); + foreach(var asset in assets) { + AssetDatabase.ImportAsset(asset); + } + } finally { + AssetDatabase.StopAssetEditing(); + } + } else { + TryImportAssets(assets); + } + }; + } + static void RefreshEcsactCodegen ( List importedPkgs , List deletedPkgs @@ -97,15 +118,9 @@ static void RefreshEcsactCodegen UnityEngine.Debug.LogError(codegen.StandardError.ReadToEnd()); Progress.Remove(progressId); } else { - Progress.Report(progressId, 0.9f); - - EditorApplication.delayCall += () => { - foreach(var importedPkg in importedPkgs) { - // Import newly created scripts - AssetDatabase.ImportAsset(importedPkg + ".cs"); - } - Progress.Remove(progressId); - }; + Progress.Finish(progressId, Progress.Status.Succeeded); + // Import newly created scripts + TryImportAssets(importedPkgs.Select(pkg => pkg + ".cs").ToList()); } }; diff --git a/packages/com.seaube.ecsact/Editor/EcsactSettings.cs b/packages/com.seaube.ecsact/Editor/EcsactSettings.cs index 19dce05..9209271 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactSettings.cs +++ b/packages/com.seaube.ecsact/Editor/EcsactSettings.cs @@ -5,6 +5,8 @@ using System.IO; using System.Collections.Generic; +#nullable enable + [System.Serializable] class EcsactSettings : ScriptableObject { public const string assetPath = "Assets/Editor/EcsactSettings.asset"; @@ -38,8 +40,10 @@ internal static SerializedObject GetSerializedSettings() { static class EcsactSettingsUIElementsRegister { [SettingsProvider] public static SettingsProvider CreateEcsactSettingsProvider() { + EcsactRuntime? testDefaultRuntime = null; + return new SettingsProvider(EcsactSettings.path, EcsactSettings.scope) { - label = "ECSACT", + label = "Ecsact", activateHandler = (searchContext, rootElement) => { var settings = EcsactSettings.GetSerializedSettings(); var template = AssetDatabase.LoadAssetAtPath( @@ -48,10 +52,25 @@ public static SettingsProvider CreateEcsactSettingsProvider() { var ui = template.Instantiate(); BindingExtensions.Bind(ui, settings); rootElement.Add(ui); + + if(testDefaultRuntime != null) { + EcsactRuntime.Free(testDefaultRuntime); + testDefaultRuntime = null; + } + + var runtimeSettings = EcsactRuntimeSettings.Get(); + testDefaultRuntime = EcsactRuntime.Load( + runtimeSettings.runtimeLibraryPaths + ); + }, + deactivateHandler = () => { + if(testDefaultRuntime != null) { + EcsactRuntime.Free(testDefaultRuntime); + testDefaultRuntime = null; + } }, keywords = new HashSet(new[] { - "ECSACT", - "ECSACT", + "Ecsact", "ECS", "ECS Plugin", "Plugin", diff --git a/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml b/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml index 01bc04b..c8dbf83 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml +++ b/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml @@ -4,5 +4,8 @@ - + + + + diff --git a/packages/com.seaube.ecsact/Editor/Importer/EcsactPackage.cs.meta b/packages/com.seaube.ecsact/Editor/Importer/EcsactPackage.cs.meta index 7608653..21eccc3 100644 --- a/packages/com.seaube.ecsact/Editor/Importer/EcsactPackage.cs.meta +++ b/packages/com.seaube.ecsact/Editor/Importer/EcsactPackage.cs.meta @@ -5,7 +5,7 @@ MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 - icon: {instanceID: 0} + icon: {fileID: 2800000, guid: eedabef44d060c54fb86eb832961a281, type: 3} userData: assetBundleName: assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Editor/ecsact-logo-128.png b/packages/com.seaube.ecsact/Editor/ecsact-logo-128.png new file mode 100644 index 0000000000000000000000000000000000000000..ecd77f779746414bbd24b1190fec97e3fe63a005 GIT binary patch literal 32184 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

^VIbG!j}*J(DSF+nH9GW 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%0A4S}#BQ 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.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 P00000NkvXXu0mjf&JWG< literal 0 HcmV?d00001 diff --git a/packages/com.seaube.ecsact/Editor/ecsact-logo-256.png.meta b/packages/com.seaube.ecsact/Editor/ecsact-logo-256.png.meta new file mode 100644 index 0000000..ac71629 --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/ecsact-logo-256.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 634118fa0a22375438cb8823cebafb9d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 0 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs new file mode 100644 index 0000000..6311ad1 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs @@ -0,0 +1,779 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +#nullable enable + +#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN +internal static class NativeLibrary { + [DllImport("Kernel32.dll", + EntryPoint = "LoadLibrary", + CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.Winapi + )] + private static extern IntPtr LoadLibrary + ( [MarshalAs(UnmanagedType.LPStr)] string lpFileName + ); + + [DllImport("Kernel32.dll", + EntryPoint = "FreeLibrary", + CallingConvention = CallingConvention.Winapi + )] + private static extern bool FreeLibrary + ( IntPtr hLibModule + ); + + [DllImport("Kernel32.dll", + EntryPoint = "GetProcAddress", + CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.Winapi + )] + private static extern IntPtr GetProcAddress + ( IntPtr hModule + , [MarshalAs(UnmanagedType.LPStr)] string procName + ); + + public static IntPtr Load + ( string libraryPath + ) + { + return LoadLibrary(libraryPath); + } + + public static void Free + ( IntPtr handle + ) + { + FreeLibrary(handle); + } + + public static bool TryGetExport + ( IntPtr handle + , string name + , out IntPtr address + ) + { + address = GetProcAddress(handle, name); + return address != IntPtr.Zero; + } +} +#endif + +public class EcsactRuntimeMissingMethod : Exception { + public EcsactRuntimeMissingMethod + ( string methodName + ) + : base(methodName) + { + } + + public EcsactRuntimeMissingMethod + ( string methodName + , Exception inner + ) + : base(methodName, inner) + { + } +} + +public class EcsactRuntime { + public enum EcsactEvent : Int32 { + InitComponent = 0, + UpdateComponent = 1, + RemoveComponent = 2, + } + + public delegate void EachComponentCallback + ( Int32 componentId + , IntPtr componentData + , IntPtr callbackUserData + ); + + public delegate void ComponentEventCallback + ( EcsactEvent ev + , Int32 entityId + , Int32 componentId + , object componentData + , IntPtr callbackUserData + ); + + public struct ExecutionOptions { + + }; + + public struct ExecutionEventsCollector { + ///

+ /// invoked after system executions are finished for every component that + /// is new. The component_data is the last value given for the component, + /// not the first. Invocation happens in the calling thread. `event` will + /// always be EcsactEvent.InitComponent + /// + public ComponentEventCallback initCallback; + + /// + /// callbackUserData passed to initCallback + /// + public IntPtr initCallbackUserData; + + /// + /// invoked after system executions are finished for every changed + /// component. Invocation happens in the calling thread. event will + /// always be EcsactEvent.UpdateComponent + /// + public ComponentEventCallback updateCallback; + + /// + /// callbackUserData passed to updateCallback + /// + public IntPtr updateCallbackUserData; + + /// + /// invoked after system executions are finished for every removed + /// component. Invocation happens in the calling thread. event will + /// always be EcsactEvent.RemoveComponent. + /// + public ComponentEventCallback removeCallback; + + /// + /// callbackUserData passed to removeCallback + /// + public IntPtr removeCallbackUserData; + } + + public static string[] dynamicMethods => new string[]{ + "ecsact_system_execution_context_action", + "ecsact_system_execution_context_add", + "ecsact_system_execution_context_remove", + "ecsact_system_execution_context_get", + "ecsact_system_execution_context_has", + "ecsact_system_execution_context_generate", + "ecsact_system_execution_context_parent", + "ecsact_system_execution_context_same", + "ecsact_create_system", + "ecsact_set_system_execution_impl", + "ecsact_create_action", + "ecsact_resize_action", + "ecsact_create_component", + "ecsact_resize_component", + "ecsact_destroy_component", + "ecsact_create_variant", + "ecsact_destroy_variant", + "ecsact_add_system_capability", + "ecsact_update_system_capability", + "ecsact_remove_system_capability", + "ecsact_add_system_generate_component_set", + "ecsact_register_component", + "ecsact_register_system", + "ecsact_register_action", + "ecsact_system_execution_context_id", + }; + + public static string[] metaMethods => new string[]{ + "ecsact_meta_registry_name", + "ecsact_meta_component_size", + "ecsact_meta_component_name", + "ecsact_meta_action_size", + "ecsact_meta_action_name", + "ecsact_meta_system_name", + "ecsact_meta_system_capabilities_count", + "ecsact_meta_system_capabilities", + }; + + public static string[] serializeMethods => new string[]{ + "ecsact_serialize_action_size", + "ecsact_serialize_component_size", + "ecsact_serialize_action", + "ecsact_serialize_component", + "ecsact_deserialize_action", + "ecsact_deserialize_component", + }; + + public static string[] staticMethods => new string[]{ + "ecsact_static_components", + "ecsact_static_variants", + "ecsact_static_systems", + "ecsact_static_actions", + "ecsact_static_on_reload", + "ecsact_static_off_reload", + }; + + private static EcsactRuntime? defaultInstance; + + public static EcsactRuntime GetOrLoadDefault() { + if(defaultInstance == null) { + var settings = EcsactRuntimeSettings.Get(); + defaultInstance = Load(settings.runtimeLibraryPaths); + } + + return defaultInstance; + } + + public static void SetDefault + ( EcsactRuntime? runtime + ) + { + if(defaultInstance != null) { + Free(defaultInstance); + } + defaultInstance = runtime; + } + + private IntPtr[]? _libs; + private Core? _core; + + public class Core { + internal List _availableMethods = new(); + public static string[] methods => new string[]{ + "ecsact_create_registry", + "ecsact_destroy_registry", + "ecsact_clear_registry", + "ecsact_create_entity", + "ecsact_ensure_entity", + "ecsact_entity_exists", + "ecsact_destroy_entity", + "ecsact_count_entities", + "ecsact_get_entities", + "ecsact_add_component", + "ecsact_has_component", + "ecsact_get_component", + "ecsact_each_component", + "ecsact_count_components", + "ecsact_get_components", + "ecsact_update_component", + "ecsact_remove_component", + "ecsact_execute_systems", + }; + + public IEnumerable availableMethods => _availableMethods; + + // Core module methods + internal delegate Int32 ecsact_create_registry_delegate + ( string registryName + ); + internal ecsact_create_registry_delegate? ecsact_create_registry; + + internal delegate void ecsact_destroy_registry_delegate + ( Int32 registryId + ); + internal ecsact_destroy_registry_delegate? ecsact_destroy_registry; + + internal delegate void ecsact_clear_registry_delegate + ( Int32 registryId + ); + internal ecsact_clear_registry_delegate? ecsact_clear_registry; + + internal delegate Int32 ecsact_create_entity_delegate + ( Int32 registryId + ); + internal ecsact_create_entity_delegate? ecsact_create_entity; + + internal delegate void ecsact_ensure_entity_delegate + ( Int32 registryId + , Int32 entityId + ); + internal ecsact_ensure_entity_delegate? ecsact_ensure_entity; + + internal delegate bool ecsact_entity_exists_delegate + ( Int32 registryId + , Int32 entityId + ); + internal ecsact_entity_exists_delegate? ecsact_entity_exists; + + internal delegate void ecsact_destroy_entity_delegate + ( Int32 registryId + , Int32 entityId + ); + internal ecsact_destroy_entity_delegate? ecsact_destroy_entity; + + internal delegate Int32 ecsact_count_entities_delegate + ( Int32 registryId + ); + internal ecsact_count_entities_delegate? ecsact_count_entities; + + internal delegate void ecsact_get_entities_delegate + ( Int32 registryId + , Int32 maxEntitiesCount + , out Int32[] outEntities + , out Int32 outEntitiesCount + ); + internal ecsact_get_entities_delegate? ecsact_get_entities; + + internal delegate void ecsact_add_component_delegate + ( Int32 registryId + , Int32 entityId + , Int32 componentId + , IntPtr componentData + ); + internal ecsact_add_component_delegate? ecsact_add_component; + + internal delegate bool ecsact_has_component_delegate + ( Int32 registryId + , Int32 entityId + , Int32 componentId + ); + internal ecsact_has_component_delegate? ecsact_has_component; + + internal delegate IntPtr ecsact_get_component_delegate + ( Int32 registryId + , Int32 entityId + , Int32 componentId + ); + internal ecsact_get_component_delegate? ecsact_get_component; + + internal delegate void ecsact_each_component_delegate + ( Int32 registryId + , Int32 entityId + , EachComponentCallback callback + , IntPtr callbackUserData + ); + internal ecsact_each_component_delegate? ecsact_each_component; + + internal delegate Int32 ecsact_count_components_delegate + ( Int32 registryId + , Int32 entityId + ); + internal ecsact_count_components_delegate? ecsact_count_components; + + internal delegate void ecsact_get_components_delegate + ( Int32 registryId + , Int32 entityId + , Int32 maxComponentsCount + , out Int32[] outComponentIds + , out IntPtr[] outComponentsData + , out Int32 outComponentsCount + ); + internal ecsact_get_components_delegate? ecsact_get_components; + + internal delegate void ecsact_update_component_delegate + ( Int32 registryId + , Int32 entityId + , Int32 componentId + , IntPtr componentData + ); + internal ecsact_update_component_delegate? ecsact_update_component; + + internal delegate void ecsact_remove_component_delegate + ( Int32 registryId + , Int32 entityId + , Int32 componentId + ); + internal ecsact_remove_component_delegate? ecsact_remove_component; + + internal delegate void ecsact_component_event_callback + ( EcsactEvent ev + , Int32 entityId + , Int32 componentId + , IntPtr componentData + , IntPtr callbackUserData + ); + internal delegate void ecsact_execute_systems_delegate + ( Int32 registryId + , Int32 executionCount + , ExecutionOptions[] executionOptionsList + , ExecutionEventsCollector eventsCollector + ); + internal ecsact_execute_systems_delegate? ecsact_execute_systems; + + public Int32 CreateRegistry + ( string registryName + ) + { + if(ecsact_create_registry == null) { + throw new EcsactRuntimeMissingMethod("ecsact_create_registry"); + } + + return ecsact_create_registry(registryName); + } + + public void DestroyRegistry + ( Int32 registryId + ) + { + if(ecsact_destroy_registry == null) { + throw new EcsactRuntimeMissingMethod("ecsact_destroy_registry"); + } + + ecsact_destroy_registry(registryId); + } + + public void ClearRegistry + ( Int32 registryId + ) + { + if(ecsact_clear_registry == null) { + throw new EcsactRuntimeMissingMethod("ecsact_clear_registry"); + } + + ecsact_clear_registry(registryId); + } + + public Int32 CreateEntity + ( Int32 registryId + ) + { + if(ecsact_create_entity == null) { + throw new EcsactRuntimeMissingMethod("ecsact_create_entity"); + } + + return ecsact_create_entity(registryId); + } + + public void EnsureEntity + ( Int32 registryId + , Int32 entityId + ) + { + if(ecsact_ensure_entity == null) { + throw new EcsactRuntimeMissingMethod("ecsact_ensure_entity"); + } + + ecsact_ensure_entity(registryId, entityId); + } + + public bool EntityExists + ( Int32 registryId + , Int32 entityId + ) + { + if(ecsact_entity_exists == null) { + throw new EcsactRuntimeMissingMethod("ecsact_entity_exists"); + } + + return ecsact_entity_exists(registryId, entityId); + } + + public void DestroyEntity + ( Int32 registryId + , Int32 entityId + ) + { + if(ecsact_destroy_entity == null) { + throw new EcsactRuntimeMissingMethod("ecsact_destroy_entity"); + } + + ecsact_destroy_entity(registryId, entityId); + } + + public Int32 CountEntities + ( Int32 registryId + ) + { + if(ecsact_count_entities == null) { + throw new EcsactRuntimeMissingMethod("ecsact_count_entities"); + } + + return ecsact_count_entities(registryId); + } + + public void GetEntities + ( Int32 registryId + , Int32 maxEntitiesCount + , out Int32[] outEntities + , out Int32 outEntitiesCount + ) + { + if(ecsact_get_entities == null) { + throw new EcsactRuntimeMissingMethod("ecsact_get_entities"); + } + + ecsact_get_entities( + registryId, + maxEntitiesCount, + out outEntities, + out outEntitiesCount + ); + } + + public Int32[] GetEntities + ( Int32 registryId + ) + { + var entitiesCount = CountEntities(registryId); + var entities = new Int32[entitiesCount]; + + GetEntities( + registryId, + entitiesCount, + out entities, + out entitiesCount + ); + + return entities; + } + + public void AddComponent + ( Int32 registryId + , Int32 entityId + , Int32 componentId + , IntPtr componentData + ) + { + if(ecsact_add_component == null) { + throw new EcsactRuntimeMissingMethod("ecsact_add_component"); + } + + ecsact_add_component( + registryId, + entityId, + componentId, + componentData + ); + } + + public bool HasComponent + ( Int32 registryId + , Int32 entityId + , Int32 componentId + ) + { + if(ecsact_has_component == null) { + throw new EcsactRuntimeMissingMethod("ecsact_has_component"); + } + + return ecsact_has_component(registryId, entityId, componentId); + } + + public IntPtr GetComponent + ( Int32 registryId + , Int32 entityId + , Int32 componentId + ) + { + if(ecsact_get_component == null) { + throw new EcsactRuntimeMissingMethod("ecsact_get_component"); + } + + return ecsact_get_component(registryId, entityId, componentId); + } + + public Int32 CountComponents + ( Int32 registryId + , Int32 entityId + ) + { + if(ecsact_count_components == null) { + throw new EcsactRuntimeMissingMethod("ecsact_count_components"); + } + + return ecsact_count_components(registryId, entityId); + } + + public void GetComponents + ( Int32 registryId + , Int32 entityId + , Int32 maxComponentsCount + , out Int32[] outComponentIds + , out IntPtr[] outComponentsData + , out Int32 outComponentsCount + ) + { + if(ecsact_get_components == null) { + throw new EcsactRuntimeMissingMethod("ecsact_get_components"); + } + + ecsact_get_components( + registryId, + entityId, + maxComponentsCount, + out outComponentIds, + out outComponentsData, + out outComponentsCount + ); + } + + public void EachComponent + ( Int32 registryId + , Int32 entityId + , EachComponentCallback callback + , IntPtr callbackUserData + ) + { + if(ecsact_each_component == null) { + throw new EcsactRuntimeMissingMethod("ecsact_each_component"); + } + + ecsact_each_component( + registryId, + entityId, + callback, + callbackUserData + ); + } + + public void UpdateComponent + ( Int32 registryId + , Int32 entityId + , Int32 componentId + , IntPtr componentData + ) + { + if(ecsact_update_component == null) { + throw new EcsactRuntimeMissingMethod("ecsact_update_component"); + } + + ecsact_update_component( + registryId, + entityId, + componentId, + componentData + ); + } + + public void RemoveComponent + ( Int32 registryId + , Int32 entityId + , Int32 componentId + ) + { + if(ecsact_remove_component == null) { + throw new EcsactRuntimeMissingMethod("ecsact_remove_component"); + } + + ecsact_remove_component(registryId, entityId, componentId); + } + + public void ExecuteSystems + ( Int32 registryId + , Int32 executionCount + , ExecutionOptions[] executionOptionsList + , ExecutionEventsCollector eventsCollector + ) + { + if(ecsact_execute_systems == null) { + throw new EcsactRuntimeMissingMethod("ecsact_execute_systems"); + } + + ecsact_execute_systems( + registryId, + executionCount, + executionOptionsList, + eventsCollector + ); + } + } + + public Core core => _core!; + + private static void LoadDelegate + ( IntPtr lib + , string name + , out D? outDelegate + , List availableMethods + ) where D : Delegate + { + IntPtr addr; + if(NativeLibrary.TryGetExport(lib, name, out addr)) { + outDelegate = Marshal.GetDelegateForFunctionPointer(addr); + availableMethods.Add(name); + UnityEngine.Debug.Log($"Load {name} SUCCESS"); + } else { + outDelegate = null; + UnityEngine.Debug.Log($"Load {name} FAIL"); + } + } + + public static EcsactRuntime Load + ( IEnumerable libraryPaths + ) + { + var runtime = new EcsactRuntime(); + runtime._core = new Core(); + runtime._libs = + libraryPaths.Select(path => NativeLibrary.Load(path)).ToArray(); + + foreach(var lib in runtime._libs) { + // Load core methods + LoadDelegate(lib, "ecsact_create_registry", out runtime._core.ecsact_create_registry, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_destroy_registry", out runtime._core.ecsact_destroy_registry, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_clear_registry", out runtime._core.ecsact_clear_registry, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_create_entity", out runtime._core.ecsact_create_entity, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_ensure_entity", out runtime._core.ecsact_ensure_entity, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_entity_exists", out runtime._core.ecsact_entity_exists, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_destroy_entity", out runtime._core.ecsact_destroy_entity, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_count_entities", out runtime._core.ecsact_count_entities, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_get_entities", out runtime._core.ecsact_get_entities, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_add_component", out runtime._core.ecsact_add_component, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_has_component", out runtime._core.ecsact_has_component, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_get_component", out runtime._core.ecsact_get_component, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_each_component", out runtime._core.ecsact_each_component, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_count_components", out runtime._core.ecsact_count_components, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_get_components", out runtime._core.ecsact_get_components, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_update_component", out runtime._core.ecsact_update_component, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_remove_component", out runtime._core.ecsact_remove_component, runtime._core._availableMethods); + LoadDelegate(lib, "ecsact_execute_systems", out runtime._core.ecsact_execute_systems, runtime._core._availableMethods); + + // Load dynamic methods + // LoadDelegate(lib, "ecsact_system_execution_context_action", out runtime.ecsact_system_execution_context_action); + // LoadDelegate(lib, "ecsact_system_execution_context_add", out runtime.ecsact_system_execution_context_add); + // LoadDelegate(lib, "ecsact_system_execution_context_remove", out runtime.ecsact_system_execution_context_remove); + // LoadDelegate(lib, "ecsact_system_execution_context_get", out runtime.ecsact_system_execution_context_get); + // LoadDelegate(lib, "ecsact_system_execution_context_has", out runtime.ecsact_system_execution_context_has); + // LoadDelegate(lib, "ecsact_system_execution_context_generate", out runtime.ecsact_system_execution_context_generate); + // LoadDelegate(lib, "ecsact_system_execution_context_parent", out runtime.ecsact_system_execution_context_parent); + // LoadDelegate(lib, "ecsact_system_execution_context_same", out runtime.ecsact_system_execution_context_same); + // LoadDelegate(lib, "ecsact_create_system", out runtime.ecsact_create_system); + // LoadDelegate(lib, "ecsact_set_system_execution_impl", out runtime.ecsact_set_system_execution_impl); + // LoadDelegate(lib, "ecsact_create_action", out runtime.ecsact_create_action); + // LoadDelegate(lib, "ecsact_resize_action", out runtime.ecsact_resize_action); + // LoadDelegate(lib, "ecsact_create_component", out runtime.ecsact_create_component); + // LoadDelegate(lib, "ecsact_resize_component", out runtime.ecsact_resize_component); + // LoadDelegate(lib, "ecsact_destroy_component", out runtime.ecsact_destroy_component); + // LoadDelegate(lib, "ecsact_create_variant", out runtime.ecsact_create_variant); + // LoadDelegate(lib, "ecsact_destroy_variant", out runtime.ecsact_destroy_variant); + // LoadDelegate(lib, "ecsact_add_system_capability", out runtime.ecsact_add_system_capability); + // LoadDelegate(lib, "ecsact_update_system_capability", out runtime.ecsact_update_system_capability); + // LoadDelegate(lib, "ecsact_remove_system_capability", out runtime.ecsact_remove_system_capability); + // LoadDelegate(lib, "ecsact_add_system_generate_component_set", out runtime.ecsact_add_system_generate_component_set); + // LoadDelegate(lib, "ecsact_register_component", out runtime.ecsact_register_component); + // LoadDelegate(lib, "ecsact_register_system", out runtime.ecsact_register_system); + // LoadDelegate(lib, "ecsact_register_action", out runtime.ecsact_register_action); + // LoadDelegate(lib, "ecsact_system_execution_context_id", out runtime.ecsact_system_execution_context_id); + + // Load meta methods + // LoadDelegate(lib, "ecsact_meta_registry_name", out runtime.ecsact_meta_registry_name); + // LoadDelegate(lib, "ecsact_meta_component_size", out runtime.ecsact_meta_component_size); + // LoadDelegate(lib, "ecsact_meta_component_name", out runtime.ecsact_meta_component_name); + // LoadDelegate(lib, "ecsact_meta_action_size", out runtime.ecsact_meta_action_size); + // LoadDelegate(lib, "ecsact_meta_action_name", out runtime.ecsact_meta_action_name); + // LoadDelegate(lib, "ecsact_meta_system_name", out runtime.ecsact_meta_system_name); + // LoadDelegate(lib, "ecsact_meta_system_capabilities_count", out runtime.ecsact_meta_system_capabilities_count); + // LoadDelegate(lib, "ecsact_meta_system_capabilities", out runtime.ecsact_meta_system_capabilities); + + // Load serialize methods + // LoadDelegate(lib, "ecsact_serialize_action_size", out runtime.ecsact_serialize_action_size); + // LoadDelegate(lib, "ecsact_serialize_component_size", out runtime.ecsact_serialize_component_size); + // LoadDelegate(lib, "ecsact_serialize_action", out runtime.ecsact_serialize_action); + // LoadDelegate(lib, "ecsact_serialize_component", out runtime.ecsact_serialize_component); + // LoadDelegate(lib, "ecsact_deserialize_action", out runtime.ecsact_deserialize_action); + // LoadDelegate(lib, "ecsact_deserialize_component", out runtime.ecsact_deserialize_component); + + // Load static methods + // LoadDelegate(lib, "ecsact_static_components", out runtime.ecsact_static_components); + // LoadDelegate(lib, "ecsact_static_variants", out runtime.ecsact_static_variants); + // LoadDelegate(lib, "ecsact_static_systems", out runtime.ecsact_static_systems); + // LoadDelegate(lib, "ecsact_static_actions", out runtime.ecsact_static_actions); + // LoadDelegate(lib, "ecsact_static_on_reload", out runtime.ecsact_static_on_reload); + // LoadDelegate(lib, "ecsact_static_off_reload", out runtime.ecsact_static_off_reload); + } + + return runtime; + } + + public static void Free + ( EcsactRuntime runtime + ) + { + if(runtime._libs != null) { + foreach(var lib in runtime._libs) { + NativeLibrary.Free(lib); + } + + runtime._libs = null; + } + } + + ~EcsactRuntime() { + Free(this); + } +} diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs.meta b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs.meta new file mode 100644 index 0000000..c0b7df9 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb87bc4710ab9b74396157c9995c9957 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs new file mode 100644 index 0000000..092f411 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs @@ -0,0 +1,38 @@ +using UnityEngine; +using System.Collections.Generic; +using System.IO; +#if UNITY_EDITOR +using UnityEditor; +#endif + +#nullable enable + +[System.Serializable] +public class EcsactRuntimeSettings : ScriptableObject { + private static EcsactRuntimeSettings? _instance; + + public const string resourcePath = "Settings/EcsactRuntimeSettings.asset"; + public const string assetPath = "Assets/Resources/" + resourcePath; + + public List runtimeLibraryPaths = new(); + + public static EcsactRuntimeSettings Get() { + if(_instance != null) return _instance; + +#if UNITY_EDITOR + _instance = AssetDatabase.LoadAssetAtPath( + assetPath + ); + if(_instance == null) { + _instance = ScriptableObject.CreateInstance(); + Directory.CreateDirectory(Path.GetDirectoryName(assetPath)); + AssetDatabase.CreateAsset(_instance, assetPath); + AssetDatabase.SaveAssetIfDirty(_instance); + } +#else + _instance = Resources.Load(resourcePath) as EcsactRuntimeSettings; +#endif + + return _instance; + } +} diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs.meta b/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs.meta new file mode 100644 index 0000000..eae513e --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c06d71d460aeb804fa9b6550d0ff0218 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/package.json b/packages/com.seaube.ecsact/package.json index 26deab7..06e31d5 100644 --- a/packages/com.seaube.ecsact/package.json +++ b/packages/com.seaube.ecsact/package.json @@ -1,8 +1,8 @@ { "name": "com.seaube.ecsact", "version": "0.2.2", - "description": "ECSACT Integration", - "displayName": "ECSACT Integration", + "description": "Ecsact Integration", + "displayName": "Ecsact Integration", "unity": "2021.2", "author": { "name" : "Seaube" From 85092e1f63405b4e7fe63bbd14e563c5f98844bd Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Tue, 28 Jun 2022 21:08:28 -0700 Subject: [PATCH 02/10] Stubbed in a bunch of methods --- .../Editor/EcsactRuntimeMethodLoadedUI.uxml | 7 + .../EcsactRuntimeMethodLoadedUI.uxml.meta | 10 + .../Editor/EcsactSettings.cs | 99 +++- .../Editor/EcsactSettings.uxml | 57 +- .../Runtime/EcsactRuntime.cs | 489 ++++++++++++++++-- 5 files changed, 604 insertions(+), 58 deletions(-) create mode 100644 packages/com.seaube.ecsact/Editor/EcsactRuntimeMethodLoadedUI.uxml create mode 100644 packages/com.seaube.ecsact/Editor/EcsactRuntimeMethodLoadedUI.uxml.meta diff --git a/packages/com.seaube.ecsact/Editor/EcsactRuntimeMethodLoadedUI.uxml b/packages/com.seaube.ecsact/Editor/EcsactRuntimeMethodLoadedUI.uxml new file mode 100644 index 0000000..689193b --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/EcsactRuntimeMethodLoadedUI.uxml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/com.seaube.ecsact/Editor/EcsactRuntimeMethodLoadedUI.uxml.meta b/packages/com.seaube.ecsact/Editor/EcsactRuntimeMethodLoadedUI.uxml.meta new file mode 100644 index 0000000..757a8f9 --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/EcsactRuntimeMethodLoadedUI.uxml.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a2a638a8b49ec24468a126f89d509035 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/packages/com.seaube.ecsact/Editor/EcsactSettings.cs b/packages/com.seaube.ecsact/Editor/EcsactSettings.cs index 9209271..d5d1cc7 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactSettings.cs +++ b/packages/com.seaube.ecsact/Editor/EcsactSettings.cs @@ -2,7 +2,7 @@ using UnityEditor; using UnityEngine.UIElements; using UnityEditor.UIElements; -using System.IO; +using System.Linq; using System.Collections.Generic; #nullable enable @@ -37,10 +37,51 @@ internal static SerializedObject GetSerializedSettings() { } } +[System.Serializable] +class EcsactMethodUIBindings : ScriptableObject { + public string methodName = ""; + public bool methodLoaded = false; +} + static class EcsactSettingsUIElementsRegister { + + internal static void SetupMethodsUI + ( TemplateContainer ui + , string moduleName + , IEnumerable methods + , IEnumerable availableMethods + ) + { + var coreMethodTemplate = ui.Q( + $"{moduleName}-method-template" + ); + + foreach(var method in methods) { + var clone = coreMethodTemplate.templateSource.CloneTree(); + var bindings = + ScriptableObject.CreateInstance(); + bindings.methodName = method; + bindings.methodLoaded = availableMethods.Contains(method); + BindingExtensions.Bind(clone, new SerializedObject(bindings)); + + if(bindings.methodLoaded) { + var missingIcon = clone.Q("method-missing-icon"); + missingIcon.style.display = DisplayStyle.None; + } else { + var availableIcon = clone.Q("method-available-icon"); + availableIcon.style.display = DisplayStyle.None; + } + + coreMethodTemplate.contentContainer.parent.Add(clone); + } + + coreMethodTemplate.contentContainer.style.display = DisplayStyle.None; + } + [SettingsProvider] public static SettingsProvider CreateEcsactSettingsProvider() { EcsactRuntime? testDefaultRuntime = null; + Editor? runtimeSettingsEditor = null; return new SettingsProvider(EcsactSettings.path, EcsactSettings.scope) { label = "Ecsact", @@ -62,12 +103,68 @@ public static SettingsProvider CreateEcsactSettingsProvider() { testDefaultRuntime = EcsactRuntime.Load( runtimeSettings.runtimeLibraryPaths ); + + SetupMethodsUI( + ui, + "async", + EcsactRuntime.Async.methods, + testDefaultRuntime.async.availableMethods + ); + + SetupMethodsUI( + ui, + "core", + EcsactRuntime.Core.methods, + testDefaultRuntime.core.availableMethods + ); + + SetupMethodsUI( + ui, + "dynamic", + EcsactRuntime.Dynamic.methods, + testDefaultRuntime.dynamic.availableMethods + ); + + SetupMethodsUI( + ui, + "meta", + EcsactRuntime.Meta.methods, + testDefaultRuntime.meta.availableMethods + ); + + SetupMethodsUI( + ui, + "serialize", + EcsactRuntime.Serialize.methods, + testDefaultRuntime.serialize.availableMethods + ); + + SetupMethodsUI( + ui, + "static", + EcsactRuntime.Static.methods, + testDefaultRuntime.@static.availableMethods + ); + + var runtimeSettingsContainer = + ui.Q("runtime-settings-container"); + + runtimeSettingsEditor = Editor.CreateEditor(runtimeSettings); + + runtimeSettingsContainer.onGUIHandler = () => { + runtimeSettingsEditor.OnInspectorGUI(); + }; }, deactivateHandler = () => { if(testDefaultRuntime != null) { EcsactRuntime.Free(testDefaultRuntime); testDefaultRuntime = null; } + + if(runtimeSettingsEditor != null) { + Editor.DestroyImmediate(runtimeSettingsEditor); + runtimeSettingsEditor = null; + } }, keywords = new HashSet(new[] { "Ecsact", diff --git a/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml b/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml index c8dbf83..e718c2f 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml +++ b/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml @@ -1,11 +1,50 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs index 6311ad1..1c42cb5 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs @@ -141,6 +141,36 @@ public struct ExecutionEventsCollector { public IntPtr removeCallbackUserData; } + public enum AsyncError { + ConnectionClosed, + ConnectFail, + SocketFail, + StateFail, + StartFail, + InvalidConnectionString, + } + + public delegate void AsyncErrorCallback + ( AsyncError err + , Int32 requestId + , IntPtr callbackUserData + ); + + public delegate void AsyncActionCommittedCallback + ( Int32 actionId + , IntPtr actionData + , Int32 committedTick + , Int32 requestId + , IntPtr callbackUserData + ); + + public struct AsyncEventsCollector { + public AsyncErrorCallback errorCallback; + public IntPtr errorCallbackUserData; + AsyncActionCommittedCallback actionCommittedCallback; + public IntPtr actionCommittedCallbackUserData; + } + public static string[] dynamicMethods => new string[]{ "ecsact_system_execution_context_action", "ecsact_system_execution_context_add", @@ -221,6 +251,53 @@ public static void SetDefault private IntPtr[]? _libs; private Core? _core; + private Async? _async; + private Dynamic? _dynamic; + private Meta? _meta; + private Serialize? _serialize; + private Static? _static; + + public class Async { + internal List _availableMethods = new(); + public static string[] methods => new string[]{ + "ecsact_async_execute_action", + "ecsact_async_execute_action_at", + "ecsact_async_flush_events", + "ecsact_async_connect", + "ecsact_async_disconnect", + }; + + public IEnumerable availableMethods => _availableMethods; + + internal delegate void ecsact_async_execute_action_delegate + ( Int32 actionId + , IntPtr actionData + ); + internal ecsact_async_execute_action_delegate? ecsact_async_execute_action; + + internal delegate void ecsact_async_execute_action_at_delegate + ( Int32 actionId + , IntPtr actionData + , Int32 tick + ); + internal ecsact_async_execute_action_at_delegate? ecsact_async_execute_action_at; + + internal delegate void ecsact_async_flush_events_delegate + ( ExecutionEventsCollector executionEventsCollector + , AsyncEventsCollector asyncEventsCollector + ); + internal ecsact_async_flush_events_delegate? ecsact_async_flush_events; + + internal delegate void ecsact_async_connect_delegate + ( [MarshalAs(UnmanagedType.LPStr)] string connectionString + ); + internal ecsact_async_connect_delegate? ecsact_async_connect; + + internal delegate void ecsact_async_disconnect_delegate + ( + ); + internal ecsact_async_disconnect_delegate? ecsact_async_disconnect; + } public class Core { internal List _availableMethods = new(); @@ -247,7 +324,6 @@ public class Core { public IEnumerable availableMethods => _availableMethods; - // Core module methods internal delegate Int32 ecsact_create_registry_delegate ( string registryName ); @@ -653,7 +729,314 @@ public void ExecuteSystems } } + public class Dynamic { + internal List _availableMethods = new(); + public static string[] methods => new string[]{ + "ecsact_system_execution_context_action", + "ecsact_system_execution_context_add", + "ecsact_system_execution_context_remove", + "ecsact_system_execution_context_get", + "ecsact_system_execution_context_has", + "ecsact_system_execution_context_generate", + "ecsact_system_execution_context_parent", + "ecsact_system_execution_context_same", + "ecsact_create_system", + "ecsact_set_system_execution_impl", + "ecsact_create_action", + "ecsact_resize_action", + "ecsact_create_component", + "ecsact_resize_component", + "ecsact_destroy_component", + "ecsact_create_variant", + "ecsact_destroy_variant", + "ecsact_add_system_capability", + "ecsact_update_system_capability", + "ecsact_remove_system_capability", + "ecsact_add_system_generate_component_set", + "ecsact_register_component", + "ecsact_register_system", + "ecsact_register_action", + "ecsact_system_execution_context_id", + }; + + public IEnumerable availableMethods => _availableMethods; + + internal delegate void ecsact_system_execution_context_action_delegate + ( + ); + internal ecsact_system_execution_context_action_delegate? ecsact_system_execution_context_action; + + internal delegate void ecsact_system_execution_context_add_delegate + ( + ); + internal ecsact_system_execution_context_add_delegate? ecsact_system_execution_context_add; + + internal delegate void ecsact_system_execution_context_remove_delegate + ( + ); + internal ecsact_system_execution_context_remove_delegate? ecsact_system_execution_context_remove; + + internal delegate void ecsact_system_execution_context_get_delegate + ( + ); + internal ecsact_system_execution_context_get_delegate? ecsact_system_execution_context_get; + + internal delegate void ecsact_system_execution_context_has_delegate + ( + ); + internal ecsact_system_execution_context_has_delegate? ecsact_system_execution_context_has; + + internal delegate void ecsact_system_execution_context_generate_delegate + ( + ); + internal ecsact_system_execution_context_generate_delegate? ecsact_system_execution_context_generate; + + internal delegate void ecsact_system_execution_context_parent_delegate + ( + ); + internal ecsact_system_execution_context_parent_delegate? ecsact_system_execution_context_parent; + + internal delegate void ecsact_system_execution_context_same_delegate + ( + ); + internal ecsact_system_execution_context_same_delegate? ecsact_system_execution_context_same; + + internal delegate void ecsact_create_system_delegate + ( + ); + internal ecsact_create_system_delegate? ecsact_create_system; + + internal delegate void ecsact_set_system_execution_impl_delegate + ( + ); + internal ecsact_set_system_execution_impl_delegate? ecsact_set_system_execution_impl; + + internal delegate void ecsact_create_action_delegate + ( + ); + internal ecsact_create_action_delegate? ecsact_create_action; + + internal delegate void ecsact_resize_action_delegate + ( + ); + internal ecsact_resize_action_delegate? ecsact_resize_action; + + internal delegate void ecsact_create_component_delegate + ( + ); + internal ecsact_create_component_delegate? ecsact_create_component; + + internal delegate void ecsact_resize_component_delegate + ( + ); + internal ecsact_resize_component_delegate? ecsact_resize_component; + + internal delegate void ecsact_destroy_component_delegate + ( + ); + internal ecsact_destroy_component_delegate? ecsact_destroy_component; + + internal delegate void ecsact_create_variant_delegate + ( + ); + internal ecsact_create_variant_delegate? ecsact_create_variant; + + internal delegate void ecsact_destroy_variant_delegate + ( + ); + internal ecsact_destroy_variant_delegate? ecsact_destroy_variant; + + internal delegate void ecsact_add_system_capability_delegate + ( + ); + internal ecsact_add_system_capability_delegate? ecsact_add_system_capability; + + internal delegate void ecsact_update_system_capability_delegate + ( + ); + internal ecsact_update_system_capability_delegate? ecsact_update_system_capability; + + internal delegate void ecsact_remove_system_capability_delegate + ( + ); + internal ecsact_remove_system_capability_delegate? ecsact_remove_system_capability; + + internal delegate void ecsact_add_system_generate_component_set_delegate + ( + ); + internal ecsact_add_system_generate_component_set_delegate? ecsact_add_system_generate_component_set; + + internal delegate void ecsact_register_component_delegate + ( + ); + internal ecsact_register_component_delegate? ecsact_register_component; + + internal delegate void ecsact_register_system_delegate + ( + ); + internal ecsact_register_system_delegate? ecsact_register_system; + + internal delegate void ecsact_register_action_delegate + ( + ); + internal ecsact_register_action_delegate? ecsact_register_action; + + internal delegate void ecsact_system_execution_context_id_delegate + ( + ); + internal ecsact_system_execution_context_id_delegate? ecsact_system_execution_context_id; + } + + public class Meta { + internal List _availableMethods = new(); + public static string[] methods => new string[]{ + "ecsact_meta_registry_name", + "ecsact_meta_component_size", + "ecsact_meta_component_name", + "ecsact_meta_action_size", + "ecsact_meta_action_name", + "ecsact_meta_system_name", + "ecsact_meta_system_capabilities_count", + "ecsact_meta_system_capabilities", + }; + + public IEnumerable availableMethods => _availableMethods; + + internal delegate void ecsact_meta_registry_name_delegate + ( + ); + internal ecsact_meta_registry_name_delegate? ecsact_meta_registry_name; + + internal delegate void ecsact_meta_component_size_delegate + ( + ); + internal ecsact_meta_component_size_delegate? ecsact_meta_component_size; + + internal delegate void ecsact_meta_component_name_delegate + ( + ); + internal ecsact_meta_component_name_delegate? ecsact_meta_component_name; + + internal delegate void ecsact_meta_action_size_delegate + ( + ); + internal ecsact_meta_action_size_delegate? ecsact_meta_action_size; + + internal delegate void ecsact_meta_action_name_delegate + ( + ); + internal ecsact_meta_action_name_delegate? ecsact_meta_action_name; + + internal delegate void ecsact_meta_system_name_delegate + ( + ); + internal ecsact_meta_system_name_delegate? ecsact_meta_system_name; + + internal delegate void ecsact_meta_system_capabilities_count_delegate + ( + ); + internal ecsact_meta_system_capabilities_count_delegate? ecsact_meta_system_capabilities_count; + + internal delegate void ecsact_meta_system_capabilities_delegate + ( + ); + internal ecsact_meta_system_capabilities_delegate? ecsact_meta_system_capabilities; + } + + public class Serialize { + internal List _availableMethods = new(); + public static string[] methods => new string[]{ + "ecsact_serialize_action_size", + "ecsact_serialize_component_size", + "ecsact_serialize_action", + "ecsact_serialize_component", + "ecsact_deserialize_action", + "ecsact_deserialize_component", + }; + + public IEnumerable availableMethods => _availableMethods; + + internal delegate void ecsact_serialize_action_size_delegate + ( + ); + internal ecsact_serialize_action_size_delegate? ecsact_serialize_action_size; + + internal delegate void ecsact_serialize_component_size_delegate + ( + ); + internal ecsact_serialize_component_size_delegate? ecsact_serialize_component_size; + + internal delegate void ecsact_serialize_action_delegate + ( + ); + internal ecsact_serialize_action_delegate? ecsact_serialize_action; + + internal delegate void ecsact_serialize_component_delegate + ( + ); + internal ecsact_serialize_component_delegate? ecsact_serialize_component; + + internal delegate void ecsact_deserialize_action_delegate + ( + ); + internal ecsact_deserialize_action_delegate? ecsact_deserialize_action; + + internal delegate void ecsact_deserialize_component_delegate + ( + ); + internal ecsact_deserialize_component_delegate? ecsact_deserialize_component; + } + + public class Static { + internal List _availableMethods = new(); + public static string[] methods => new string[]{ + "ecsact_static_components", + "ecsact_static_variants", + "ecsact_static_systems", + "ecsact_static_actions", + "ecsact_static_on_reload", + "ecsact_static_off_reload", + }; + + public IEnumerable availableMethods => _availableMethods; + + internal delegate void ecsact_static_components_delegate + ( + ); + internal ecsact_static_components_delegate? ecsact_static_components; + + internal delegate void ecsact_static_variants_delegate + ( + ); + internal ecsact_static_variants_delegate? ecsact_static_variants; + + internal delegate void ecsact_static_systems_delegate + ( + ); + internal ecsact_static_systems_delegate? ecsact_static_systems; + + internal delegate void ecsact_static_actions_delegate + ( + ); + internal ecsact_static_actions_delegate? ecsact_static_actions; + + internal delegate void ecsact_static_on_reload_delegate + ( + ); + internal ecsact_static_on_reload_delegate? ecsact_static_on_reload; + + internal delegate void ecsact_static_off_reload_delegate + ( + ); + internal ecsact_static_off_reload_delegate? ecsact_static_off_reload; + } + public Core core => _core!; + public Async async => _async!; + public Dynamic dynamic => _dynamic!; + public Meta meta => _meta!; + public Serialize serialize => _serialize!; + public Static @static => _static!; private static void LoadDelegate ( IntPtr lib @@ -666,10 +1049,8 @@ private static void LoadDelegate if(NativeLibrary.TryGetExport(lib, name, out addr)) { outDelegate = Marshal.GetDelegateForFunctionPointer(addr); availableMethods.Add(name); - UnityEngine.Debug.Log($"Load {name} SUCCESS"); } else { outDelegate = null; - UnityEngine.Debug.Log($"Load {name} FAIL"); } } @@ -679,10 +1060,22 @@ public static EcsactRuntime Load { var runtime = new EcsactRuntime(); runtime._core = new Core(); + runtime._async = new Async(); + runtime._dynamic = new Dynamic(); + runtime._meta = new Meta(); + runtime._serialize = new Serialize(); + runtime._static = new Static(); runtime._libs = libraryPaths.Select(path => NativeLibrary.Load(path)).ToArray(); foreach(var lib in runtime._libs) { + // Load async methods + LoadDelegate(lib, "ecsact_async_execute_action", out runtime._async.ecsact_async_execute_action, runtime._async._availableMethods); + LoadDelegate(lib, "ecsact_async_execute_action_at", out runtime._async.ecsact_async_execute_action_at, runtime._async._availableMethods); + LoadDelegate(lib, "ecsact_async_flush_events", out runtime._async.ecsact_async_flush_events, runtime._async._availableMethods); + LoadDelegate(lib, "ecsact_async_connect", out runtime._async.ecsact_async_connect, runtime._async._availableMethods); + LoadDelegate(lib, "ecsact_async_disconnect", out runtime._async.ecsact_async_disconnect, runtime._async._availableMethods); + // Load core methods LoadDelegate(lib, "ecsact_create_registry", out runtime._core.ecsact_create_registry, runtime._core._availableMethods); LoadDelegate(lib, "ecsact_destroy_registry", out runtime._core.ecsact_destroy_registry, runtime._core._availableMethods); @@ -704,57 +1097,57 @@ public static EcsactRuntime Load LoadDelegate(lib, "ecsact_execute_systems", out runtime._core.ecsact_execute_systems, runtime._core._availableMethods); // Load dynamic methods - // LoadDelegate(lib, "ecsact_system_execution_context_action", out runtime.ecsact_system_execution_context_action); - // LoadDelegate(lib, "ecsact_system_execution_context_add", out runtime.ecsact_system_execution_context_add); - // LoadDelegate(lib, "ecsact_system_execution_context_remove", out runtime.ecsact_system_execution_context_remove); - // LoadDelegate(lib, "ecsact_system_execution_context_get", out runtime.ecsact_system_execution_context_get); - // LoadDelegate(lib, "ecsact_system_execution_context_has", out runtime.ecsact_system_execution_context_has); - // LoadDelegate(lib, "ecsact_system_execution_context_generate", out runtime.ecsact_system_execution_context_generate); - // LoadDelegate(lib, "ecsact_system_execution_context_parent", out runtime.ecsact_system_execution_context_parent); - // LoadDelegate(lib, "ecsact_system_execution_context_same", out runtime.ecsact_system_execution_context_same); - // LoadDelegate(lib, "ecsact_create_system", out runtime.ecsact_create_system); - // LoadDelegate(lib, "ecsact_set_system_execution_impl", out runtime.ecsact_set_system_execution_impl); - // LoadDelegate(lib, "ecsact_create_action", out runtime.ecsact_create_action); - // LoadDelegate(lib, "ecsact_resize_action", out runtime.ecsact_resize_action); - // LoadDelegate(lib, "ecsact_create_component", out runtime.ecsact_create_component); - // LoadDelegate(lib, "ecsact_resize_component", out runtime.ecsact_resize_component); - // LoadDelegate(lib, "ecsact_destroy_component", out runtime.ecsact_destroy_component); - // LoadDelegate(lib, "ecsact_create_variant", out runtime.ecsact_create_variant); - // LoadDelegate(lib, "ecsact_destroy_variant", out runtime.ecsact_destroy_variant); - // LoadDelegate(lib, "ecsact_add_system_capability", out runtime.ecsact_add_system_capability); - // LoadDelegate(lib, "ecsact_update_system_capability", out runtime.ecsact_update_system_capability); - // LoadDelegate(lib, "ecsact_remove_system_capability", out runtime.ecsact_remove_system_capability); - // LoadDelegate(lib, "ecsact_add_system_generate_component_set", out runtime.ecsact_add_system_generate_component_set); - // LoadDelegate(lib, "ecsact_register_component", out runtime.ecsact_register_component); - // LoadDelegate(lib, "ecsact_register_system", out runtime.ecsact_register_system); - // LoadDelegate(lib, "ecsact_register_action", out runtime.ecsact_register_action); - // LoadDelegate(lib, "ecsact_system_execution_context_id", out runtime.ecsact_system_execution_context_id); + LoadDelegate(lib, "ecsact_system_execution_context_action", out runtime._dynamic.ecsact_system_execution_context_action, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_add", out runtime._dynamic.ecsact_system_execution_context_add, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_remove", out runtime._dynamic.ecsact_system_execution_context_remove, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_get", out runtime._dynamic.ecsact_system_execution_context_get, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_has", out runtime._dynamic.ecsact_system_execution_context_has, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_generate", out runtime._dynamic.ecsact_system_execution_context_generate, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_parent", out runtime._dynamic.ecsact_system_execution_context_parent, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_same", out runtime._dynamic.ecsact_system_execution_context_same, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_create_system", out runtime._dynamic.ecsact_create_system, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_set_system_execution_impl", out runtime._dynamic.ecsact_set_system_execution_impl, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_create_action", out runtime._dynamic.ecsact_create_action, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_resize_action", out runtime._dynamic.ecsact_resize_action, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_create_component", out runtime._dynamic.ecsact_create_component, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_resize_component", out runtime._dynamic.ecsact_resize_component, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_destroy_component", out runtime._dynamic.ecsact_destroy_component, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_create_variant", out runtime._dynamic.ecsact_create_variant, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_destroy_variant", out runtime._dynamic.ecsact_destroy_variant, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_add_system_capability", out runtime._dynamic.ecsact_add_system_capability, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_update_system_capability", out runtime._dynamic.ecsact_update_system_capability, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_remove_system_capability", out runtime._dynamic.ecsact_remove_system_capability, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_add_system_generate_component_set", out runtime._dynamic.ecsact_add_system_generate_component_set, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_register_component", out runtime._dynamic.ecsact_register_component, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_register_system", out runtime._dynamic.ecsact_register_system, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_register_action", out runtime._dynamic.ecsact_register_action, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_id", out runtime._dynamic.ecsact_system_execution_context_id, runtime._dynamic._availableMethods); // Load meta methods - // LoadDelegate(lib, "ecsact_meta_registry_name", out runtime.ecsact_meta_registry_name); - // LoadDelegate(lib, "ecsact_meta_component_size", out runtime.ecsact_meta_component_size); - // LoadDelegate(lib, "ecsact_meta_component_name", out runtime.ecsact_meta_component_name); - // LoadDelegate(lib, "ecsact_meta_action_size", out runtime.ecsact_meta_action_size); - // LoadDelegate(lib, "ecsact_meta_action_name", out runtime.ecsact_meta_action_name); - // LoadDelegate(lib, "ecsact_meta_system_name", out runtime.ecsact_meta_system_name); - // LoadDelegate(lib, "ecsact_meta_system_capabilities_count", out runtime.ecsact_meta_system_capabilities_count); - // LoadDelegate(lib, "ecsact_meta_system_capabilities", out runtime.ecsact_meta_system_capabilities); + LoadDelegate(lib, "ecsact_meta_registry_name", out runtime._meta.ecsact_meta_registry_name, runtime._meta._availableMethods); + LoadDelegate(lib, "ecsact_meta_component_size", out runtime._meta.ecsact_meta_component_size, runtime._meta._availableMethods); + LoadDelegate(lib, "ecsact_meta_component_name", out runtime._meta.ecsact_meta_component_name, runtime._meta._availableMethods); + LoadDelegate(lib, "ecsact_meta_action_size", out runtime._meta.ecsact_meta_action_size, runtime._meta._availableMethods); + LoadDelegate(lib, "ecsact_meta_action_name", out runtime._meta.ecsact_meta_action_name, runtime._meta._availableMethods); + LoadDelegate(lib, "ecsact_meta_system_name", out runtime._meta.ecsact_meta_system_name, runtime._meta._availableMethods); + LoadDelegate(lib, "ecsact_meta_system_capabilities_count", out runtime._meta.ecsact_meta_system_capabilities_count, runtime._meta._availableMethods); + LoadDelegate(lib, "ecsact_meta_system_capabilities", out runtime._meta.ecsact_meta_system_capabilities, runtime._meta._availableMethods); // Load serialize methods - // LoadDelegate(lib, "ecsact_serialize_action_size", out runtime.ecsact_serialize_action_size); - // LoadDelegate(lib, "ecsact_serialize_component_size", out runtime.ecsact_serialize_component_size); - // LoadDelegate(lib, "ecsact_serialize_action", out runtime.ecsact_serialize_action); - // LoadDelegate(lib, "ecsact_serialize_component", out runtime.ecsact_serialize_component); - // LoadDelegate(lib, "ecsact_deserialize_action", out runtime.ecsact_deserialize_action); - // LoadDelegate(lib, "ecsact_deserialize_component", out runtime.ecsact_deserialize_component); + LoadDelegate(lib, "ecsact_serialize_action_size", out runtime._serialize.ecsact_serialize_action_size, runtime._serialize._availableMethods); + LoadDelegate(lib, "ecsact_serialize_component_size", out runtime._serialize.ecsact_serialize_component_size, runtime._serialize._availableMethods); + LoadDelegate(lib, "ecsact_serialize_action", out runtime._serialize.ecsact_serialize_action, runtime._serialize._availableMethods); + LoadDelegate(lib, "ecsact_serialize_component", out runtime._serialize.ecsact_serialize_component, runtime._serialize._availableMethods); + LoadDelegate(lib, "ecsact_deserialize_action", out runtime._serialize.ecsact_deserialize_action, runtime._serialize._availableMethods); + LoadDelegate(lib, "ecsact_deserialize_component", out runtime._serialize.ecsact_deserialize_component, runtime._serialize._availableMethods); // Load static methods - // LoadDelegate(lib, "ecsact_static_components", out runtime.ecsact_static_components); - // LoadDelegate(lib, "ecsact_static_variants", out runtime.ecsact_static_variants); - // LoadDelegate(lib, "ecsact_static_systems", out runtime.ecsact_static_systems); - // LoadDelegate(lib, "ecsact_static_actions", out runtime.ecsact_static_actions); - // LoadDelegate(lib, "ecsact_static_on_reload", out runtime.ecsact_static_on_reload); - // LoadDelegate(lib, "ecsact_static_off_reload", out runtime.ecsact_static_off_reload); + LoadDelegate(lib, "ecsact_static_components", out runtime._static.ecsact_static_components, runtime._static._availableMethods); + LoadDelegate(lib, "ecsact_static_variants", out runtime._static.ecsact_static_variants, runtime._static._availableMethods); + LoadDelegate(lib, "ecsact_static_systems", out runtime._static.ecsact_static_systems, runtime._static._availableMethods); + LoadDelegate(lib, "ecsact_static_actions", out runtime._static.ecsact_static_actions, runtime._static._availableMethods); + LoadDelegate(lib, "ecsact_static_on_reload", out runtime._static.ecsact_static_on_reload, runtime._static._availableMethods); + LoadDelegate(lib, "ecsact_static_off_reload", out runtime._static.ecsact_static_off_reload, runtime._static._availableMethods); } return runtime; From 6f9b5935297737a5720ef823bd4a333e835a4727 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Tue, 28 Jun 2022 21:49:55 -0700 Subject: [PATCH 03/10] All delegates filled in for better or worse --- .../Runtime/EcsactRuntime.cs | 325 +++++++++++------- 1 file changed, 193 insertions(+), 132 deletions(-) diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs index 1c42cb5..60fe308 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs @@ -141,7 +141,46 @@ public struct ExecutionEventsCollector { public IntPtr removeCallbackUserData; } - public enum AsyncError { + public struct StaticComponentInfo { + public Int32 componentId; + [MarshalAs(UnmanagedType.LPStr)] public string componentName; + public Int32 componentSize; + public ComponentCompareFn componentCompareFn; + [MarshalAs(UnmanagedType.I1)] public bool transient; + } + + public struct StaticSystemInfo { + public Int32 systemId; + public Int32 order; + [MarshalAs(UnmanagedType.LPStr)] public string systemName; + public Int32 parentSystemId; + public Int32 childSystemsCount; + public Int32[] childSystemIds; + public Int32 capabilitiesCount; + public Int32[] capabilityComponents; + public SystemCapability[] capabilities; + public SystemExecutionImpl executionImpl; + } + + public struct StaticActionInfo { + public Int32 actionId; + public Int32 order; + [MarshalAs(UnmanagedType.LPStr)] public string actionName; + public Int32 actionSize; + public ActionCompareFn actionCompareFn; + public Int32 childSystemsCount; + public Int32[] childSystemIds; + public Int32 capabilitiesCount; + public Int32[] capabilityComponents; + public SystemCapability[] capabilities; + public SystemExecutionImpl executionImpl; + } + + public delegate void StaticReloadCallback + ( IntPtr userData + ); + + public enum AsyncError : Int32 { ConnectionClosed, ConnectFail, SocketFail, @@ -150,6 +189,38 @@ public enum AsyncError { InvalidConnectionString, } + public enum SystemCapability : Int32 { + Readonly = 1, + Writeonly = 2, + ReadWrite = 3, + OptionalReadonly = 4 | Readonly, + OptionalWriteonly = 4 | Writeonly, + OptionalReadWrite = 4 | ReadWrite, + Include = 8, + Exclude = 16, + Adds = 32 | Exclude, + Removes = 64 | Include, + } + + public enum SystemGenerate : Int32 { + Required = 1, + Optional = 2, + } + + public delegate void SystemExecutionImpl + ( IntPtr context + ); + + public delegate Int32 ActionCompareFn + ( IntPtr firstAction + , IntPtr secondAction + ); + + public delegate Int32 ComponentCompareFn + ( IntPtr firstComponent + , IntPtr secondComponent + ); + public delegate void AsyncErrorCallback ( AsyncError err , Int32 requestId @@ -171,63 +242,6 @@ public struct AsyncEventsCollector { public IntPtr actionCommittedCallbackUserData; } - public static string[] dynamicMethods => new string[]{ - "ecsact_system_execution_context_action", - "ecsact_system_execution_context_add", - "ecsact_system_execution_context_remove", - "ecsact_system_execution_context_get", - "ecsact_system_execution_context_has", - "ecsact_system_execution_context_generate", - "ecsact_system_execution_context_parent", - "ecsact_system_execution_context_same", - "ecsact_create_system", - "ecsact_set_system_execution_impl", - "ecsact_create_action", - "ecsact_resize_action", - "ecsact_create_component", - "ecsact_resize_component", - "ecsact_destroy_component", - "ecsact_create_variant", - "ecsact_destroy_variant", - "ecsact_add_system_capability", - "ecsact_update_system_capability", - "ecsact_remove_system_capability", - "ecsact_add_system_generate_component_set", - "ecsact_register_component", - "ecsact_register_system", - "ecsact_register_action", - "ecsact_system_execution_context_id", - }; - - public static string[] metaMethods => new string[]{ - "ecsact_meta_registry_name", - "ecsact_meta_component_size", - "ecsact_meta_component_name", - "ecsact_meta_action_size", - "ecsact_meta_action_name", - "ecsact_meta_system_name", - "ecsact_meta_system_capabilities_count", - "ecsact_meta_system_capabilities", - }; - - public static string[] serializeMethods => new string[]{ - "ecsact_serialize_action_size", - "ecsact_serialize_component_size", - "ecsact_serialize_action", - "ecsact_serialize_component", - "ecsact_deserialize_action", - "ecsact_deserialize_component", - }; - - public static string[] staticMethods => new string[]{ - "ecsact_static_components", - "ecsact_static_variants", - "ecsact_static_systems", - "ecsact_static_actions", - "ecsact_static_on_reload", - "ecsact_static_off_reload", - }; - private static EcsactRuntime? defaultInstance; public static EcsactRuntime GetOrLoadDefault() { @@ -762,127 +776,164 @@ public class Dynamic { public IEnumerable availableMethods => _availableMethods; internal delegate void ecsact_system_execution_context_action_delegate - ( + ( IntPtr context + , IntPtr outActionData ); internal ecsact_system_execution_context_action_delegate? ecsact_system_execution_context_action; internal delegate void ecsact_system_execution_context_add_delegate - ( + ( IntPtr context + , Int32 componentId + , IntPtr componentData ); internal ecsact_system_execution_context_add_delegate? ecsact_system_execution_context_add; internal delegate void ecsact_system_execution_context_remove_delegate - ( + ( IntPtr context + , Int32 componentId ); internal ecsact_system_execution_context_remove_delegate? ecsact_system_execution_context_remove; internal delegate void ecsact_system_execution_context_get_delegate - ( + ( IntPtr context + , Int32 componentId + , IntPtr outComponentData ); internal ecsact_system_execution_context_get_delegate? ecsact_system_execution_context_get; - internal delegate void ecsact_system_execution_context_has_delegate - ( + internal delegate void ecsact_system_execution_context_update_delegate + ( IntPtr context + , Int32 componentId + , IntPtr componentData + ); + internal ecsact_system_execution_context_update_delegate? ecsact_system_execution_context_update; + + internal delegate bool ecsact_system_execution_context_has_delegate + ( IntPtr context + , Int32 componentId ); internal ecsact_system_execution_context_has_delegate? ecsact_system_execution_context_has; internal delegate void ecsact_system_execution_context_generate_delegate - ( + ( IntPtr context + , Int32 componentCount + , Int32[] componentIds + , IntPtr[] componentsData ); internal ecsact_system_execution_context_generate_delegate? ecsact_system_execution_context_generate; - internal delegate void ecsact_system_execution_context_parent_delegate - ( + internal delegate IntPtr ecsact_system_execution_context_parent_delegate + ( IntPtr context ); internal ecsact_system_execution_context_parent_delegate? ecsact_system_execution_context_parent; - internal delegate void ecsact_system_execution_context_same_delegate - ( + internal delegate bool ecsact_system_execution_context_same_delegate + ( IntPtr firstContext + , IntPtr secondContext ); internal ecsact_system_execution_context_same_delegate? ecsact_system_execution_context_same; internal delegate void ecsact_create_system_delegate - ( + ( [MarshalAs(UnmanagedType.LPStr)] string systemName + , Int32 parentSystemId + , Int32[] capabilityComponentIds + , SystemCapability[] capabilities + , Int32 capabilitiesCount + , SystemExecutionImpl executionImpl ); internal ecsact_create_system_delegate? ecsact_create_system; internal delegate void ecsact_set_system_execution_impl_delegate - ( + ( Int32 systemId + , SystemExecutionImpl executionImpl ); internal ecsact_set_system_execution_impl_delegate? ecsact_set_system_execution_impl; internal delegate void ecsact_create_action_delegate - ( + ( [MarshalAs(UnmanagedType.LPStr)] string actionName + , Int32 actionSize + , ActionCompareFn actionCompareFn + , Int32[] capabilityComponentIds + , SystemCapability[] capabilities + , Int32 capabilitiesCount + , SystemExecutionImpl executionImpl ); internal ecsact_create_action_delegate? ecsact_create_action; internal delegate void ecsact_resize_action_delegate - ( + ( Int32 actionId + , Int32 newActionSize + , ActionCompareFn newActionCompareFn ); internal ecsact_resize_action_delegate? ecsact_resize_action; internal delegate void ecsact_create_component_delegate - ( + ( [MarshalAs(UnmanagedType.LPStr)] string componentName + , Int32 componentSize + , ComponentCompareFn componentCompareFn ); internal ecsact_create_component_delegate? ecsact_create_component; internal delegate void ecsact_resize_component_delegate - ( + ( Int32 componentId + , Int32 newComponentSize + , ComponentCompareFn newComponentCompareFn ); internal ecsact_resize_component_delegate? ecsact_resize_component; internal delegate void ecsact_destroy_component_delegate - ( + ( Int32 componentId ); internal ecsact_destroy_component_delegate? ecsact_destroy_component; - internal delegate void ecsact_create_variant_delegate - ( - ); - internal ecsact_create_variant_delegate? ecsact_create_variant; - - internal delegate void ecsact_destroy_variant_delegate - ( - ); - internal ecsact_destroy_variant_delegate? ecsact_destroy_variant; - internal delegate void ecsact_add_system_capability_delegate - ( + ( Int32 systemId + , Int32 componentId + , SystemCapability systemCapability ); internal ecsact_add_system_capability_delegate? ecsact_add_system_capability; internal delegate void ecsact_update_system_capability_delegate - ( + ( Int32 systemId + , Int32 componentId + , SystemCapability systemCapability ); internal ecsact_update_system_capability_delegate? ecsact_update_system_capability; internal delegate void ecsact_remove_system_capability_delegate - ( + ( Int32 systemId + , Int32 componentId ); internal ecsact_remove_system_capability_delegate? ecsact_remove_system_capability; internal delegate void ecsact_add_system_generate_component_set_delegate - ( + ( Int32 systemId + , Int32 componentsCount + , Int32[] componentIds + , SystemGenerate[] componentGenerateFlags ); internal ecsact_add_system_generate_component_set_delegate? ecsact_add_system_generate_component_set; internal delegate void ecsact_register_component_delegate - ( + ( Int32 registryId + , Int32 componentId ); internal ecsact_register_component_delegate? ecsact_register_component; internal delegate void ecsact_register_system_delegate - ( + ( Int32 registryId + , Int32 systemId ); internal ecsact_register_system_delegate? ecsact_register_system; internal delegate void ecsact_register_action_delegate - ( + ( Int32 registryId + , Int32 actionId ); internal ecsact_register_action_delegate? ecsact_register_action; - internal delegate void ecsact_system_execution_context_id_delegate - ( + internal delegate Int32 ecsact_system_execution_context_id_delegate + ( IntPtr context ); internal ecsact_system_execution_context_id_delegate? ecsact_system_execution_context_id; } @@ -902,43 +953,49 @@ public class Meta { public IEnumerable availableMethods => _availableMethods; - internal delegate void ecsact_meta_registry_name_delegate - ( + [return: MarshalAs(UnmanagedType.LPStr)] + internal delegate string ecsact_meta_registry_name_delegate + ( Int32 registryId ); internal ecsact_meta_registry_name_delegate? ecsact_meta_registry_name; - internal delegate void ecsact_meta_component_size_delegate - ( + internal delegate Int32 ecsact_meta_component_size_delegate + ( Int32 componentId ); internal ecsact_meta_component_size_delegate? ecsact_meta_component_size; - internal delegate void ecsact_meta_component_name_delegate - ( + [return: MarshalAs(UnmanagedType.LPStr)] + internal delegate string ecsact_meta_component_name_delegate + ( Int32 componentId ); internal ecsact_meta_component_name_delegate? ecsact_meta_component_name; - internal delegate void ecsact_meta_action_size_delegate - ( + internal delegate Int32 ecsact_meta_action_size_delegate + ( Int32 actionId ); internal ecsact_meta_action_size_delegate? ecsact_meta_action_size; - internal delegate void ecsact_meta_action_name_delegate - ( + [return: MarshalAs(UnmanagedType.LPStr)] + internal delegate string ecsact_meta_action_name_delegate + ( Int32 actionId ); internal ecsact_meta_action_name_delegate? ecsact_meta_action_name; - internal delegate void ecsact_meta_system_name_delegate - ( + [return: MarshalAs(UnmanagedType.LPStr)] + internal delegate string ecsact_meta_system_name_delegate + ( Int32 systemId ); internal ecsact_meta_system_name_delegate? ecsact_meta_system_name; - internal delegate void ecsact_meta_system_capabilities_count_delegate - ( + internal delegate Int32 ecsact_meta_system_capabilities_count_delegate + ( Int32 systemId ); internal ecsact_meta_system_capabilities_count_delegate? ecsact_meta_system_capabilities_count; internal delegate void ecsact_meta_system_capabilities_delegate - ( + ( Int32 systemId + , out Int32[] outCapabilityComponentIds + , out SystemCapability[] outCapabilities ); internal ecsact_meta_system_capabilities_delegate? ecsact_meta_system_capabilities; } @@ -956,33 +1013,41 @@ public class Serialize { public IEnumerable availableMethods => _availableMethods; - internal delegate void ecsact_serialize_action_size_delegate - ( + internal delegate Int32 ecsact_serialize_action_size_delegate + ( Int32 actionId ); internal ecsact_serialize_action_size_delegate? ecsact_serialize_action_size; - internal delegate void ecsact_serialize_component_size_delegate - ( + internal delegate Int32 ecsact_serialize_component_size_delegate + ( Int32 componentId ); internal ecsact_serialize_component_size_delegate? ecsact_serialize_component_size; internal delegate void ecsact_serialize_action_delegate - ( + ( Int32 actionId + , IntPtr actionData + , IntPtr outBytes ); internal ecsact_serialize_action_delegate? ecsact_serialize_action; internal delegate void ecsact_serialize_component_delegate - ( + ( Int32 componentId + , IntPtr inComponentData + , IntPtr outBytes ); internal ecsact_serialize_component_delegate? ecsact_serialize_component; internal delegate void ecsact_deserialize_action_delegate - ( + ( Int32 actionId + , IntPtr inBytes + , IntPtr outActionData ); internal ecsact_deserialize_action_delegate? ecsact_deserialize_action; internal delegate void ecsact_deserialize_component_delegate - ( + ( Int32 componentId + , IntPtr inBytes + , IntPtr outComponentData ); internal ecsact_deserialize_component_delegate? ecsact_deserialize_component; } @@ -991,7 +1056,6 @@ public class Static { internal List _availableMethods = new(); public static string[] methods => new string[]{ "ecsact_static_components", - "ecsact_static_variants", "ecsact_static_systems", "ecsact_static_actions", "ecsact_static_on_reload", @@ -1001,32 +1065,31 @@ public class Static { public IEnumerable availableMethods => _availableMethods; internal delegate void ecsact_static_components_delegate - ( + ( out StaticComponentInfo[] outComponents + , out Int32 outComponentsCount ); internal ecsact_static_components_delegate? ecsact_static_components; - internal delegate void ecsact_static_variants_delegate - ( - ); - internal ecsact_static_variants_delegate? ecsact_static_variants; - internal delegate void ecsact_static_systems_delegate - ( + ( out StaticSystemInfo[] outSystems + , out Int32 outSystemsCount ); internal ecsact_static_systems_delegate? ecsact_static_systems; internal delegate void ecsact_static_actions_delegate - ( + ( out StaticActionInfo[] outActions + , out Int32 outActionsCount ); internal ecsact_static_actions_delegate? ecsact_static_actions; internal delegate void ecsact_static_on_reload_delegate - ( + ( StaticReloadCallback callback + , IntPtr callbackUserData ); internal ecsact_static_on_reload_delegate? ecsact_static_on_reload; internal delegate void ecsact_static_off_reload_delegate - ( + ( StaticReloadCallback callback ); internal ecsact_static_off_reload_delegate? ecsact_static_off_reload; } @@ -1100,6 +1163,7 @@ public static EcsactRuntime Load LoadDelegate(lib, "ecsact_system_execution_context_action", out runtime._dynamic.ecsact_system_execution_context_action, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_system_execution_context_add", out runtime._dynamic.ecsact_system_execution_context_add, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_system_execution_context_remove", out runtime._dynamic.ecsact_system_execution_context_remove, runtime._dynamic._availableMethods); + LoadDelegate(lib, "ecsact_system_execution_context_update", out runtime._dynamic.ecsact_system_execution_context_update, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_system_execution_context_get", out runtime._dynamic.ecsact_system_execution_context_get, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_system_execution_context_has", out runtime._dynamic.ecsact_system_execution_context_has, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_system_execution_context_generate", out runtime._dynamic.ecsact_system_execution_context_generate, runtime._dynamic._availableMethods); @@ -1112,8 +1176,6 @@ public static EcsactRuntime Load LoadDelegate(lib, "ecsact_create_component", out runtime._dynamic.ecsact_create_component, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_resize_component", out runtime._dynamic.ecsact_resize_component, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_destroy_component", out runtime._dynamic.ecsact_destroy_component, runtime._dynamic._availableMethods); - LoadDelegate(lib, "ecsact_create_variant", out runtime._dynamic.ecsact_create_variant, runtime._dynamic._availableMethods); - LoadDelegate(lib, "ecsact_destroy_variant", out runtime._dynamic.ecsact_destroy_variant, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_add_system_capability", out runtime._dynamic.ecsact_add_system_capability, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_update_system_capability", out runtime._dynamic.ecsact_update_system_capability, runtime._dynamic._availableMethods); LoadDelegate(lib, "ecsact_remove_system_capability", out runtime._dynamic.ecsact_remove_system_capability, runtime._dynamic._availableMethods); @@ -1143,7 +1205,6 @@ public static EcsactRuntime Load // Load static methods LoadDelegate(lib, "ecsact_static_components", out runtime._static.ecsact_static_components, runtime._static._availableMethods); - LoadDelegate(lib, "ecsact_static_variants", out runtime._static.ecsact_static_variants, runtime._static._availableMethods); LoadDelegate(lib, "ecsact_static_systems", out runtime._static.ecsact_static_systems, runtime._static._availableMethods); LoadDelegate(lib, "ecsact_static_actions", out runtime._static.ecsact_static_actions, runtime._static._availableMethods); LoadDelegate(lib, "ecsact_static_on_reload", out runtime._static.ecsact_static_on_reload, runtime._static._availableMethods); From 39a04f95fa9ff983341107574d6e6b07833957ed Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Tue, 28 Jun 2022 22:51:43 -0700 Subject: [PATCH 04/10] sorted methods --- .../Runtime/EcsactRuntime.cs | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs index 60fe308..a64257a 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs @@ -274,11 +274,11 @@ public static void SetDefault public class Async { internal List _availableMethods = new(); public static string[] methods => new string[]{ - "ecsact_async_execute_action", - "ecsact_async_execute_action_at", - "ecsact_async_flush_events", "ecsact_async_connect", "ecsact_async_disconnect", + "ecsact_async_execute_action_at", + "ecsact_async_execute_action", + "ecsact_async_flush_events", }; public IEnumerable availableMethods => _availableMethods; @@ -316,24 +316,24 @@ internal delegate void ecsact_async_disconnect_delegate public class Core { internal List _availableMethods = new(); public static string[] methods => new string[]{ - "ecsact_create_registry", - "ecsact_destroy_registry", + "ecsact_add_component", "ecsact_clear_registry", + "ecsact_count_components", + "ecsact_count_entities", "ecsact_create_entity", + "ecsact_create_registry", + "ecsact_destroy_entity", + "ecsact_destroy_registry", + "ecsact_each_component", "ecsact_ensure_entity", "ecsact_entity_exists", - "ecsact_destroy_entity", - "ecsact_count_entities", - "ecsact_get_entities", - "ecsact_add_component", - "ecsact_has_component", + "ecsact_execute_systems", "ecsact_get_component", - "ecsact_each_component", - "ecsact_count_components", "ecsact_get_components", - "ecsact_update_component", + "ecsact_get_entities", + "ecsact_has_component", "ecsact_remove_component", - "ecsact_execute_systems", + "ecsact_update_component", }; public IEnumerable availableMethods => _availableMethods; @@ -746,31 +746,31 @@ public void ExecuteSystems public class Dynamic { internal List _availableMethods = new(); public static string[] methods => new string[]{ - "ecsact_system_execution_context_action", - "ecsact_system_execution_context_add", - "ecsact_system_execution_context_remove", - "ecsact_system_execution_context_get", - "ecsact_system_execution_context_has", - "ecsact_system_execution_context_generate", - "ecsact_system_execution_context_parent", - "ecsact_system_execution_context_same", - "ecsact_create_system", - "ecsact_set_system_execution_impl", + "ecsact_add_system_capability", + "ecsact_add_system_generate_component_set", "ecsact_create_action", - "ecsact_resize_action", "ecsact_create_component", - "ecsact_resize_component", - "ecsact_destroy_component", + "ecsact_create_system", "ecsact_create_variant", + "ecsact_destroy_component", "ecsact_destroy_variant", - "ecsact_add_system_capability", - "ecsact_update_system_capability", - "ecsact_remove_system_capability", - "ecsact_add_system_generate_component_set", + "ecsact_register_action", "ecsact_register_component", "ecsact_register_system", - "ecsact_register_action", + "ecsact_remove_system_capability", + "ecsact_resize_action", + "ecsact_resize_component", + "ecsact_set_system_execution_impl", + "ecsact_system_execution_context_action", + "ecsact_system_execution_context_add", + "ecsact_system_execution_context_generate", + "ecsact_system_execution_context_get", + "ecsact_system_execution_context_has", "ecsact_system_execution_context_id", + "ecsact_system_execution_context_parent", + "ecsact_system_execution_context_remove", + "ecsact_system_execution_context_same", + "ecsact_update_system_capability", }; public IEnumerable availableMethods => _availableMethods; @@ -941,14 +941,14 @@ internal delegate Int32 ecsact_system_execution_context_id_delegate public class Meta { internal List _availableMethods = new(); public static string[] methods => new string[]{ - "ecsact_meta_registry_name", - "ecsact_meta_component_size", - "ecsact_meta_component_name", - "ecsact_meta_action_size", "ecsact_meta_action_name", - "ecsact_meta_system_name", + "ecsact_meta_action_size", + "ecsact_meta_component_name", + "ecsact_meta_component_size", + "ecsact_meta_registry_name", "ecsact_meta_system_capabilities_count", "ecsact_meta_system_capabilities", + "ecsact_meta_system_name", }; public IEnumerable availableMethods => _availableMethods; @@ -1003,12 +1003,12 @@ internal delegate void ecsact_meta_system_capabilities_delegate public class Serialize { internal List _availableMethods = new(); public static string[] methods => new string[]{ + "ecsact_deserialize_action", + "ecsact_deserialize_component", "ecsact_serialize_action_size", - "ecsact_serialize_component_size", "ecsact_serialize_action", + "ecsact_serialize_component_size", "ecsact_serialize_component", - "ecsact_deserialize_action", - "ecsact_deserialize_component", }; public IEnumerable availableMethods => _availableMethods; @@ -1055,11 +1055,11 @@ internal delegate void ecsact_deserialize_component_delegate public class Static { internal List _availableMethods = new(); public static string[] methods => new string[]{ - "ecsact_static_components", - "ecsact_static_systems", "ecsact_static_actions", - "ecsact_static_on_reload", + "ecsact_static_components", "ecsact_static_off_reload", + "ecsact_static_on_reload", + "ecsact_static_systems", }; public IEnumerable availableMethods => _availableMethods; From 0d6ada78761fffdf7d3a78206a2934cc3e242563 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Wed, 29 Jun 2022 09:17:46 -0700 Subject: [PATCH 05/10] Added ecsact rtb as a dependency --- .vscode/extensions.json | 6 ++++++ .vscode/settings.json | 8 ++++++++ WORKSPACE | 34 ++++++++++++++++++---------------- packages/BUILD.bazel | 9 +++++---- 4 files changed, 37 insertions(+), 20 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..2530691 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "BazelBuild.vscode-bazel", + "ms-dotnettools.csharp" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5c151f3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.insertSpaces": false, + "files.insertFinalNewline": true, + "[starlark]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "BazelBuild.vscode-bazel" + } +} diff --git a/WORKSPACE b/WORKSPACE index daaee7c..879c2bd 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,40 +4,40 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") git_repository( - name = "ecsact", - remote = "git@github.com:seaube/ecsact.git", - commit = "6bce75438e812fd887dbe24e757d2a523993fa26", - shallow_since = "1651763515 -0400", + name = "ecsact_rtb", + commit = "6d730c4ebcbd395d3ad8810ba842f9c0d8ed15d1", + remote = "git@github.com:seaube/ecsact-rtb.git", + shallow_since = "1656519400 -0700", ) -http_archive( - name = "boost", - strip_prefix = "boost-563e8e0de4eac4b48a02d296581dc2454127608e", - urls = ["https://github.com/bazelboost/boost/archive/563e8e0de4eac4b48a02d296581dc2454127608e.zip"], - sha256 = "c41441a6e9f8038ad626e9f937ddc3675ab896b6c3512eefc6840037b3816d03", -) +load("@ecsact_rtb//:repositories.bzl", "ecsact_rtb_repositories") + +ecsact_rtb_repositories() + +load("@ecsact_rtb//:workspace.bzl", "ecsact_rtb_workspace") -load("@boost//:index.bzl", "boost_http_archives") -boost_http_archives() +ecsact_rtb_workspace() http_archive( name = "bzlws", + sha256 = "5bebb821b158b11d81dd25cf031b5b26bae97dbb02025df7d0e41a262b3a030b", strip_prefix = "bzlws-f929e5380f441f50a77776d34a7df8cacdbdf986", url = "https://github.com/zaucy/bzlws/archive/f929e5380f441f50a77776d34a7df8cacdbdf986.zip", - sha256 = "5bebb821b158b11d81dd25cf031b5b26bae97dbb02025df7d0e41a262b3a030b", ) load("@bzlws//:repo.bzl", "bzlws_deps") + bzlws_deps() http_archive( name = "rules_7zip", + sha256 = "29ba984e2a7d48540faa839efaf09be4b880d211a93575e7ac87abffc12dbdea", strip_prefix = "rules_7zip-25d3b858a37580dbc1f1ced002e210be15012e2f", urls = ["https://github.com/zaucy/rules_7zip/archive/25d3b858a37580dbc1f1ced002e210be15012e2f.zip"], - sha256 = "29ba984e2a7d48540faa839efaf09be4b880d211a93575e7ac87abffc12dbdea", ) load("@rules_7zip//:setup.bzl", "setup_7zip") + setup_7zip() _nlohmann_json_build_file = """ @@ -54,9 +54,9 @@ cc_library( http_archive( name = "nlohmann_json", - url = "https://github.com/nlohmann/json/releases/download/v3.10.4/include.zip", - sha256 = "62c585468054e2d8e7c2759c0d990fd339d13be988577699366fe195162d16cb", build_file_content = _nlohmann_json_build_file, + sha256 = "62c585468054e2d8e7c2759c0d990fd339d13be988577699366fe195162d16cb", + url = "https://github.com/nlohmann/json/releases/download/v3.10.4/include.zip", ) http_archive( @@ -66,6 +66,7 @@ http_archive( ) load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories") + node_repositories() http_archive( @@ -76,4 +77,5 @@ http_archive( ) load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies") + aspect_bazel_lib_dependencies() diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index b9fc89c..7fde910 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -4,13 +4,14 @@ package(default_visibility = ["//visibility:public"]) bzlws_copy( name = "copy_for_dev", - visibility = ["//visibility:private"], testonly = True, - force = True, - out = "packages/com.seaube.ecsact/generators~/{FILENAME}", - metafile_path = "generators~/.copy_for_dev.yml", srcs = [ "@ecsact//generator/csharp:cli", "@ecsact//generator/parser_info:cli", + "@ecsact_rtb//cli", ], + out = "packages/com.seaube.ecsact/generators~/{FILENAME}", + force = True, + metafile_path = "generators~/.copy_for_dev.yml", + visibility = ["//visibility:private"], ) From fdb089a991dc2515872daa672ce79c3b204209ed Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Wed, 29 Jun 2022 09:19:54 -0700 Subject: [PATCH 06/10] formatting --- packages/com.seaube.ecsact/BUILD.bazel | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/com.seaube.ecsact/BUILD.bazel b/packages/com.seaube.ecsact/BUILD.bazel index 3f395b8..31857f0 100644 --- a/packages/com.seaube.ecsact/BUILD.bazel +++ b/packages/com.seaube.ecsact/BUILD.bazel @@ -3,25 +3,32 @@ load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") copy_to_directory( name = "generators~", - replace_prefixes = { - "generator/csharp/": "", - "generator/parser_info/": "", - }, - include_external_repositories = [ - "ecsact", - ], srcs = [ "@ecsact//generator/csharp:cli", "@ecsact//generator/parser_info:cli", ], + include_external_repositories = [ + "ecsact", + ], + replace_prefixes = { + "generator/csharp/": "", + "generator/parser_info/": "", + }, ) pkg_npm( name = "com.seaube.ecsact", package_name = "com.seaube.ecsact", - deps = [":generators~"], srcs = glob(["**/*"]), + deps = [":generators~"], +) + +alias( + name = "pack", + actual = "com.seaube.ecsact.pack", ) -alias(name = "pack", actual = "com.seaube.ecsact.pack") -alias(name = "publish", actual = "com.seaube.ecsact.publish") +alias( + name = "publish", + actual = "com.seaube.ecsact.publish", +) From a686b789ce37c6fe81aaf27b27737c6bf80fd057 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Sun, 10 Jul 2022 22:24:15 -0700 Subject: [PATCH 07/10] wip --- WORKSPACE | 17 +- packages/BUILD.bazel | 22 +- packages/com.seaube.ecsact/.gitignore | 2 +- packages/com.seaube.ecsact/BUILD.bazel | 48 +- .../Editor/DefaultRunnerEditor.cs | 41 ++ .../Editor/DefaultRunnerEditor.cs.meta | 11 + .../Editor/Ecsact.Editor.asmdef | 8 +- .../Editor/EcsactPackagesPostprocessor.cs | 12 +- .../Editor/EcsactRuntimeBuilder.cs | 105 ++++ .../Editor/EcsactRuntimeBuilder.cs.meta | 11 + .../Editor/EcsactSettings.cs | 6 +- .../Editor/EcsactSettings.uxml | 21 + .../com.seaube.ecsact/Runtime/AsyncRunner.cs | 41 ++ .../Runtime/AsyncRunner.cs.meta | 11 + .../Runtime/DefaultRunner.cs | 68 +++ .../Runtime/DefaultRunner.cs.meta | 11 + .../com.seaube.ecsact/Runtime/Ecsact.asmdef | 29 +- .../Runtime/EcsactRuntime.cs | 527 ++++++++++++++++-- .../Runtime/EcsactRuntimeSettings.cs | 29 +- .../Runtime/VisualScripting.meta | 8 + .../VisualScripting/AsyncConnectEvent.cs | 50 ++ .../VisualScripting/AsyncConnectEvent.cs.meta | 11 + .../VisualScripting/AsyncConnectNode.cs | 33 ++ .../VisualScripting/AsyncConnectNode.cs.meta | 11 + .../VisualScripting/AsyncErrorEvent.cs | 51 ++ .../VisualScripting/AsyncErrorEvent.cs.meta | 11 + .../Runtime/VisualScriptingEvents.cs | 49 ++ .../Runtime/VisualScriptingEvents.cs.meta | 11 + packages/com.seaube.ecsact/package.json | 3 +- packages/com.seaube.ecsact/pkg.bzl | 100 ++++ packages/com.seaube.ecsact/pkg.bzl.meta | 7 + 31 files changed, 1255 insertions(+), 110 deletions(-) create mode 100644 packages/com.seaube.ecsact/Editor/DefaultRunnerEditor.cs create mode 100644 packages/com.seaube.ecsact/Editor/DefaultRunnerEditor.cs.meta create mode 100644 packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs create mode 100644 packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/AsyncRunner.cs create mode 100644 packages/com.seaube.ecsact/Runtime/AsyncRunner.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/DefaultRunner.cs create mode 100644 packages/com.seaube.ecsact/Runtime/DefaultRunner.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScripting.meta create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectEvent.cs create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectEvent.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectNode.cs create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectNode.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncErrorEvent.cs create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncErrorEvent.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs create mode 100644 packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs.meta create mode 100644 packages/com.seaube.ecsact/pkg.bzl create mode 100644 packages/com.seaube.ecsact/pkg.bzl.meta diff --git a/WORKSPACE b/WORKSPACE index 879c2bd..85db5ff 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -5,9 +5,9 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") git_repository( name = "ecsact_rtb", - commit = "6d730c4ebcbd395d3ad8810ba842f9c0d8ed15d1", + commit = "53d0a81636cc24b0fea4ac4dd626c94962fee0d5", remote = "git@github.com:seaube/ecsact-rtb.git", - shallow_since = "1656519400 -0700", + # shallow_since = "1656611470 -0700", ) load("@ecsact_rtb//:repositories.bzl", "ecsact_rtb_repositories") @@ -79,3 +79,16 @@ http_archive( load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies") aspect_bazel_lib_dependencies() + +http_archive( + name = "rules_pkg", + sha256 = "8a298e832762eda1830597d64fe7db58178aa84cd5926d76d5b744d6558941c2", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz", + "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz", + ], +) + +load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") + +rules_pkg_dependencies() diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 7fde910..6a14a5f 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -1,16 +1,22 @@ -load("@bzlws//:index.bzl", "bzlws_copy") +load("@bzlws//:index.bzl", "bzlws_copy", "bzlws_extract") package(default_visibility = ["//visibility:public"]) -bzlws_copy( +# bzlws_copy( +# name = "copy_for_dev", +# testonly = True, +# srcs = ["//packages/com.seaube.ecsact:generators"], +# out = "packages/com.seaube.ecsact/generators~.tar", +# force = True, +# metafile_path = "generators~/.copy_for_dev.yml", +# visibility = ["//visibility:private"], +# ) + +bzlws_extract( name = "copy_for_dev", testonly = True, - srcs = [ - "@ecsact//generator/csharp:cli", - "@ecsact//generator/parser_info:cli", - "@ecsact_rtb//cli", - ], - out = "packages/com.seaube.ecsact/generators~/{FILENAME}", + srcs = ["//packages/com.seaube.ecsact:generators"], + out = "packages/com.seaube.ecsact/generators~", force = True, metafile_path = "generators~/.copy_for_dev.yml", visibility = ["//visibility:private"], diff --git a/packages/com.seaube.ecsact/.gitignore b/packages/com.seaube.ecsact/.gitignore index 7e5c0b9..bf96c4f 100644 --- a/packages/com.seaube.ecsact/.gitignore +++ b/packages/com.seaube.ecsact/.gitignore @@ -71,4 +71,4 @@ crashlytics-build.properties /[Aa]ssets/[Ss]treamingAssets/aa/* # Folder that stores codegen executables -# /generators~ +/generators~ diff --git a/packages/com.seaube.ecsact/BUILD.bazel b/packages/com.seaube.ecsact/BUILD.bazel index 31857f0..5b2ee26 100644 --- a/packages/com.seaube.ecsact/BUILD.bazel +++ b/packages/com.seaube.ecsact/BUILD.bazel @@ -1,34 +1,30 @@ -load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") -load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") +load(":pkg.bzl", "pkg_executable") +load("@rules_pkg//pkg:tar.bzl", "pkg_tar") -copy_to_directory( - name = "generators~", - srcs = [ - "@ecsact//generator/csharp:cli", - "@ecsact//generator/parser_info:cli", - ], - include_external_repositories = [ - "ecsact", - ], - replace_prefixes = { - "generator/csharp/": "", - "generator/parser_info/": "", - }, +pkg_executable( + name = "ecsact_csharp_codegen_with_runfiles", + bin = "@ecsact//generator/csharp:cli", + path = "ecsact_csharp_codegen", ) -pkg_npm( - name = "com.seaube.ecsact", - package_name = "com.seaube.ecsact", - srcs = glob(["**/*"]), - deps = [":generators~"], +pkg_executable( + name = "ecsact_parser_info_codegen_with_runfiles", + bin = "@ecsact//generator/parser_info:cli", + path = "ecsact_parser_info_codegen", ) -alias( - name = "pack", - actual = "com.seaube.ecsact.pack", +pkg_executable( + name = "ecsact_rtb_with_runfiles", + bin = "@ecsact_rtb//cli:ecsact-rtb", + path = "ecsact-rtb", ) -alias( - name = "publish", - actual = "com.seaube.ecsact.publish", +pkg_tar( + name = "generators", + srcs = [ + ":ecsact_csharp_codegen_with_runfiles", + ":ecsact_parser_info_codegen_with_runfiles", + ":ecsact_rtb_with_runfiles", + ], + visibility = ["//visibility:public"], ) diff --git a/packages/com.seaube.ecsact/Editor/DefaultRunnerEditor.cs b/packages/com.seaube.ecsact/Editor/DefaultRunnerEditor.cs new file mode 100644 index 0000000..794d13e --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/DefaultRunnerEditor.cs @@ -0,0 +1,41 @@ +using UnityEditor; +using UnityEngine; + +namespace Ecsact.Editor { + [CustomEditor(typeof(Ecsact.DefaultRunner))] + public class DefaultRunnerEditor : UnityEditor.Editor { + public override bool RequiresConstantRepaint() { + return Application.isPlaying; + } + + public override void OnInspectorGUI() { + var runner = target as DefaultRunner; + var executionHeatStyle = new GUIStyle(EditorStyles.label); + executionHeatStyle.normal.textColor = Color.green; + + float deltaTimePc = 0f; + if(runner.debugExecutionTimeMs > 0) { + // 0.0 - 1.0 how much the execution time takes up from the delta time + deltaTimePc = + Time.deltaTime / ((float)runner.debugExecutionTimeMs) / 1000f; + + if(deltaTimePc > 0.5) { + executionHeatStyle.normal.textColor = Color.red; + } else if(deltaTimePc > 0.2) { + executionHeatStyle.normal.textColor = Color.yellow; + } + } + + EditorGUILayout.LabelField( + "Execution Count", + $"{runner.debugExecutionCountTotal}" + ); + EditorGUILayout.LabelField( + "Execution Time", + $"{runner.debugExecutionTimeMs}ms " + + $"({deltaTimePc*100:0.0}% of delta time)", + executionHeatStyle + ); + } + } +} diff --git a/packages/com.seaube.ecsact/Editor/DefaultRunnerEditor.cs.meta b/packages/com.seaube.ecsact/Editor/DefaultRunnerEditor.cs.meta new file mode 100644 index 0000000..1ddc3f3 --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/DefaultRunnerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a250f3f440349154990c57791c722ac2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Editor/Ecsact.Editor.asmdef b/packages/com.seaube.ecsact/Editor/Ecsact.Editor.asmdef index 1dac4c2..102d171 100644 --- a/packages/com.seaube.ecsact/Editor/Ecsact.Editor.asmdef +++ b/packages/com.seaube.ecsact/Editor/Ecsact.Editor.asmdef @@ -2,8 +2,12 @@ "name": "Ecsact.Editor", "rootNamespace": "", "references": [ - "Ecsact", - "Unity.EditorCoroutines.Editor" + "GUID:6e7029456009ab94da19a8f93d5e3523", + "GUID:478a2357cc57436488a56e564b08d223", + "GUID:21b0c8d1703a94250bfac916590cea4f", + "GUID:54cb1906aeb4e4264bc1d2aa3818a43f", + "GUID:ea715009a4efd4c6cbc85be3ae097dd3", + "GUID:23f3c6063e13e4fd9addce3606ff4e42" ], "includePlatforms": [ "Editor" diff --git a/packages/com.seaube.ecsact/Editor/EcsactPackagesPostprocessor.cs b/packages/com.seaube.ecsact/Editor/EcsactPackagesPostprocessor.cs index 6d6a9a6..5649e3e 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactPackagesPostprocessor.cs +++ b/packages/com.seaube.ecsact/Editor/EcsactPackagesPostprocessor.cs @@ -91,7 +91,7 @@ static void RefreshEcsactCodegen ); var progressId = Progress.Start( - "ECSACT Codegen", + "Ecsact Codegen", "Generating C# files..." ); @@ -120,11 +120,13 @@ static void RefreshEcsactCodegen } else { Progress.Finish(progressId, Progress.Status.Succeeded); // Import newly created scripts - TryImportAssets(importedPkgs.Select(pkg => pkg + ".cs").ToList()); + // TryImportAssets(importedPkgs.Select(pkg => pkg + ".cs").ToList()); } }; - foreach(var (pkg, pkgPath) in FindEcsactPackages()) { + var packages = FindEcsactPackages().ToList(); + + foreach(var (pkg, pkgPath) in packages) { codegen.StartInfo.Arguments += pkgPath + " "; } @@ -134,6 +136,10 @@ static void RefreshEcsactCodegen foreach(var plugin in GetCodegenPlugins()) { // TODO: Custom codegen plugins } + + EcsactRuntimeBuilder.Build(new EcsactRuntimeBuilder.Options{ + ecsactFiles = packages.Select(item => item.Item2).ToList(), + }); } static IEnumerable GetCodegenPlugins() { diff --git a/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs b/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs new file mode 100644 index 0000000..ca7dbd4 --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs @@ -0,0 +1,105 @@ +using UnityEditor; +using UnityEngine; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; + +public static class EcsactRuntimeBuilder { + + public struct Options { + public List ecsactFiles; + } + + public static void Build + ( Options options + ) + { + if(options.ecsactFiles.Count == 0) { + UnityEngine.Debug.LogError( + "Cannot build ecsact runtime without any ecsact files" + ); + return; + } + + var settings = EcsactSettings.GetOrCreateSettings(); + if(string.IsNullOrWhiteSpace(settings.runtimeBuilderOutputPath)) { + UnityEngine.Debug.LogError( + "Cannot build ecsact runtime without output path" + ); + return; + } + + string runtimeBuilderExecutablePath = Path.GetFullPath( + "Packages/com.seaube.ecsact/generators~/ecsact-rtb.exe" + ); + + var progressId = Progress.Start("Ecsact Runtime Builder"); + + var proc = new Process(); + proc.StartInfo.FileName = runtimeBuilderExecutablePath; + proc.StartInfo.CreateNoWindow = true; + proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + proc.EnableRaisingEvents = true; + proc.StartInfo.Arguments = ""; + proc.StartInfo.RedirectStandardError = true; + proc.StartInfo.RedirectStandardOutput = true; + proc.StartInfo.UseShellExecute = false; + + proc.ErrorDataReceived += (_, ev) => { + var line = ev.Data; + if(!string.IsNullOrWhiteSpace(line)) { + UnityEngine.Debug.LogError(line); + Progress.SetDescription(progressId, line); + } + }; + + proc.OutputDataReceived += (_, ev) => { + var line = ev.Data; + if(!string.IsNullOrWhiteSpace(line)) { + Progress.SetDescription(progressId, line); + } + }; + + proc.Exited += (_, _) => { + if(proc.ExitCode != 0) { + Progress.Finish(progressId, Progress.Status.Failed); + } else { + Progress.Finish(progressId, Progress.Status.Succeeded); + } + }; + + foreach(var ecsactFile in options.ecsactFiles) { + proc.StartInfo.Arguments += "\"" + ecsactFile + "\" "; + } + + proc.StartInfo.Arguments += "--output=\""; + proc.StartInfo.Arguments += settings.runtimeBuilderOutputPath; +#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + proc.StartInfo.Arguments += ".dll"; +#elif UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX + proc.StartInfo.Arguments += ".so"; +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + proc.StartInfo.Arguments += ".dylib"; +#elif UNITY_WEBGL + proc.StartInfo.Arguments += ".wasm"; +#endif + proc.StartInfo.Arguments += "\" "; + + if(!string.IsNullOrWhiteSpace(settings.runtimeBuilderCompilerPath)) { + proc.StartInfo.Arguments += "--compiler_path=\""; + proc.StartInfo.Arguments += settings.runtimeBuilderCompilerPath; + proc.StartInfo.Arguments += "\" "; + } + + proc.StartInfo.Arguments += "--temp_dir="; + proc.StartInfo.Arguments += FileUtil.GetUniqueTempPathInProject(); + + UnityEngine.Debug.Log(proc.StartInfo.FileName); + UnityEngine.Debug.Log(proc.StartInfo.Arguments); + + Progress.Report(progressId, 0.1f); + proc.Start(); + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + } +} diff --git a/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs.meta b/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs.meta new file mode 100644 index 0000000..394ede3 --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: abd5ca5c67fee5447af8ed6740f58af2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Editor/EcsactSettings.cs b/packages/com.seaube.ecsact/Editor/EcsactSettings.cs index d5d1cc7..47cc821 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactSettings.cs +++ b/packages/com.seaube.ecsact/Editor/EcsactSettings.cs @@ -21,7 +21,11 @@ class EcsactSettings : ScriptableObject { /// will be downloaded and used instead. public string csharpCodegenPluginPath = ""; - internal static EcsactSettings GetOrCreateSettings() { + public string runtimeBuilderOutputPath = "Assets/Plugins/EcsactRuntime.dll"; + + public string runtimeBuilderCompilerPath = ""; + + public static EcsactSettings GetOrCreateSettings() { var settings = AssetDatabase.LoadAssetAtPath(assetPath); if(settings == null) { settings = ScriptableObject.CreateInstance(); diff --git a/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml b/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml index e718c2f..6e348d6 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml +++ b/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml @@ -6,6 +6,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/com.seaube.ecsact/Runtime/AsyncRunner.cs b/packages/com.seaube.ecsact/Runtime/AsyncRunner.cs new file mode 100644 index 0000000..6db937a --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/AsyncRunner.cs @@ -0,0 +1,41 @@ +using UnityEngine; + +#nullable enable + +namespace Ecsact { + [AddComponentMenu("")] + public class AsyncRunner : MonoBehaviour { + private static AsyncRunner? instance = null; + private EcsactRuntime? runtimeInstance = null; + + [RuntimeInitializeOnLoadMethod] + private static void OnRuntimeLoad() { + if(instance != null) { + return; + } + + var settings = EcsactRuntimeSettings.Get(); + if(!settings.useAsyncRunner) { + return; + } + + var gameObject = new GameObject("Ecsact Async Runner"); + instance = gameObject.AddComponent(); + DontDestroyOnLoad(gameObject); + } + + void Awake() { + runtimeInstance = EcsactRuntime.GetOrLoadDefault(); + } + + void Update() { + runtimeInstance!.async.FlushEvents(); + } + + void OnDestroy() { + if(this.Equals(instance)) { + instance = null; + } + } + } +} diff --git a/packages/com.seaube.ecsact/Runtime/AsyncRunner.cs.meta b/packages/com.seaube.ecsact/Runtime/AsyncRunner.cs.meta new file mode 100644 index 0000000..d8b5505 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/AsyncRunner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: af1b115eb8d72ec42897c763d838ad15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs b/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs new file mode 100644 index 0000000..ef19b78 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs @@ -0,0 +1,68 @@ +using UnityEngine; +using System; +using System.Runtime.CompilerServices; + +#nullable enable + +namespace Ecsact { + [AddComponentMenu("")] + public class DefaultRunner : MonoBehaviour { + private EcsactRuntime? runtimeInstance = null; + + private EcsactRuntimeDefaultRegistry? defReg; + +#if UNITY_EDITOR + [NonSerialized] + public int debugExecutionCountTotal = 0; + [NonSerialized] + public int debugExecutionTimeMs = 0; +#endif + + [RuntimeInitializeOnLoadMethod] + private static void OnRuntimeLoad() { + var settings = EcsactRuntimeSettings.Get(); + + foreach(var defReg in settings.defaultRegistries) { + if(!defReg.useDefaultRunner) continue; + + var gameObjectName = $"Ecsact Default Runner "; + if(!string.IsNullOrWhiteSpace(defReg.registryName)) { + gameObjectName += " - " + defReg.registryName; + } + gameObjectName += $"({defReg.registryId})"; + + var gameObject = new GameObject(gameObjectName); + var runner = gameObject.AddComponent(); + runner.defReg = defReg; + DontDestroyOnLoad(gameObject); + } + } + + void Awake() { + runtimeInstance = EcsactRuntime.GetOrLoadDefault(); + } + + void Update() { + if(defReg == null) return; + Debug.Assert(defReg.registryId != -1); + +#if UNITY_EDITOR + var executionTimeWatch = System.Diagnostics.Stopwatch.StartNew(); +#endif + + runtimeInstance!.core.ExecuteSystems( + registryId: defReg.registryId, + executionCount: 1, + new EcsactRuntime.ExecutionOptions[]{defReg.executionOptions} + ); + +#if UNITY_EDITOR + executionTimeWatch.Stop(); + debugExecutionTimeMs = (int)executionTimeWatch.ElapsedMilliseconds; + debugExecutionCountTotal += 1; +#endif + + defReg.executionOptions = new EcsactRuntime.ExecutionOptions(); + } + } +} diff --git a/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs.meta b/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs.meta new file mode 100644 index 0000000..84a2855 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 524a4ad0b84b1eb4b80ac980181df7fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/Ecsact.asmdef b/packages/com.seaube.ecsact/Runtime/Ecsact.asmdef index bf8422e..2f61bf2 100644 --- a/packages/com.seaube.ecsact/Runtime/Ecsact.asmdef +++ b/packages/com.seaube.ecsact/Runtime/Ecsact.asmdef @@ -1,14 +1,17 @@ { - "name": "Ecsact", - "rootNamespace": "", - "references": [], - "includePlatforms": [], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false -} + "name": "Ecsact", + "rootNamespace": "", + "references": [ + "GUID:21b0c8d1703a94250bfac916590cea4f", + "GUID:ea715009a4efd4c6cbc85be3ae097dd3" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs index a64257a..d9894e3 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs @@ -5,6 +5,17 @@ #nullable enable +namespace Ecsact { + public enum AsyncError : Int32 { + ConnectionClosed, + ConnectFail, + SocketFail, + StateFail, + StartFail, + InvalidConnectionString, + } +} + #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN internal static class NativeLibrary { [DllImport("Kernel32.dll", @@ -78,6 +89,11 @@ public EcsactRuntimeMissingMethod } public class EcsactRuntime { + public static class VisualScriptingEventNames { + public const string AsyncError = "EcsactAsyncErrorEvent"; + public const string AsyncConnectStateChange = "EcsactAsyncConnectStateChange"; + } + public enum EcsactEvent : Int32 { InitComponent = 0, UpdateComponent = 1, @@ -86,7 +102,7 @@ public enum EcsactEvent : Int32 { public delegate void EachComponentCallback ( Int32 componentId - , IntPtr componentData + , object componentData , IntPtr callbackUserData ); @@ -99,7 +115,20 @@ public delegate void ComponentEventCallback ); public struct ExecutionOptions { + public Int32 addComponentsLength; + public Int32[] addComponentsEntities; + public object[] addComponents; + + public Int32 updateComponentsLength; + public Int32[] updateComponentsEntities; + public object[] updateComponents; + public Int32 removeComponentsLength; + public Int32[] removeComponentsEntities; + public Int32[] removeComponents; + + public Int32 actionsLength; + public object[] actions; }; public struct ExecutionEventsCollector { @@ -180,15 +209,6 @@ public delegate void StaticReloadCallback ( IntPtr userData ); - public enum AsyncError : Int32 { - ConnectionClosed, - ConnectFail, - SocketFail, - StateFail, - StartFail, - InvalidConnectionString, - } - public enum SystemCapability : Int32 { Readonly = 1, Writeonly = 2, @@ -222,14 +242,20 @@ public delegate Int32 ComponentCompareFn ); public delegate void AsyncErrorCallback - ( AsyncError err - , Int32 requestId - , IntPtr callbackUserData + ( Ecsact.AsyncError err + , Int32 requestId + , IntPtr callbackUserData + ); + + public delegate void AsyncConnectCallback + ( [MarshalAs(UnmanagedType.LPStr)] string connectAddress + , Int32 connectPort + , IntPtr callbackUserData ); public delegate void AsyncActionCommittedCallback ( Int32 actionId - , IntPtr actionData + , object actionData , Int32 committedTick , Int32 requestId , IntPtr callbackUserData @@ -238,7 +264,9 @@ public delegate void AsyncActionCommittedCallback public struct AsyncEventsCollector { public AsyncErrorCallback errorCallback; public IntPtr errorCallbackUserData; - AsyncActionCommittedCallback actionCommittedCallback; + public AsyncConnectCallback connectCallback; + public IntPtr connectCallbackUserData; + public AsyncActionCommittedCallback actionCommittedCallback; public IntPtr actionCommittedCallbackUserData; } @@ -248,6 +276,12 @@ public static EcsactRuntime GetOrLoadDefault() { if(defaultInstance == null) { var settings = EcsactRuntimeSettings.Get(); defaultInstance = Load(settings.runtimeLibraryPaths); + + foreach(var defReg in settings.defaultRegistries) { + defReg.registryId = defaultInstance.core.CreateRegistry( + defReg.registryName + ); + } } return defaultInstance; @@ -297,8 +331,8 @@ internal delegate void ecsact_async_execute_action_at_delegate internal ecsact_async_execute_action_at_delegate? ecsact_async_execute_action_at; internal delegate void ecsact_async_flush_events_delegate - ( ExecutionEventsCollector executionEventsCollector - , AsyncEventsCollector asyncEventsCollector + ( in ExecutionEventsCollector executionEventsCollector + , in AsyncEventsCollector asyncEventsCollector ); internal ecsact_async_flush_events_delegate? ecsact_async_flush_events; @@ -311,6 +345,129 @@ internal delegate void ecsact_async_disconnect_delegate ( ); internal ecsact_async_disconnect_delegate? ecsact_async_disconnect; + + public delegate void ErrorCallback + ( Ecsact.AsyncError err + , Int32 requestId + ); + + public delegate void ConnectCallback + ( string connectAddress + , Int32 connectPort + ); + + private AsyncEventsCollector _asyncEvs; + private List _errCallbacks = new(); + private List _connectCallbacks = new(); + private EcsactRuntime _owner; + + internal Async + ( EcsactRuntime owner + ) + { + _owner = owner; + _asyncEvs = new AsyncEventsCollector{ + actionCommittedCallback = OnActionCommittedHandler, + actionCommittedCallbackUserData = IntPtr.Zero, + errorCallback = OnErrorHandler, + errorCallbackUserData = IntPtr.Zero, + connectCallback = OnConnectHandler, + connectCallbackUserData = IntPtr.Zero, + }; + } + + [AOT.MonoPInvokeCallback(typeof(AsyncActionCommittedCallback))] + private static void OnActionCommittedHandler + ( Int32 actionId + , object actionData + , Int32 committedTick + , Int32 requestId + , IntPtr callbackUserData + ) + { + var self = (GCHandle.FromIntPtr(callbackUserData).Target as Async)!; + } + + [AOT.MonoPInvokeCallback(typeof(AsyncErrorCallback))] + private static void OnErrorHandler + ( Ecsact.AsyncError err + , Int32 requestId + , IntPtr callbackUserData + ) + { + var self = (GCHandle.FromIntPtr(callbackUserData).Target as Async)!; + foreach(var cb in self._errCallbacks) { + cb(err, requestId); + } + } + + [AOT.MonoPInvokeCallback(typeof(AsyncConnectCallback))] + private static void OnConnectHandler + ( [MarshalAs(UnmanagedType.LPStr)] string connectAddress + , Int32 connectPort + , IntPtr callbackUserData + ) + { + var self = (GCHandle.FromIntPtr(callbackUserData).Target as Async)!; + foreach(var cb in self._connectCallbacks) { + cb(connectAddress, connectPort); + } + } + + public Action OnError + ( ErrorCallback callback + ) + { + _errCallbacks.Add(callback); + + return () => { + _errCallbacks.Remove(callback); + }; + } + + public Action OnConnect + ( ConnectCallback callback + ) + { + _connectCallbacks.Add(callback); + + return () => { + _connectCallbacks.Remove(callback); + }; + } + + public void Connect + ( string connectionString + ) + { + if(ecsact_async_connect == null) { + throw new EcsactRuntimeMissingMethod("ecsact_async_connect"); + } + ecsact_async_connect(connectionString); + } + + public void FlushEvents() { + if(ecsact_async_flush_events == null) { + throw new EcsactRuntimeMissingMethod("ecsact_async_flush_events"); + } + + var selfPinned = GCHandle.Alloc(this, GCHandleType.Pinned); + var ownerPinned = GCHandle.Alloc(_owner, GCHandleType.Pinned); + try { + var selfIntPtr = GCHandle.ToIntPtr(selfPinned); + var ownerIntPtr = GCHandle.ToIntPtr(ownerPinned); + _owner._execEvs.initCallbackUserData = ownerIntPtr; + _owner._execEvs.updateCallbackUserData = ownerIntPtr; + _owner._execEvs.removeCallbackUserData = ownerIntPtr; + _asyncEvs.errorCallbackUserData = selfIntPtr; + _asyncEvs.connectCallbackUserData = selfIntPtr; + _asyncEvs.actionCommittedCallbackUserData = selfIntPtr; + ecsact_async_flush_events(in _owner._execEvs, in _asyncEvs); + } finally { + selfPinned.Free(); + ownerPinned.Free(); + } + } } public class Core { @@ -393,7 +550,7 @@ internal delegate void ecsact_add_component_delegate ( Int32 registryId , Int32 entityId , Int32 componentId - , IntPtr componentData + , object componentData ); internal ecsact_add_component_delegate? ecsact_add_component; @@ -439,7 +596,7 @@ internal delegate void ecsact_update_component_delegate ( Int32 registryId , Int32 entityId , Int32 componentId - , IntPtr componentData + , object componentData ); internal ecsact_update_component_delegate? ecsact_update_component; @@ -454,17 +611,26 @@ internal delegate void ecsact_component_event_callback ( EcsactEvent ev , Int32 entityId , Int32 componentId - , IntPtr componentData + , object componentData , IntPtr callbackUserData ); internal delegate void ecsact_execute_systems_delegate - ( Int32 registryId - , Int32 executionCount - , ExecutionOptions[] executionOptionsList - , ExecutionEventsCollector eventsCollector + ( Int32 registryId + , Int32 executionCount + , ExecutionOptions[] executionOptionsList + , in ExecutionEventsCollector eventsCollector ); internal ecsact_execute_systems_delegate? ecsact_execute_systems; + private EcsactRuntime _owner; + + internal Core + ( EcsactRuntime owner + ) + { + _owner = owner; + } + public Int32 CreateRegistry ( string registryName ) @@ -596,7 +762,7 @@ public void AddComponent ( Int32 registryId , Int32 entityId , Int32 componentId - , IntPtr componentData + , object componentData ) { if(ecsact_add_component == null) { @@ -695,7 +861,7 @@ public void UpdateComponent ( Int32 registryId , Int32 entityId , Int32 componentId - , IntPtr componentData + , object componentData ) { if(ecsact_update_component == null) { @@ -724,22 +890,31 @@ public void RemoveComponent } public void ExecuteSystems - ( Int32 registryId - , Int32 executionCount - , ExecutionOptions[] executionOptionsList - , ExecutionEventsCollector eventsCollector + ( Int32 registryId + , Int32 executionCount + , ExecutionOptions[] executionOptionsList ) { if(ecsact_execute_systems == null) { throw new EcsactRuntimeMissingMethod("ecsact_execute_systems"); } - ecsact_execute_systems( - registryId, - executionCount, - executionOptionsList, - eventsCollector - ); + var ownerPinned = GCHandle.Alloc(_owner, GCHandleType.Pinned); + + try { + var ownerIntPtr = GCHandle.ToIntPtr(ownerPinned); + _owner._execEvs.initCallbackUserData = ownerIntPtr; + _owner._execEvs.updateCallbackUserData = ownerIntPtr; + _owner._execEvs.removeCallbackUserData = ownerIntPtr; + ecsact_execute_systems( + registryId, + executionCount, + executionOptionsList, + in _owner._execEvs + ); + } finally { + ownerPinned.Free(); + } } } @@ -784,7 +959,7 @@ internal delegate void ecsact_system_execution_context_action_delegate internal delegate void ecsact_system_execution_context_add_delegate ( IntPtr context , Int32 componentId - , IntPtr componentData + , object componentData ); internal ecsact_system_execution_context_add_delegate? ecsact_system_execution_context_add; @@ -795,16 +970,16 @@ internal delegate void ecsact_system_execution_context_remove_delegate internal ecsact_system_execution_context_remove_delegate? ecsact_system_execution_context_remove; internal delegate void ecsact_system_execution_context_get_delegate - ( IntPtr context - , Int32 componentId - , IntPtr outComponentData + ( IntPtr context + , Int32 componentId + , out object outComponentData ); internal ecsact_system_execution_context_get_delegate? ecsact_system_execution_context_get; internal delegate void ecsact_system_execution_context_update_delegate ( IntPtr context , Int32 componentId - , IntPtr componentData + , object componentData ); internal ecsact_system_execution_context_update_delegate? ecsact_system_execution_context_update; @@ -818,7 +993,7 @@ internal delegate void ecsact_system_execution_context_generate_delegate ( IntPtr context , Int32 componentCount , Int32[] componentIds - , IntPtr[] componentsData + , object[] componentsData ); internal ecsact_system_execution_context_generate_delegate? ecsact_system_execution_context_generate; @@ -1032,7 +1207,7 @@ internal delegate void ecsact_serialize_action_delegate internal delegate void ecsact_serialize_component_delegate ( Int32 componentId - , IntPtr inComponentData + , object inComponentData , IntPtr outBytes ); internal ecsact_serialize_component_delegate? ecsact_serialize_component; @@ -1045,9 +1220,9 @@ internal delegate void ecsact_deserialize_action_delegate internal ecsact_deserialize_action_delegate? ecsact_deserialize_action; internal delegate void ecsact_deserialize_component_delegate - ( Int32 componentId - , IntPtr inBytes - , IntPtr outComponentData + ( Int32 componentId + , IntPtr inBytes + , out object outComponentData ); internal ecsact_deserialize_component_delegate? ecsact_deserialize_component; } @@ -1111,7 +1286,13 @@ private static void LoadDelegate IntPtr addr; if(NativeLibrary.TryGetExport(lib, name, out addr)) { outDelegate = Marshal.GetDelegateForFunctionPointer(addr); - availableMethods.Add(name); + if(outDelegate != null) { + availableMethods.Add(name); + } else { + UnityEngine.Debug.LogError( + $"{name} is not a function in runtime library" + ); + } } else { outDelegate = null; } @@ -1122,8 +1303,8 @@ public static EcsactRuntime Load ) { var runtime = new EcsactRuntime(); - runtime._core = new Core(); - runtime._async = new Async(); + runtime._core = new Core(runtime); + runtime._async = new Async(runtime); runtime._dynamic = new Dynamic(); runtime._meta = new Meta(); runtime._serialize = new Serialize(); @@ -1227,7 +1408,255 @@ public static void Free } } + /// Init Component Untyped Callback + private delegate void InitCompUtCb + ( Int32 entityId + , object component + ); + + /// Update Component Untyped Callback + private delegate void UpCompUtCb + ( Int32 entityId + , object component + ); + + /// Remove Component Untyped Callback + private delegate void RmvCompUtCb + ( Int32 entityId + , object component + ); + + private List _initAnyCompCbs = new(); + private List _updateAnyCompCbs = new(); + private List _removeAnyCompCbs = new(); + private Dictionary> _initCompCbs = new(); + private Dictionary> _updateCompCbs = new(); + private Dictionary> _removeCompCbs = new(); + internal ExecutionEventsCollector _execEvs; + + private EcsactRuntime() { + _execEvs = new ExecutionEventsCollector{ + initCallback = OnInitComponentHandler, + initCallbackUserData = IntPtr.Zero, + updateCallback = OnUpdateComponentHandler, + updateCallbackUserData = IntPtr.Zero, + removeCallback = OnRemoveComponentHandler, + removeCallbackUserData = IntPtr.Zero, + }; + } + ~EcsactRuntime() { Free(this); } + + public delegate void InitComponentCallback + ( Int32 entityId + , Int32 componentId + , object component + ); + + public Action OnInitComponent + ( InitComponentCallback callback + ) + { + _initAnyCompCbs.Add(callback); + return () => { + _initAnyCompCbs.Remove(callback); + }; + } + + public delegate void InitComponentCallback + ( Int32 entityId + , ComponentT component + ) where ComponentT : Ecsact.Component; + + /// Adds a callback for when component init event is fired. + /// Action that clears callback upon invocation. + public Action OnInitComponent + ( InitComponentCallback callback + ) where ComponentT : Ecsact.Component + { + var compId = Ecsact.Util.GetComponentID()!; + + if(!_initCompCbs.TryGetValue(compId, out var callbacks)) { + callbacks = new(); + _initCompCbs.Add(compId, callbacks); + } + + InitCompUtCb cb = (entityId, comp) => { + callback(entityId, (ComponentT)comp); + }; + + callbacks.Add(cb); + + return () => { + if(_initCompCbs.TryGetValue(compId, out var callbacks)) { + callbacks.Remove(cb); + } + }; + } + + public delegate void UpdateComponentCallback + ( Int32 entityId + , Int32 componentId + , object component + ); + + public Action OnUpdateComponent + ( UpdateComponentCallback callback + ) + { + _updateAnyCompCbs.Add(callback); + return () => { + _updateAnyCompCbs.Remove(callback); + }; + } + + public delegate void UpdateComponentCallback + ( Int32 entityId + , ComponentT component + ) where ComponentT : Ecsact.Component; + + public Action OnUpdateComponent + ( UpdateComponentCallback callback + ) where ComponentT : Ecsact.Component + { + var compId = Ecsact.Util.GetComponentID()!; + + if(!_updateCompCbs.TryGetValue(compId, out var callbacks)) { + callbacks = new(); + _updateCompCbs.Add(compId, callbacks); + } + + UpCompUtCb cb = (entityId, comp) => { + callback(entityId, (ComponentT)comp); + }; + + callbacks.Add(cb); + + return () => { + if(_updateCompCbs.TryGetValue(compId, out var callbacks)) { + callbacks.Remove(cb); + } + }; + } + + public delegate void RemoveComponentCallback + ( Int32 entityId + , Int32 componentId + , object component + ); + + public Action OnRemoveComponent + ( RemoveComponentCallback callback + ) + { + _removeAnyCompCbs.Add(callback); + return () => { + _removeAnyCompCbs.Remove(callback); + }; + } + + public delegate void RemoveComponentCallback + ( Int32 entityId + , ComponentT component + ) where ComponentT : Ecsact.Component; + + public Action OnRemoveComponent + ( RemoveComponentCallback callback + ) where ComponentT : Ecsact.Component + { + var compId = Ecsact.Util.GetComponentID()!; + + if(!_removeCompCbs.TryGetValue(compId, out var callbacks)) { + callbacks = new(); + _removeCompCbs.Add(compId, callbacks); + } + + RmvCompUtCb cb = (entityId, comp) => { + callback(entityId, (ComponentT)comp); + }; + + callbacks.Add(cb); + + return () => { + if(_removeCompCbs.TryGetValue(compId, out var callbacks)) { + callbacks.Remove(cb); + } + }; + } + + + [AOT.MonoPInvokeCallback(typeof(ComponentEventCallback))] + private static void OnInitComponentHandler + ( EcsactEvent ev + , Int32 entityId + , Int32 componentId + , object componentData + , IntPtr callbackUserData + ) + { + UnityEngine.Debug.Assert(ev == EcsactEvent.InitComponent); + + var self = (GCHandle.FromIntPtr(callbackUserData).Target as EcsactRuntime)!; + + if(self._initCompCbs.TryGetValue(componentId, out var cbs)) { + foreach(var cb in cbs) { + cb(entityId, componentData); + } + } + + foreach(var cb in self._initAnyCompCbs) { + cb(entityId, componentId, componentData); + } + } + + [AOT.MonoPInvokeCallback(typeof(ComponentEventCallback))] + private static void OnUpdateComponentHandler + ( EcsactEvent ev + , Int32 entityId + , Int32 componentId + , object componentData + , IntPtr callbackUserData + ) + { + UnityEngine.Debug.Assert(ev == EcsactEvent.UpdateComponent); + + var self = (GCHandle.FromIntPtr(callbackUserData).Target as EcsactRuntime)!; + + if(self._updateCompCbs.TryGetValue(componentId, out var cbs)) { + foreach(var cb in cbs) { + cb(entityId, componentData); + } + } + + foreach(var cb in self._updateAnyCompCbs) { + cb(entityId, componentId, componentData); + } + } + + [AOT.MonoPInvokeCallback(typeof(ComponentEventCallback))] + private static void OnRemoveComponentHandler + ( EcsactEvent ev + , Int32 entityId + , Int32 componentId + , object componentData + , IntPtr callbackUserData + ) + { + UnityEngine.Debug.Assert(ev == EcsactEvent.RemoveComponent); + + var self = (GCHandle.FromIntPtr(callbackUserData).Target as EcsactRuntime)!; + + if(self._removeCompCbs.TryGetValue(componentId, out var cbs)) { + foreach(var cb in cbs) { + cb(entityId, componentData); + } + } + + foreach(var cb in self._removeAnyCompCbs) { + cb(entityId, componentId, componentData); + } + } + } diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs index 092f411..9837ea5 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs @@ -1,12 +1,26 @@ using UnityEngine; using System.Collections.Generic; using System.IO; +using System; #if UNITY_EDITOR using UnityEditor; #endif #nullable enable +[System.Serializable] +public class EcsactRuntimeDefaultRegistry { + [Tooltip("Name given to registry. For display and debug purposes only")] + public string registryName = ""; + [Tooltip( + "Create game object at startup with the Ecsact.DefaultRunner script for " + + "this registry." + )] + public bool useDefaultRunner = true; + public EcsactRuntime.ExecutionOptions executionOptions; + public Int32 registryId { get; internal set; } = -1; +} + [System.Serializable] public class EcsactRuntimeSettings : ScriptableObject { private static EcsactRuntimeSettings? _instance; @@ -14,15 +28,22 @@ public class EcsactRuntimeSettings : ScriptableObject { public const string resourcePath = "Settings/EcsactRuntimeSettings.asset"; public const string assetPath = "Assets/Resources/" + resourcePath; + public bool useAsyncRunner = true; + public bool useVisualScriptingEvents = true; + [Tooltip( + "List of ecsact registries that are created automatically when " + + "the game loads." + )] + public List defaultRegistries = new(); public List runtimeLibraryPaths = new(); public static EcsactRuntimeSettings Get() { - if(_instance != null) return _instance; + if(_instance != null) { + return _instance; + } #if UNITY_EDITOR - _instance = AssetDatabase.LoadAssetAtPath( - assetPath - ); + _instance = AssetDatabase.LoadAssetAtPath(assetPath); if(_instance == null) { _instance = ScriptableObject.CreateInstance(); Directory.CreateDirectory(Path.GetDirectoryName(assetPath)); diff --git a/packages/com.seaube.ecsact/Runtime/VisualScripting.meta b/packages/com.seaube.ecsact/Runtime/VisualScripting.meta new file mode 100644 index 0000000..f31bbcd --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScripting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79e56f1c5516a1349abd4c1c3e659eb4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectEvent.cs b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectEvent.cs new file mode 100644 index 0000000..76642b0 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectEvent.cs @@ -0,0 +1,50 @@ +using Unity.VisualScripting; +using UnityEngine; +using System; + +namespace Ecsact.VisualScripting { + public class AsyncConnectEventData { + public string connectAddress; + public Int32 connectPort; + } + + [UnitTitle("On Connect")] + [UnitCategory("Ecsact\\Async")] + [UnitSurtitle("Ecsact / Async")] + public class AsyncConnectEvent : EventUnit { + public const string eventName = "EcsactAsyncConnectEvent"; + + [PortLabel("Connect Address")] + [DoNotSerialize] + public ValueOutput connectAddressOutput { get; private set; } + + [PortLabel("Connect port")] + [DoNotSerialize] + public ValueOutput connectPortOutput { get; private set; } + + protected override bool register => true; + + public override EventHook GetHook + ( GraphReference reference + ) + { + return new EventHook(eventName); + } + + protected override void Definition() { + base.Definition(); + + connectAddressOutput = ValueOutput(nameof(connectAddressOutput)); + connectPortOutput = ValueOutput(nameof(connectPortOutput)); + } + + protected override void AssignArguments + ( Flow flow + , AsyncConnectEventData data + ) + { + flow.SetValue(connectAddressOutput, data.connectAddress); + flow.SetValue(connectPortOutput, data.connectPort); + } + } +} diff --git a/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectEvent.cs.meta b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectEvent.cs.meta new file mode 100644 index 0000000..97cbf8e --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 758219e01a2eabe4c8056cface8bfd5a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectNode.cs b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectNode.cs new file mode 100644 index 0000000..e042399 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectNode.cs @@ -0,0 +1,33 @@ +using Unity.VisualScripting; +using UnityEngine; + +namespace Ecsact.VisualScripting { + [UnitTitle("Connect")] + [UnitCategory("Ecsact\\Async")] + [UnitSurtitle("Ecsact / Async")] + public class AsyncConnectNode : Unit { + [PortLabelHidden] + [DoNotSerialize] + public ControlInput controlInput; + + [PortLabelHidden] + [DoNotSerialize] + public ControlOutput controlOutput; + + [DoNotSerialize] + public ValueInput connectionStringInput; + + protected override void Definition() { + connectionStringInput = ValueInput("connectionString"); + controlOutput = ControlOutput("controlOutput"); + controlInput = ControlInput("controlInput", flow => { + var connectUri = flow.GetValue(connectionStringInput); + EcsactRuntime.GetOrLoadDefault().async.Connect(connectUri); + return controlOutput; + }); + + Requirement(connectionStringInput, controlInput); + Succession(controlInput, controlOutput); + } + } +} diff --git a/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectNode.cs.meta b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectNode.cs.meta new file mode 100644 index 0000000..137d168 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncConnectNode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd5ecdb22ce6e1c43855635c294d0c62 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncErrorEvent.cs b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncErrorEvent.cs new file mode 100644 index 0000000..058be7b --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncErrorEvent.cs @@ -0,0 +1,51 @@ +using Unity.VisualScripting; +using UnityEngine; +using System; + +namespace Ecsact.VisualScripting { + [Serializable] + public class AsyncErrorEventData { + public Ecsact.AsyncError error; + public Int32 requestId; + } + + [UnitTitle("On Error")] + [UnitCategory("Ecsact\\Async")] + [UnitSurtitle("Ecsact / Async")] + public class AsyncErrorEvent : EventUnit { + public const string eventName = "EcsactAsyncErrorEvent"; + + [PortLabel("Error")] + [DoNotSerialize] + public ValueOutput errorOutput { get; private set; } + + [PortLabel("Request ID")] + [DoNotSerialize] + public ValueOutput requestIdOutput { get; private set; } + + protected override bool register => true; + + public override EventHook GetHook + ( GraphReference reference + ) + { + return new EventHook(eventName); + } + + protected override void Definition() { + base.Definition(); + + errorOutput = ValueOutput(nameof(errorOutput)); + requestIdOutput = ValueOutput(nameof(requestIdOutput)); + } + + protected override void AssignArguments + ( Flow flow + , AsyncErrorEventData data + ) + { + flow.SetValue(errorOutput, data.error); + flow.SetValue(requestIdOutput, data.requestId); + } + } +} diff --git a/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncErrorEvent.cs.meta b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncErrorEvent.cs.meta new file mode 100644 index 0000000..17a9f50 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScripting/AsyncErrorEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 096ed9de536db6041b724f6153225f26 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs b/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs new file mode 100644 index 0000000..a9f0615 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs @@ -0,0 +1,49 @@ +using UnityEngine; +using Unity.VisualScripting; +using System; +using System.Collections.Generic; + +#nullable enable + +namespace Ecsact { + [AddComponentMenu("")] + public class VisualScriptingEvents : MonoBehaviour { + private static VisualScriptingEvents? instance = null; + private EcsactRuntime? runtimeInstance = null; + + [RuntimeInitializeOnLoadMethod] + private static void OnRuntimeLoad() { + if(instance != null) { + return; + } + + var settings = EcsactRuntimeSettings.Get(); + if(!settings.useVisualScriptingEvents) { + return; + } + + var gameObject = new GameObject("Ecsact Visual Scripting Events"); + instance = gameObject.AddComponent(); + DontDestroyOnLoad(gameObject); + } + + List diposeCallbacks = new(); + + void OnEnable() { + runtimeInstance = EcsactRuntime.GetOrLoadDefault(); + // diposeCallbacks.Add(runtimeInstance!.@async.OnError((err, reqId) => { + // Unity.VisualScripting.EventBus.Trigger( + // EcsactRuntime.VisualScriptingEventNames.AsyncError, + // err + // ); + // })); + } + + void OnDisable() { + foreach(var diposeCb in diposeCallbacks) { + diposeCb(); + } + diposeCallbacks.Clear(); + } + } +} diff --git a/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs.meta b/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs.meta new file mode 100644 index 0000000..b277d95 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5b22bc46ad84f04aa3c02e7aae89e0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/package.json b/packages/com.seaube.ecsact/package.json index 06e31d5..39de995 100644 --- a/packages/com.seaube.ecsact/package.json +++ b/packages/com.seaube.ecsact/package.json @@ -8,7 +8,8 @@ "name" : "Seaube" }, "dependencies": { - "com.unity.editorcoroutines": "1.0.0" + "com.unity.editorcoroutines": "1.0.0", + "com.unity.visualscripting": "1.7.8" }, "repository": "https://github.com/seaube/ecsact-unity" } diff --git a/packages/com.seaube.ecsact/pkg.bzl b/packages/com.seaube.ecsact/pkg.bzl new file mode 100644 index 0000000..b9acf53 --- /dev/null +++ b/packages/com.seaube.ecsact/pkg.bzl @@ -0,0 +1,100 @@ +load("@rules_pkg//:providers.bzl", "PackageFilegroupInfo", "PackageFilesInfo", "PackageSymlinkInfo") + +def _runfile_path(workspace_name, file): + path = file.short_path + return path[len("../"):] if path.startswith("../") else "%s/%s" % (workspace_name, path) + +def _runfiles_pkg_files(workspace_name, runfiles): + files = {} + for file in runfiles.files.to_list(): + files[_runfile_path(workspace_name, file)] = file + for file in runfiles.symlinks.to_list(): + files[file.path] = "%s/%s" % (workspace_name, files.target_file) + for file in runfiles.root_symlinks.to_list(): + files[file.path] = files.target_file + + return PackageFilesInfo( + dest_src_map = files, + attributes = {"mode": "0755"}, + ) + +def _pkg_runfiles_impl(ctx): + runfiles = ctx.attr.runfiles[DefaultInfo] + label = ctx.label + workspace_name = ctx.workspace_name + + runfiles_files = _runfiles_pkg_files(workspace_name, runfiles.default_runfiles) + + pkg_filegroup_info = PackageFilegroupInfo( + pkg_dirs = [], + pkg_files = [(runfiles_files, label)], + pkg_symlinks = [], + ) + + default_info = DefaultInfo(files = depset(runfiles_files.dest_src_map.values())) + + return [default_info, pkg_filegroup_info] + +pkg_runfiles = rule( + implementation = _pkg_runfiles_impl, + attrs = { + "runfiles": attr.label( + doc = "Runfiles.", + mandatory = True, + ), + }, + provides = [PackageFilegroupInfo], +) + +def _pkg_executable_impl(ctx): + bin = ctx.attr.bin[DefaultInfo] + bin_executable = ctx.executable.bin + bin_executable_ext = bin_executable.extension + if bin_executable_ext: + bin_executable_ext = "." + bin_executable_ext + + path = ctx.attr.path + bin_executable_ext + label = ctx.label + workspace_name = ctx.workspace_name + + runfiles_files = _runfiles_pkg_files(workspace_name, bin.default_runfiles) + + dest_src_map = {"%s.runfiles/%s" % (path, p): file for p, file in runfiles_files.dest_src_map.items()} + + runfiles_files = PackageFilesInfo( + dest_src_map = dest_src_map, + attributes = runfiles_files.attributes, + ) + + executable_symlink = PackageSymlinkInfo( + attributes = {"mode": "0755"}, + destination = path, + target = "%s.runfiles/%s" % (path, _runfile_path(workspace_name, bin_executable)), + ) + + pkg_filegroup_info = PackageFilegroupInfo( + pkg_dirs = [], + pkg_files = [(runfiles_files, label)], + pkg_symlinks = [(executable_symlink, label)], + ) + + default_info = DefaultInfo(files = depset(runfiles_files.dest_src_map.values())) + + return [default_info, pkg_filegroup_info] + +pkg_executable = rule( + implementation = _pkg_executable_impl, + attrs = { + "bin": attr.label( + doc = "Executable.", + executable = True, + cfg = "target", + mandatory = True, + ), + "path": attr.string( + doc = "Packaged path of executable. (Runfiles tree will be at .runfiles.)", + mandatory = True, + ), + }, + provides = [PackageFilegroupInfo], +) diff --git a/packages/com.seaube.ecsact/pkg.bzl.meta b/packages/com.seaube.ecsact/pkg.bzl.meta new file mode 100644 index 0000000..0beb30f --- /dev/null +++ b/packages/com.seaube.ecsact/pkg.bzl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9189d70ae7e8b41459e74b436f859333 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 10d6dad835bb0e603917f4a51d280273170601de Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Mon, 11 Jul 2022 08:43:07 -0700 Subject: [PATCH 08/10] Updated ecsact rtb --- WORKSPACE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 85db5ff..bf615db 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -5,9 +5,9 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") git_repository( name = "ecsact_rtb", - commit = "53d0a81636cc24b0fea4ac4dd626c94962fee0d5", + commit = "28da3744a513210620f63b84e04901387d81b0e5", remote = "git@github.com:seaube/ecsact-rtb.git", - # shallow_since = "1656611470 -0700", + shallow_since = "1657554031 -0700", ) load("@ecsact_rtb//:repositories.bzl", "ecsact_rtb_repositories") From 5426201de8d0017be513315aee49995606ba5bc3 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Fri, 15 Jul 2022 00:52:21 -0700 Subject: [PATCH 09/10] wasm support --- WORKSPACE | 10 +- .../Editor/Ecsact.Editor.asmdef | 46 +++---- .../Editor/EcsactRuntimeBuilder.cs | 17 ++- .../Editor/EcsactSettings.cs | 18 +++ .../Editor/EcsactSettings.uxml | 10 ++ .../Editor/EcsactWasmRuntimeSettingsEditor.cs | 112 +++++++++++++++++ .../EcsactWasmRuntimeSettingsEditor.cs.meta | 11 ++ .../Runtime/DefaultRunner.cs | 5 +- .../com.seaube.ecsact/Runtime/Ecsact.asmdef | 3 +- packages/com.seaube.ecsact/Runtime/Ecsact.cs | 85 ++++++++++--- .../Runtime/EcsactRuntime.cs | 116 +++++++++++++++++- .../Runtime/EcsactRuntimeSettings.cs | 8 +- .../Runtime/EcsactUnitySync.cs | 16 +-- .../Runtime/EcsactWasmRuntimeLoader.cs | 36 ++++++ .../Runtime/EcsactWasmRuntimeLoader.cs.meta | 11 ++ .../Runtime/EcsactWasmRuntimeSettings.cs | 53 ++++++++ .../Runtime/EcsactWasmRuntimeSettings.cs.meta | 11 ++ .../Runtime/EntityGameObjectPool.cs | 48 ++++---- .../Runtime/VisualScriptingEvents.cs | 2 +- 19 files changed, 528 insertions(+), 90 deletions(-) create mode 100644 packages/com.seaube.ecsact/Editor/EcsactWasmRuntimeSettingsEditor.cs create mode 100644 packages/com.seaube.ecsact/Editor/EcsactWasmRuntimeSettingsEditor.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeLoader.cs create mode 100644 packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeLoader.cs.meta create mode 100644 packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeSettings.cs create mode 100644 packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeSettings.cs.meta diff --git a/WORKSPACE b/WORKSPACE index bf615db..3e6dbf1 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -5,9 +5,9 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") git_repository( name = "ecsact_rtb", - commit = "28da3744a513210620f63b84e04901387d81b0e5", + commit = "4d4856d697300a1e3c8b2e49b25acb72e9328b9d", remote = "git@github.com:seaube/ecsact-rtb.git", - shallow_since = "1657554031 -0700", + # shallow_since = "1657554031 -0700", ) load("@ecsact_rtb//:repositories.bzl", "ecsact_rtb_repositories") @@ -20,9 +20,9 @@ ecsact_rtb_workspace() http_archive( name = "bzlws", - sha256 = "5bebb821b158b11d81dd25cf031b5b26bae97dbb02025df7d0e41a262b3a030b", - strip_prefix = "bzlws-f929e5380f441f50a77776d34a7df8cacdbdf986", - url = "https://github.com/zaucy/bzlws/archive/f929e5380f441f50a77776d34a7df8cacdbdf986.zip", + sha256 = "9bc9d6bf1d885992d58a4ad9dc7476a8cd48d672b497707b0ae2c0899c6d369b", + strip_prefix = "bzlws-344801b9b3105bd13e4b51ec9776f04bd5e01972", + url = "https://github.com/zaucy/bzlws/archive/344801b9b3105bd13e4b51ec9776f04bd5e01972.zip", ) load("@bzlws//:repo.bzl", "bzlws_deps") diff --git a/packages/com.seaube.ecsact/Editor/Ecsact.Editor.asmdef b/packages/com.seaube.ecsact/Editor/Ecsact.Editor.asmdef index 102d171..88c1311 100644 --- a/packages/com.seaube.ecsact/Editor/Ecsact.Editor.asmdef +++ b/packages/com.seaube.ecsact/Editor/Ecsact.Editor.asmdef @@ -1,23 +1,25 @@ { - "name": "Ecsact.Editor", - "rootNamespace": "", - "references": [ - "GUID:6e7029456009ab94da19a8f93d5e3523", - "GUID:478a2357cc57436488a56e564b08d223", - "GUID:21b0c8d1703a94250bfac916590cea4f", - "GUID:54cb1906aeb4e4264bc1d2aa3818a43f", - "GUID:ea715009a4efd4c6cbc85be3ae097dd3", - "GUID:23f3c6063e13e4fd9addce3606ff4e42" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false -} + "name": "Ecsact.Editor", + "rootNamespace": "", + "references": [ + "GUID:6e7029456009ab94da19a8f93d5e3523", + "GUID:478a2357cc57436488a56e564b08d223", + "GUID:21b0c8d1703a94250bfac916590cea4f", + "GUID:54cb1906aeb4e4264bc1d2aa3818a43f", + "GUID:ea715009a4efd4c6cbc85be3ae097dd3", + "GUID:23f3c6063e13e4fd9addce3606ff4e42", + "GUID:7faa839a710e565409361b9f0c62a4da", + "GUID:2cd956959600cec4a97cd312e2adbdbb" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs b/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs index ca7dbd4..63fbeef 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs +++ b/packages/com.seaube.ecsact/Editor/EcsactRuntimeBuilder.cs @@ -57,11 +57,15 @@ public static void Build var line = ev.Data; if(!string.IsNullOrWhiteSpace(line)) { Progress.SetDescription(progressId, line); + UnityEngine.Debug.Log(line); } }; proc.Exited += (_, _) => { if(proc.ExitCode != 0) { + UnityEngine.Debug.LogError( + $"ecsact-rtb exited with code {proc.ExitCode}" + ); Progress.Finish(progressId, Progress.Status.Failed); } else { Progress.Finish(progressId, Progress.Status.Succeeded); @@ -73,7 +77,9 @@ public static void Build } proc.StartInfo.Arguments += "--output=\""; - proc.StartInfo.Arguments += settings.runtimeBuilderOutputPath; + proc.StartInfo.Arguments += Path.GetFullPath( + settings.runtimeBuilderOutputPath + ); #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN proc.StartInfo.Arguments += ".dll"; #elif UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX @@ -87,15 +93,20 @@ public static void Build if(!string.IsNullOrWhiteSpace(settings.runtimeBuilderCompilerPath)) { proc.StartInfo.Arguments += "--compiler_path=\""; - proc.StartInfo.Arguments += settings.runtimeBuilderCompilerPath; + proc.StartInfo.Arguments += Path.GetFullPath( + settings.runtimeBuilderCompilerPath + ); proc.StartInfo.Arguments += "\" "; } proc.StartInfo.Arguments += "--temp_dir="; - proc.StartInfo.Arguments += FileUtil.GetUniqueTempPathInProject(); + proc.StartInfo.Arguments += Path.GetFullPath( + FileUtil.GetUniqueTempPathInProject() + ); UnityEngine.Debug.Log(proc.StartInfo.FileName); UnityEngine.Debug.Log(proc.StartInfo.Arguments); + UnityEngine.Debug.Log($"CWD: {System.IO.Directory.GetCurrentDirectory()}"); Progress.Report(progressId, 0.1f); proc.Start(); diff --git a/packages/com.seaube.ecsact/Editor/EcsactSettings.cs b/packages/com.seaube.ecsact/Editor/EcsactSettings.cs index 47cc821..7061280 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactSettings.cs +++ b/packages/com.seaube.ecsact/Editor/EcsactSettings.cs @@ -86,6 +86,7 @@ internal static void SetupMethodsUI public static SettingsProvider CreateEcsactSettingsProvider() { EcsactRuntime? testDefaultRuntime = null; Editor? runtimeSettingsEditor = null; + Editor? wasmRuntimeSettingsEditor = null; return new SettingsProvider(EcsactSettings.path, EcsactSettings.scope) { label = "Ecsact", @@ -150,6 +151,13 @@ public static SettingsProvider CreateEcsactSettingsProvider() { testDefaultRuntime.@static.availableMethods ); + SetupMethodsUI( + ui, + "wasm", + EcsactRuntime.Wasm.methods, + testDefaultRuntime.wasm.availableMethods + ); + var runtimeSettingsContainer = ui.Q("runtime-settings-container"); @@ -158,6 +166,16 @@ public static SettingsProvider CreateEcsactSettingsProvider() { runtimeSettingsContainer.onGUIHandler = () => { runtimeSettingsEditor.OnInspectorGUI(); }; + + var wasmRuntimeSettings = EcsactWasmRuntimeSettings.Get(); + var wasmRuntimeSettingsContainer = + ui.Q("wasm-runtime-settings-container"); + + wasmRuntimeSettingsEditor = Editor.CreateEditor(wasmRuntimeSettings); + + wasmRuntimeSettingsContainer.onGUIHandler = () => { + wasmRuntimeSettingsEditor.OnInspectorGUI(); + }; }, deactivateHandler = () => { if(testDefaultRuntime != null) { diff --git a/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml b/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml index 6e348d6..f553aff 100644 --- a/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml +++ b/packages/com.seaube.ecsact/Editor/EcsactSettings.uxml @@ -31,6 +31,10 @@ + + + + @@ -67,5 +71,11 @@ + + + + + + diff --git a/packages/com.seaube.ecsact/Editor/EcsactWasmRuntimeSettingsEditor.cs b/packages/com.seaube.ecsact/Editor/EcsactWasmRuntimeSettingsEditor.cs new file mode 100644 index 0000000..f72b3fa --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/EcsactWasmRuntimeSettingsEditor.cs @@ -0,0 +1,112 @@ +using System; +using UnityEditor; +using UnityEngine; +using System.Linq; +using System.Collections.Generic; + +#nullable enable + +[CustomEditor(typeof(EcsactWasmRuntimeSettings))] +public class EcsactWasmRuntimeSettingsEditor : Editor { + + private List? systemLikeTypes; + private Dictionary wasmInfos = new(); + private bool allWasmInfoLoaded = false; + + public override void OnInspectorGUI() { + var settings = target as EcsactWasmRuntimeSettings; + if(settings == null) return; + + if(systemLikeTypes == null) { + systemLikeTypes = Ecsact.Util.GetAllSystemLikeTypes().ToList(); + } + + EditorGUILayout.LabelField( + label: "System Implementations", + style: EditorStyles.boldLabel + ); + + allWasmInfoLoaded = true; + + foreach(Type systemLikeType in systemLikeTypes) { + EditorGUILayout.BeginHorizontal(); + + var systemLikeId = Ecsact.Util.GetSystemID(systemLikeType); + + EditorGUILayout.LabelField(systemLikeType.FullName); + var entry = settings.wasmSystemEntries.Find( + entry => entry.systemId == systemLikeId + ); + if(entry == null) { + settings.wasmSystemEntries.Add(new()); + entry = settings.wasmSystemEntries.Last(); + } + entry.systemId = systemLikeId; + entry.wasmAsset = EditorGUILayout.ObjectField( + obj: entry.wasmAsset, + objType: typeof(WasmAsset), + allowSceneObjects: false + ) as WasmAsset; + + if(entry.wasmAsset != null) { + if(!wasmInfos.ContainsKey(systemLikeId)) { + wasmInfos.Add(systemLikeId, null); + WasmInfo.Load( + AssetDatabase.GetAssetPath(entry.wasmAsset), + loadedWasmInfo => { wasmInfos[systemLikeId] = loadedWasmInfo; }, + () => { wasmInfos.Remove(systemLikeId); }, + settings + ); + } + } + + var wasmInfo = wasmInfos.ContainsKey(systemLikeId) + ? wasmInfos[systemLikeId] + : null; + + if(wasmInfo == null) { + allWasmInfoLoaded = false; + } + + EditorGUI.BeginDisabledGroup(entry.wasmAsset == null); + var dropdownContent = new GUIContent(entry.wasmExportName); + var dropdownPressed = EditorGUILayout.DropdownButton( + dropdownContent, + FocusType.Keyboard + ); + EditorGUI.EndDisabledGroup(); + + if(dropdownPressed && wasmInfo != null) { + var dropdownMenu = new GenericMenu(); + dropdownMenu.allowDuplicateNames = false; + foreach(var export in wasmInfo.exports) { + bool isValidSystemImpl = + export.type == "WASM_EXTERN_FUNC" && + export.results!.Count == 0 && + export.@params!.Count == 1; + + var menuItemContent = new GUIContent(export.name); + + if(isValidSystemImpl) { + dropdownMenu.AddItem( + menuItemContent, + entry.wasmExportName == export.name, + () => { entry.wasmExportName = export.name; } + ); + } else { + dropdownMenu.AddDisabledItem(menuItemContent, false); + } + } + dropdownMenu.ShowAsContext(); + } + + EditorGUILayout.EndHorizontal(); + } + + serializedObject.ApplyModifiedProperties(); + } + + public override bool RequiresConstantRepaint() { + return !allWasmInfoLoaded; + } +} diff --git a/packages/com.seaube.ecsact/Editor/EcsactWasmRuntimeSettingsEditor.cs.meta b/packages/com.seaube.ecsact/Editor/EcsactWasmRuntimeSettingsEditor.cs.meta new file mode 100644 index 0000000..d75fef5 --- /dev/null +++ b/packages/com.seaube.ecsact/Editor/EcsactWasmRuntimeSettingsEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 32b5d0323c80fdf4fbd6990ff981a1ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs b/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs index ef19b78..b97e16b 100644 --- a/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs +++ b/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs @@ -1,5 +1,6 @@ using UnityEngine; using System; +using System.Diagnostics; using System.Runtime.CompilerServices; #nullable enable @@ -44,10 +45,10 @@ void Awake() { void Update() { if(defReg == null) return; - Debug.Assert(defReg.registryId != -1); + UnityEngine.Debug.Assert(defReg.registryId != -1); #if UNITY_EDITOR - var executionTimeWatch = System.Diagnostics.Stopwatch.StartNew(); + var executionTimeWatch = Stopwatch.StartNew(); #endif runtimeInstance!.core.ExecuteSystems( diff --git a/packages/com.seaube.ecsact/Runtime/Ecsact.asmdef b/packages/com.seaube.ecsact/Runtime/Ecsact.asmdef index 2f61bf2..e168f72 100644 --- a/packages/com.seaube.ecsact/Runtime/Ecsact.asmdef +++ b/packages/com.seaube.ecsact/Runtime/Ecsact.asmdef @@ -3,7 +3,8 @@ "rootNamespace": "", "references": [ "GUID:21b0c8d1703a94250bfac916590cea4f", - "GUID:ea715009a4efd4c6cbc85be3ae097dd3" + "GUID:ea715009a4efd4c6cbc85be3ae097dd3", + "GUID:7faa839a710e565409361b9f0c62a4da" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/packages/com.seaube.ecsact/Runtime/Ecsact.cs b/packages/com.seaube.ecsact/Runtime/Ecsact.cs index 1b9790d..9dfdc0a 100644 --- a/packages/com.seaube.ecsact/Runtime/Ecsact.cs +++ b/packages/com.seaube.ecsact/Runtime/Ecsact.cs @@ -15,6 +15,9 @@ public interface Component {} /// ECSACT Action Marker Interface public interface Action {} + /// ECSACT System Marker Interface + public interface System {} + public static class Util { private static Dictionary cachedComponentTypes; @@ -23,7 +26,7 @@ static Util() { } public static bool IsComponent - ( System.Type componentType + ( Type componentType ) { foreach(var i in componentType.GetInterfaces()) { @@ -35,16 +38,42 @@ public static bool IsComponent return false; } + public static bool IsSystem + ( Type systemType + ) + { + foreach(var i in systemType.GetInterfaces()) { + if(i == typeof(Ecsact.System)) { + return true; + } + } + + return false; + } + + public static bool IsAction + ( Type actionType + ) + { + foreach(var i in actionType.GetInterfaces()) { + if(i == typeof(Ecsact.Action)) { + return true; + } + } + + return false; + } + public static object? PtrToComponent - ( IntPtr componentIntPtr - , System.Int32 componentId + ( IntPtr componentIntPtr + , Int32 componentId ) { var componentType = GetComponentType(componentId); if(componentType != null) { if(componentIntPtr == IntPtr.Zero) { - return System.Activator.CreateInstance(componentType); + return Activator.CreateInstance(componentType); } return Marshal.PtrToStructure(componentIntPtr, componentType); } @@ -53,23 +82,23 @@ public static object? PtrToComponent } public static void ComponentToPtr - ( object component - , System.Int32 componentId - , IntPtr componentIntPtr + ( object component + , Int32 componentId + , IntPtr componentIntPtr ) { Marshal.StructureToPtr(component, componentIntPtr, false); } - public static System.Type? GetComponentType - ( System.Int32 componentId + public static Type? GetComponentType + ( Int32 componentId ) { if(cachedComponentTypes.TryGetValue(componentId, out var t)) { return t; } - foreach(var assembly in System.AppDomain.CurrentDomain.GetAssemblies()) { + foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies()) { foreach(var type in assembly.GetTypes()) { if(IsComponent(type)) { var typeComponentId = GetComponentID(type); @@ -87,12 +116,12 @@ public static void ClearComponentTypeCache() { cachedComponentTypes.Clear(); } - public static System.Int32 GetComponentID() where T : Ecsact.Component { + public static Int32 GetComponentID() where T : Ecsact.Component { return GetComponentID(typeof(T)); } - public static System.Int32 GetComponentID - ( System.Type componentType + public static Int32 GetComponentID + ( Type componentType ) { if(!IsComponent(componentType)) { @@ -104,17 +133,28 @@ public static System.Int32 GetComponentID BindingFlags.Static | BindingFlags.Public ); - var componentId = (System.Int32)idField.GetValue(null); + var componentId = (Int32)idField.GetValue(null); cachedComponentTypes[componentId] = componentType; return componentId; } - public static System.Int32 GetActionID() where T : Ecsact.Action { + public static Int32 GetActionID() where T : Ecsact.Action { return GetActionID(typeof(T)); } - public static System.Int32 GetActionID - ( System.Type actionType + public static Int32 GetActionID + ( Type actionType + ) + { + return GetSystemID(actionType); + } + + public static Int32 GetSystemID() where T : Ecsact.System { + return GetSystemID(typeof(T)); + } + + public static Int32 GetSystemID + ( Type actionType ) { var idField = actionType.GetField( @@ -122,7 +162,7 @@ public static System.Int32 GetActionID BindingFlags.Static | BindingFlags.Public ); - return (System.Int32)idField.GetValue(null); + return (Int32)idField.GetValue(null); } public static IEnumerable GetComponentIdPermutations @@ -170,5 +210,14 @@ public static IEnumerable GetComponentIdPermutations UnityEngine.Debug.Log("PERMUTATION END"); } + public static IEnumerable GetAllSystemLikeTypes() { + foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies()) { + foreach(var type in assembly.GetTypes()) { + if(Util.IsSystem(type) || Util.IsAction(type)) { + yield return type; + } + } + } + } } } diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs index d9894e3..161e38c 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs @@ -277,11 +277,34 @@ public static EcsactRuntime GetOrLoadDefault() { var settings = EcsactRuntimeSettings.Get(); defaultInstance = Load(settings.runtimeLibraryPaths); - foreach(var defReg in settings.defaultRegistries) { - defReg.registryId = defaultInstance.core.CreateRegistry( - defReg.registryName - ); + if(defaultInstance != null) { + foreach(var defReg in settings.defaultRegistries) { + defReg.registryId = defaultInstance.core.CreateRegistry( + defReg.registryName + ); + } + } + } + + if(defaultInstance == null) { +#if UNITY_EDITOR + UnityEditor.EditorApplication.isPlaying = false; + var okQuit = UnityEditor.EditorUtility.DisplayDialog( + title: "Failed to load default ecsact runtime", + message: "Please check your ecsact runtime settings", + ok: "Ok Quit", + cancel: "Continue Anyways" + ); + + if(okQuit) { + UnityEditor.EditorApplication.isPlaying = false; } + UnityEngine.Application.Quit(1); +#else + UnityEngine.Debug.LogError("Failed to load default ecsact runtime"); + UnityEngine.Application.Quit(1); +#endif + throw new Exception("Failed to load default ecsact runtime"); } return defaultInstance; @@ -304,6 +327,7 @@ public static void SetDefault private Meta? _meta; private Serialize? _serialize; private Static? _static; + private Wasm? _wasm; public class Async { internal List _availableMethods = new(); @@ -1269,12 +1293,90 @@ internal delegate void ecsact_static_off_reload_delegate internal ecsact_static_off_reload_delegate? ecsact_static_off_reload; } + // TODO(zaucy): This is misplaced here. It should be organized somewhere else + // maybe in another package + // ecsactsi_* fns + public class Wasm { + public enum Error { + Ok, + OpenFail, + ReadFail, + CompileFail, + InstantiateFail, + ExportNotFound, + ExportInvalid, + GuestImportUnknown, + } + + internal List _availableMethods = new(); + public static string[] methods => new string[]{ + "ecsactsi_wasm_load", + "ecsactsi_wasm_load_file", + "ecsactsi_wasm_set_trap_handler", + }; + + public IEnumerable availableMethods => _availableMethods; + + internal delegate void ecsactsi_wasm_load_delegate + ( sbyte[] wasmData + , Int32 wasmDataSize + , Int32 systemsCount + , Int32[] systmIds + , string[] wasmExports + ); + + internal ecsactsi_wasm_load_delegate? ecsactsi_wasm_load; + + internal delegate void ecsactsi_wasm_load_file_delegate + ( [MarshalAs(UnmanagedType.LPStr)] string wasmFilePath + , Int32 systemsCount + , Int32[] systmIds + , string[] wasmExports + ); + + internal ecsactsi_wasm_load_file_delegate? ecsactsi_wasm_load_file; + + internal delegate void ecsactsi_wasm_trap_handler + ( Int32 systemId + , [MarshalAs(UnmanagedType.LPStr)] string trapMessage + ); + + internal delegate void ecsactsi_wasm_set_trap_handler_delegate + ( ecsactsi_wasm_trap_handler handler + ); + + internal ecsactsi_wasm_set_trap_handler_delegate? ecsactsi_wasm_set_trap_handler; + + public void Load + ( byte[] wasmData + , Int32 systemId + , string exportName + ) + { + if(ecsactsi_wasm_load == null) { + throw new EcsactRuntimeMissingMethod("ecsactsi_wasm_load"); + } + + var systemIds = new Int32[]{systemId}; + var exportNames = new string[]{exportName}; + + ecsactsi_wasm_load( + (sbyte[]) (Array) wasmData, + wasmData.Length, + 1, + systemIds, + exportNames + ); + } + } + public Core core => _core!; public Async async => _async!; public Dynamic dynamic => _dynamic!; public Meta meta => _meta!; public Serialize serialize => _serialize!; public Static @static => _static!; + public Wasm wasm => _wasm!; private static void LoadDelegate ( IntPtr lib @@ -1309,6 +1411,7 @@ public static EcsactRuntime Load runtime._meta = new Meta(); runtime._serialize = new Serialize(); runtime._static = new Static(); + runtime._wasm = new Wasm(); runtime._libs = libraryPaths.Select(path => NativeLibrary.Load(path)).ToArray(); @@ -1390,6 +1493,11 @@ public static EcsactRuntime Load LoadDelegate(lib, "ecsact_static_actions", out runtime._static.ecsact_static_actions, runtime._static._availableMethods); LoadDelegate(lib, "ecsact_static_on_reload", out runtime._static.ecsact_static_on_reload, runtime._static._availableMethods); LoadDelegate(lib, "ecsact_static_off_reload", out runtime._static.ecsact_static_off_reload, runtime._static._availableMethods); + + // Load system implementation wasm methods + LoadDelegate(lib, "ecsactsi_wasm_load", out runtime._wasm.ecsactsi_wasm_load, runtime._wasm._availableMethods); + LoadDelegate(lib, "ecsactsi_wasm_load_file", out runtime._wasm.ecsactsi_wasm_load_file, runtime._wasm._availableMethods); + LoadDelegate(lib, "ecsactsi_wasm_set_trap_handler", out runtime._wasm.ecsactsi_wasm_set_trap_handler, runtime._wasm._availableMethods); } return runtime; diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs index 9837ea5..1b05171 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntimeSettings.cs @@ -25,8 +25,8 @@ public class EcsactRuntimeDefaultRegistry { public class EcsactRuntimeSettings : ScriptableObject { private static EcsactRuntimeSettings? _instance; - public const string resourcePath = "Settings/EcsactRuntimeSettings.asset"; - public const string assetPath = "Assets/Resources/" + resourcePath; + public const string resourcePath = "Settings/EcsactRuntimeSettings"; + public const string assetPath = "Assets/Resources/" + resourcePath + ".asset"; public bool useAsyncRunner = true; public bool useVisualScriptingEvents = true; @@ -54,6 +54,10 @@ public static EcsactRuntimeSettings Get() { _instance = Resources.Load(resourcePath) as EcsactRuntimeSettings; #endif + if(_instance == null) { + throw new Exception("Failed to load ecsact runtime settings"); + } + return _instance; } } diff --git a/packages/com.seaube.ecsact/Runtime/EcsactUnitySync.cs b/packages/com.seaube.ecsact/Runtime/EcsactUnitySync.cs index 39d4474..0729b1e 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactUnitySync.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactUnitySync.cs @@ -26,7 +26,7 @@ namespace Ecsact.UnitySync { public interface IRequired where T : Ecsact.Component {} public interface IOnInitEntity { - void OnInitEntity(System.Int32 entityId); + void OnInitEntity(Int32 entityId); } public interface IOnInitComponent where T : Ecsact.Component { @@ -374,8 +374,8 @@ public static IEnumerable GetInterfaces } } - public static IEnumerable GetInitComponentIds - ( System.Type type + public static IEnumerable GetInitComponentIds + ( Type type ) { if(onInitComponentsMap.TryGetValue(type, out var compIds)) { @@ -385,8 +385,8 @@ public static IEnumerable GetInitComponentIds } } - public static IEnumerable GetRemoveComponentIds - ( System.Type type + public static IEnumerable GetRemoveComponentIds + ( Type type ) { if(onInitComponentsMap.TryGetValue(type, out var compIds)) { @@ -396,8 +396,8 @@ public static IEnumerable GetRemoveComponentIds } } - public static IEnumerable GetRequiredComponentIds - ( System.Type type + public static IEnumerable GetRequiredComponentIds + ( Type type ) { if(requiredComponentsMap.TryGetValue(type, out var compIds)) { @@ -555,7 +555,7 @@ private static void RegisterOnRemoveComponentInterface private static void AddKnownComponentId - ( System.Int32 componentId + ( Int32 componentId ) { if(knownComponentIds.Add(componentId)) { diff --git a/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeLoader.cs b/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeLoader.cs new file mode 100644 index 0000000..5212cc7 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeLoader.cs @@ -0,0 +1,36 @@ +using System; +using UnityEngine; + +namespace Ecsact { + public static class EcsactWasmRuntimeLoader { + [RuntimeInitializeOnLoadMethod] + public static void OnLoad() { + var settings = EcsactWasmRuntimeSettings.Get(); + if(!settings.useDefaultLoader) { + return; + } + + var defaultRuntime = EcsactRuntime.GetOrLoadDefault(); + // defaultRuntime.wasm. + + foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies()) { + foreach(var type in assembly.GetTypes()) { + if(Util.IsSystem(type) || Util.IsAction(type)) { + Debug.Log($"System {type.Name} ID is {Util.GetSystemID(type)}"); + } + } + } + + foreach(var entry in settings.wasmSystemEntries) { + if(string.IsNullOrWhiteSpace(entry.wasmExportName)) continue; + + Debug.Log($"Loading {entry.wasmExportName}"); + defaultRuntime.wasm.Load( + entry.wasmAsset.bytes, + entry.systemId, + entry.wasmExportName + ); + } + } + } +} diff --git a/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeLoader.cs.meta b/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeLoader.cs.meta new file mode 100644 index 0000000..21dc2c2 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeLoader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: daba126f39fdfdd4381f0b63dff1369c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeSettings.cs b/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeSettings.cs new file mode 100644 index 0000000..fd50391 --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeSettings.cs @@ -0,0 +1,53 @@ +using UnityEngine; +using System.Collections.Generic; +using System.IO; +using System; +#if UNITY_EDITOR +using UnityEditor; +#endif + +#nullable enable + +[System.Serializable] +public class EcsactWasmRuntimeSettings : ScriptableObject { + [System.Serializable] + public class SystemMapEntry { + public Int32 systemId = -1; + public WasmAsset? wasmAsset; + public string wasmExportName = ""; + } + + private static EcsactWasmRuntimeSettings? _instance; + + public const string resourcePath = "Settings/EcsactWasmRuntimeSettings"; + public const string assetPath = "Assets/Resources/" + resourcePath + ".asset"; + + public bool useDefaultLoader = true; + public List wasmSystemEntries = new(); + + public static EcsactWasmRuntimeSettings Get() { + if(_instance != null) { + return _instance; + } + +#if UNITY_EDITOR + _instance = AssetDatabase.LoadAssetAtPath( + assetPath + ); + if(_instance == null) { + _instance = ScriptableObject.CreateInstance(); + Directory.CreateDirectory(Path.GetDirectoryName(assetPath)); + AssetDatabase.CreateAsset(_instance, assetPath); + AssetDatabase.SaveAssetIfDirty(_instance); + } +#else + _instance = Resources.Load(resourcePath) as EcsactWasmRuntimeSettings; +#endif + + if(_instance == null) { + throw new Exception("Failed to load ecsact wasm runtime settings"); + } + + return _instance; + } +} diff --git a/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeSettings.cs.meta b/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeSettings.cs.meta new file mode 100644 index 0000000..78b295c --- /dev/null +++ b/packages/com.seaube.ecsact/Runtime/EcsactWasmRuntimeSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61f6048043c761b47b4e0be2b0a0e30e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/com.seaube.ecsact/Runtime/EntityGameObjectPool.cs b/packages/com.seaube.ecsact/Runtime/EntityGameObjectPool.cs index 65efbc2..610cef6 100644 --- a/packages/com.seaube.ecsact/Runtime/EntityGameObjectPool.cs +++ b/packages/com.seaube.ecsact/Runtime/EntityGameObjectPool.cs @@ -14,12 +14,12 @@ namespace Ecsact.UnitySync { public class EntityGameObjectPool : ScriptableObject { public abstract class EntitySource { public abstract object GetComponent - ( System.Int32 entityId - , System.Int32 componentId + ( Int32 entityId + , Int32 componentId ); public abstract bool HasComponent - ( System.Int32 entityId - , System.Int32 componentId + ( Int32 entityId + , Int32 componentId ); } @@ -58,7 +58,7 @@ public Scene? targetScene { get => _targetScene; set { if(_rootGameObject != null) { - throw new System.ArgumentException( + throw new ArgumentException( "EntityGameObjectPool.targetScene may not be set if " + "EntityGameObjectPool.rootGameObject is set." ); @@ -80,7 +80,7 @@ public GameObject? rootGameObject { get => _rootGameObject; set { if(_targetScene != null) { - throw new System.ArgumentException( + throw new ArgumentException( "EntityGameObjectPool.rootGameObject may not be set if " + "EntityGameObjectPool.targetScene is set." ); @@ -112,7 +112,7 @@ void OnDisable() { } public GameObject? GetEntityGameObject - ( System.Int32 entityId + ( Int32 entityId ) { if(entityGameObjects.Count > entityId) { @@ -135,8 +135,8 @@ public void Clear() { } public void InitComponent - ( System.Int32 entityId - , in T component + ( Int32 entityId + , in T component ) where T : Ecsact.Component { InitComponent( @@ -147,9 +147,9 @@ public void InitComponent } public void InitComponent - ( System.Int32 entityId - , System.Int32 componentId - , in object component + ( Int32 entityId + , Int32 componentId + , in object component ) { EnsureEntityLists(entityId); @@ -216,8 +216,8 @@ in component } public void UpdateComponent - ( System.Int32 entityId - , in T component + ( Int32 entityId + , in T component ) where T : Ecsact.Component { UpdateComponent( @@ -228,9 +228,9 @@ public void UpdateComponent } public void UpdateComponent - ( System.Int32 entityId - , System.Int32 componentId - , in object component + ( Int32 entityId + , Int32 componentId + , in object component ) { var gameObject = GetEntityGameObject(entityId); @@ -244,8 +244,8 @@ in component } public void RemoveComponent - ( System.Int32 entityId - , in T component + ( Int32 entityId + , in T component ) where T : Ecsact.Component { var compObj = (object)component; @@ -257,9 +257,9 @@ in compObj } public void RemoveComponent - ( System.Int32 entityId - , System.Int32 componentId - , in object component + ( Int32 entityId + , Int32 componentId + , in object component ) { if(entityGameObjects[entityId] != null) { @@ -333,7 +333,7 @@ in component } private GameObject EnsureEntityGameObject - ( System.Int32 entityId + ( Int32 entityId ) { GameObject? gameObject = entityGameObjects[entityId]; @@ -351,7 +351,7 @@ private GameObject EnsureEntityGameObject } private void EnsureEntityLists - ( System.Int32 entityId + ( Int32 entityId ) { var capacity = Math.Max(entityId + 1, entityComponentIds.Count); diff --git a/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs b/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs index a9f0615..3a390e9 100644 --- a/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs +++ b/packages/com.seaube.ecsact/Runtime/VisualScriptingEvents.cs @@ -27,7 +27,7 @@ private static void OnRuntimeLoad() { DontDestroyOnLoad(gameObject); } - List diposeCallbacks = new(); + List diposeCallbacks = new(); void OnEnable() { runtimeInstance = EcsactRuntime.GetOrLoadDefault(); From 60afba5184f82ae34093c7cee48dae77f6890677 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Fri, 15 Jul 2022 10:07:11 -0700 Subject: [PATCH 10/10] wip --- .../Runtime/DefaultRunner.cs | 9 ++- .../Runtime/EcsactRuntime.cs | 71 +++++++++++++------ 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs b/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs index b97e16b..a35d18f 100644 --- a/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs +++ b/packages/com.seaube.ecsact/Runtime/DefaultRunner.cs @@ -12,6 +12,8 @@ public class DefaultRunner : MonoBehaviour { private EcsactRuntimeDefaultRegistry? defReg; + public Int32 registryId => defReg?.registryId ?? -1; + #if UNITY_EDITOR [NonSerialized] public int debugExecutionCountTotal = 0; @@ -26,11 +28,10 @@ private static void OnRuntimeLoad() { foreach(var defReg in settings.defaultRegistries) { if(!defReg.useDefaultRunner) continue; - var gameObjectName = $"Ecsact Default Runner "; + var gameObjectName = $"Ecsact Default Runner"; if(!string.IsNullOrWhiteSpace(defReg.registryName)) { gameObjectName += " - " + defReg.registryName; } - gameObjectName += $"({defReg.registryId})"; var gameObject = new GameObject(gameObjectName); var runner = gameObject.AddComponent(); @@ -43,6 +44,10 @@ void Awake() { runtimeInstance = EcsactRuntime.GetOrLoadDefault(); } + void Start() { + gameObject.name += $" ({defReg!.registryId})"; + } + void Update() { if(defReg == null) return; UnityEngine.Debug.Assert(defReg.registryId != -1); diff --git a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs index 161e38c..7a36aaf 100644 --- a/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs +++ b/packages/com.seaube.ecsact/Runtime/EcsactRuntime.cs @@ -110,7 +110,7 @@ public delegate void ComponentEventCallback ( EcsactEvent ev , Int32 entityId , Int32 componentId - , object componentData + , IntPtr componentData , IntPtr callbackUserData ); @@ -574,7 +574,7 @@ internal delegate void ecsact_add_component_delegate ( Int32 registryId , Int32 entityId , Int32 componentId - , object componentData + , IntPtr componentData ); internal ecsact_add_component_delegate? ecsact_add_component; @@ -782,23 +782,30 @@ out entitiesCount return entities; } - public void AddComponent - ( Int32 registryId - , Int32 entityId - , Int32 componentId - , object componentData - ) + public void AddComponent + ( Int32 registryId + , Int32 entityId + , C component + ) where C : Ecsact.Component { if(ecsact_add_component == null) { throw new EcsactRuntimeMissingMethod("ecsact_add_component"); } - ecsact_add_component( - registryId, - entityId, - componentId, - componentData - ); + var componentId = Ecsact.Util.GetComponentID(); + var componentPtr = Marshal.AllocHGlobal(Marshal.SizeOf(component)); + + try { + Marshal.StructureToPtr(component, componentPtr, false); + ecsact_add_component( + registryId, + entityId, + componentId, + componentPtr + ); + } finally { + Marshal.FreeHGlobal(componentPtr); + } } public bool HasComponent @@ -1378,6 +1385,17 @@ public void Load public Static @static => _static!; public Wasm wasm => _wasm!; + [AOT.MonoPInvokeCallback(typeof(Wasm.ecsactsi_wasm_trap_handler))] + private static void DefaultWasmTrapHandler + ( Int32 systemId + , [MarshalAs(UnmanagedType.LPStr)] string trapMessage + ) + { + UnityEngine.Debug.LogError( + $"[Wasm Trap (systemId={systemId})] {trapMessage}" + ); + } + private static void LoadDelegate ( IntPtr lib , string name @@ -1500,6 +1518,10 @@ public static EcsactRuntime Load LoadDelegate(lib, "ecsactsi_wasm_set_trap_handler", out runtime._wasm.ecsactsi_wasm_set_trap_handler, runtime._wasm._availableMethods); } + if(runtime._wasm.ecsactsi_wasm_set_trap_handler != null) { + runtime._wasm.ecsactsi_wasm_set_trap_handler(DefaultWasmTrapHandler); + } + return runtime; } @@ -1700,22 +1722,23 @@ private static void OnInitComponentHandler ( EcsactEvent ev , Int32 entityId , Int32 componentId - , object componentData + , IntPtr componentData , IntPtr callbackUserData ) { UnityEngine.Debug.Assert(ev == EcsactEvent.InitComponent); var self = (GCHandle.FromIntPtr(callbackUserData).Target as EcsactRuntime)!; + var component = Ecsact.Util.PtrToComponent(componentData, componentId); if(self._initCompCbs.TryGetValue(componentId, out var cbs)) { foreach(var cb in cbs) { - cb(entityId, componentData); + cb(entityId, component); } } foreach(var cb in self._initAnyCompCbs) { - cb(entityId, componentId, componentData); + cb(entityId, componentId, component); } } @@ -1724,22 +1747,23 @@ private static void OnUpdateComponentHandler ( EcsactEvent ev , Int32 entityId , Int32 componentId - , object componentData + , IntPtr componentData , IntPtr callbackUserData ) { UnityEngine.Debug.Assert(ev == EcsactEvent.UpdateComponent); var self = (GCHandle.FromIntPtr(callbackUserData).Target as EcsactRuntime)!; + var component = Ecsact.Util.PtrToComponent(componentData, componentId); if(self._updateCompCbs.TryGetValue(componentId, out var cbs)) { foreach(var cb in cbs) { - cb(entityId, componentData); + cb(entityId, component); } } foreach(var cb in self._updateAnyCompCbs) { - cb(entityId, componentId, componentData); + cb(entityId, componentId, component); } } @@ -1748,22 +1772,23 @@ private static void OnRemoveComponentHandler ( EcsactEvent ev , Int32 entityId , Int32 componentId - , object componentData + , IntPtr componentData , IntPtr callbackUserData ) { UnityEngine.Debug.Assert(ev == EcsactEvent.RemoveComponent); var self = (GCHandle.FromIntPtr(callbackUserData).Target as EcsactRuntime)!; + var component = Ecsact.Util.PtrToComponent(componentData, componentId); if(self._removeCompCbs.TryGetValue(componentId, out var cbs)) { foreach(var cb in cbs) { - cb(entityId, componentData); + cb(entityId, component); } } foreach(var cb in self._removeAnyCompCbs) { - cb(entityId, componentId, componentData); + cb(entityId, componentId, component); } }