diff --git a/.clang-format b/.clang-format
index e15b43f0..afd0049f 100644
--- a/.clang-format
+++ b/.clang-format
@@ -3,7 +3,7 @@ BasedOnStyle: Microsoft
AccessModifierOffset: '-4'
AlignAfterOpenBracket: DontAlign
AlignEscapedNewlines: Left
-AllowShortFunctionsOnASingleLine: Inline
+AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: 'Yes'
diff --git a/.gitignore b/.gitignore
index 301f476b..368ebc31 100644
--- a/.gitignore
+++ b/.gitignore
@@ -387,9 +387,16 @@ vcpkg_installed/
[Ss]ource/Mocha.Common/Glue/UnmanagedArgs.cs
cvars.json
[Ss]amples/mocha-minimal/code/*.csproj
+[Ss]amples/mocha-minimal/code/Properties
# Content meta files
![Cc]ontent/**/*.meta
# Cmake build files
[Cc]ompile/
+
+# Server configuration
+server.cfg
+
+# JetBrains IDEs
+Source/.idea
diff --git a/.gitmodules b/.gitmodules
index c841e76c..3025f2f3 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,15 +1,15 @@
-[submodule "Source/Host/thirdparty/imgui"]
- path = Source/Mocha.Host/thirdparty/imgui
+[submodule "Source/Host/Thirdparty/imgui"]
+ path = Source/Mocha.Host/Thirdparty/imgui
url = https://github.com/ocornut/imgui/
-[submodule "Source/Host/thirdparty/JoltPhysics"]
- path = Source/Mocha.Host/thirdparty/JoltPhysics
+[submodule "Source/Host/Thirdparty/JoltPhysics"]
+ path = Source/Mocha.Host/Thirdparty/JoltPhysics
url = https://github.com/jrouwe/JoltPhysics/
-[submodule "Source/Host/thirdparty/vk-bootstrap"]
- path = Source/Mocha.Host/thirdparty/vk-bootstrap
+[submodule "Source/Host/Thirdparty/vk-bootstrap"]
+ path = Source/Mocha.Host/Thirdparty/vk-bootstrap
url = https://github.com/charles-lunarg/vk-bootstrap
-[submodule "Source/Host/thirdparty/implot"]
- path = Source/Mocha.Host/thirdparty/implot
+[submodule "Source/Host/Thirdparty/implot"]
+ path = Source/Mocha.Host/Thirdparty/implot
url = https://github.com/epezent/implot
-[submodule "Source/Host/thirdparty/volk"]
- path = Source/Mocha.Host/thirdparty/volk
+[submodule "Source/Host/Thirdparty/volk"]
+ path = Source/Mocha.Host/Thirdparty/volk
url = https://github.com/zeux/volk
diff --git a/Content/core/materials/dev/dev_floor.mmat b/Content/core/materials/dev/dev_floor.mmat
index 28895dbf..676fec4e 100644
--- a/Content/core/materials/dev/dev_floor.mmat
+++ b/Content/core/materials/dev/dev_floor.mmat
@@ -1 +1 @@
-{"DiffuseTexture":"textures/dev/dev_floor.mtex","NormalTexture":null,"AmbientOcclusionTexture":null,"MetalnessTexture":null,"RoughnessTexture":null}
\ No newline at end of file
+{"DiffuseTexture":"textures/dev/floor_basecolor.mtex","NormalTexture":"textures/dev/floor_normal.mtex","AmbientOcclusionTexture":"textures/dev/floor_ambientocclusion.mtex","MetalnessTexture":"textures/dev/floor_metallic.mtex","RoughnessTexture":"textures/dev/floor_roughness.mtex"}
\ No newline at end of file
diff --git a/Content/core/materials/dev/dev_wall.mmat b/Content/core/materials/dev/dev_wall.mmat
index 7780e62e..6117f6ff 100644
--- a/Content/core/materials/dev/dev_wall.mmat
+++ b/Content/core/materials/dev/dev_wall.mmat
@@ -1,3 +1 @@
-{
- "DiffuseTexture": "textures/dev/dev_wall.mtex"
-}
\ No newline at end of file
+{"DiffuseTexture":"textures/dev/wall_basecolor.mtex","NormalTexture":"textures/dev/wall_normal.mtex","AmbientOcclusionTexture":"textures/dev/wall_ambientocclusion.mtex","MetalnessTexture":"textures/dev/wall_metallic.mtex","RoughnessTexture":"textures/dev/wall_roughness.mtex"}
\ No newline at end of file
diff --git a/Content/core/materials/dev/dev_white.mmat b/Content/core/materials/dev/dev_white.mmat
index 5acebfa5..2caca7e6 100644
--- a/Content/core/materials/dev/dev_white.mmat
+++ b/Content/core/materials/dev/dev_white.mmat
@@ -1 +1 @@
-{"DiffuseTexture":"textures/dev/dev_white.mtex","NormalTexture":null,"AmbientOcclusionTexture":null,"MetalnessTexture":null,"RoughnessTexture":null}
\ No newline at end of file
+{"DiffuseTexture":"textures/dev/generic_basecolor.mtex","NormalTexture":"textures/dev/generic_normal.mtex","AmbientOcclusionTexture":"textures/dev/generic_ambientocclusion.mtex","MetalnessTexture":"textures/dev/generic_metallic.mtex","RoughnessTexture":"textures/dev/generic_roughness.mtex"}
\ No newline at end of file
diff --git a/Content/core/textures/dev/dev_floor.png b/Content/core/textures/dev/dev_floor.png
deleted file mode 100644
index 69be2111..00000000
Binary files a/Content/core/textures/dev/dev_floor.png and /dev/null differ
diff --git a/Content/core/textures/dev/dev_wall.png b/Content/core/textures/dev/dev_wall.png
deleted file mode 100644
index 4f5bf92e..00000000
Binary files a/Content/core/textures/dev/dev_wall.png and /dev/null differ
diff --git a/Content/core/textures/dev/dev_white.png b/Content/core/textures/dev/dev_white.png
deleted file mode 100644
index 72e1a077..00000000
Binary files a/Content/core/textures/dev/dev_white.png and /dev/null differ
diff --git a/Content/core/textures/dev/floor_ambientocclusion.png b/Content/core/textures/dev/floor_ambientocclusion.png
new file mode 100644
index 00000000..a8d9f02e
Binary files /dev/null and b/Content/core/textures/dev/floor_ambientocclusion.png differ
diff --git a/Content/core/textures/dev/floor_basecolor.png b/Content/core/textures/dev/floor_basecolor.png
new file mode 100644
index 00000000..410a23ac
Binary files /dev/null and b/Content/core/textures/dev/floor_basecolor.png differ
diff --git a/Content/core/textures/dev/floor_height.png b/Content/core/textures/dev/floor_height.png
new file mode 100644
index 00000000..7e41fd9f
Binary files /dev/null and b/Content/core/textures/dev/floor_height.png differ
diff --git a/Content/core/textures/dev/floor_metallic.png b/Content/core/textures/dev/floor_metallic.png
new file mode 100644
index 00000000..58ad6111
Binary files /dev/null and b/Content/core/textures/dev/floor_metallic.png differ
diff --git a/Content/core/textures/dev/floor_normal.png b/Content/core/textures/dev/floor_normal.png
new file mode 100644
index 00000000..4c94e07f
Binary files /dev/null and b/Content/core/textures/dev/floor_normal.png differ
diff --git a/Content/core/textures/dev/floor_roughness.png b/Content/core/textures/dev/floor_roughness.png
new file mode 100644
index 00000000..f141f8bf
Binary files /dev/null and b/Content/core/textures/dev/floor_roughness.png differ
diff --git a/Content/core/textures/dev/generic_ambientocclusion.png b/Content/core/textures/dev/generic_ambientocclusion.png
new file mode 100644
index 00000000..a8d9f02e
Binary files /dev/null and b/Content/core/textures/dev/generic_ambientocclusion.png differ
diff --git a/Content/core/textures/dev/generic_basecolor.png b/Content/core/textures/dev/generic_basecolor.png
new file mode 100644
index 00000000..16dbc41a
Binary files /dev/null and b/Content/core/textures/dev/generic_basecolor.png differ
diff --git a/Content/core/textures/dev/generic_height.png b/Content/core/textures/dev/generic_height.png
new file mode 100644
index 00000000..7e41fd9f
Binary files /dev/null and b/Content/core/textures/dev/generic_height.png differ
diff --git a/Content/core/textures/dev/generic_metallic.png b/Content/core/textures/dev/generic_metallic.png
new file mode 100644
index 00000000..58ad6111
Binary files /dev/null and b/Content/core/textures/dev/generic_metallic.png differ
diff --git a/Content/core/textures/dev/generic_normal.png b/Content/core/textures/dev/generic_normal.png
new file mode 100644
index 00000000..4c94e07f
Binary files /dev/null and b/Content/core/textures/dev/generic_normal.png differ
diff --git a/Content/core/textures/dev/generic_roughness.png b/Content/core/textures/dev/generic_roughness.png
new file mode 100644
index 00000000..f141f8bf
Binary files /dev/null and b/Content/core/textures/dev/generic_roughness.png differ
diff --git a/Content/core/textures/dev/wall_ambientocclusion.png b/Content/core/textures/dev/wall_ambientocclusion.png
new file mode 100644
index 00000000..a8d9f02e
Binary files /dev/null and b/Content/core/textures/dev/wall_ambientocclusion.png differ
diff --git a/Content/core/textures/dev/wall_basecolor.png b/Content/core/textures/dev/wall_basecolor.png
new file mode 100644
index 00000000..d7b56137
Binary files /dev/null and b/Content/core/textures/dev/wall_basecolor.png differ
diff --git a/Content/core/textures/dev/wall_height.png b/Content/core/textures/dev/wall_height.png
new file mode 100644
index 00000000..7e41fd9f
Binary files /dev/null and b/Content/core/textures/dev/wall_height.png differ
diff --git a/Content/core/textures/dev/wall_metallic.png b/Content/core/textures/dev/wall_metallic.png
new file mode 100644
index 00000000..58ad6111
Binary files /dev/null and b/Content/core/textures/dev/wall_metallic.png differ
diff --git a/Content/core/textures/dev/wall_normal.png b/Content/core/textures/dev/wall_normal.png
new file mode 100644
index 00000000..4c94e07f
Binary files /dev/null and b/Content/core/textures/dev/wall_normal.png differ
diff --git a/Content/core/textures/dev/wall_roughness.png b/Content/core/textures/dev/wall_roughness.png
new file mode 100644
index 00000000..f141f8bf
Binary files /dev/null and b/Content/core/textures/dev/wall_roughness.png differ
diff --git a/Samples/mocha-minimal/code/Game.cs b/Samples/mocha-minimal/code/Game.cs
index 002cd167..cfc1944a 100644
--- a/Samples/mocha-minimal/code/Game.cs
+++ b/Samples/mocha-minimal/code/Game.cs
@@ -5,20 +5,36 @@ namespace Minimal;
public class Game : BaseGame
{
- [HotloadSkip]
- private UIManager Hud { get; set; }
+ [HotloadSkip] private UIManager Hud { get; set; }
- public override void Startup()
+ public string NetworkedString { get; set; }
+
+ public override void OnStartup()
{
- // Set up UI
- Hud = new UIManager();
- Hud.SetTemplate( "ui/Game.html" );
+ if ( Core.IsServer )
+ {
+ // We only want to create these entities on the server.
+ // They will automatically be replicated to clients.
+
+ // Spawn a model to walk around in
+ var map = new ModelEntity( "models/dev/dev_map.mmdl" );
+ map.SetMeshPhysics( "models/dev/dev_map.mmdl" );
- // Spawn a model to walk around in
- var map = new ModelEntity( "models/dev/dev_map.mmdl" );
- map.SetMeshPhysics( "models/dev/dev_map.mmdl" );
+ // Spawn a player
+ var player = new Player();
+ player.Position = new Vector3( 0, 0, 50 );
+ }
+ else
+ {
+ // UI is client-only
+ Hud = new UIManager();
+ Hud.SetTemplate( "ui/Game.html" );
+ }
+ }
- // Spawn a player
- var player = new Player();
+ [Event.Tick]
+ public void Tick()
+ {
+ DebugOverlay.ScreenText( $"Tick... ({GetType().Assembly.GetHashCode()})" );
}
}
diff --git a/Samples/mocha-minimal/code/Player.cs b/Samples/mocha-minimal/code/Player.cs
index 6f66f53b..8dc780b7 100644
--- a/Samples/mocha-minimal/code/Player.cs
+++ b/Samples/mocha-minimal/code/Player.cs
@@ -1,24 +1,23 @@
-using System.ComponentModel;
-
-namespace Minimal;
+namespace Minimal;
public class Player : Mocha.Player
{
- private Vector3 PlayerBounds = new( 0.5f, 0.5f, 1.8f ); // Metres
-
- public QuakeWalkController WalkController { get; private set; }
+ public WalkController WalkController { get; private set; }
- [Category( "Player" )]
- public bool IsGrounded => WalkController.IsGrounded;
+ public float Health { get; set; }
- [Category( "Player" )]
- public BaseEntity GroundEntity => WalkController.GroundEntity;
+ protected override void Spawn()
+ {
+ // TODO: This would be better as just a ctor
+ base.Spawn();
- public float Health { get; set; }
+ PlayerBounds = new( 0.5f, 0.5f, 1.8f ); // Metres
+ SetCubePhysics( PlayerBounds, false );
+ }
private void UpdateEyeTransform()
{
- EyePosition = Position + Vector3.Up * PlayerHalfExtents.Z;
+ EyePosition = Position + Vector3.Up * PlayerBounds.Z;
EyeRotation = Input.Rotation;
}
@@ -26,18 +25,14 @@ public override void Respawn()
{
base.Respawn();
- PlayerHalfExtents = PlayerBounds / 2f;
-
WalkController = new( this );
Velocity = Vector3.Zero;
- Position = new Vector3( 0.0f, 4.0f, 0.9f );
+ Position = new Vector3( 0.0f, 4.0f, 5.0f );
}
- public override void Update()
+ public void PredictedUpdate()
{
UpdateEyeTransform();
-
- WalkController.Update();
}
public override void FrameUpdate()
@@ -48,7 +43,6 @@ public override void FrameUpdate()
Health = MathX.Sin01( Time.Now ) * 100f;
}
- float lastHeight = 1.8f;
float lastFov = 90f;
private void UpdateCamera()
@@ -63,19 +57,11 @@ private void UpdateCamera()
//
Camera.Position = Position + LocalEyePosition;
- // Smooth out z-axis so that stairs, crouching are not sudden changes
- Camera.Position = Camera.Position.WithZ( lastHeight.LerpTo( Camera.Position.Z, 10f * Time.Delta ) );
- lastHeight = Camera.Position.Z;
-
//
// Field of view
//
float targetFov = 90f;
- // Interpolate velocity when sprinting
- if ( WalkController?.Sprinting ?? false && Velocity.WithZ( 0 ).Length > 1.0f )
- targetFov = 100f;
-
Camera.FieldOfView = lastFov.LerpTo( targetFov, 10 * Time.Delta );
lastFov = Camera.FieldOfView;
diff --git a/Samples/mocha-minimal/code/WalkController.cs b/Samples/mocha-minimal/code/WalkController.cs
new file mode 100644
index 00000000..da5fa928
--- /dev/null
+++ b/Samples/mocha-minimal/code/WalkController.cs
@@ -0,0 +1,223 @@
+using System;
+
+namespace Minimal;
+
+/*
+ * This is just a test for now. Will get put in base engine
+ * once it's done
+ */
+
+public class WalkController
+{
+ public float Friction => 12.0f;
+ public float GroundAccelerate => 50.0f;
+ public float MaxVelocityGround => 50.0f;
+ public float AirAccelerate => 5.0f;
+ public float MaxVelocityAir => 100.0f;
+ public float GroundDistance => 0.1f;
+ public float StepSize => 0.5f;
+ public float MaxAngle => 60.0f;
+
+ private bool IsGrounded => GroundEntity != null;
+ private BaseEntity GroundEntity;
+
+ private Player Player { get; set; }
+
+ private Vector3 Velocity;
+
+ public WalkController( Player player )
+ {
+ Player = player;
+ Player.IgnoreRigidbodyRotation = true;
+
+ Event.Register( this );
+
+ Player.Position = new Vector3( 0, 0, 10 );
+ }
+
+ [Event.Tick]
+ public void PredictedUpdate()
+ {
+ DebugOverlay.ScreenText( $"--------------------------------------------------------------------------------" );
+ DebugOverlay.ScreenText( $"{(Core.IsClient ? "Client" : "Server")}" );
+ DebugOverlay.ScreenText( $"Velocity: {Velocity}" );
+ DebugOverlay.ScreenText( $"GroundEntity: {GroundEntity?.Name ?? "None"}" );
+ DebugOverlay.ScreenText( $"IsGrounded: {IsGrounded}" );
+
+ CheckGrounded();
+ var wishDir = GetWishDir();
+
+ DebugOverlay.ScreenText( $"wishDir: {wishDir}" );
+ DebugOverlay.ScreenText( $"GroundAccelerate: {GroundAccelerate}" );
+
+ if ( IsGrounded )
+ {
+ Velocity = MoveGround( wishDir, Velocity );
+
+ Velocity.Z = 0;
+
+ if ( Input.Jump )
+ {
+ Velocity.Z += 4.0f;
+ GroundEntity = null;
+ }
+ }
+ else
+ {
+ Velocity = MoveAir( wishDir, Velocity );
+
+ Velocity.Z -= 9.8f * Time.Delta;
+ }
+
+ Player.Velocity = Velocity * 10f;
+ Move();
+
+ DebugOverlay.ScreenText( $"--------------------------------------------------------------------------------" );
+ }
+
+ private Vector3 GetWishDir()
+ {
+ var eulerRotation = Input.Rotation.ToEulerAngles();
+ var rotation = Rotation.From( eulerRotation.WithX( 0 ).WithZ( 0 ) );
+
+ var direction = Input.Direction.WithZ( 0 );
+
+ return direction * rotation;
+ }
+
+ private Vector3 Accelerate( Vector3 accelDir, Vector3 oldVelocity, float accelerate, float maxSpeed )
+ {
+ float projVel = Vector3.Dot( oldVelocity, accelDir );
+ float accelVel = accelerate * Time.Delta;
+
+ if ( projVel + accelVel > maxSpeed )
+ accelVel = maxSpeed - projVel;
+
+ return oldVelocity + accelDir * accelVel;
+ }
+
+ private Vector3 MoveGround( Vector3 accelDir, Vector3 oldVelocity )
+ {
+ float speed = oldVelocity.Length;
+
+ if ( speed != 0 ) // Avoid divide by zero
+ {
+ float drop = speed * Friction * Time.Delta;
+ oldVelocity *= MathF.Max( speed - drop, 0 ) / speed;
+ }
+
+ return Accelerate( accelDir, oldVelocity, GroundAccelerate, MaxVelocityGround );
+ }
+
+ private Vector3 MoveAir( Vector3 accelDir, Vector3 prevVelocity )
+ {
+ return Accelerate( accelDir, prevVelocity, AirAccelerate, MaxVelocityAir );
+ }
+
+ public Mocha.TraceResult TraceBBox( Vector3 start, Vector3 end )
+ {
+ return Cast.Ray( start, end ).WithHalfExtents( Player.PlayerBounds ).Ignore( Player ).Run();
+ }
+
+ private void CheckGrounded()
+ {
+ var tr = TraceBBox( Player.Position, Player.Position + Vector3.Down * GroundDistance );
+
+ // Grounded only counts if the normal is facing upwards
+ var angle = Vector3.GetAngle( tr.Normal, Vector3.Up );
+
+ if ( tr.Hit && angle < MaxAngle )
+ GroundEntity = tr.Entity;
+ else
+ GroundEntity = null;
+ }
+
+ private Vector3 ProjectOnPlane( Vector3 a, Vector3 plane )
+ {
+ plane = plane.Normal;
+ var dot = Vector3.Dot( a, plane );
+ return a - plane * dot;
+ }
+
+ private (bool wasSuccess, Vector3 endPosition) SlideMove( Vector3 startPos, Vector3 endPos )
+ {
+ var tr = TraceBBox( startPos, endPos );
+
+ if ( tr.Fraction < 1 )
+ {
+ // The player is colliding with a wall
+ var normal = tr.Normal;
+ var newVel = ProjectOnPlane( Velocity, normal );
+ endPos = startPos + newVel * Time.Delta;
+
+ // Check for collisions with adjacent walls
+ var tr2 = TraceBBox( endPos, endPos + Vector3.Up * StepSize );
+ if ( tr2.Fraction == 0 )
+ {
+ // The player is stuck in a corner
+ var sideVel = ProjectOnPlane( Velocity, tr2.Normal );
+ newVel += sideVel;
+ endPos = startPos + newVel * Time.Delta;
+ }
+ }
+
+ var finalTr = TraceBBox( startPos, endPos );
+ Player.Position = finalTr.EndPosition;
+
+ return (true, finalTr.EndPosition);
+ }
+
+ private MoveResult StepMove( Vector3 startPos, Vector3 endPos )
+ {
+ var tr = TraceBBox( startPos, endPos );
+
+ if ( tr.Fraction < 1 )
+ {
+ // Try stepping up
+ var stepPos = endPos + Vector3.Up * StepSize;
+ var stepTr = TraceBBox( stepPos, stepPos );
+
+ if ( stepTr.Fraction > 0 )
+ {
+ // Trace back down to see how far we should step
+ var stepDownTr = TraceBBox( stepPos, stepPos + Vector3.Down * StepSize );
+ return (true, stepDownTr.EndPosition);
+ }
+ }
+
+ return (false, endPos);
+ }
+
+ private void Move()
+ {
+ var startPos = Player.Position;
+ var endPos = startPos + Velocity * Time.Delta;
+
+ var (stepSuccess, stepPosition) = StepMove( startPos, endPos );
+ if ( stepSuccess )
+ {
+ Player.Position = stepPosition;
+ return;
+ }
+
+ var (slideSuccess, slidePosition) = SlideMove( startPos, stepPosition );
+ if ( slideSuccess )
+ {
+ Player.Position = slidePosition;
+ return;
+ }
+ }
+}
+
+internal record struct MoveResult( bool wasSuccess, Vector3 endPosition )
+{
+ public static implicit operator (bool wasSuccess, Vector3 endPosition)( MoveResult value )
+ {
+ return (value.wasSuccess, value.endPosition);
+ }
+
+ public static implicit operator MoveResult( (bool wasSuccess, Vector3 endPosition) value )
+ {
+ return new MoveResult( value.wasSuccess, value.endPosition );
+ }
+}
diff --git a/Source/Mocha.Common/AssemblyInfo.cs b/Source/Mocha.Common/AssemblyInfo.cs
new file mode 100644
index 00000000..5bedafd7
--- /dev/null
+++ b/Source/Mocha.Common/AssemblyInfo.cs
@@ -0,0 +1,26 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// In SDK-style projects such as this one, several assembly attributes that were historically
+// defined in this file are now automatically added during build and populated with
+// values defined in project properties. For details of which attributes are included
+// and how to customise this process see: https://aka.ms/assembly-info-properties
+
+
+// Setting ComVisible to false makes the types in this assembly not visible to COM
+// components. If you need to access a type in this assembly from COM, set the ComVisible
+// attribute to true on that type.
+
+[assembly: ComVisible( false )]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM.
+
+[assembly: Guid( "c43b1a2c-60e4-4832-a0ea-066eb190ba74" )]
+
+[assembly: InternalsVisibleTo( "Mocha.Tests" )]
+[assembly: InternalsVisibleTo( "Mocha.Engine" )]
+[assembly: InternalsVisibleTo( "Mocha.UI" )]
+[assembly: InternalsVisibleTo( "Mocha.Hotload" )]
+[assembly: InternalsVisibleTo( "Mocha.Networking" )]
+[assembly: InternalsVisibleTo( "Mocha.CodeGen" )]
+[assembly: InternalsVisibleTo( "Mocha.Editor" )]
diff --git a/Source/Mocha.Common/Callbacks/CallbackDispatcher.cs b/Source/Mocha.Common/Callbacks/CallbackDispatcher.cs
new file mode 100644
index 00000000..c5c92fdf
--- /dev/null
+++ b/Source/Mocha.Common/Callbacks/CallbackDispatcher.cs
@@ -0,0 +1,29 @@
+namespace Mocha.Common;
+
+///
+/// We store all of the information required to call a C# method from a native
+/// context here.
+///
+public class CallbackDispatcher
+{
+ private static Dictionary _callbacks = new();
+ private static uint _nextCallbackId = 0;
+
+ public static uint RegisterCallback( T callback ) where T : Delegate
+ {
+ var callbackId = _nextCallbackId++;
+ _callbacks.Add( callbackId, new CallbackInfo( callback ) );
+
+ return callbackId;
+ }
+
+ public static void Invoke( uint callbackId )
+ {
+ _callbacks[callbackId].Invoke();
+ }
+
+ public static void Invoke( uint callbackId, IntPtr arg )
+ {
+ _callbacks[callbackId].Invoke( arg );
+ }
+}
diff --git a/Source/Mocha.Common/Callbacks/CallbackInfo.cs b/Source/Mocha.Common/Callbacks/CallbackInfo.cs
new file mode 100644
index 00000000..6d64f940
--- /dev/null
+++ b/Source/Mocha.Common/Callbacks/CallbackInfo.cs
@@ -0,0 +1,21 @@
+namespace Mocha.Common;
+
+public readonly struct CallbackInfo
+{
+ public Delegate Callback { get; init; }
+
+ public CallbackInfo( Delegate callback )
+ {
+ Callback = callback;
+ }
+
+ public void Invoke( IntPtr arg )
+ {
+ Callback.DynamicInvoke( arg );
+ }
+
+ public void Invoke()
+ {
+ Callback.DynamicInvoke();
+ }
+}
diff --git a/Source/Mocha.Common/Client/Logger.cs b/Source/Mocha.Common/Client/Logger.cs
index 9aae70fd..ff722a6e 100644
--- a/Source/Mocha.Common/Client/Logger.cs
+++ b/Source/Mocha.Common/Client/Logger.cs
@@ -15,33 +15,19 @@ private string GetString( object? obj )
return logStr;
}
- public void Trace( object? obj )
+ private void Log( object? obj, Action logAction )
{
- string str = GetString( obj );
- OnLog?.Invoke( str );
- Glue.LogManager.ManagedTrace( str );
- }
+ var loggerName = Core.IsClient ? "cl" : "sv";
- public void Info( object? obj )
- {
string str = GetString( obj );
OnLog?.Invoke( str );
- Glue.LogManager.ManagedInfo( str );
+ logAction( loggerName, str );
}
- public void Warning( object? obj )
- {
- string str = GetString( obj );
- OnLog?.Invoke( str );
- Glue.LogManager.ManagedWarning( str );
- }
-
- public void Error( object? obj )
- {
- string str = GetString( obj );
- OnLog?.Invoke( str );
- Glue.LogManager.ManagedError( str );
- }
+ public void Trace( object? obj ) => Log( obj, NativeEngine.GetLogManager().ManagedTrace );
+ public void Info( object? obj ) => Log( obj, NativeEngine.GetLogManager().ManagedInfo );
+ public void Warning( object? obj ) => Log( obj, NativeEngine.GetLogManager().ManagedWarning );
+ public void Error( object? obj ) => Log( obj, NativeEngine.GetLogManager().ManagedError );
public struct LogEntry
{
@@ -60,7 +46,8 @@ public struct LogEntry
public List GetHistory()
{
- var logHistory = Glue.LogManager.GetLogHistory();
+ var logManager = NativeEngine.GetLogManager();
+ var logHistory = logManager.GetLogHistory();
LogEntry[] logEntries = new LogEntry[logHistory.count];
var ptr = logHistory.items;
diff --git a/Source/Mocha.Common/Client/Time.cs b/Source/Mocha.Common/Client/Time.cs
index 721cd61d..a52eeee0 100644
--- a/Source/Mocha.Common/Client/Time.cs
+++ b/Source/Mocha.Common/Client/Time.cs
@@ -8,15 +8,19 @@ public static class Time
public static List FPSHistory { get; } = new();
- public static void UpdateFrom( float tickDeltaTime )
- {
- Delta = tickDeltaTime;
- Now = Glue.Engine.GetTime();
+ private const int TimeScale = 5;
- FPS = Glue.Engine.GetFramesPerSecond().CeilToInt();
+ public static void UpdateFrom( float deltaTime )
+ {
+ Delta = deltaTime;
+ Now = NativeEngine.GetTime();
+ FPS = NativeEngine.GetFramesPerSecond().CeilToInt();
FPSHistory.Add( FPS );
- if ( FPSHistory.Count > 512 )
+
+ if ( FPSHistory.Count > TimeScale / Delta )
+ {
FPSHistory.RemoveAt( 0 );
+ }
}
}
diff --git a/Source/Mocha.Common/Console/ConVar.cs b/Source/Mocha.Common/Console/ConVar.cs
index 932c3e35..960e896b 100644
--- a/Source/Mocha.Common/Console/ConVar.cs
+++ b/Source/Mocha.Common/Console/ConVar.cs
@@ -6,11 +6,11 @@ public abstract class BaseAttribute : Attribute
{
public required string Name { get; init; }
public required CVarFlags Flags { get; init; }
- public required string Description { get; init; }
- }
+ public required string Description { get; init; }
+}
- public class TestAttribute : Attribute
- {
+public class TestAttribute : Attribute
+{
- }
+}
}
diff --git a/Source/Mocha.Common/Console/ConsoleDispatchInfo.cs b/Source/Mocha.Common/Console/ConsoleDispatchInfo.cs
index e0f4f66a..e2475ebf 100644
--- a/Source/Mocha.Common/Console/ConsoleDispatchInfo.cs
+++ b/Source/Mocha.Common/Console/ConsoleDispatchInfo.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
+using System.Runtime.InteropServices;
namespace Mocha.Common.Console;
@@ -39,3 +34,11 @@ public struct BoolCVarDispatchInfo
public bool oldValue;
public bool newValue;
}
+
+[StructLayout( LayoutKind.Sequential )]
+public struct IntCVarDispatchInfo
+{
+ public IntPtr name;
+ public int oldValue;
+ public int newValue;
+}
diff --git a/Source/Mocha.Common/EntityRegistry.cs b/Source/Mocha.Common/Entities/EntityRegistry.cs
similarity index 100%
rename from Source/Mocha.Common/EntityRegistry.cs
rename to Source/Mocha.Common/Entities/EntityRegistry.cs
diff --git a/Source/Mocha.Common/Types/IEntity.cs b/Source/Mocha.Common/Entities/IEntity.cs
similarity index 99%
rename from Source/Mocha.Common/Types/IEntity.cs
rename to Source/Mocha.Common/Entities/IEntity.cs
index 700f2be0..760336c3 100644
--- a/Source/Mocha.Common/Types/IEntity.cs
+++ b/Source/Mocha.Common/Entities/IEntity.cs
@@ -4,6 +4,7 @@ public interface IEntity
{
string Name { get; set; }
uint NativeHandle { get; }
+
Vector3 Position { get; set; }
Rotation Rotation { get; set; }
Vector3 Scale { get; set; }
diff --git a/Source/Mocha.Common/Event/Event.cs b/Source/Mocha.Common/Event/Event.cs
index 5dfd94d1..881b6728 100644
--- a/Source/Mocha.Common/Event/Event.cs
+++ b/Source/Mocha.Common/Event/Event.cs
@@ -46,16 +46,28 @@ public static void RegisterStatics()
public static void Run( string name, params object[] parameters )
{
- s_events.ForEach( e =>
+ s_events.ToList().ForEach( e =>
{
if ( e.Name == name )
e.Method?.Invoke( e.Object, parameters );
} );
}
+ public static void Run( Assembly targetAssembly, string name )
+ {
+ s_events.ToList().ForEach( e =>
+ {
+ if ( e.Object.GetType().Assembly != targetAssembly )
+ return;
+
+ if ( e.Name == name )
+ e.Method?.Invoke( e.Object, null );
+ } );
+ }
+
public static void Run( string name )
{
- s_events.ForEach( e =>
+ s_events.ToList().ForEach( e =>
{
if ( e.Name == name )
e.Method?.Invoke( e.Object, null );
diff --git a/Source/Mocha.Common/Event/Events/Generic.cs b/Source/Mocha.Common/Event/Events/Generic.cs
new file mode 100644
index 00000000..7f1e7b92
--- /dev/null
+++ b/Source/Mocha.Common/Event/Events/Generic.cs
@@ -0,0 +1,10 @@
+namespace Mocha.Common;
+
+partial class Event
+{
+ public class TickAttribute : EventAttribute
+ {
+ public const string Name = "Event.Tick";
+ public TickAttribute() : base( Name ) { }
+ }
+}
diff --git a/Source/Mocha.Common/Global.cs b/Source/Mocha.Common/Global/Global.cs
similarity index 78%
rename from Source/Mocha.Common/Global.cs
rename to Source/Mocha.Common/Global/Global.cs
index 88db35cd..41a903b5 100644
--- a/Source/Mocha.Common/Global.cs
+++ b/Source/Mocha.Common/Global/Global.cs
@@ -9,7 +9,7 @@ namespace Mocha.Common;
public static class Global
{
- public static ILogger Log { get; set; }
- public static bool IsClient { get; }
public static UnmanagedArgs UnmanagedArgs { get; set; }
+ public static Glue.Root NativeEngine { get; set; } = null!;
+ public static ILogger Log { get; set; } = null!;
}
diff --git a/Source/Mocha.Common/Global/GlobalVars.cs b/Source/Mocha.Common/Global/GlobalVars.cs
new file mode 100644
index 00000000..a277527d
--- /dev/null
+++ b/Source/Mocha.Common/Global/GlobalVars.cs
@@ -0,0 +1,8 @@
+namespace Mocha.Common;
+
+public static class Core
+{
+ public static bool IsServer { get; set; }
+ public static bool IsClient { get; set; }
+ public static int TickRate { get; set; }
+}
diff --git a/Source/Mocha.Common/IGame.cs b/Source/Mocha.Common/IGame.cs
index b8ad91a7..359be959 100644
--- a/Source/Mocha.Common/IGame.cs
+++ b/Source/Mocha.Common/IGame.cs
@@ -5,6 +5,14 @@ public interface IGame
void Startup();
void Shutdown();
+ ///
+ /// Called every frame on the client.
+ /// Note that there is nothing for this on the server, because servers don't render anything.
+ ///
void FrameUpdate();
+
+ ///
+ /// Called every tick on the client and the server.
+ ///
void Update();
}
diff --git a/Source/Mocha.Common/Input/Input.cs b/Source/Mocha.Common/Input/Input.cs
index 8586cb35..e19d3f89 100644
--- a/Source/Mocha.Common/Input/Input.cs
+++ b/Source/Mocha.Common/Input/Input.cs
@@ -2,24 +2,26 @@
public static partial class Input
{
- public static bool Left => Glue.Input.IsButtonDown( 1 );
- public static bool Middle => Glue.Input.IsButtonDown( 2 );
- public static bool Right => Glue.Input.IsButtonDown( 3 );
+ private static Glue.InputManager NativeInput => NativeEngine.GetInputManager();
- public static bool Button4 => Glue.Input.IsButtonDown( 4 );
- public static bool Button5 => Glue.Input.IsButtonDown( 5 );
+ public static bool Left => NativeInput.IsButtonDown( 1 );
+ public static bool Middle => NativeInput.IsButtonDown( 2 );
+ public static bool Right => NativeInput.IsButtonDown( 3 );
+
+ public static bool Button4 => NativeInput.IsButtonDown( 4 );
+ public static bool Button5 => NativeInput.IsButtonDown( 5 );
// TODO: [ConVar.Archive( "mouse_sensitivity", 2.0f, "Player mouse look sensitivity" )]
public static float MouseSensitivity { get; set; } = 2.5f;
- public static Vector2 MousePosition => Glue.Input.GetMousePosition();
- public static Vector2 MouseDelta => Glue.Input.GetMouseDelta();
+ public static Vector2 MousePosition => NativeInput.GetMousePosition();
+ public static Vector2 MouseDelta => NativeInput.GetMouseDelta();
public static Rotation Rotation { get; private set; } = Rotation.Identity;
public static Vector3 Direction { get; private set; }
- private static bool IsKeyDown( InputButton key ) => Glue.Input.IsKeyDown( (int)key );
+ private static bool IsKeyDown( InputButton key ) => NativeInput.IsKeyDown( (int)key );
public static bool Jump => IsKeyDown( InputButton.KeySpace );
public static bool Crouch => IsKeyDown( InputButton.KeyControl );
diff --git a/Source/Mocha.Common/Types/Vector3.cs b/Source/Mocha.Common/Types/Vector3.cs
index e50351d0..f64ececc 100644
--- a/Source/Mocha.Common/Types/Vector3.cs
+++ b/Source/Mocha.Common/Types/Vector3.cs
@@ -48,8 +48,8 @@ public float Z
public readonly float Length => _internalVector.Length();
public readonly float LengthSquared => _internalVector.LengthSquared();
-
public readonly Vector3 Normal => (Length == 0) ? new( 0 ) : (this / Length);
+
public void Normalize()
{
this = Normal;
diff --git a/Source/Mocha.Common/Utils/Glue/INativeGlue.cs b/Source/Mocha.Common/Utils/Glue/INativeGlue.cs
index 0eaf8c2c..2ac567e9 100644
--- a/Source/Mocha.Common/Utils/Glue/INativeGlue.cs
+++ b/Source/Mocha.Common/Utils/Glue/INativeGlue.cs
@@ -2,5 +2,5 @@
public interface INativeGlue
{
- public IntPtr NativePtr { get; }
+ public IntPtr NativePtr { get; set; }
}
diff --git a/Source/Mocha.Common/Utils/Glue/ManagedCallbackDispatchInfo.cs b/Source/Mocha.Common/Utils/Glue/ManagedCallbackDispatchInfo.cs
new file mode 100644
index 00000000..e8023359
--- /dev/null
+++ b/Source/Mocha.Common/Utils/Glue/ManagedCallbackDispatchInfo.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace Mocha.Common;
+
+[StructLayout( LayoutKind.Sequential )]
+public struct ManagedCallbackDispatchInfo
+{
+ public uint handle;
+ public int argsSize;
+ public IntPtr args;
+};
diff --git a/Source/Mocha.Common/Utils/HexDump.cs b/Source/Mocha.Common/Utils/HexDump.cs
new file mode 100644
index 00000000..70bc012b
--- /dev/null
+++ b/Source/Mocha.Common/Utils/HexDump.cs
@@ -0,0 +1,53 @@
+using System.Text;
+
+namespace Mocha.Common;
+
+public class HexDump
+{
+ public static string Dump( byte[] bytes, int bytesPerLine )
+ {
+ StringBuilder sb = new StringBuilder();
+ int offset = 0;
+
+ while ( offset < bytes.Length )
+ {
+ int remainingBytes = bytes.Length - offset;
+ int lineBytes = Math.Min( bytesPerLine, remainingBytes );
+
+ sb.AppendFormat( "{0:x8} ", offset );
+
+ for ( int i = 0; i < lineBytes; i++ )
+ {
+ sb.AppendFormat( "{0:x2} ", bytes[offset + i] );
+ }
+
+ if ( lineBytes < bytesPerLine )
+ {
+ sb.Append( new string( ' ', (bytesPerLine - lineBytes) * 3 ) );
+ }
+
+ sb.Append( "|" );
+
+ for ( int i = 0; i < lineBytes; i++ )
+ {
+ char c = (char)bytes[offset + i];
+
+ // If char isn't a letter, symbol, or number, replace it with a dot.
+ if ( (int)c > 32 && (int)c < 127 )
+ {
+ sb.Append( c );
+ }
+ else
+ {
+ sb.Append( "." );
+ }
+ }
+
+ sb.AppendLine( "|" );
+
+ offset += lineBytes;
+ }
+
+ return sb.ToString();
+ }
+}
diff --git a/Source/Mocha.Editor/Editor/ConsoleOverlay.cs b/Source/Mocha.Editor/Editor/ConsoleOverlay.cs
index ecd6f3f1..7bee86b7 100644
--- a/Source/Mocha.Editor/Editor/ConsoleOverlay.cs
+++ b/Source/Mocha.Editor/Editor/ConsoleOverlay.cs
@@ -10,17 +10,18 @@ public static void Render()
// Setup invisible window
//
ImGui.SetNextWindowViewport( ImGui.GetMainViewport().ID );
- ImGui.SetNextWindowSize( new Vector2( 0 ) );
- ImGui.PushStyleColor( ImGuiCol.WindowBg, Theme.Gray.ToBackground( 0.75f ) );
- ImGui.PushStyleColor( ImGuiCol.Border, Theme.Transparent );
- ImGui.PushStyleVar( ImGuiStyleVar.WindowRounding, 0 );
-
- if ( ImGui.Begin( "consoleoverlay", ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoInputs | ImGuiWindowFlags.NoTitleBar ) )
+ if ( ImGuiX.BeginOverlay( "ConsoleOverlay" ) )
{
+ Vector2 workPos = ImGui.GetMainViewport().WorkPos;
+ Vector2 workSize = ImGui.GetMainViewport().WorkSize;
+ Vector2 windowSize = ImGui.GetWindowSize();
+
+ ImGui.SetWindowPos( new Vector2( workPos.X + workSize.X - windowSize.X - 16, workPos.Y + workSize.Y - windowSize.Y - 16 ) );
+
var logEntries = Log.GetHistory().TakeLast( Count ).ToArray();
- for ( int i = 0; i < Count; ++i )
+ for ( int i = 0; i < logEntries.Length; ++i )
{
var logEntry = logEntries[i];
var alpha = i / (float)Count;
@@ -43,13 +44,6 @@ public static void Render()
}
}
- var windowSize = ImGui.GetWindowSize();
- var windowPos = ImGui.GetMainViewport().WorkPos + new System.Numerics.Vector2( ImGui.GetMainViewport().WorkSize.X - windowSize.X, ImGui.GetMainViewport().WorkSize.Y - windowSize.Y );
- ImGui.SetWindowPos( windowPos );
-
ImGui.End();
-
- ImGui.PopStyleColor( 2 );
- ImGui.PopStyleVar();
}
}
diff --git a/Source/Mocha.Editor/Editor/Editor.cs b/Source/Mocha.Editor/Editor/Editor.cs
index e8deb42f..452693a6 100644
--- a/Source/Mocha.Editor/Editor/Editor.cs
+++ b/Source/Mocha.Editor/Editor/Editor.cs
@@ -10,7 +10,9 @@ public class Editor
{
new ConsoleWindow(),
new BrowserWindow(),
- new InspectorWindow()
+ new InspectorWindow(),
+
+ new NetworkingWindow()
};
public static void Draw()
@@ -94,14 +96,67 @@ private static void DrawStatusBar()
private static void DrawPerformanceOverlay()
{
- if ( ImGuiX.BeginOverlay( "Time" ) )
+ if ( ImGuiX.BeginOverlay( "PerformanceOverlay" ) )
{
- var gpuName = ImGuiX.GetGPUName();
+ Vector2 workPos = ImGui.GetMainViewport().WorkPos;
+ Vector2 workSize = ImGui.GetMainViewport().WorkSize;
+ Vector2 windowSize = ImGui.GetWindowSize();
+
+ ImGui.SetWindowPos( new Vector2( workPos.X + workSize.X - windowSize.X - 16, workPos.Y + workSize.Y - windowSize.Y - 128 - 16 ) );
+
+ var cursorPos = ImGui.GetCursorPos();
+ void DrawProperty( string name, string value )
+ {
+ ImGuiX.TextBold( name );
+ ImGui.SameLine();
+
+ var textWidth = ImGui.CalcTextSize( value ).X;
+ ImGui.SetCursorPosX( cursorPos.X + 128 - textWidth );
+ ImGui.Text( value );
+ }
+
+ {
+ var left = $"{Time.FPS} FPS";
+ var right = $"{Time.Delta * 1000f:F0}ms";
+
+ ImGuiX.TextSubheading( left );
+ ImGui.SameLine();
+
+ var textWidth = ImGui.CalcTextSize( right ).X * 1.15f;
+ ImGui.SetCursorPosX( cursorPos.X + 128 - textWidth );
+ ImGui.SetCursorPosY( cursorPos.Y );
+ ImGuiX.TextSubheading( right );
+ }
+
+ var fpsHistory = Time.FPSHistory.Select( x => (float)x ).ToArray();
+ var scaleMax = fpsHistory.Max();
+
+ ImGui.PushStyleColor( ImGuiCol.FrameBg, Vector4.Zero );
+ ImGui.PushStyleVar( ImGuiStyleVar.FramePadding, new Vector2( 0, 0 ) );
+ ImGui.PlotHistogram( "##FrameTimes", ref fpsHistory[0], Time.FPSHistory.Count, 0, "", 0f, scaleMax, new Vector2( 128f, 32 ) );
+ ImGui.PopStyleVar();
+ ImGui.PopStyleColor();
+
+ ImGuiX.Separator( new Vector4( 1, 1, 1, 0.05f ) );
+
+ var min = fpsHistory.Min();
+ DrawProperty( $"Min", $"{min:F0}fps" );
+ var max = fpsHistory.Max();
+ DrawProperty( $"Max", $"{max:F0}fps" );
+ var avg = fpsHistory.Average();
+ DrawProperty( $"Avg", $"{avg:F0}fps" );
+
+ ImGuiX.Separator( new Vector4( 1, 1, 1, 0.05f ) );
+
+ DrawProperty( $"Elapsed time", $"{Time.Now:F0}s" );
+ DrawProperty( $"Current tick", $"{NativeEngine.GetCurrentTick():F0}" );
+ DrawProperty( $"Tick rate", $"{Core.TickRate}" );
+
+ ImGuiX.Separator( new Vector4( 1, 1, 1, 0.05f ) );
- ImGui.Text( $"GPU: {gpuName}" );
- ImGui.Text( $"FPS: {Time.FPS}" );
- ImGui.Text( $"Current time: {Time.Now}" );
- ImGui.Text( $"Frame time: {(Time.Delta * 1000f).CeilToInt()}ms" );
+ DrawProperty( $"Ping", $"{0}ms" );
+ DrawProperty( $"Jitter", $"{0}ms" );
+ DrawProperty( $"Loss", $"{0}" );
}
ImGui.End();
diff --git a/Source/Mocha.Editor/Editor/ImGuiX.cs b/Source/Mocha.Editor/Editor/ImGuiX.cs
index 5d22a04a..31e91902 100644
--- a/Source/Mocha.Editor/Editor/ImGuiX.cs
+++ b/Source/Mocha.Editor/Editor/ImGuiX.cs
@@ -2,6 +2,8 @@
public static class ImGuiX
{
+ private static Glue.EditorManager NativeEditor => NativeEngine.GetEditorManager();
+
public static void SeparatorH()
{
ImGui.Dummy( new Vector2( 0, 4 ) );
@@ -48,32 +50,32 @@ public static bool BeginWindow( string name, ref bool isOpen )
public static void TextMonospace( string text )
{
- Glue.Editor.TextMonospace( text );
+ NativeEditor.TextMonospace( text );
}
public static void TextLight( string text )
{
- Glue.Editor.TextLight( text );
+ NativeEditor.TextLight( text );
}
public static void TextBold( string text )
{
- Glue.Editor.TextBold( text );
+ NativeEditor.TextBold( text );
}
public static void TextHeading( string text )
{
- Glue.Editor.TextHeading( text );
+ NativeEditor.TextHeading( text );
}
public static void TextSubheading( string text )
{
- Glue.Editor.TextSubheading( text );
+ NativeEditor.TextSubheading( text );
}
public static bool BeginMainStatusBar()
{
- return Glue.Editor.BeginMainStatusBar();
+ return NativeEditor.BeginMainStatusBar();
}
public static void EndMainStatusBar()
@@ -86,27 +88,27 @@ public static bool BeginOverlay( string name )
{
ImGui.SetNextWindowViewport( ImGui.GetMainViewport().ID );
+ ImGui.SetNextWindowBgAlpha( 0.5f );
+ ImGui.PushStyleVar( ImGuiStyleVar.WindowBorderSize, 0 );
+
bool b = ImGui.Begin( name, ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoInputs );
if ( b )
- {
- Vector2 workPos = ImGui.GetMainViewport().WorkPos;
-
- ImGui.SetWindowPos( new Vector2( workPos.X + 16, workPos.Y + 16 ) );
ImGui.SetWindowSize( new Vector2( -1, -1 ) );
- }
+
+ ImGui.PopStyleVar();
return b;
}
public static string GetGPUName()
{
- return Glue.Editor.GetGPUName();
+ return NativeEditor.GetGPUName();
}
public static void RenderViewDropdown()
{
- Glue.Editor.RenderViewDropdown();
+ NativeEditor.RenderViewDropdown();
}
public static string Align( string str ) => str.PadRight( 16, ' ' );
@@ -135,12 +137,12 @@ public static bool ImageButton( Texture texture, Vector2 size )
public static void Image( Texture texture, Vector2 size )
{
- Glue.Editor.Image( texture.NativeTexture, texture.Width, texture.Height, (int)size.X, (int)size.Y );
+ NativeEditor.Image( texture.NativeTexture, texture.Width, texture.Height, (int)size.X, (int)size.Y );
}
public static void Image( Texture texture, Vector2 size, Vector4 tint )
{
- Glue.Editor.Image( texture.NativeTexture, texture.Width, texture.Height, (int)size.X, (int)size.Y );
+ NativeEditor.Image( texture.NativeTexture, texture.Width, texture.Height, (int)size.X, (int)size.Y );
}
public static void Image( string texturePath, Vector2 size )
@@ -153,10 +155,12 @@ public static void Image( string texture, Vector2 size, Vector4 tint )
Image( Texture.FromCache( texture ), size, tint );
}
- public static void Separator()
+ public static void Separator( Vector4? _color = null )
{
+ var color = _color ?? new Vector4( 0.28f, 0.28f, 0.28f, 0.29f );
+
ImGui.Dummy( new( 0, 4 ) );
- ImGui.PushStyleColor( ImGuiCol.Separator, new System.Numerics.Vector4( 0.28f, 0.28f, 0.28f, 0.29f ) );
+ ImGui.PushStyleColor( ImGuiCol.Separator, color );
ImGui.Separator();
ImGui.PopStyleColor();
ImGui.Dummy( new( 0, 4 ) );
diff --git a/Source/Mocha.Editor/Editor/Notifications.cs b/Source/Mocha.Editor/Editor/Notifications.cs
index ab62e196..a01484d4 100644
--- a/Source/Mocha.Editor/Editor/Notifications.cs
+++ b/Source/Mocha.Editor/Editor/Notifications.cs
@@ -14,7 +14,7 @@ private static void DrawNotifications()
ImGuiWindowFlags.NoMove;
const float padding = 24.0f;
- const float margin = 16.0f;
+ const float margin = 8.0f;
var viewport = ImGui.GetMainViewport();
var workPos = viewport.WorkPos;
@@ -28,6 +28,7 @@ private static void DrawNotifications()
float y = 0;
var notifications = Common.Notify.Notifications.ToArray();
+
for ( int i = 0; i < notifications.Length; i++ )
{
var notification = notifications[i];
@@ -52,7 +53,7 @@ private static void DrawNotifications()
ImGui.SetNextWindowSize( new System.Numerics.Vector2( 0, 0 ) );
ImGui.SetNextWindowViewport( ImGui.GetMainViewport().ID );
- if ( ImGui.Begin( $"##{notification.GetHashCode()}_overlay", windowFlags ) )
+ if ( ImGui.Begin( $"##notification_{i}_overlay", windowFlags ) )
{
ImGui.PushStyleColor( ImGuiCol.Text, new Vector4( 1, 1, 1, alpha ) );
diff --git a/Source/Mocha.Editor/Editor/Windows/NetworkingWindow.cs b/Source/Mocha.Editor/Editor/Windows/NetworkingWindow.cs
new file mode 100644
index 00000000..342d1ad8
--- /dev/null
+++ b/Source/Mocha.Editor/Editor/Windows/NetworkingWindow.cs
@@ -0,0 +1,20 @@
+using Mocha.Editor;
+
+[Title( "Networking" )]
+public class NetworkingWindow : EditorWindow
+{
+ public NetworkingWindow()
+ {
+ isVisible = false;
+ }
+
+ public override void Draw()
+ {
+ if ( ImGuiX.BeginWindow( name: $"Networking", ref isVisible ) )
+ {
+
+ }
+
+ ImGui.End();
+ }
+}
diff --git a/Source/Mocha.Editor/Game.cs b/Source/Mocha.Editor/Game.cs
index 43e1a394..671d6828 100644
--- a/Source/Mocha.Editor/Game.cs
+++ b/Source/Mocha.Editor/Game.cs
@@ -17,7 +17,7 @@ public void Shutdown()
public void Startup()
{
- ImGuiNative.igSetCurrentContext( Glue.Editor.GetContextPointer() );
+ ImGuiNative.igSetCurrentContext( NativeEngine.GetEditorManager().GetContextPointer() );
}
public void Update()
diff --git a/Source/Mocha.Engine/AssemblyInfo.cs b/Source/Mocha.Engine/AssemblyInfo.cs
new file mode 100644
index 00000000..d8e00fc9
--- /dev/null
+++ b/Source/Mocha.Engine/AssemblyInfo.cs
@@ -0,0 +1,19 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// In SDK-style projects such as this one, several assembly attributes that were historically
+// defined in this file are now automatically added during build and populated with
+// values defined in project properties. For details of which attributes are included
+// and how to customise this process see: https://aka.ms/assembly-info-properties
+
+
+// Setting ComVisible to false makes the types in this assembly not visible to COM
+// components. If you need to access a type in this assembly from COM, set the ComVisible
+// attribute to true on that type.
+
+[assembly: ComVisible( false )]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM.
+
+[assembly: Guid( "1ffcb1b0-8fd9-465c-8c90-bc4c3069e6e1" )]
+[assembly: InternalsVisibleTo( "Mocha.Tests" )]
diff --git a/Source/Mocha.Engine/BaseGame.cs b/Source/Mocha.Engine/BaseGame.cs
index 6edbe124..6c719c99 100644
--- a/Source/Mocha.Engine/BaseGame.cs
+++ b/Source/Mocha.Engine/BaseGame.cs
@@ -46,32 +46,55 @@ private void TryCallMethodOnEntity( string methodName )
} );
}
- public virtual void FrameUpdate()
+ public void FrameUpdate()
{
UIManager.Instance.Render();
TryCallMethodOnEntity( "FrameUpdate" );
}
- public virtual void Update()
+ public void Update()
{
- // HACK: Clear DebugOverlay here because doing it
- // per-frame doesn't play nice with tick-based
- // entries (needs fix)
+ if ( Core.IsClient )
+ {
+ // HACK: Clear DebugOverlay here because doing it
+ // per-frame doesn't play nice with tick-based
+ // entries (needs fix)
- DebugOverlay.screenTextList.Clear();
- DebugOverlay.currentLine = 0;
+ DebugOverlay.screenTextList.Clear();
+ DebugOverlay.currentLine = 0;
+ }
- TryCallMethodOnEntity( "Update" );
+ DebugOverlay.ScreenText( $"BaseGame.Update assembly {GetType().Assembly.GetHashCode()}" );
}
- public virtual void Shutdown()
+ public void Shutdown()
{
+ OnShutdown();
}
- public virtual void Startup()
+ public void Startup()
{
+ OnStartup();
+ }
+
+ #region "Public API"
+ ///
+ /// Called on the server when the game starts up
+ ///
+ public virtual void OnStartup()
+ {
+
+ }
+
+ ///
+ /// Called on the server when the game shuts down
+ ///
+ public virtual void OnShutdown()
+ {
+
}
+ #endregion
[Event.Game.Hotload]
public void OnHotload()
diff --git a/Source/Mocha.Engine/DebugOverlay.cs b/Source/Mocha.Engine/DebugOverlay.cs
deleted file mode 100644
index eef0e9f7..00000000
--- a/Source/Mocha.Engine/DebugOverlay.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace Mocha;
-
-public struct DebugOverlayText
-{
- public Vector2 position;
- public string text;
- public float time;
-
- public DebugOverlayText( Vector2 position, string text, float? time = null )
- {
- this.position = position;
- this.text = text;
- this.time = time ?? 0f;
- }
-}
-
-///
-/// Public API for
-///
-public static partial class DebugOverlay
-{
- ///
- /// This allows us to buffer debug overlay commands in advance.
- ///
- public readonly static List screenTextList = new();
- public static int currentLine = 0;
-
- public static void ScreenText( int line, object obj )
- {
- line++;
-
- var lineHeight = 16.0f;
- screenTextList.Add( new( new Vector2( 32, lineHeight * line ), obj.ToString()! ) );
- }
-
- public static void ScreenText( object obj )
- {
- currentLine++;
-
- var lineHeight = 16.0f;
- screenTextList.Add( new( new Vector2( 32, lineHeight * currentLine ), obj.ToString()! ) );
- }
-}
diff --git a/Source/Mocha.Engine/TraceResult.cs b/Source/Mocha.Engine/Physics/TraceResult.cs
similarity index 100%
rename from Source/Mocha.Engine/TraceResult.cs
rename to Source/Mocha.Engine/Physics/TraceResult.cs
diff --git a/Source/Mocha.Engine/Render/Assets/Model.cs b/Source/Mocha.Engine/Render/Assets/Model.cs
index 234fc218..37e7b4fd 100644
--- a/Source/Mocha.Engine/Render/Assets/Model.cs
+++ b/Source/Mocha.Engine/Render/Assets/Model.cs
@@ -39,12 +39,7 @@ public Model( Vertex[] vertices, Material material )
[Icon( FontAwesome.Cube ), Title( "Model" )]
public partial class Model : Asset, IModel where T : struct
{
- public Glue.Model NativeModel { get; set; }
-
- public Model()
- {
- NativeModel = new();
- }
+ public Glue.Model NativeModel { get; } = new();
protected void AddMesh( T[] vertices, Material material )
{
@@ -59,5 +54,6 @@ protected void AddMesh( T[] vertices, uint[] indices, Material material )
public interface IModel
{
- Glue.Model NativeModel { get; set; }
+ string Path { get; set; }
+ Glue.Model NativeModel { get; }
}
diff --git a/Source/Mocha.Engine/Runtimes/Facepunch.Steamworks.Win64.dll b/Source/Mocha.Engine/Runtimes/Facepunch.Steamworks.Win64.dll
new file mode 100644
index 00000000..bceb9103
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/Facepunch.Steamworks.Win64.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/GameNetworkingSockets.dll b/Source/Mocha.Engine/Runtimes/GameNetworkingSockets.dll
new file mode 100644
index 00000000..779ef51b
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/GameNetworkingSockets.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/MessagePack.Annotations.dll b/Source/Mocha.Engine/Runtimes/MessagePack.Annotations.dll
new file mode 100644
index 00000000..afe839d4
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/MessagePack.Annotations.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/MessagePack.dll b/Source/Mocha.Engine/Runtimes/MessagePack.dll
new file mode 100644
index 00000000..731ebef4
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/MessagePack.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/Microsoft.Build.Framework.dll b/Source/Mocha.Engine/Runtimes/Microsoft.Build.Framework.dll
deleted file mode 100644
index b84100aa..00000000
Binary files a/Source/Mocha.Engine/Runtimes/Microsoft.Build.Framework.dll and /dev/null differ
diff --git a/Source/Mocha.Engine/Runtimes/Microsoft.Build.Locator.dll b/Source/Mocha.Engine/Runtimes/Microsoft.Build.Locator.dll
deleted file mode 100644
index 36fc52c4..00000000
Binary files a/Source/Mocha.Engine/Runtimes/Microsoft.Build.Locator.dll and /dev/null differ
diff --git a/Source/Mocha.Engine/Runtimes/Microsoft.Build.dll b/Source/Mocha.Engine/Runtimes/Microsoft.Build.dll
deleted file mode 100644
index 486f6100..00000000
Binary files a/Source/Mocha.Engine/Runtimes/Microsoft.Build.dll and /dev/null differ
diff --git a/Source/Mocha.Engine/Runtimes/Microsoft.CodeAnalysis.CSharp.Workspaces.dll b/Source/Mocha.Engine/Runtimes/Microsoft.CodeAnalysis.CSharp.Workspaces.dll
new file mode 100644
index 00000000..dc218f98
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/Microsoft.CodeAnalysis.CSharp.Workspaces.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/Microsoft.CodeAnalysis.Workspaces.dll b/Source/Mocha.Engine/Runtimes/Microsoft.CodeAnalysis.Workspaces.dll
new file mode 100644
index 00000000..8dec4418
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/Microsoft.CodeAnalysis.Workspaces.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/Newtonsoft.Json.dll b/Source/Mocha.Engine/Runtimes/Newtonsoft.Json.dll
new file mode 100644
index 00000000..8ba89bf3
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/Newtonsoft.Json.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/NuGet.Common.dll b/Source/Mocha.Engine/Runtimes/NuGet.Common.dll
new file mode 100644
index 00000000..e09cbe54
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/NuGet.Common.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/NuGet.Configuration.dll b/Source/Mocha.Engine/Runtimes/NuGet.Configuration.dll
new file mode 100644
index 00000000..246c8839
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/NuGet.Configuration.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/NuGet.Frameworks.dll b/Source/Mocha.Engine/Runtimes/NuGet.Frameworks.dll
new file mode 100644
index 00000000..d78c4786
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/NuGet.Frameworks.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/NuGet.Packaging.dll b/Source/Mocha.Engine/Runtimes/NuGet.Packaging.dll
new file mode 100644
index 00000000..33779601
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/NuGet.Packaging.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/NuGet.Protocol.dll b/Source/Mocha.Engine/Runtimes/NuGet.Protocol.dll
new file mode 100644
index 00000000..23749ef4
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/NuGet.Protocol.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/NuGet.Versioning.dll b/Source/Mocha.Engine/Runtimes/NuGet.Versioning.dll
new file mode 100644
index 00000000..8b301668
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/NuGet.Versioning.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/System.Composition.AttributedModel.dll b/Source/Mocha.Engine/Runtimes/System.Composition.AttributedModel.dll
new file mode 100644
index 00000000..d37283b1
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/System.Composition.AttributedModel.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/System.Composition.Hosting.dll b/Source/Mocha.Engine/Runtimes/System.Composition.Hosting.dll
new file mode 100644
index 00000000..c67f1c02
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/System.Composition.Hosting.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/System.Composition.Runtime.dll b/Source/Mocha.Engine/Runtimes/System.Composition.Runtime.dll
new file mode 100644
index 00000000..2a4b38c9
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/System.Composition.Runtime.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/System.Composition.TypedParts.dll b/Source/Mocha.Engine/Runtimes/System.Composition.TypedParts.dll
new file mode 100644
index 00000000..7c0c780d
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/System.Composition.TypedParts.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/libcrypto-3-x64.dll b/Source/Mocha.Engine/Runtimes/libcrypto-3-x64.dll
new file mode 100644
index 00000000..f855ff05
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/libcrypto-3-x64.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/libprotobuf.dll b/Source/Mocha.Engine/Runtimes/libprotobuf.dll
new file mode 100644
index 00000000..911305f5
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/libprotobuf.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/steam_api.dll b/Source/Mocha.Engine/Runtimes/steam_api.dll
new file mode 100644
index 00000000..6e0f4401
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/steam_api.dll differ
diff --git a/Source/Mocha.Engine/Runtimes/steam_api64.dll b/Source/Mocha.Engine/Runtimes/steam_api64.dll
new file mode 100644
index 00000000..ad13f2b6
Binary files /dev/null and b/Source/Mocha.Engine/Runtimes/steam_api64.dll differ
diff --git a/Source/Mocha.Engine/UI/UIManager.cs b/Source/Mocha.Engine/UI/UIManager.cs
index e9667e6f..259a3ecd 100644
--- a/Source/Mocha.Engine/UI/UIManager.cs
+++ b/Source/Mocha.Engine/UI/UIManager.cs
@@ -41,7 +41,7 @@ public void LoadTemplate( string? file = null )
if ( shouldLoad )
{
- Screen.UpdateFrom( Glue.Editor.GetRenderSize() );
+ Screen.UpdateFrom( NativeEngine.GetRenderSize() );
RootPanel = Template.FromFile( _renderer, _templatePath );
_isDirty = true;
}
diff --git a/Source/Mocha.Engine/World/Base/BaseEntity.cs b/Source/Mocha.Engine/World/Base/BaseEntity.cs
index 013dbc90..b3dd640a 100644
--- a/Source/Mocha.Engine/World/Base/BaseEntity.cs
+++ b/Source/Mocha.Engine/World/Base/BaseEntity.cs
@@ -10,6 +10,9 @@ public class BaseEntity : IEntity
[HideInInspector]
public uint NativeHandle { get; protected set; }
+ [HideInInspector]
+ private Glue.BaseEntity NativeEntity => NativeEngine.GetEntityManager().GetBaseEntity( NativeHandle );
+
public bool IsValid()
{
return true;
@@ -18,39 +21,39 @@ public bool IsValid()
[Category( "Transform" )]
public Vector3 Scale
{
- get => Glue.Entities.GetScale( NativeHandle );
- set => Glue.Entities.SetScale( NativeHandle, value );
+ get => NativeEntity.GetScale();
+ set => NativeEntity.SetScale( value );
}
[Category( "Transform" )]
public Vector3 Position
{
- get => Glue.Entities.GetPosition( NativeHandle );
- set => Glue.Entities.SetPosition( NativeHandle, value );
+ get => NativeEntity.GetPosition();
+ set => NativeEntity.SetPosition( value );
}
[Category( "Transform" )]
public Rotation Rotation
{
- get => Glue.Entities.GetRotation( NativeHandle );
- set => Glue.Entities.SetRotation( NativeHandle, value );
+ get => NativeEntity.GetRotation();
+ set => NativeEntity.SetRotation( value );
}
[HideInInspector]
public string Name
{
- get => Glue.Entities.GetName( NativeHandle );
- set => Glue.Entities.SetName( NativeHandle, value );
+ get => NativeEntity.GetName();
+ set => NativeEntity.SetName( value );
}
public bool IsViewModel
{
- set => Glue.Entities.SetViewmodel( NativeHandle, value );
+ set => NativeEntity.SetViewmodel( value );
}
public bool IsUI
{
- set => Glue.Entities.SetUI( NativeHandle, value );
+ set => NativeEntity.SetUI( value );
}
public BaseEntity()
@@ -66,6 +69,9 @@ public BaseEntity()
var displayInfo = DisplayInfo.For( this );
Name = $"[{displayInfo.Category}] {displayInfo.Name} {NativeHandle}";
+ Event.Register( this );
+ Log.Info( $"Spawning entity {Name} on {(Core.IsClient ? "client" : "server")}" );
+
Spawn();
}
@@ -75,7 +81,7 @@ protected virtual void Spawn()
protected virtual void CreateNativeEntity()
{
- NativeHandle = Glue.Entities.CreateBaseEntity();
+ NativeHandle = NativeEngine.CreateBaseEntity();
}
public virtual void Update() { }
diff --git a/Source/Mocha.Engine/World/Base/ModelEntity.cs b/Source/Mocha.Engine/World/Base/ModelEntity.cs
index 7eaa2ecf..70e78eae 100644
--- a/Source/Mocha.Engine/World/Base/ModelEntity.cs
+++ b/Source/Mocha.Engine/World/Base/ModelEntity.cs
@@ -1,58 +1,86 @@
-using System.Runtime.InteropServices;
-
-namespace Mocha;
+namespace Mocha;
[Category( "World" ), Title( "Model Entity" ), Icon( FontAwesome.Cube )]
public partial class ModelEntity : BaseEntity
{
+ // This is a stop-gap solution until we have a proper physics body implementation
+
+ public struct Physics
+ {
+ public string PhysicsModelPath { get; set; }
+ }
+
+ [HideInInspector]
+ private Glue.ModelEntity NativeModelEntity => NativeEngine.GetEntityManager().GetModelEntity( NativeHandle );
+
[Category( "Physics" )]
public Vector3 Velocity
{
- get => Glue.Entities.GetVelocity( NativeHandle );
- set => Glue.Entities.SetVelocity( NativeHandle, value );
+ get => NativeModelEntity.GetVelocity();
+ set => NativeModelEntity.SetVelocity( value );
}
[Category( "Physics" )]
public float Mass
{
- get => Glue.Entities.GetMass( NativeHandle );
- set => Glue.Entities.SetMass( NativeHandle, value );
+ get => NativeModelEntity.GetMass();
+ set => NativeModelEntity.SetMass( value );
}
[Category( "Physics" )]
public float Friction
{
- get => Glue.Entities.GetFriction( NativeHandle );
- set => Glue.Entities.SetFriction( NativeHandle, value );
+ get => NativeModelEntity.GetFriction();
+ set => NativeModelEntity.SetFriction( value );
}
[Category( "Physics" )]
public float Restitution
{
- get => Glue.Entities.GetRestitution( NativeHandle );
- set => Glue.Entities.SetRestitution( NativeHandle, value );
+ get => NativeModelEntity.GetRestitution();
+ set => NativeModelEntity.SetRestitution( value );
}
[Category( "Physics" )]
public bool IgnoreRigidbodyRotation
{
- get => Glue.Entities.GetIgnoreRigidbodyRotation( NativeHandle );
- set => Glue.Entities.SetIgnoreRigidbodyRotation( NativeHandle, value );
+ get => NativeModelEntity.GetIgnoreRigidbodyRotation();
+ set => NativeModelEntity.SetIgnoreRigidbodyRotation( value );
}
[Category( "Physics" )]
public bool IgnoreRigidbodyPosition
{
- get => Glue.Entities.GetIgnoreRigidbodyPosition( NativeHandle );
- set => Glue.Entities.SetIgnoreRigidbodyPosition( NativeHandle, value );
+ get => NativeModelEntity.GetIgnoreRigidbodyPosition();
+ set => NativeModelEntity.SetIgnoreRigidbodyPosition( value );
}
+ private string _modelPath;
+
[Category( "Rendering" )]
public IModel Model
{
- set => Glue.Entities.SetModel( NativeHandle, value.NativeModel );
+ set
+ {
+ NativeModelEntity.SetModel( value.NativeModel );
+ _modelPath = value.Path;
+ }
}
+ [Category( "Rendering" )]
+ public string ModelPath
+ {
+ get => _modelPath;
+ set
+ {
+ _modelPath = value;
+ Model = new Model( value );
+ }
+ }
+
+ [HideInInspector]
+ public Physics PhysicsSetup { get; set; }
+
public ModelEntity()
{
}
@@ -64,22 +92,37 @@ public ModelEntity( string path )
protected override void CreateNativeEntity()
{
- NativeHandle = Glue.Entities.CreateModelEntity();
+ NativeHandle = NativeEngine.CreateModelEntity();
}
public void SetCubePhysics( Vector3 bounds, bool isStatic )
{
- Glue.Entities.SetCubePhysics( NativeHandle, bounds, isStatic );
+ // TODO: Predicted physics
+ if ( !Core.IsServer )
+ return;
+
+ NativeModelEntity.SetCubePhysics( bounds, isStatic );
}
public void SetSpherePhysics( float radius, bool isStatic )
{
- Glue.Entities.SetSpherePhysics( NativeHandle, radius, isStatic );
+ // TODO: Predicted physics
+ if ( !Core.IsServer )
+ return;
+
+ NativeModelEntity.SetSpherePhysics( radius, isStatic );
}
// TODO: Replace...
public void SetMeshPhysics( string path )
{
+ PhysicsSetup = new Physics()
+ {
+ PhysicsModelPath = path
+ };
+
+ Log.Info( $"SetMeshPhysics: {path}" );
+
using var _ = new Stopwatch( "Mocha phys model generation" );
var fileBytes = FileSystem.Mounted.ReadAllBytes( path );
var modelFile = Serializer.Deserialize>( fileBytes );
@@ -157,16 +200,6 @@ Vector2 ReadVector2()
// Reverse winding order
//
// vertexList.Reverse();
-
- unsafe
- {
- int vertexStride = Marshal.SizeOf( typeof( Vector3 ) );
- int vertexSize = vertexStride * vertexList.Count;
-
- fixed ( void* vertexData = vertexList.ToArray() )
- {
- Glue.Entities.SetMeshPhysics( NativeHandle, vertexSize, (IntPtr)vertexData );
- }
- }
+ NativeModelEntity.SetMeshPhysics( vertexList.ToInterop() );
}
}
diff --git a/Source/Mocha.Engine/World/Camera.cs b/Source/Mocha.Engine/World/Camera.cs
index 65f41f3b..9be3f3fb 100644
--- a/Source/Mocha.Engine/World/Camera.cs
+++ b/Source/Mocha.Engine/World/Camera.cs
@@ -4,31 +4,31 @@ public static class Camera
{
public static Vector3 Position
{
- get => Glue.Entities.GetCameraPosition();
- set => Glue.Entities.SetCameraPosition( value );
+ get => NativeEngine.GetCameraPosition();
+ set => NativeEngine.SetCameraPosition( value );
}
public static Rotation Rotation
{
- get => Glue.Entities.GetCameraRotation();
- set => Glue.Entities.SetCameraRotation( value );
+ get => NativeEngine.GetCameraRotation();
+ set => NativeEngine.SetCameraRotation( value );
}
public static float FieldOfView
{
- get => Glue.Entities.GetCameraFieldOfView();
- set => Glue.Entities.SetCameraFieldOfView( value );
+ get => NativeEngine.GetCameraFieldOfView();
+ set => NativeEngine.SetCameraFieldOfView( value );
}
public static float ZNear
{
- get => Glue.Entities.GetCameraZNear();
- set => Glue.Entities.SetCameraZNear( value );
+ get => NativeEngine.GetCameraZNear();
+ set => NativeEngine.SetCameraZNear( value );
}
public static float ZFar
{
- get => Glue.Entities.GetCameraZFar();
- set => Glue.Entities.SetCameraZFar( value );
+ get => NativeEngine.GetCameraZFar();
+ set => NativeEngine.SetCameraZFar( value );
}
}
diff --git a/Source/Mocha.Engine/World/Player.cs b/Source/Mocha.Engine/World/Player.cs
index 15b60976..c51f6ac8 100644
--- a/Source/Mocha.Engine/World/Player.cs
+++ b/Source/Mocha.Engine/World/Player.cs
@@ -10,7 +10,7 @@ public class Player : ModelEntity
public Ray EyeRay => new Ray( EyePosition, EyeRotation.Forward );
[Category( "Player" )]
- public Vector3 PlayerHalfExtents { get; set; }
+ public Vector3 PlayerBounds { get; set; }
[Category( "Player" )]
public Vector3 EyePosition { get; set; }
diff --git a/Source/Mocha.Engine/World/PlayerController/QuakeWalkController.cs b/Source/Mocha.Engine/World/PlayerController/QuakeWalkController.cs
index 6e1526e1..4d8fb2bb 100644
--- a/Source/Mocha.Engine/World/PlayerController/QuakeWalkController.cs
+++ b/Source/Mocha.Engine/World/PlayerController/QuakeWalkController.cs
@@ -263,7 +263,7 @@ private void WalkMove()
public TraceResult TraceBBox( Vector3 start, Vector3 end )
{
- return Cast.Ray( start, end ).WithHalfExtents( Player.PlayerHalfExtents ).Ignore( Player ).Run();
+ return Cast.Ray( start, end ).WithHalfExtents( Player.PlayerBounds ).Ignore( Player ).Run();
}
private void TraceToGround()
diff --git a/Source/Mocha.Engine/World/Trace.cs b/Source/Mocha.Engine/World/Trace.cs
index bedcd534..7a5f2c82 100644
--- a/Source/Mocha.Engine/World/Trace.cs
+++ b/Source/Mocha.Engine/World/Trace.cs
@@ -65,7 +65,9 @@ public TraceResult Run()
fixed ( void* data = _ignoredEntities.Select( x => x.NativeHandle ).ToArray() )
{
traceInfo.ignoredEntityHandles = (IntPtr)data;
- var result = Glue.Physics.Trace( traceInfo );
+
+ var physicsManager = NativeEngine.GetPhysicsManager();
+ var result = physicsManager.Trace( traceInfo );
return TraceResult.From( result );
}
}
diff --git a/Source/Mocha.Engine/World/ViewModel.cs b/Source/Mocha.Engine/World/ViewModel.cs
index 1b5744e2..aabff619 100644
--- a/Source/Mocha.Engine/World/ViewModel.cs
+++ b/Source/Mocha.Engine/World/ViewModel.cs
@@ -46,7 +46,7 @@ protected override void Spawn()
public Dictionary Offsets = new()
{
- { "Base", new( new( 0.4f, 0.12f, -0.18f ) )},
+ { "Base", new( new( 0.4f, 0.12f, -0.18f ) ) },
{ "Sprint", new( new( 0.02f, 0.0f, 0.07f ), new( 30.89f, -11.25f, -5.6f ) ) },
{ "Avoid", new( new( 0.02f, 0.0f, 0.07f ), new( 30.89f, -11.25f, -5.6f ) ) }
};
diff --git a/Source/Mocha.FrameworkBench/Mocha.FrameworkBench.cpp b/Source/Mocha.FrameworkBench/Mocha.FrameworkBench.cpp
new file mode 100644
index 00000000..2913cfec
--- /dev/null
+++ b/Source/Mocha.FrameworkBench/Mocha.FrameworkBench.cpp
@@ -0,0 +1,175 @@
+//
+#include
+//
+#include
+#include
+
+const double CheckHandleMapSpeed_Single();
+const double CheckArraySpeed_SystemAlloc_Single();
+const double CheckArraySpeed_LinearAlloc_Single();
+const double CheckVectorSpeed_Single();
+const void CheckSpeed( const std::function func );
+
+const int g_benchmarkCount = 1000;
+
+int main()
+{
+ std::cout << "Running " << g_benchmarkCount << " benchmarks per type..." << std::endl;
+ std::cout << "--------------------" << std::endl;
+ std::cout << "[Mocha::HandleMap]" << std::endl;
+ CheckSpeed( CheckHandleMapSpeed_Single );
+
+ std::cout << "--------------------" << std::endl;
+ std::cout << "[Mocha::Array - SystemAllocator]" << std::endl;
+ CheckSpeed( CheckArraySpeed_SystemAlloc_Single );
+
+ std::cout << "--------------------" << std::endl;
+ std::cout << "[Mocha::Array - LinearAllocator]" << std::endl;
+ CheckSpeed( CheckArraySpeed_LinearAlloc_Single );
+
+ std::cout << "--------------------" << std::endl;
+ std::cout << "[std::vector]" << std::endl;
+ CheckSpeed( CheckVectorSpeed_Single );
+
+ std::cout << "--------------------" << std::endl;
+ return 0;
+}
+
+#define SPEED_TEST_BEGIN()
+
+const void CheckSpeed( const std::function func )
+{
+ double totalDurationSeconds = 0.0;
+
+ for ( int i = 0; i < g_benchmarkCount; ++i )
+ {
+ // std::cout << "\tRunning benchmark " << ( i + 1 ) << " of " << g_benchmarkCount << "... " << std::endl;
+
+ double durationSeconds = func();
+ // std::cout << "\tTook " << durationSeconds << " seconds" << std::endl;
+
+ totalDurationSeconds += durationSeconds;
+ }
+
+ double averageDurationSeconds = totalDurationSeconds / static_cast( g_benchmarkCount );
+
+ // std::cout << "\tAvg. time taken: " << averageDurationSeconds << " seconds" << std::endl;
+
+ const double lookupsPerSecond = 1000000.0 / averageDurationSeconds;
+ const double millionLookupsPerSecond = lookupsPerSecond / 1000000.0;
+ std::cout << "\tAvg. lookup speed: " << millionLookupsPerSecond << "M/s" << std::endl;
+}
+
+inline const std::chrono::steady_clock::time_point StartClock()
+{
+ return std::chrono::high_resolution_clock::now();
+}
+
+inline const double CalculateDurationSeconds( const std::chrono::steady_clock::time_point start, const int count )
+{
+ const auto end = std::chrono::high_resolution_clock::now();
+
+ // Convert the duration into seconds with high precision
+ const auto duration = std::chrono::duration_cast( end - start );
+ const double durationSeconds = duration.count() / static_cast( count );
+ return durationSeconds;
+}
+
+const double CheckArraySpeed_SystemAlloc_Single()
+{
+ const int count = 1'000'000;
+
+ using Mocha::Array;
+ using Mocha::SystemAllocator;
+
+ SystemAllocator alloc;
+ Array array;
+ array.Init( &alloc, count, count );
+
+ // Add values to the container
+ for ( int i = 0; i < count; i++ )
+ {
+ array.Push( i );
+ }
+
+ const auto start = StartClock();
+
+ for ( int i = 0; i < count; i++ )
+ {
+ int _ = array[i];
+ }
+
+ return CalculateDurationSeconds( start, count );
+}
+
+const double CheckArraySpeed_LinearAlloc_Single()
+{
+ const int count = 1'000'000;
+
+ using Mocha::Array;
+ using Mocha::LinearAllocator;
+
+ LinearAllocator alloc( sizeof( int ) * count * 4 );
+ Array array;
+ array.Init( &alloc, count, count );
+
+ // Add values to the container
+ for ( int i = 0; i < count; i++ )
+ {
+ array.Push( i );
+ }
+
+ const auto start = StartClock();
+
+ for ( int i = 0; i < count; i++ )
+ {
+ int _ = array[i];
+ }
+
+ return CalculateDurationSeconds( start, count );
+}
+
+const double CheckVectorSpeed_Single()
+{
+ const int count = 1'000'000;
+
+ std::vector array;
+
+ // Add values to the container
+ for ( int i = 0; i < count; i++ )
+ {
+ array.push_back( i );
+ }
+
+ const auto start = StartClock();
+
+ for ( int i = 0; i < count; i++ )
+ {
+ int _ = array[i];
+ }
+
+ return CalculateDurationSeconds( start, count );
+}
+
+const double CheckHandleMapSpeed_Single()
+{
+ const int count = 1'000;
+
+ using Mocha::HandleMap;
+ HandleMap handleMap;
+
+ // Add values to the container
+ for ( int i = 0; i < count; i++ )
+ {
+ handleMap.Add( i );
+ }
+
+ const auto start = StartClock();
+
+ for ( int i = 0; i < count; i++ )
+ {
+ auto _ = handleMap.Get( i );
+ }
+
+ return CalculateDurationSeconds( start, count );
+}
\ No newline at end of file
diff --git a/Source/Mocha.Host/Entities/baseentity.h b/Source/Mocha.Host/Entities/baseentity.h
new file mode 100644
index 00000000..52826da1
--- /dev/null
+++ b/Source/Mocha.Host/Entities/baseentity.h
@@ -0,0 +1,78 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+
+enum EntityFlags : int
+{
+ ENTITY_NONE = 1 << 0,
+ ENTITY_MANAGED = 1 << 1,
+ ENTITY_RENDERABLE = 1 << 2,
+ ENTITY_VIEWMODEL = 1 << 3,
+ ENTITY_UI = 1 << 4,
+};
+
+DEFINE_FLAG_OPERATORS( EntityFlags );
+
+class Camera;
+
+class BaseEntity
+{
+public:
+ BaseEntity()
+ : m_spawnTime( Globals::m_curTick ){};
+
+ virtual ~BaseEntity() {}
+
+ int m_spawnTime;
+
+ EntityFlags m_flags = ENTITY_NONE;
+
+ std::string m_type = "No type";
+ std::string m_name = "Unnamed";
+
+ Transform m_transformLastFrame = {};
+ Transform m_transformCurrentFrame = {};
+
+ Transform m_transform = {};
+
+ inline void AddFlag( EntityFlags flags ) { m_flags = m_flags | flags; }
+ inline void RemoveFlag( EntityFlags flags ) { m_flags = m_flags & ~flags; }
+ inline bool HasFlag( EntityFlags flag ) { return ( m_flags & flag ) != 0; }
+
+ //
+ // Managed bindings
+ //
+ GENERATE_BINDINGS inline void SetName( const char* name ) { m_name = name; }
+ GENERATE_BINDINGS inline const char* GetName() { return m_name.c_str(); }
+
+ GENERATE_BINDINGS inline void SetType( const char* type ) { m_type = type; }
+ GENERATE_BINDINGS inline const char* GetType() { return m_type.c_str(); }
+
+ GENERATE_BINDINGS inline void SetPosition( const Vector3& pos ) { m_transform.position = pos; }
+ GENERATE_BINDINGS inline Vector3 GetPosition() { return m_transform.position; }
+
+ GENERATE_BINDINGS inline void SetRotation( const Quaternion& rot ) { m_transform.rotation = rot; }
+ GENERATE_BINDINGS inline Quaternion GetRotation() { return m_transform.rotation; }
+
+ GENERATE_BINDINGS inline void SetScale( const Vector3& scale ) { m_transform.scale = scale; }
+ GENERATE_BINDINGS inline Vector3 GetScale() { return m_transform.scale; }
+
+ GENERATE_BINDINGS inline void SetViewmodel( bool isViewmodel )
+ {
+ if ( isViewmodel )
+ AddFlag( ENTITY_VIEWMODEL );
+ else
+ RemoveFlag( ENTITY_VIEWMODEL );
+ }
+
+ GENERATE_BINDINGS inline void SetUI( bool isUI )
+ {
+ if ( isUI )
+ AddFlag( ENTITY_UI );
+ else
+ RemoveFlag( ENTITY_UI );
+ }
+};
diff --git a/Source/Mocha.Host/entitymanager.h b/Source/Mocha.Host/Entities/entitymanager.h
similarity index 77%
rename from Source/Mocha.Host/entitymanager.h
rename to Source/Mocha.Host/Entities/entitymanager.h
index 65dab976..8b90b873 100644
--- a/Source/Mocha.Host/entitymanager.h
+++ b/Source/Mocha.Host/Entities/entitymanager.h
@@ -1,11 +1,13 @@
#pragma once
-#include
+#include
+#include
+#include
+#include
+#include
+#include
#include
-#include
-#include
#include
-#include
#include
class EntityManager : HandleMap, ISubSystem
@@ -27,6 +29,12 @@ class EntityManager : HandleMap, ISubSystem
void Startup() override{};
void Shutdown() override{};
+
+ GENERATE_BINDINGS BaseEntity* GetBaseEntity( uint32_t entityHandle ) { return GetEntity( entityHandle ).get(); }
+ GENERATE_BINDINGS ModelEntity* GetModelEntity( uint32_t entityHandle )
+ {
+ return GetEntity( entityHandle ).get();
+ }
};
template
diff --git a/Source/Mocha.Host/modelentity.cpp b/Source/Mocha.Host/Entities/modelentity.cpp
similarity index 74%
rename from Source/Mocha.Host/modelentity.cpp
rename to Source/Mocha.Host/Entities/modelentity.cpp
index ae1a6abb..a8839e2a 100644
--- a/Source/Mocha.Host/modelentity.cpp
+++ b/Source/Mocha.Host/Entities/modelentity.cpp
@@ -1,7 +1,7 @@
#include "modelentity.h"
-#include
-#include
+#include
+#include
void ModelEntity::SetSpherePhysics( float radius, bool isStatic )
{
@@ -18,7 +18,7 @@ void ModelEntity::SetSpherePhysics( float radius, bool isStatic )
body.shape.shapeData.radius = radius;
body.shape.shapeType = PhysicsShapeType::PHYSICS_SHAPE_SPHERE;
- m_physicsHandle = g_physicsManager->AddBody( this, body );
+ m_physicsHandle = Globals::m_physicsManager->AddBody( this, body );
}
void ModelEntity::SetCubePhysics( Vector3 bounds, bool isStatic )
@@ -36,11 +36,13 @@ void ModelEntity::SetCubePhysics( Vector3 bounds, bool isStatic )
body.shape.shapeData.extents = bounds;
body.shape.shapeType = PhysicsShapeType::PHYSICS_SHAPE_BOX;
- m_physicsHandle = g_physicsManager->AddBody( this, body );
+ m_physicsHandle = Globals::m_physicsManager->AddBody( this, body );
}
-void ModelEntity::SetMeshPhysics( std::vector vertices )
+void ModelEntity::SetMeshPhysics( UtilArray interopVertices )
{
+ std::vector vertices = interopVertices.GetData();
+
PhysicsBody body = {};
body.friction = 1.0f;
@@ -54,5 +56,5 @@ void ModelEntity::SetMeshPhysics( std::vector vertices )
body.shape.shapeData.vertices = vertices;
body.shape.shapeType = PhysicsShapeType::PHYSICS_SHAPE_MESH;
- m_physicsHandle = g_physicsManager->AddBody( this, body );
+ m_physicsHandle = Globals::m_physicsManager->AddBody( this, body );
}
\ No newline at end of file
diff --git a/Source/Mocha.Host/Entities/modelentity.h b/Source/Mocha.Host/Entities/modelentity.h
new file mode 100644
index 00000000..319e1898
--- /dev/null
+++ b/Source/Mocha.Host/Entities/modelentity.h
@@ -0,0 +1,58 @@
+#pragma once
+#include
+#include
+#include
+#include
+
+struct PhysicsBody;
+
+class ModelEntity : public BaseEntity
+{
+private:
+ Model m_model;
+
+ //
+ // Physics values
+ //
+ uint32_t m_physicsHandle = UINT32_MAX;
+
+ Vector3 m_velocity = {};
+ float m_friction = 0.5f;
+ float m_mass = 10.0f;
+ float m_restitution = 0.5f;
+
+ bool m_ignoreRigidbodyRotation;
+ bool m_ignoreRigidbodyPosition;
+
+public:
+ // If this model has no physics, this function will return UINT32_MAX.
+ uint32_t GetPhysicsHandle() { return m_physicsHandle; };
+
+ GENERATE_BINDINGS void SetModel( Model* model ) { m_model = *model; }
+ GENERATE_BINDINGS Model* GetModel() { return &m_model; }
+
+ //
+ // Managed bindings
+ //
+ GENERATE_BINDINGS void SetSpherePhysics( float radius, bool isStatic );
+ GENERATE_BINDINGS void SetCubePhysics( Vector3 bounds, bool isStatic );
+ GENERATE_BINDINGS void SetMeshPhysics( UtilArray vertices );
+
+ GENERATE_BINDINGS Vector3 GetVelocity() { return m_velocity; }
+ GENERATE_BINDINGS void SetVelocity( Vector3 velocity ) { m_velocity = velocity; }
+
+ GENERATE_BINDINGS float GetFriction() { return m_friction; }
+ GENERATE_BINDINGS void SetFriction( float friction ) { m_friction = friction; }
+
+ GENERATE_BINDINGS float GetMass() { return m_mass; }
+ GENERATE_BINDINGS void SetMass( float mass ) { m_mass = mass; }
+
+ GENERATE_BINDINGS float GetRestitution() { return m_restitution; }
+ GENERATE_BINDINGS void SetRestitution( float restitution ) { m_restitution = restitution; }
+
+ GENERATE_BINDINGS bool GetIgnoreRigidbodyRotation() { return m_ignoreRigidbodyRotation; }
+ GENERATE_BINDINGS void SetIgnoreRigidbodyRotation( bool ignore ) { m_ignoreRigidbodyRotation = ignore; }
+
+ GENERATE_BINDINGS bool GetIgnoreRigidbodyPosition() { return m_ignoreRigidbodyPosition; }
+ GENERATE_BINDINGS void SetIgnoreRigidbodyPosition( bool ignore ) { m_ignoreRigidbodyPosition = ignore; }
+};
diff --git a/Source/Mocha.Host/Framework/allocators.h b/Source/Mocha.Host/Framework/allocators.h
new file mode 100644
index 00000000..88f89d00
--- /dev/null
+++ b/Source/Mocha.Host/Framework/allocators.h
@@ -0,0 +1,108 @@
+#pragma once
+#include
+#include
+
+namespace Mocha
+{
+ ///
+ /// Base allocator interface
+ ///
+ class IAllocator
+ {
+ public:
+ virtual void* Alloc( const size_t size, const size_t alignment, const size_t offset ) = 0;
+ virtual void Free( void* ptr ) = 0;
+ virtual void Reset() = 0;
+ };
+
+ ///
+ /// Basic linear allocator. Allocates memory in a linear fashion, and can be reset to free all memory.
+ ///
+ class LinearAllocator : public IAllocator
+ {
+ private:
+ char* m_start{ nullptr };
+ char* m_end{ nullptr };
+ char* m_current{ nullptr };
+
+ public:
+ LinearAllocator( void* start, void* end )
+ {
+ m_start = ( char* )start;
+ m_end = ( char* )end;
+ Reset();
+ }
+
+ explicit LinearAllocator( size_t size )
+ {
+ m_start = new char[size];
+ m_end = m_start + size;
+ Reset();
+ }
+
+ ///
+ /// Increments a value indicating the current buffer offset
+ ///
+ inline void* Alloc( const size_t size, const size_t alignment, const size_t offset )
+ {
+ void* ptr = m_current + offset;
+ size_t space = m_end - m_current;
+
+ m_current = ( char* )std::align( alignment, size, ptr, space ) - offset;
+
+ void* result = m_current;
+ m_current += size;
+
+ assert( m_current < m_end );
+
+ return ptr;
+ }
+
+ ///
+ /// Does nothing
+ ///
+ void Free( void* ptr )
+ {
+ //
+ }
+
+ ///
+ /// Free all allocated memory and return the allocator to its original initialized state
+ ///
+ void Reset()
+ {
+ m_current = m_start;
+ }
+ };
+
+ ///
+ /// Basic passthrough allocator, wraps malloc & free
+ ///
+ class SystemAllocator : public IAllocator
+ {
+ ///
+ /// Allocate a block of memory.
+ /// Alignment and offset do not do anything here.
+ ///
+ inline void* Alloc( const size_t size, const size_t alignment, const size_t offset )
+ {
+ return malloc( size );
+ }
+
+ ///
+ /// Free a block of memory.
+ ///
+ inline void Free( void* ptr )
+ {
+ return free( ptr );
+ }
+
+ ///
+ /// Does nothing
+ ///
+ inline void Reset()
+ {
+ //
+ }
+ };
+} // namespace Mocha
\ No newline at end of file
diff --git a/Source/Mocha.Host/Framework/array.h b/Source/Mocha.Host/Framework/array.h
new file mode 100644
index 00000000..42f1b7a6
--- /dev/null
+++ b/Source/Mocha.Host/Framework/array.h
@@ -0,0 +1,207 @@
+#pragma once
+
+#include
+
+namespace Mocha
+{
+ template
+ class Array
+ {
+ private:
+ T* m_data{ nullptr };
+ size_t m_size{ 0 };
+ size_t m_capacity{ 0 };
+
+ IAllocator* m_allocator{ nullptr };
+
+ public:
+ void Init( IAllocator* allocator, size_t capacity, size_t size );
+ void Destroy();
+
+ // ----------------------------------------
+
+ void Push( const T& object );
+ void Pop();
+ T& PushUse();
+
+ // ----------------------------------------
+
+ T& operator[]( size_t index );
+ const T& operator[]( size_t index ) const;
+
+ T* Data();
+ const T* Data() const;
+
+ // ----------------------------------------
+
+ size_t Size() const;
+ size_t Capacity() const;
+
+ void Clear();
+ void Grow( size_t capacity );
+
+ // ----------------------------------------
+
+ T& Back();
+ const T& Back() const;
+
+ T& Front();
+ const T& Front() const;
+ };
+
+ // ----------------------------------------------------------------------------------------------------------------------------
+
+ template
+ inline void Array::Init( IAllocator* allocator, size_t capacity, size_t size )
+ {
+ m_data = nullptr;
+ m_size = size;
+
+ m_capacity = 0;
+ m_allocator = allocator;
+
+ if ( capacity > 0 )
+ {
+ Grow( capacity );
+ }
+ }
+
+ template
+ inline void Array::Destroy()
+ {
+ if ( m_capacity > 0 )
+ {
+ m_allocator->Free( m_data );
+ }
+
+ m_data = nullptr;
+
+ m_size = 0;
+ m_capacity = 0;
+ }
+
+ template
+ inline void Array::Push( const T& object )
+ {
+ if ( m_size >= m_capacity )
+ {
+ Grow( m_capacity + 1 );
+ }
+
+ m_data[m_size++] = object;
+ }
+
+ template
+ inline void Array::Pop()
+ {
+ assert( m_size > 0 );
+ --m_size;
+ }
+
+ template
+ inline T& Array::PushUse()
+ {
+ if ( m_size >= m_capacity )
+ {
+ Grow( m_capacity + 1 );
+ }
+
+ ++m_size;
+
+ return Back();
+ }
+
+ template
+ inline T& Array::operator[]( size_t index )
+ {
+ assert( index >= 0 && index < m_size );
+ return m_data[index];
+ }
+
+ template
+ inline const T& Array::operator[]( size_t index ) const
+ {
+ assert( index >= 0 && index < m_size );
+ return m_data[index];
+ }
+
+ template
+ inline T* Array::Data()
+ {
+ return m_data;
+ }
+
+ template
+ inline const T* Array::Data() const
+ {
+ return m_data;
+ }
+
+ template
+ inline size_t Array::Size() const
+ {
+ return m_size;
+ }
+
+ template
+ inline size_t Array::Capacity() const
+ {
+ return m_capacity;
+ }
+
+ template
+ inline void Array::Clear()
+ {
+ m_size = 0;
+ }
+
+ template
+ inline void Array::Grow( size_t capacity )
+ {
+ assert( capacity > 0 );
+
+ if ( capacity < m_capacity * 2 )
+ capacity = capacity * 2;
+ else if ( capacity < 4 )
+ capacity = 4;
+
+ T* newData = ( T* )m_allocator->Alloc( capacity * sizeof( T ), alignof( T ), 0 );
+
+ if ( m_capacity )
+ {
+ memcpy_s( newData, capacity * sizeof( T ), m_data, m_size * sizeof( T ) );
+ m_allocator->Free( m_data );
+ }
+
+ m_data = newData;
+ m_capacity = capacity;
+ }
+
+ template
+ inline T& Array::Back()
+ {
+ assert( m_size > 0 );
+ return m_data[m_size - 1];
+ }
+
+ template
+ inline const T& Array::Back() const
+ {
+ assert( m_size > 0 );
+ return m_data[m_size - 1];
+ }
+
+ template
+ inline T& Array::Front()
+ {
+ assert( m_size >= 0 );
+ return m_data[0];
+ }
+
+ template
+ inline const T& Array::Front() const
+ {
+ assert( m_size >= 0 );
+ return m_data[0];
+ }
+} // namespace Mocha
diff --git a/Source/Mocha.Host/Framework/handlemap.h b/Source/Mocha.Host/Framework/handlemap.h
new file mode 100644
index 00000000..3f24cb3a
--- /dev/null
+++ b/Source/Mocha.Host/Framework/handlemap.h
@@ -0,0 +1,144 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Mocha
+{
+ template
+ class HandleMap
+ {
+ private:
+ std::shared_mutex m_mutex;
+
+ std::vector> m_objects;
+ std::unique_ptr m_allocator;
+
+ public:
+ HandleMap()
+ {
+ m_allocator = std::make_unique();
+ // m_objects.Init( m_allocator.get(), 0, 0 );
+ }
+
+ void Remove( Handle handle );
+ Handle Add( T object );
+
+ const std::unique_ptr& Get( Handle handle );
+
+ template
+ const std::unique_ptr& GetSpecific( Handle handle );
+
+ template
+ Handle AddSpecific( T1 object );
+
+ void ForEach( std::function& object )> func );
+ void For( std::function& object )> func );
+
+ const std::unique_ptr& operator[]( Handle handle );
+
+ const std::unique_ptr& Front() const;
+ const std::unique_ptr& Back() const;
+ };
+
+ template
+ inline Handle HandleMap::Add( T object )
+ {
+ std::unique_lock lock( m_mutex );
+
+ Handle handle = m_objects.size();
+
+ auto objectPtr = std::make_unique( object );
+ m_objects.push_back( std::move( objectPtr ) );
+ return handle;
+ }
+
+ template
+ inline const std::unique_ptr& HandleMap::Get( Handle handle )
+ {
+ std::shared_lock lock( m_mutex );
+ std::unique_ptr& object = m_objects[handle];
+
+ return object;
+ }
+
+ template
+ template
+ inline const std::unique_ptr& HandleMap::GetSpecific( Handle handle )
+ {
+ static_assert( std::is_base_of::value, "T1 must be derived from T" );
+
+ std::unique_ptr object = Get( handle );
+
+ return std::dynamic_pointer_cast( object );
+ }
+
+ template
+ template
+ inline Handle HandleMap::AddSpecific( T1 object )
+ {
+ static_assert( std::is_base_of::value, "T1 must be derived from T" );
+ std::unique_lock lock( m_mutex );
+
+ Handle handle = m_objects.size();
+
+ auto objectPtr = std::make_unique( object );
+ m_objects.push_back( std::move( objectPtr ) );
+ return handle;
+ }
+
+ template
+ inline void HandleMap::ForEach( std::function& object )> func )
+ {
+ std::shared_lock lock( m_mutex );
+
+ for ( const auto& [handle, object] : m_objects )
+ {
+ func( object );
+ }
+ }
+
+ template
+ inline void HandleMap::For( std::function& object )> func )
+ {
+ std::shared_lock lock( m_mutex );
+
+ for ( const auto& [handle, object] : m_objects )
+ {
+ func( handle, object );
+ }
+ }
+
+ template
+ inline void HandleMap::Remove( Handle handle )
+ {
+ std::unique_lock lock( m_mutex );
+
+ m_objects.erase( handle );
+ }
+
+ template
+ inline const std::unique_ptr& HandleMap::operator[]( Handle handle )
+ {
+ return Get( handle );
+ }
+
+ template
+ inline const std::unique_ptr& HandleMap::Front() const
+ {
+ return m_objects.front();
+ }
+
+ template
+ inline const std::unique_ptr& HandleMap::Back() const
+ {
+ return m_objects.back();
+ }
+} // namespace Mocha
\ No newline at end of file
diff --git a/Source/Mocha.Host/Managed/Bindings/client.h b/Source/Mocha.Host/Managed/Bindings/client.h
new file mode 100644
index 00000000..e543c71f
--- /dev/null
+++ b/Source/Mocha.Host/Managed/Bindings/client.h
@@ -0,0 +1,11 @@
+#pragma once
+#include
+#include
+
+namespace Client
+{
+ GENERATE_BINDINGS inline void StartListenServer()
+ {
+ // TODO
+ }
+}; // namespace Client
\ No newline at end of file
diff --git a/Source/Mocha.Host/consolesystem.h b/Source/Mocha.Host/Managed/Bindings/consolesystem.h
similarity index 83%
rename from Source/Mocha.Host/consolesystem.h
rename to Source/Mocha.Host/Managed/Bindings/consolesystem.h
index e6e649cb..a9aa8c48 100644
--- a/Source/Mocha.Host/consolesystem.h
+++ b/Source/Mocha.Host/Managed/Bindings/consolesystem.h
@@ -1,8 +1,8 @@
#pragma once
-#include
-#include
-#include
+#include
+#include
+#include
#include
#include
@@ -23,7 +23,8 @@ namespace ConsoleSystem
CVarSystem::Instance().RegisterCommand( name, ( CVarFlags )( CVarFlags::Managed | flags ), description, nullptr );
}
- GENERATE_BINDINGS inline void RegisterString( const char* name, const char* value, CVarFlags flags, const char* description )
+ GENERATE_BINDINGS inline void RegisterString(
+ const char* name, const char* value, CVarFlags flags, const char* description )
{
CVarSystem::Instance().RegisterString( name, value, ( CVarFlags )( CVarFlags::Managed | flags ), description, nullptr );
}
@@ -47,14 +48,14 @@ namespace ConsoleSystem
{
return CVarSystem::Instance().GetFlags( name );
}
-
+
// TODO: Not until all memory leak concerns are addressed
-/*
- GENERATE_BINDINGS inline const char* GetString( const char* name )
- {
- return CVarSystem::Instance().GetString( name ).c_str();
- }
-*/
+ /*
+ GENERATE_BINDINGS inline const char* GetString( const char* name )
+ {
+ return CVarSystem::Instance().GetString( name ).c_str();
+ }
+ */
GENERATE_BINDINGS inline float GetFloat( const char* name )
{
@@ -82,12 +83,12 @@ namespace ConsoleSystem
}
// TODO: Not until all memory leak concerns are addressed
-/*
- GENERATE_BINDINGS inline const char* ToString( const char* name )
- {
-
- }
-*/
+ /*
+ GENERATE_BINDINGS inline const char* ToString( const char* name )
+ {
+
+ }
+ */
GENERATE_BINDINGS inline void FromString( const char* name, const char* valueStr )
{
diff --git a/Source/Mocha.Host/Managed/hostmanager.cpp b/Source/Mocha.Host/Managed/hostmanager.cpp
new file mode 100644
index 00000000..c48d9489
--- /dev/null
+++ b/Source/Mocha.Host/Managed/hostmanager.cpp
@@ -0,0 +1,184 @@
+#include "hostmanager.h"
+
+#include
+
+void* HostGlobals::LoadHostLibrary( const char_t* path )
+{
+ const HMODULE h = ::LoadLibraryW( path );
+ assert( h != nullptr );
+ return ( void* )h;
+}
+
+void* HostGlobals::GetExport( void* h, const char* name )
+{
+ void* f = ::GetProcAddress( ( HMODULE )h, name );
+ assert( f != nullptr );
+ return f;
+}
+
+bool HostGlobals::LoadHostFxr()
+{
+ // Pre-allocate a large buffer for the path to hostfxr
+ char_t buffer[MAX_PATH];
+ size_t buffer_size = sizeof( buffer ) / sizeof( char_t );
+
+ const int getHostfxrPathResult = get_hostfxr_path( buffer, &buffer_size, nullptr );
+ if (getHostfxrPathResult != 0)
+ return false;
+
+ // Load hostfxr and get desired exports
+ void* lib = LoadHostLibrary( buffer );
+ init = static_cast( GetExport( lib,
+
+ "hostfxr_initialize_for_runtime_config" ) );
+ getDelegate = static_cast( GetExport( lib, "hostfxr_get_runtime_delegate" ) );
+ setProperty = static_cast( GetExport( lib,
+ "hostfxr_set_runtime_property_value" ) );
+ close = static_cast( GetExport( lib, "hostfxr_close" ) );
+
+ return ( init && getDelegate && close );
+}
+
+load_assembly_and_get_function_pointer_fn HostGlobals::GetDotnetLoadAssembly( const char_t* configPath )
+{
+ LoadHostFxr();
+
+ // Load .NET Core
+ void* load_assembly_and_get_function_pointer = nullptr;
+ hostfxr_handle cxt = nullptr;
+ int rc = init( configPath, nullptr, &cxt );
+ if (rc != 0 || cxt == nullptr)
+ {
+ spdlog::error( "Failed to initialize: 0x{:x}", rc );
+ close( cxt );
+ return nullptr;
+ }
+
+ // Get current working directory
+ char_t cwd[MAX_PATH];
+ const DWORD cwdLength = GetCurrentDirectoryW( MAX_PATH, cwd );
+ if (cwdLength == 0)
+ {
+ spdlog::error( "Failed to get current directory" );
+ close( cxt );
+ return nullptr;
+ }
+
+ // Add "build" to cwd
+ std::wstring buildPath = cwd;
+ buildPath += L"\\build";
+
+ // Set CoreCLR properties
+ setProperty( cxt, L"APP_CONTEXT_BASE_DIRECTORY", buildPath.c_str() );
+ setProperty( cxt, L"APP_PATHS", buildPath.c_str() );
+ setProperty( cxt, L"APP_NI_PATHS", buildPath.c_str() );
+ setProperty( cxt, L"NATIVE_DLL_SEARCH_DIRECTORIES", buildPath.c_str() );
+ setProperty( cxt, L"PLATFORM_RESOURCE_ROOTS", buildPath.c_str() );
+
+ // Get the load assembly function pointer
+ rc = getDelegate( cxt, hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer );
+ if (rc != 0 || load_assembly_and_get_function_pointer == nullptr)
+ spdlog::error( "Get delegate failed: 0x{:x}", rc );
+
+ close( cxt );
+ return static_cast( load_assembly_and_get_function_pointer );
+}
+
+HostManager::HostManager()
+{
+ // TODO: Hardcoding these might be a bad idea?
+ std::wstring basePath = L".\\build\\Mocha.Hotload";
+ std::wstring signature = L"Mocha.Hotload.Main, Mocha.Hotload";
+
+ m_dllPath = basePath + L".dll";
+ m_configPath = basePath + L".runtimeconfig.json";
+ m_signature = signature;
+
+ if (!IsAssemblyLoaded.load())
+ {
+ IsAssemblyLoaded.store( true );
+ LoadFnPtr.store( HostGlobals::GetDotnetLoadAssembly( m_configPath.c_str() ) );
+ }
+}
+
+void HostManager::Update() const
+{
+ Invoke( "Update" );
+}
+
+void HostManager::Render() const
+{
+ Invoke( "Render" );
+}
+
+void HostManager::DrawEditor() const
+{
+ Invoke( "DrawEditor" );
+}
+
+void HostManager::Startup()
+{
+ Invoke( "Run", ( void* )&args );
+}
+
+void HostManager::Shutdown() {}
+
+void HostManager::FireEvent( std::string eventName ) const
+{
+ Invoke( "FireEvent", ( void* )eventName.c_str() );
+}
+
+void HostManager::DispatchCommand( CVarManagedCmdDispatchInfo info )
+{
+ Invoke( "DispatchCommand", &info );
+}
+
+void HostManager::DispatchStringCVarCallback( CVarManagedVarDispatchInfo info )
+{
+ Invoke( "DispatchStringCVarCallback", &info );
+}
+
+void HostManager::DispatchFloatCVarCallback( CVarManagedVarDispatchInfo info )
+{
+ Invoke( "DispatchFloatCVarCallback", &info );
+}
+
+void HostManager::DispatchBoolCVarCallback( CVarManagedVarDispatchInfo info )
+{
+ Invoke( "DispatchBoolCVarCallback", &info );
+}
+
+void HostManager::DispatchIntCVarCallback( CVarManagedVarDispatchInfo info )
+{
+ Invoke( "DispatchIntCVarCallback", &info );
+}
+
+void HostManager::InvokeCallback( Handle callbackHandle, int argsCount, void* args ) const
+{
+ ManagedCallbackDispatchInfo dispatchInfo{};
+ dispatchInfo.args = args;
+ dispatchInfo.argsSize = argsCount;
+ dispatchInfo.handle = callbackHandle;
+
+ Invoke( "InvokeCallback", &dispatchInfo );
+}
+
+inline void HostManager::Invoke( std::string _method, void* params, const char_t* delegateTypeName ) const
+{
+ // Convert to std::wstring
+ const std::wstring method( _method.begin(), _method.end() );
+
+ // Function pointer to managed delegate
+ void* fnPtr = nullptr;
+
+ int rc =
+ LoadFnPtr.load()( m_dllPath.c_str(), m_signature.c_str(), method.c_str(), delegateTypeName, nullptr, ( void** )&fnPtr );
+
+ if (fnPtr == nullptr)
+ {
+ spdlog::error( "Failed to load managed method {}", _method );
+ }
+
+ // Invoke method
+ static_cast( fnPtr )( params );
+}
diff --git a/Source/Mocha.Host/hostmanager.h b/Source/Mocha.Host/Managed/hostmanager.h
similarity index 52%
rename from Source/Mocha.Host/hostmanager.h
rename to Source/Mocha.Host/Managed/hostmanager.h
index 8f0fdfb7..0dd2c4f0 100644
--- a/Source/Mocha.Host/hostmanager.h
+++ b/Source/Mocha.Host/Managed/hostmanager.h
@@ -1,7 +1,10 @@
#pragma once
+#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -9,12 +12,11 @@
#include
#include
#include
-#include
#include
using string_t = std::basic_string;
-typedef int( CORECLR_DELEGATE_CALLTYPE* run_fn )( UnmanagedArgs* args );
+typedef int( CORECLR_DELEGATE_CALLTYPE* RunFn )( UnmanagedArgs* args );
struct CVarManagedCmdDispatchInfo;
@@ -24,42 +26,48 @@ struct CVarManagedVarDispatchInfo;
namespace HostGlobals
{
// Globals to hold hostfxr exports
- inline hostfxr_initialize_for_runtime_config_fn init_fptr;
- inline hostfxr_get_runtime_delegate_fn get_delegate_fptr;
- inline hostfxr_set_runtime_property_value_fn set_property_fptr;
- inline hostfxr_close_fn close_fptr;
+ inline hostfxr_initialize_for_runtime_config_fn init;
+ inline hostfxr_get_runtime_delegate_fn getDelegate;
+ inline hostfxr_set_runtime_property_value_fn setProperty;
+ inline hostfxr_close_fn close;
- void* load_library( const char_t* path );
- void* get_export( void* h, const char* name );
+ void* LoadHostLibrary( const char_t* path );
+ void* GetExport( void* h, const char* name );
bool LoadHostFxr();
load_assembly_and_get_function_pointer_fn GetDotnetLoadAssembly( const char_t* configPath );
}; // namespace HostGlobals
+inline static std::atomic IsAssemblyLoaded = false;
+inline static std::atomic LoadFnPtr;
+
class HostManager : ISubSystem
{
private:
- load_assembly_and_get_function_pointer_fn m_lagfp;
+ load_assembly_and_get_function_pointer_fn m_loadAssemblyFunction;
std::wstring m_dllPath;
std::wstring m_configPath;
std::wstring m_signature;
- void Invoke( std::string _method, void* params = nullptr, const char_t* delegateTypeName = UNMANAGEDCALLERSONLY_METHOD );
-
+ void Invoke( std::string _method, void* params = nullptr, const char_t* delegateTypeName = UNMANAGEDCALLERSONLY_METHOD ) const;
public:
HostManager();
void Startup();
void Shutdown();
- void Update();
- void Render();
- void DrawEditor();
- void FireEvent( std::string eventName );
+ void Update() const;
+ void Render() const;
+ void DrawEditor() const;
+
+ void FireEvent( std::string eventName ) const;
+ void InvokeCallback( Handle callbackHandle, int argsCount, void* args ) const;
+ // TODO: Remove all below
void DispatchCommand( CVarManagedCmdDispatchInfo info );
void DispatchStringCVarCallback( CVarManagedVarDispatchInfo info );
void DispatchFloatCVarCallback( CVarManagedVarDispatchInfo info );
void DispatchBoolCVarCallback( CVarManagedVarDispatchInfo info );
-};
+ void DispatchIntCVarCallback( CVarManagedVarDispatchInfo info );
+};
\ No newline at end of file
diff --git a/Source/Mocha.Host/Managed/managedcallback.cpp b/Source/Mocha.Host/Managed/managedcallback.cpp
new file mode 100644
index 00000000..4fc90f16
--- /dev/null
+++ b/Source/Mocha.Host/Managed/managedcallback.cpp
@@ -0,0 +1,24 @@
+#include "managedcallback.h"
+
+#include
+
+ManagedCallback::ManagedCallback( Handle handle )
+{
+ m_handle = handle;
+}
+
+void ManagedCallback::Invoke()
+{
+ InternalInvoke( 0, nullptr );
+}
+
+void ManagedCallback::Invoke( void* args )
+{
+ InternalInvoke( 1, args );
+}
+
+void ManagedCallback::InternalInvoke( int argsCount, void* args )
+{
+ if ( m_handle != HANDLE_INVALID )
+ Globals::m_hostManager->InvokeCallback( m_handle, argsCount, args );
+}
diff --git a/Source/Mocha.Host/Managed/managedcallback.h b/Source/Mocha.Host/Managed/managedcallback.h
new file mode 100644
index 00000000..c459fe9e
--- /dev/null
+++ b/Source/Mocha.Host/Managed/managedcallback.h
@@ -0,0 +1,18 @@
+#pragma once
+#include
+#include
+
+class ManagedCallback
+{
+private:
+ Handle m_handle = HANDLE_INVALID;
+ void InternalInvoke( int argsCount, void* args );
+
+public:
+ ManagedCallback() {}
+
+ ManagedCallback( Handle handle );
+
+ void Invoke();
+ void Invoke( void* args );
+};
\ No newline at end of file
diff --git a/Source/Mocha.Host/Managed/managedcallbackdispatchinfo.h b/Source/Mocha.Host/Managed/managedcallbackdispatchinfo.h
new file mode 100644
index 00000000..719d2843
--- /dev/null
+++ b/Source/Mocha.Host/Managed/managedcallbackdispatchinfo.h
@@ -0,0 +1,10 @@
+#pragma once
+#include
+
+struct ManagedCallbackDispatchInfo
+{
+ Handle handle = HANDLE_INVALID;
+
+ int argsSize;
+ void* args;
+};
diff --git a/Source/Mocha.Host/cvarmanager.cpp b/Source/Mocha.Host/Misc/cvarmanager.cpp
similarity index 80%
rename from Source/Mocha.Host/cvarmanager.cpp
rename to Source/Mocha.Host/Misc/cvarmanager.cpp
index 50b5b100..754049c1 100644
--- a/Source/Mocha.Host/cvarmanager.cpp
+++ b/Source/Mocha.Host/Misc/cvarmanager.cpp
@@ -1,12 +1,11 @@
#include "cvarmanager.h"
+#include
#include
-#include
size_t CVarSystem::GetHash( std::string string )
{
- std::transform( string.begin(), string.end(), string.begin(),
- []( unsigned char c ) { return std::tolower( c ); } );
+ std::transform( string.begin(), string.end(), string.begin(), []( unsigned char c ) { return std::tolower( c ); } );
return std::hash{}( string );
}
@@ -47,6 +46,19 @@ void CVarSystem::Startup()
if ( entry.m_flags & CVarFlags::Archive )
entry.FromString( value );
}
+
+ // Register commands
+ RegisterCommand( "cvars.run", CVarFlags::Command, "Run commands from a file", [&]( std::vector args ) {
+ if ( args.size() != 1 )
+ {
+ spdlog::error( "Invalid number of arguments" );
+ return;
+ }
+
+ std::string fileName = args[0];
+
+ RunFile( fileName );
+ } );
}
void CVarSystem::Shutdown()
@@ -348,6 +360,26 @@ void CVarSystem::Run( const char* input )
}
}
+void CVarSystem::RunFile( std::string fileName )
+{
+ std::ifstream file( fileName );
+
+ if ( !file.is_open() )
+ {
+ spdlog::error( "Couldn't open '{}'", fileName );
+ return;
+ }
+
+ std::string line;
+
+ while ( std::getline( file, line ) )
+ {
+ Run( line.c_str() );
+ }
+
+ file.close();
+}
+
bool CVarSystem::Exists( std::string name )
{
return m_cvarEntries.find( GetHash( name ) ) != m_cvarEntries.end();
@@ -381,7 +413,8 @@ void CVarSystem::RegisterCommand( std::string name, CVarFlags flags, std::string
}
template
-inline void CVarSystem::RegisterVariable( std::string name, T value, CVarFlags flags, std::string description, CVarCallback callback )
+inline void CVarSystem::RegisterVariable(
+ std::string name, T value, CVarFlags flags, std::string description, CVarCallback callback )
{
// This *must not* have the command flag
flags = ( CVarFlags )( flags & ~( CVarFlags::Command ) );
@@ -397,21 +430,29 @@ inline void CVarSystem::RegisterVariable( std::string name, T value, CVarFlags f
m_cvarEntries[hash] = entry;
}
-void CVarSystem::RegisterString( std::string name, std::string value, CVarFlags flags, std::string description, CVarCallback callback )
+void CVarSystem::RegisterString(
+ std::string name, std::string value, CVarFlags flags, std::string description, CVarCallback callback )
{
RegisterVariable( name, value, flags, description, callback );
}
-void CVarSystem::RegisterFloat( std::string name, float value, CVarFlags flags, std::string description, CVarCallback callback )
+void CVarSystem::RegisterFloat(
+ std::string name, float value, CVarFlags flags, std::string description, CVarCallback callback )
{
RegisterVariable( name, value, flags, description, callback );
}
-void CVarSystem::RegisterBool( std::string name, bool value, CVarFlags flags, std::string description, CVarCallback callback )
+void CVarSystem::RegisterBool(
+ std::string name, bool value, CVarFlags flags, std::string description, CVarCallback callback )
{
RegisterVariable( name, value, flags, description, callback );
}
+void CVarSystem::RegisterInt(
+ std::string name, int value, CVarFlags flags, std::string description, CVarCallback callback )
+{
+ RegisterVariable( name, value, flags, description, callback );
+}
void CVarSystem::Remove( std::string name )
{
@@ -419,7 +460,6 @@ void CVarSystem::Remove( std::string name )
m_cvarEntries.erase( hash );
}
-
void CVarEntry::InvokeCommand( std::vector arguments )
{
assert( IsCommand() );
@@ -431,16 +471,12 @@ void CVarEntry::InvokeCommand( std::vector arguments )
for ( auto& argument : arguments )
managedArguments.push_back( argument.c_str() );
- CVarManagedCmdDispatchInfo info
- {
- m_name.c_str(),
- managedArguments.data(),
- managedArguments.size()
- };
+ CVarManagedCmdDispatchInfo info{ m_name.c_str(), managedArguments.data(), managedArguments.size() };
- g_hostManager->DispatchCommand( info );
+ Globals::m_hostManager->DispatchCommand( info );
}
- else {
+ else
+ {
auto callback = std::any_cast( m_callback );
if ( callback )
@@ -465,11 +501,10 @@ void CVarSystem::InvokeCommand( std::string name, std::vector argum
spdlog::error( "Tried to invoke command '{}', but it's a variable!", name );
return;
}
-
+
entry.InvokeCommand( arguments );
}
-
CVarFlags CVarSystem::GetFlags( std::string name )
{
if ( !Exists( name ) )
@@ -480,7 +515,6 @@ CVarFlags CVarSystem::GetFlags( std::string name )
return ( CVarFlags )GetEntry( name ).m_flags;
}
-
// Putting this stuff in the header caused bad juju
template
@@ -513,19 +547,25 @@ inline void CVarEntry::SetValue( T value )
{
CVarManagedVarDispatchInfo stringInfo{ m_name.c_str(), oldValue.c_str(), value.c_str() };
- g_hostManager->DispatchStringCVarCallback( stringInfo );
+ Globals::m_hostManager->DispatchStringCVarCallback( stringInfo );
}
else if constexpr ( std::is_same::value )
{
CVarManagedVarDispatchInfo primitiveInfo{ m_name.c_str(), oldValue, value };
- g_hostManager->DispatchFloatCVarCallback( primitiveInfo );
+ Globals::m_hostManager->DispatchFloatCVarCallback( primitiveInfo );
}
else if constexpr ( std::is_same::value )
{
CVarManagedVarDispatchInfo primitiveInfo{ m_name.c_str(), oldValue, value };
- g_hostManager->DispatchBoolCVarCallback( primitiveInfo );
+ Globals::m_hostManager->DispatchBoolCVarCallback( primitiveInfo );
+ }
+ else if constexpr ( std::is_same::value )
+ {
+ CVarManagedVarDispatchInfo primitiveInfo{ m_name.c_str(), oldValue, value };
+
+ Globals::m_hostManager->DispatchIntCVarCallback( primitiveInfo );
}
}
else
@@ -541,7 +581,6 @@ inline void CVarEntry::SetValue( T value )
spdlog::info( "{} was set to '{}'.", m_name, value );
}
-
std::string CVarEntry::GetString()
{
return GetValue();
@@ -557,7 +596,6 @@ std::string CVarSystem::GetString( std::string name )
return GetEntry( name ).GetString();
}
-
float CVarEntry::GetFloat()
{
return GetValue();
@@ -573,7 +611,6 @@ float CVarSystem::GetFloat( std::string name )
return GetEntry( name ).GetFloat();
}
-
bool CVarEntry::GetBool()
{
return GetValue();
@@ -589,6 +626,20 @@ bool CVarSystem::GetBool( std::string name )
return GetEntry( name ).GetBool();
}
+int CVarEntry::GetInt()
+{
+ return GetValue();
+}
+
+int CVarSystem::GetInt( std::string name )
+{
+ if ( !Exists( name ) )
+ {
+ return false;
+ }
+
+ return GetEntry( name ).GetInt();
+}
void CVarEntry::SetString( std::string value )
{
@@ -601,11 +652,10 @@ void CVarSystem::SetString( std::string name, std::string value )
{
return;
}
-
+
GetEntry( name ).SetString( value );
}
-
void CVarEntry::SetFloat( float value )
{
SetValue( value );
@@ -617,11 +667,10 @@ void CVarSystem::SetFloat( std::string name, float value )
{
return;
}
-
+
GetEntry( name ).SetFloat( value );
}
-
void CVarEntry::SetBool( bool value )
{
SetValue( value );
@@ -633,10 +682,25 @@ void CVarSystem::SetBool( std::string name, bool value )
{
return;
}
-
+
GetEntry( name ).SetBool( value );
}
+void CVarEntry::SetInt( int value )
+{
+ SetValue( value );
+}
+
+void CVarSystem::SetInt( std::string name, int value )
+{
+ if ( !Exists( name ) )
+ {
+ return;
+ }
+
+ GetEntry( name ).SetInt( value );
+}
+
std::string CVarEntry::ToString()
{
const std::type_info& type = m_value.type();
@@ -648,6 +712,8 @@ std::string CVarEntry::ToString()
valueStr = std::to_string( std::any_cast( m_value ) );
else if ( type == typeid( bool ) )
valueStr = std::any_cast( m_value ) ? "true" : "false";
+ else if ( type == typeid( int ) )
+ valueStr = std::to_string( std::any_cast( m_value ) );
return valueStr;
}
@@ -662,7 +728,6 @@ std::string CVarSystem::ToString( std::string name )
return GetEntry( name ).ToString();
}
-
void CVarEntry::FromString( std::string valueStr )
{
std::stringstream valueStream( valueStr );
@@ -693,6 +758,13 @@ void CVarEntry::FromString( std::string valueStr )
{
SetValue( valueStr );
}
+ else if ( type == typeid( int ) )
+ {
+ float value;
+ valueStream >> value;
+
+ SetValue( value );
+ }
}
void CVarSystem::FromString( std::string name, std::string valueStr )
@@ -701,11 +773,10 @@ void CVarSystem::FromString( std::string name, std::string valueStr )
{
return;
}
-
+
GetEntry( name ).FromString( valueStr );
}
-
void CVarSystem::ForEach( std::function func )
{
for ( auto& entry : m_cvarEntries )
@@ -742,11 +813,16 @@ void CVarManager::Shutdown()
CVarSystem::Instance().Shutdown();
}
+void CVarManager::Run( const char* input )
+{
+ CVarSystem::Instance().Run( input );
+}
+
// ----------------------------------------
// Built-in CVars
// ----------------------------------------
-static std::string GetFlagsString(CVarFlags flags)
+static std::string GetFlagsString( CVarFlags flags )
{
std::vector flagNames;
@@ -786,46 +862,36 @@ static std::string GetFlagsString(CVarFlags flags)
return ss.str();
}
-static CCmd ccmd_list( "list", CVarFlags::None, "List all commands and variables",
- []( std::vector arguments )
- {
- auto instance = CVarSystem::Instance();
+static CCmd ccmd_list( "list", CVarFlags::None, "List all commands and variables", []( std::vector arguments ) {
+ auto instance = CVarSystem::Instance();
// This fails on libclang so we'll ignore it for now...
#ifndef __clang__
- // List all available cvars
- instance.ForEach( [&]( CVarEntry& entry ) {
- std::string flagNames = GetFlagsString( (CVarFlags) entry.m_flags );
-
- if ( entry.IsCommand() )
- spdlog::info( "- '{}' - {}", entry.m_name, flagNames );
- else
- spdlog::info( "- '{}': '{}' - {}", entry.m_name, entry.ToString(), flagNames );
- spdlog::info( "\t{}", entry.m_description );
- } );
+ // List all available cvars
+ instance.ForEach( [&]( CVarEntry& entry ) {
+ std::string flagNames = GetFlagsString( ( CVarFlags )entry.m_flags );
+
+ if ( entry.IsCommand() )
+ spdlog::info( "- '{}' - {}", entry.m_name, flagNames );
+ else
+ spdlog::info( "- '{}': '{}' - {}", entry.m_name, entry.ToString(), flagNames );
+ spdlog::info( "\t{}", entry.m_description );
+ } );
#endif
- }
-);
+} );
// ----------------------------------------
// Test CVars
// ----------------------------------------
static FloatCVar cvartest_float( "cvartest.float", 0.0f, CVarFlags::None, "Yeah",
- []( float oldValue, float newValue )
- {
- spdlog::trace( "cvartest.float changed! old {}, new {}", oldValue, newValue );
- }
-);
+ []( float oldValue, float newValue ) { spdlog::trace( "cvartest.float changed! old {}, new {}", oldValue, newValue ); } );
+
+static CCmd cvartest_command( "cvartest.command", CVarFlags::None, "A test command", []( std::vector arguments ) {
+ spdlog::trace( "cvartest.command has been invoked! Hooray" );
-static CCmd cvartest_command( "cvartest.command", CVarFlags::None, "A test command",
- []( std::vector arguments )
+ for ( int i = 0; i < arguments.size(); i++ )
{
- spdlog::trace( "cvartest.command has been invoked! Hooray" );
-
- for ( int i = 0; i < arguments.size(); i++ )
- {
- spdlog::trace( "\t{} - '{}'", i, arguments.at( i ) );
- }
+ spdlog::trace( "\t{} - '{}'", i, arguments.at( i ) );
}
-);
\ No newline at end of file
+} );
\ No newline at end of file
diff --git a/Source/Mocha.Host/cvarmanager.h b/Source/Mocha.Host/Misc/cvarmanager.h
similarity index 83%
rename from Source/Mocha.Host/cvarmanager.h
rename to Source/Mocha.Host/Misc/cvarmanager.h
index af172ada..dcd803cf 100644
--- a/Source/Mocha.Host/cvarmanager.h
+++ b/Source/Mocha.Host/Misc/cvarmanager.h
@@ -1,13 +1,14 @@
#pragma once
+#include
+#include
+#include
#include
#include