Browse Source

Working on C#

Josh Engebretson 10 years ago
parent
commit
afd5674768

+ 19 - 147
Build/AtomicSharp/AtomicEngine.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Reflection;
 using System.Runtime.InteropServices;
 
 using AtomicPlayer;
@@ -51,6 +52,11 @@ namespace AtomicEngine
 			return atomicsharp_runframe ();
 		}
 
+		public static void RegisterAssemblyComponents(Assembly assembly)
+		{
+			ComponentCore.RegisterAssemblyComponents (assembly);
+		}
+
 		public static void Initialize()
 		{
 			ContainerModule.Initialize ();
@@ -62,6 +68,8 @@ namespace AtomicEngine
 
 			AtomicPlayer.PlayerModule.Initialize ();
 
+			AtomicInterop.Initialize ();
+
 			atomicsharp_initialize ();
 
 			initSubsystems ();
@@ -91,6 +99,17 @@ namespace AtomicEngine
 		public const string LIBNAME = "/Users/josh/Dev/atomic/AtomicGameEngineSharp-build/Source/AtomicSharp/AtomicSharp";
 	}
 
+	public partial class Node
+	{
+		public T AddComponent<T> () where T:Component, new()
+		{
+			T component = new T ();
+			AddComponent( component, 0, CreateMode.REPLICATED);
+			return component;
+		}
+//		/new MyComponent (), 0, CreateMode.REPLICATED
+	}
+
 	public partial class RefCounted
 	{
 		protected RefCounted (IntPtr native)
@@ -124,153 +143,6 @@ namespace AtomicEngine
 		
 	}
 
-	static class NativeCore
-	{
-		// given an existing instance classID, construct the managed instance, with downcast support (ask for Component, get StaticModel for example)
-		public static Dictionary<IntPtr, Func<IntPtr, RefCounted>> nativeClassIDToManagedConstructor = new Dictionary<IntPtr, Func<IntPtr, RefCounted>>();
-
-		public static Dictionary<IntPtr, WeakReference> nativeLookup = new Dictionary<IntPtr, WeakReference> ();
-
-		public static IntPtr RegisterNative (IntPtr native, RefCounted r)
-		{
-			var w = new WeakReference (r);
-			NativeCore.nativeLookup [native] = w;
-			RefCounted._AddRef (native);
-			return native;
-		}
-
-		public static void ReleaseExpiredNativeReferences()
-		{
-			List<IntPtr> released = new List<IntPtr> ();
-
-			foreach(KeyValuePair<IntPtr, WeakReference> entry in nativeLookup)
-			{
-
-				if (entry.Value.Target == null || !entry.Value.IsAlive) {										
-					released.Add (entry.Key);
-
-				} else {
-					
-				}
-
-			}
-
-			foreach (IntPtr native in released) {
-				RefCounted._ReleaseRef(native);
-				nativeLookup.Remove (native);
-			}
-
-		}
-
-		// wraps an existing native instance, with downcast support
-		public static T WrapNative<T> (IntPtr native) where T:RefCounted
-		{
-			if (native == IntPtr.Zero)
-				return null;
-
-			WeakReference w;
-
-			// first see if we're already available
-			if (nativeLookup.TryGetValue (native, out w)) {
-
-				if (w.IsAlive) {
-
-					// we're alive!
-					return (T)w.Target; 
-
-				} else {
-
-					// we were seen before, but have since been GC'd, remove!
-					nativeLookup.Remove (native);
-				}
-			}
-
-			IntPtr classID = RefCounted.csb_RefCounted_GetClassID (native);
-
-			// and store, with downcast support for instance Component -> StaticModel
-			w = new WeakReference (nativeClassIDToManagedConstructor[classID](native));
-			NativeCore.nativeLookup [native] = w;
-
-			// store a ref, so native side will not be released while we still have a reference in managed code
-			RefCounted._AddRef (native);
-
-			return (T) w.Target;
-
-		}
-	}
-
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct Vector3
-	{
-		public Vector3 (float x, float y, float z)
-		{
-			this.x = x;
-			this.y = y;
-			this.z = z;
-		}
-
-		public override string ToString()
-		{
-			return x + ", " + y + ", " + z;
-		}
-
-		public float x;
-		public float y;
-		public float z;
-	}
-
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct Vector4
-	{
-	}
-		
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct Vector2
-	{
-	}
-
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct Quaternion
-	{
-	}
-
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct Color
-	{
-		public Color (float r, float g, float b, float a = 1.0f)
-		{
-			this.r = r;
-			this.g = g;
-			this.b = b;
-			this.a = a;
-		}
-
-		public float r;
-		public float g;
-		public float b;
-		public float a;
-
-	}
-
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct IntRect
-	{
-	}
-
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct IntVector2
-	{
-	}
-
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct BoundingBox
-	{
-	}
-
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct Rect
-	{
-	}
 
 
 }

+ 41 - 0
Build/AtomicSharp/AtomicInterop.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+	internal static class AtomicInterop
+	{
+		[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+		delegate void CSComponentCreateDelegate (string name, IntPtr nativeCSComponent);
+
+		static void CSComponentCreate(string name, IntPtr nativeCSComponent)
+		{
+			ComponentCore.CreateCSComponent (name, nativeCSComponent);
+		}
+
+		[DllImport (Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+		private static extern void csb_AtomicEngine_AtomicInterop_Set_CSComponentCreate(CSComponentCreateDelegate method);
+
+
+		// ----
+
+		[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+		delegate void CSComponentCallMethodDelegate (uint componentID, CSComponentMethod method, float value);
+
+		[DllImport (Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+		private static extern void csb_AtomicEngine_AtomicInterop_Set_CSComponentCallMethod(CSComponentCallMethodDelegate method);	
+
+		static void CSComponentCallMethod(uint componentID, CSComponentMethod method, float value)
+		{
+			ComponentCore.CallComponentMethod (componentID, method, value);
+		}
+
+		public static void Initialize()
+		{
+			csb_AtomicEngine_AtomicInterop_Set_CSComponentCreate (CSComponentCreate);	
+			csb_AtomicEngine_AtomicInterop_Set_CSComponentCallMethod (CSComponentCallMethod);
+		}
+
+	}
+}
+

+ 6 - 1
Build/AtomicSharp/AtomicSharp.csproj

@@ -5,7 +5,7 @@
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <ProjectGuid>{3A4C776B-61D6-412E-B1E9-7A1C84CD6B5B}</ProjectGuid>
     <OutputType>Library</OutputType>
-    <RootNamespace>Atomic</RootNamespace>
+    <RootNamespace>AtomicEngine</RootNamespace>
     <AssemblyName>AtomicSharp</AssemblyName>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
@@ -54,6 +54,11 @@
       <Link>CSModulePlayer.cs</Link>
     </Compile>
     <Compile Include="AtomicEngine.cs" />
+    <Compile Include="ComponentCore.cs" />
+    <Compile Include="Math.cs" />
+    <Compile Include="CSComponent.cs" />
+    <Compile Include="AtomicInterop.cs" />
+    <Compile Include="NativeCore.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 </Project>

+ 23 - 3
Build/AtomicSharp/AtomicSharpTest/Program.cs

@@ -1,19 +1,39 @@
 
+using System;
 using AtomicEngine;
 using AtomicPlayer;
 
+class MyComponent : CSComponent
+{
+	public float Speed = 1.0f;
+
+	override public void Update(float timeStep)
+	{
+		Node.Yaw (timeStep * 75 * Speed, TransformSpace.TS_LOCAL);
+	}
+}
+	
 class MyGame
-{				
+{
 	public static void Main (string[] args)
-	{
+	{		
+
+		Atomic.RegisterAssemblyComponents (typeof(MyGame).Assembly);
+
 		var player = Atomic.GetSubsystem<Player> ();
 
-		player.LoadScene ("Scenes/Scene.scene");		
+		player.LoadScene ("Scenes/Scene.scene", null);		
 
 		var scene = player.CurrentScene;
 
 		var zone = scene.GetComponent <Zone> (true);
 
+		var name = zone.Node.Name;
+
+		var chestNode = scene.GetChild ("Chest", true);
+		var c = chestNode.AddComponent <MyComponent> ();
+		c.Speed = 10.0f;
+
 		zone.SetAmbientColor( new Color(1, 0, 0) );
 
 		Atomic.Run ();

+ 47 - 0
Build/AtomicSharp/CSComponent.cs

@@ -0,0 +1,47 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+	public class CSComponent : Component
+	{
+		public uint ManagedID;
+
+		public CSComponent ()
+		{	
+			// component being created native side
+			if (ComponentCore.CurrentCSComponentNativeInstance != IntPtr.Zero) {
+
+				nativeInstance = ComponentCore.CurrentCSComponentNativeInstance;
+				NativeCore.RegisterNative (nativeInstance, this);
+				ComponentCore.CurrentCSComponentNativeInstance = IntPtr.Zero;
+				
+			} else {
+				
+				nativeInstance = NativeCore.RegisterNative (csb_Atomic_CSComponent_Constructor(), this);	
+				ComponentCore.RegisterCSComponent (this);
+			
+			}
+
+		}
+
+		virtual public void Update(float timeStep)
+		{
+			
+		}
+
+		public void SetManagedID(uint id)
+		{
+			csb_Atomic_CSComponent_SetManagedID (nativeInstance, id);
+		}
+
+		[DllImport (Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+		private static extern IntPtr csb_Atomic_CSComponent_Constructor();
+
+		[DllImport (Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+		private static extern void csb_Atomic_CSComponent_SetManagedID(IntPtr self, uint managedID);
+
+
+	}
+}
+

+ 113 - 0
Build/AtomicSharp/ComponentCore.cs

@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Reflection;
+
+namespace AtomicEngine
+{
+	internal enum CSComponentMethod
+	{
+		Start,
+		DelayedStart,
+		Update,
+		PostUpdate,
+		FixedUpdate,
+		PostFixedUpdate
+	};
+
+	static internal class ComponentCore
+	{
+		static Dictionary<String, Type> componentTypes = new Dictionary<String, Type>();
+
+		static Dictionary<uint, GCHandle> liveComponents = new Dictionary<uint, GCHandle>();
+
+		static uint idGen = 1;
+
+		public static IntPtr CurrentCSComponentNativeInstance = default(IntPtr);
+
+		public static void CallComponentMethod(uint componentID, CSComponentMethod method, float value)
+		{
+			GCHandle handle;
+
+			if (liveComponents.TryGetValue (componentID, out handle)) {
+
+				var component = (CSComponent)handle.Target;
+
+				switch (method) {
+
+				case CSComponentMethod.Update:
+					component.Update (value);
+					break;
+				}
+					
+			}
+			
+		}
+
+		public static void RegisterCSComponent(CSComponent component)
+		{
+			// keep the instance from being GC'd as it may be referenced solely in native code 
+			// attached to a node
+			GCHandle handle = GCHandle.Alloc (component);
+
+			// register
+			liveComponents [idGen] = handle;
+			component.SetManagedID (idGen);
+			idGen++;
+		}
+
+		// native create CSComponent
+		public static void CreateCSComponent(string name, IntPtr nativeCSComponent) 
+		{
+			Type type;
+
+			if (componentTypes.TryGetValue (name, out type)) {
+
+				// create an instance of the component type
+				CurrentCSComponentNativeInstance = nativeCSComponent;
+				var component = (CSComponent) Activator.CreateInstance(type);
+				CurrentCSComponentNativeInstance = IntPtr.Zero;
+
+				RegisterCSComponent (component);
+
+
+			
+			}
+
+		}
+
+		static void RegisterComponentType(Type componentType)
+		{
+			// TODO: Check for name clash, we don't want to use assembly qualified names, etc to keep it simple
+			componentTypes [componentType.Name] = componentType;			
+		}
+
+		public static void RegisterAssemblyComponents(Assembly assembly)
+		{
+			Type csComponentType = typeof(CSComponent);
+			Type[] types = assembly.GetTypes ();
+
+			List<Type> componentTypes = new List<Type> ();
+
+			foreach (Type type in types)				
+			{
+				for (var current = type.BaseType; current != null; current = current.BaseType) {
+
+					if (current == csComponentType) {
+						componentTypes.Add(type);
+						break;						
+					}
+				}				
+			}
+
+			foreach (Type type in componentTypes) {
+
+				RegisterComponentType (type);
+
+			}
+
+		}
+
+	}
+}
+

+ 78 - 0
Build/AtomicSharp/Math.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct Vector3
+	{
+		public Vector3 (float x, float y, float z)
+		{
+			this.x = x;
+			this.y = y;
+			this.z = z;
+		}
+
+		public override string ToString()
+		{
+			return x + ", " + y + ", " + z;
+		}
+
+		public float x;
+		public float y;
+		public float z;
+	}
+
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct Vector4
+	{
+	}
+
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct Vector2
+	{
+	}
+
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct Quaternion
+	{
+	}
+
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct Color
+	{
+		public Color (float r, float g, float b, float a = 1.0f)
+		{
+			this.r = r;
+			this.g = g;
+			this.b = b;
+			this.a = a;
+		}
+
+		public float r;
+		public float g;
+		public float b;
+		public float a;
+
+	}
+
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct IntRect
+	{
+	}
+
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct IntVector2
+	{
+	}
+
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct BoundingBox
+	{
+	}
+
+	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+	public struct Rect
+	{
+	}}
+

+ 82 - 0
Build/AtomicSharp/NativeCore.cs

@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+
+namespace AtomicEngine
+{
+	static class NativeCore
+	{
+		// given an existing instance classID, construct the managed instance, with downcast support (ask for Component, get StaticModel for example)
+		public static Dictionary<IntPtr, Func<IntPtr, RefCounted>> nativeClassIDToManagedConstructor = new Dictionary<IntPtr, Func<IntPtr, RefCounted>>();
+
+		public static Dictionary<IntPtr, WeakReference> nativeLookup = new Dictionary<IntPtr, WeakReference> ();
+
+		public static IntPtr RegisterNative (IntPtr native, RefCounted r)
+		{
+			var w = new WeakReference (r);
+			NativeCore.nativeLookup [native] = w;
+			RefCounted._AddRef (native);
+			return native;
+		}
+
+		public static void ReleaseExpiredNativeReferences()
+		{
+			List<IntPtr> released = new List<IntPtr> ();
+
+			foreach(KeyValuePair<IntPtr, WeakReference> entry in nativeLookup)
+			{
+
+				if (entry.Value.Target == null || !entry.Value.IsAlive) {										
+					released.Add (entry.Key);
+
+				} else {
+
+				}
+
+			}
+
+			foreach (IntPtr native in released) {
+				RefCounted._ReleaseRef(native);
+				nativeLookup.Remove (native);
+			}
+
+		}
+
+		// wraps an existing native instance, with downcast support
+		public static T WrapNative<T> (IntPtr native) where T:RefCounted
+		{
+			if (native == IntPtr.Zero)
+				return null;
+
+			WeakReference w;
+
+			// first see if we're already available
+			if (nativeLookup.TryGetValue (native, out w)) {
+
+				if (w.IsAlive) {
+
+					// we're alive!
+					return (T)w.Target; 
+
+				} else {
+
+					// we were seen before, but have since been GC'd, remove!
+					nativeLookup.Remove (native);
+				}
+			}
+
+			IntPtr classID = RefCounted.csb_RefCounted_GetClassID (native);
+
+			// and store, with downcast support for instance Component -> StaticModel
+			w = new WeakReference (nativeClassIDToManagedConstructor[classID](native));
+			NativeCore.nativeLookup [native] = w;
+
+			// store a ref, so native side will not be released while we still have a reference in managed code
+			RefCounted._AddRef (native);
+
+			return (T) w.Target;
+
+		}
+	}
+
+}
+

+ 12 - 0
Source/AtomicSharp/AtomicSharp.cpp

@@ -1,16 +1,22 @@
 
+#include "CSComponent.h"
 #include "AtomicSharp.h"
 
 
 namespace Atomic
 {
 
+/// Register AtomicSharp library objects.
+void ATOMIC_API RegisterAtomicSharpLibrary(Context* context);
+
 WeakPtr<Context> AtomicSharp::csContext_;
 WeakPtr<AtomicSharp> AtomicSharp::instance_;
 
 AtomicSharp::AtomicSharp(Context* context) :
     Object(context)
 {
+    RegisterAtomicSharpLibrary(context_);
+
     assert(!instance_);
     instance_ = this;
     csContext_ = context;
@@ -22,5 +28,11 @@ AtomicSharp::~AtomicSharp()
 }
 
 
+void RegisterAtomicSharpLibrary(Context* context)
+{
+    CSComponent::RegisterObject(context);
+}
+
+
 }
 

+ 44 - 0
Source/AtomicSharp/AtomicSharpAPI.cpp

@@ -12,6 +12,7 @@
 //
 
 #include "AtomicSharp.h"
+#include "AtomicSharpAPI.h"
 
 #ifdef ATOMIC_PLATFORM_WINDOWS
 #pragma warning(disable: 4244) // possible loss of data
@@ -25,6 +26,49 @@ using namespace Atomic;
 extern "C"
 {
 
+typedef void (*CSComponentCreatePtr)(const char* csComponentTypeName, CSComponent* instance);
+CSComponentCreatePtr _CSComponentCreate = 0;
+ATOMIC_EXPORT_API void csb_AtomicEngine_AtomicInterop_Set_CSComponentCreate(CSComponentCreatePtr method)
+{
+    _CSComponentCreate = method;
+}
+
+void CSComponentCreate(String name, CSComponent* instance)
+{
+    assert(_CSComponentCreate);
+    _CSComponentCreate(name.CString(), instance);
+}
+
+
+typedef void (*CSComponentCallMethodPtr)(unsigned id, CSComponentMethod method, float value);
+CSComponentCallMethodPtr _CSComponentCallMethod = 0;
+ATOMIC_EXPORT_API void csb_AtomicEngine_AtomicInterop_Set_CSComponentCallMethod(CSComponentCallMethodPtr method)
+{
+    _CSComponentCallMethod = method;
+}
+
+void CSComponentCallMethod(unsigned id, CSComponentMethod methodID, float value)
+{
+    assert(_CSComponentCreate);
+    _CSComponentCallMethod(id, methodID, value);
+}
+
+// Instance methods
+
+ATOMIC_EXPORT_API RefCounted* csb_Atomic_CSComponent_Constructor()
+{
+   return new CSComponent(AtomicSharp::GetContext());
+}
+
+ATOMIC_EXPORT_API void csb_Atomic_CSComponent_SetManagedID(CSComponent* self, unsigned id)
+{
+   if (!self)
+       return;
+
+    self->SetManagedID(id);
+}
+
+
 ATOMIC_EXPORT_API ClassID csb_RefCounted_GetClassID(RefCounted* refCounted)
 {
     if (!refCounted)

+ 18 - 0
Source/AtomicSharp/AtomicSharpAPI.h

@@ -10,3 +10,21 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
+
+#pragma once
+
+#include "CSComponent.h"
+
+namespace Atomic
+{
+
+extern "C"
+{
+
+void CSComponentCreate(String name, CSComponent* instance);
+
+void CSComponentCallMethod(unsigned id, CSComponentMethod methodID, float value = 0.0f);
+
+}
+
+}

+ 360 - 0
Source/AtomicSharp/CSComponent.cpp

@@ -0,0 +1,360 @@
+//
+// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Core/Context.h>
+#include <Atomic/Resource/ResourceCache.h>
+
+#ifdef ATOMIC_PHYSICS
+#include <Atomic/Physics/PhysicsEvents.h>
+#include <Atomic/Physics/PhysicsWorld.h>
+#endif
+#include <Atomic/Scene/Scene.h>
+#include <Atomic/Scene/SceneEvents.h>
+
+#include "CSComponent.h"
+#include "AtomicSharpAPI.h"
+
+namespace Atomic
+{
+
+extern const char* LOGIC_CATEGORY;
+
+class CSComponentFactory : public ObjectFactory
+{
+public:
+    /// Construct.
+    CSComponentFactory(Context* context) :
+        ObjectFactory(context)
+    {
+        type_ = CSComponent::GetTypeStatic();
+        baseType_ = CSComponent::GetBaseTypeStatic();
+        typeName_ = CSComponent::GetTypeNameStatic();
+    }
+
+    /// Create an object of the specific type.
+    SharedPtr<Object> CreateObject(const XMLElement& source = XMLElement::EMPTY)
+    {
+
+        // if in editor, just create the CSComponent
+        if (context_->GetEditorContext())
+        {
+            return SharedPtr<Object>(new CSComponent(context_));
+        }
+
+        // At runtime, a XML CSComponent may refer to a managed component
+
+        String managedClass;
+
+        if (source != XMLElement::EMPTY)
+        {
+            XMLElement attrElem = source.GetChild("attribute");
+
+            while (attrElem)
+            {
+                if (attrElem.GetAttribute("name") == "ManagedClass")
+                {
+                    managedClass = attrElem.GetAttribute("value");
+                    break;
+                }
+
+                attrElem = attrElem.GetNext("attribute");
+            }
+        }
+
+        SharedPtr<Object> ptr;
+
+        if (managedClass.Length())
+        {
+            CSComponent* csc = new CSComponent(context_);
+            CSComponentCreate(managedClass, csc);
+            assert(csc->managedID_);
+            ptr = csc;
+
+        }
+
+        if (ptr.Null())
+        {
+            ptr = new CSComponent(context_);
+        }
+
+        return ptr;    }
+};
+
+
+CSComponent::CSComponent(Context* context) :
+    Component(context),
+    updateEventMask_(USE_UPDATE | USE_POSTUPDATE | USE_FIXEDUPDATE | USE_FIXEDPOSTUPDATE),
+    currentEventMask_(0),
+    instanceInitialized_(false),
+    started_(false),
+    destroyed_(false),
+    scriptClassInstance_(false),
+    delayedStartCalled_(false),
+    loading_(false),
+    managedID_(0)
+{
+
+}
+
+CSComponent::~CSComponent()
+{
+
+}
+
+void CSComponent::RegisterObject(Context* context)
+{
+    context->RegisterFactory(new CSComponentFactory(context), LOGIC_CATEGORY);
+    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+}
+
+void CSComponent::OnSetEnabled()
+{
+    UpdateEventSubscription();
+}
+
+void CSComponent::SetUpdateEventMask(unsigned char mask)
+{
+    if (updateEventMask_ != mask)
+    {
+        updateEventMask_ = mask;
+        UpdateEventSubscription();
+    }
+}
+
+void CSComponent::ApplyAttributes()
+{
+}
+
+void CSComponent::CallScriptMethod(CSComponentMethod method, float value)
+{
+    if (!managedID_ || destroyed_ || !node_ || !node_->GetScene())
+        return;
+
+    CSComponentCallMethod(managedID_, method, value);
+
+}
+
+void CSComponent::Start()
+{
+    CallScriptMethod(CSComponentMethod_Start);
+}
+
+void CSComponent::DelayedStart()
+{
+    CallScriptMethod(CSComponentMethod_DelayedStart);
+}
+
+void CSComponent::Update(float timeStep)
+{
+    //if (!instanceInitialized_)
+    //    InitInstance();
+
+    if (!started_)
+    {
+        started_ = true;
+        Start();
+    }
+
+    CallScriptMethod(CSComponentMethod_Update, timeStep);
+}
+
+void CSComponent::PostUpdate(float timeStep)
+{
+    CallScriptMethod(CSComponentMethod_PostUpdate, timeStep);
+}
+
+void CSComponent::FixedUpdate(float timeStep)
+{
+    CallScriptMethod(CSComponentMethod_FixedUpdate, timeStep);
+}
+
+void CSComponent::FixedPostUpdate(float timeStep)
+{
+    CallScriptMethod(CSComponentMethod_PostFixedUpdate, timeStep);
+}
+
+void CSComponent::OnNodeSet(Node* node)
+{
+    if (node)
+    {
+        //UpdateReferences();
+    }
+    else
+    {
+        // We are being detached from a node: execute user-defined stop function and prepare for destruction
+        //UpdateReferences(true);
+        Stop();
+    }
+}
+
+void CSComponent::OnSceneSet(Scene* scene)
+{
+    if (scene)
+        UpdateEventSubscription();
+    else
+    {
+        UnsubscribeFromEvent(E_SCENEUPDATE);
+        UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
+#ifdef ATOMIC_PHYSICS
+        UnsubscribeFromEvent(E_PHYSICSPRESTEP);
+        UnsubscribeFromEvent(E_PHYSICSPOSTSTEP);
+#endif
+        currentEventMask_ = 0;
+    }
+}
+
+void CSComponent::UpdateEventSubscription()
+{
+    Scene* scene = GetScene();
+    if (!scene)
+        return;
+
+    bool enabled = IsEnabledEffective();
+
+    bool needUpdate = enabled && ((updateEventMask_ & USE_UPDATE) || !delayedStartCalled_);
+    if (needUpdate && !(currentEventMask_ & USE_UPDATE))
+    {
+        SubscribeToEvent(scene, E_SCENEUPDATE, HANDLER(CSComponent, HandleSceneUpdate));
+        currentEventMask_ |= USE_UPDATE;
+    }
+    else if (!needUpdate && (currentEventMask_ & USE_UPDATE))
+    {
+        UnsubscribeFromEvent(scene, E_SCENEUPDATE);
+        currentEventMask_ &= ~USE_UPDATE;
+    }
+
+    bool needPostUpdate = enabled && (updateEventMask_ & USE_POSTUPDATE);
+    if (needPostUpdate && !(currentEventMask_ & USE_POSTUPDATE))
+    {
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(CSComponent, HandleScenePostUpdate));
+        currentEventMask_ |= USE_POSTUPDATE;
+    }
+    else if (!needUpdate && (currentEventMask_ & USE_POSTUPDATE))
+    {
+        UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
+        currentEventMask_ &= ~USE_POSTUPDATE;
+    }
+
+#ifdef ATOMIC_PHYSICS
+    PhysicsWorld* world = scene->GetComponent<PhysicsWorld>();
+    if (!world)
+        return;
+
+    bool needFixedUpdate = enabled && (updateEventMask_ & USE_FIXEDUPDATE);
+    if (needFixedUpdate && !(currentEventMask_ & USE_FIXEDUPDATE))
+    {
+        SubscribeToEvent(world, E_PHYSICSPRESTEP, HANDLER(CSComponent, HandlePhysicsPreStep));
+        currentEventMask_ |= USE_FIXEDUPDATE;
+    }
+    else if (!needFixedUpdate && (currentEventMask_ & USE_FIXEDUPDATE))
+    {
+        UnsubscribeFromEvent(world, E_PHYSICSPRESTEP);
+        currentEventMask_ &= ~USE_FIXEDUPDATE;
+    }
+
+    bool needFixedPostUpdate = enabled && (updateEventMask_ & USE_FIXEDPOSTUPDATE);
+    if (needFixedPostUpdate && !(currentEventMask_ & USE_FIXEDPOSTUPDATE))
+    {
+        SubscribeToEvent(world, E_PHYSICSPOSTSTEP, HANDLER(CSComponent, HandlePhysicsPostStep));
+        currentEventMask_ |= USE_FIXEDPOSTUPDATE;
+    }
+    else if (!needFixedPostUpdate && (currentEventMask_ & USE_FIXEDPOSTUPDATE))
+    {
+        UnsubscribeFromEvent(world, E_PHYSICSPOSTSTEP);
+        currentEventMask_ &= ~USE_FIXEDPOSTUPDATE;
+    }
+#endif
+}
+
+void CSComponent::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
+{
+    using namespace SceneUpdate;
+
+    assert(!destroyed_);
+
+    // Execute user-defined delayed start function before first update
+    if (!delayedStartCalled_)
+    {
+        DelayedStart();
+        delayedStartCalled_ = true;
+
+        // If did not need actual update events, unsubscribe now
+        if (!(updateEventMask_ & USE_UPDATE))
+        {
+            UnsubscribeFromEvent(GetScene(), E_SCENEUPDATE);
+            currentEventMask_ &= ~USE_UPDATE;
+            return;
+        }
+    }
+
+    // Then execute user-defined update function
+    Update(eventData[P_TIMESTEP].GetFloat());
+}
+
+void CSComponent::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
+{
+    using namespace ScenePostUpdate;
+
+    // Execute user-defined post-update function
+    PostUpdate(eventData[P_TIMESTEP].GetFloat());
+}
+
+#ifdef ATOMIC_PHYSICS
+void CSComponent::HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
+{
+    using namespace PhysicsPreStep;
+
+    // Execute user-defined fixed update function
+    FixedUpdate(eventData[P_TIMESTEP].GetFloat());
+}
+
+void CSComponent::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
+{
+    using namespace PhysicsPostStep;
+
+    // Execute user-defined fixed post-update function
+    FixedPostUpdate(eventData[P_TIMESTEP].GetFloat());
+}
+#endif
+
+bool CSComponent::Load(Deserializer& source, bool setInstanceDefault)
+{
+    loading_ = true;
+    bool success = Component::Load(source, setInstanceDefault);
+    loading_ = false;
+
+    return success;
+}
+
+bool CSComponent::LoadXML(const XMLElement& source, bool setInstanceDefault)
+{
+    loading_ = true;
+    bool success = Component::LoadXML(source, setInstanceDefault);
+    loading_ = false;
+
+    return success;
+}
+
+}

+ 140 - 0
Source/AtomicSharp/CSComponent.h

@@ -0,0 +1,140 @@
+//
+// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include <Atomic/Scene/Component.h>
+
+namespace Atomic
+{
+
+enum CSComponentMethod
+{
+    CSComponentMethod_Start,
+    CSComponentMethod_DelayedStart,
+    CSComponentMethod_Update,
+    CSComponentMethod_PostUpdate,
+    CSComponentMethod_FixedUpdate,
+    CSComponentMethod_PostFixedUpdate
+};
+
+/// Helper base class for user-defined game logic components that hooks up to update events and forwards them to virtual functions similar to ScriptInstance class.
+class ATOMIC_API CSComponent : public Component
+{
+    friend class CSComponentFactory;
+
+    enum EventFlags
+    {
+        USE_UPDATE = 0x1,
+        USE_POSTUPDATE = 0x2,
+        USE_FIXEDUPDATE = 0x4,
+        USE_FIXEDPOSTUPDATE = 0x8
+    };
+
+public:
+
+    OBJECT(CSComponent);
+
+    /// Construct.
+    CSComponent(Context* context);
+    /// Destruct.
+    virtual ~CSComponent();
+
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+    bool Load(Deserializer& source, bool setInstanceDefault);
+    bool LoadXML(const XMLElement& source, bool setInstanceDefault);
+    void ApplyAttributes();
+
+    /// Handle enabled/disabled state change. Changes update event subscription.
+    virtual void OnSetEnabled();
+
+    /// Set what update events should be subscribed to. Use this for optimization: by default all are in use. Note that this is not an attribute and is not saved or network-serialized, therefore it should always be called eg. in the subclass constructor.
+    void SetUpdateEventMask(unsigned char mask);
+
+    /// Return what update events are subscribed to.
+    unsigned char GetUpdateEventMask() const { return updateEventMask_; }
+    /// Return whether the DelayedStart() function has been called.
+    bool IsDelayedStartCalled() const { return delayedStartCalled_; }
+
+    void SetDestroyed() { destroyed_ = true; }
+    void SetManagedID(unsigned id) { managedID_ = id; }
+
+protected:
+    /// Handle scene node being assigned at creation.
+    virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
+
+private:
+    /// Subscribe/unsubscribe to update events based on current enabled state and update event mask.
+    void UpdateEventSubscription();
+    /// Handle scene update event.
+    void HandleSceneUpdate(StringHash eventType, VariantMap& eventData);
+    /// Handle scene post-update event.
+    void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
+#ifdef ATOMIC_PHYSICS
+    /// Handle physics pre-step event.
+    void HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData);
+    /// Handle physics post-step event.
+    void HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData);
+#endif
+
+    void CallScriptMethod(CSComponentMethod method, float value = 0.0f);
+
+    /// Called when the component is added to a scene node. Other components may not yet exist.
+    virtual void Start();
+    /// Called before the first update. At this point all other components of the node should exist. Will also be called if update events are not wanted; in that case the event is immediately unsubscribed afterward.
+    virtual void DelayedStart();
+    /// Called when the component is detached from a scene node, usually on destruction. Note that you will no longer have access to the node and scene at that point.
+    virtual void Stop() {}
+    /// Called on scene update, variable timestep.
+    virtual void Update(float timeStep);
+    /// Called on scene post-update, variable timestep.
+    virtual void PostUpdate(float timeStep);
+    /// Called on physics update, fixed timestep.
+    virtual void FixedUpdate(float timeStep);
+    /// Called on physics post-update, fixed timestep.
+    virtual void FixedPostUpdate(float timeStep);
+
+    /// Requested event subscription mask.
+    unsigned char updateEventMask_;
+    /// Current event subscription mask.
+    unsigned char currentEventMask_;
+
+    bool instanceInitialized_;
+    bool started_;
+    bool destroyed_;
+    bool scriptClassInstance_;
+
+    /// Flag for delayed start.
+    bool delayedStartCalled_;
+
+    bool loading_;
+
+    unsigned managedID_;
+
+};
+
+}