Implement some common block operations

This commit is contained in:
NGnius (Graham) 2019-12-16 20:55:52 -05:00
parent c55454e4a3
commit 0357728f0e
13 changed files with 551 additions and 13 deletions

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Svelto.ECS;
using RobocraftX.Common;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// ExclusiveGroups and IDs used with blocks
/// </summary>
public static class BlockIdentifiers
{
/// <summary>
/// Blocks placed by the player
/// </summary>
public static ExclusiveGroup OWNED_BLOCKS { get { return CommonExclusiveGroups.OWNED_BLOCKS_GROUP; } }
/// <summary>
/// Extra parts used in functional blocks
/// </summary>
public static ExclusiveGroup FUNCTIONAL_BLOCK_PARTS { get { return CommonExclusiveGroups.FUNCTIONAL_BLOCK_PART_GROUP; } }
/// <summary>
/// Blocks which are disabled in Simulation mode
/// </summary>
public static ExclusiveGroup SIM_BLOCKS_DISABLED { get { return CommonExclusiveGroups.BLOCKS_DISABLED_IN_SIM_GROUP; } }
public static ExclusiveGroup SPAWN_POINTS { get { return CommonExclusiveGroups.SPAWN_POINTS_GROUP; } }
public static ExclusiveGroup SPAWN_POINTS_DISABLED { get { return CommonExclusiveGroups.SPAWN_POINTS_DISABLED_GROUP; } }
/// <summary>
/// The ID of the most recently placed block
/// </summary>
public static uint LatestBlockID { get { return CommonExclusiveGroups.CurrentBlockEntityID - 1; } }
}
}

View file

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Mathematics;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Common block movement operations
/// </summary>
public static class Movement
{
private static MovementEngine movementEngine = new MovementEngine();
/// <summary>
/// Move a single block by a specific (x,y,z) amount
/// </summary>
/// <param name="id">The block's id</param>
/// <param name="vector">The movement amount (x,y,z)</param>
/// <returns></returns>
public static bool MoveBlock(uint id, float3 vector)
{
if (movementEngine.IsInGame && movementEngine.IsBuildMode())
{
movementEngine.MoveBlock(id, vector);
return true;
}
return false;
}
/// <summary>
/// Move all connected blocks by a specific (x,y,z) amount
/// </summary>
/// <param name="id">The starting block's id</param>
/// <param name="vector">The movement amount (x,y,z)</param>
/// <returns></returns>
public static bool MoveConnectedBlocks(uint id, float3 vector)
{
if (movementEngine.IsInGame && movementEngine.IsBuildMode())
{
movementEngine.MoveConnectedBlocks(id, vector);
return true;
}
return false;
}
public static void Init()
{
GamecraftModdingAPI.Utility.GameEngineManager.AddGameEngine(movementEngine);
}
}
}

View file

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RobocraftX;
using RobocraftX.Blocks;
using RobocraftX.Blocks.Ghost;
using RobocraftX.Common;
using RobocraftX.Multiplayer;
using RobocraftX.SimulationModeState;
using RobocraftX.UECS;
using Unity.Entities;
using Svelto.Context;
using Svelto.ECS;
using Svelto.ECS.EntityStructs;
using Unity.Transforms;
using Unity.Mathematics;
using UnityEngine;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine which executes block movement actions
/// </summary>
class MovementEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIMovementGameEngine";
public IEntitiesDB entitiesDB { set; private get; }
public bool IsInGame = false;
public void Dispose()
{
IsInGame = false;
}
public void Ready()
{
IsInGame = true;
}
// implementations for Movement static class
public float3 MoveBlock(uint blockID, float3 vector)
{
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
// main (persistent) position
posStruct.position += vector;
// placement grid position
gridStruct.position += vector;
// rendered position
transStruct.position += vector;
// collision position
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation
{
Value = posStruct.position
});
return posStruct.position;
}
public float3 MoveConnectedBlocks(uint blockID, float3 vector)
{
Stack<uint> cubeStack = new Stack<uint>();
Svelto.DataStructures.FasterList<uint> cubesToMove = new Svelto.DataStructures.FasterList<uint>();
ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubesToMove, (in GridConnectionsEntityStruct g) => { return false; });
for (int i = 0; i < cubesToMove.Count; i++)
{
MoveBlock(cubesToMove[i], vector);
entitiesDB.QueryEntity<GridConnectionsEntityStruct>(cubesToMove[i], CommonExclusiveGroups.OWNED_BLOCKS_GROUP).isProcessed = false;
}
return this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP).position;
}
public bool IsBuildMode()
{
return this.entitiesDB.QueryUniqueEntity<SimulationModeStateEntityStruct>(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).simulationMode == SimulationMode.Build;
}
}
}

View file

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Mathematics;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Common block movement operations
/// </summary>
public static class Rotation
{
private static RotationEngine rotationEngine = new RotationEngine();
/// <summary>
/// Rotate a single block by a specific amount in degrees
/// </summary>
/// <param name="id">The block's id</param>
/// <param name="vector">The rotation amount around the x,y,z-planes</param>
/// <returns></returns>
public static bool MoveBlock(uint id, float3 vector)
{
if (rotationEngine.IsInGame && rotationEngine.IsBuildMode())
{
rotationEngine.RotateBlock(id, vector);
return true;
}
return false;
}
/// <summary>
/// Rotate all connected blocks by a specific amount in degrees
/// </summary>
/// <param name="id">The starting block's id</param>
/// <param name="vector">The rotation around the x,y,z-planes</param>
/// <returns></returns>
public static bool MoveRotateBlocks(uint id, float3 vector)
{
if (rotationEngine.IsInGame && rotationEngine.IsBuildMode())
{
rotationEngine.MoveConnectedBlocks(id, vector);
return true;
}
return false;
}
public static void Init()
{
GamecraftModdingAPI.Utility.GameEngineManager.AddGameEngine(rotationEngine);
}
}
}

View file

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RobocraftX;
using RobocraftX.Blocks;
using RobocraftX.Blocks.Ghost;
using RobocraftX.Common;
using RobocraftX.Multiplayer;
using RobocraftX.SimulationModeState;
using RobocraftX.UECS;
using Unity.Entities;
using Svelto.Context;
using Svelto.ECS;
using Svelto.ECS.EntityStructs;
using Unity.Transforms;
using Unity.Mathematics;
using UnityEngine;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Blocks
{
/// <summary>
/// Engine which executes block movement actions
/// </summary>
class RotationEngine : IApiEngine
{
public string Name { get; } = "GamecraftModdingAPIRotationGameEngine";
public IEntitiesDB entitiesDB { set; private get; }
public bool IsInGame = false;
public void Dispose()
{
IsInGame = false;
}
public void Ready()
{
IsInGame = true;
}
// implementations for Movement static class
public float3 RotateBlock(uint blockID, Vector3 vector)
{
ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity<RotationEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity<GridRotationStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity<LocalTransformEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity<UECSPhysicsEntityStruct>(blockID, CommonExclusiveGroups.OWNED_BLOCKS_GROUP);
// main (persistent) position
Quaternion newRotation = (Quaternion)rotStruct.rotation;
newRotation.eulerAngles += vector;
rotStruct.rotation = (quaternion)newRotation;
// placement grid rotation
Quaternion newGridRotation = (Quaternion)gridStruct.rotation;
newGridRotation.eulerAngles += vector;
gridStruct.rotation = (quaternion)newGridRotation;
// rendered position
Quaternion newTransRotation = (Quaternion)rotStruct.rotation;
newTransRotation.eulerAngles += vector;
transStruct.rotation = newTransRotation;
// collision position
FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Unity.Transforms.Rotation
{
Value = rotStruct.rotation
});
return ((Quaternion)rotStruct.rotation).eulerAngles;
}
public float3 MoveConnectedBlocks(uint blockID, Vector3 vector)
{
throw new NotImplementedException();
}
public bool IsBuildMode()
{
return this.entitiesDB.QueryUniqueEntity<SimulationModeStateEntityStruct>(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).simulationMode == SimulationMode.Build;
}
}
}

View file

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Commands
{
/// <summary>
@ -50,6 +52,7 @@ namespace GamecraftModdingAPI.Commands
{
foreach (var key in _customCommands.Keys)
{
Logging.MetaDebugLog($"Registering ICustomCommandEngine {_customCommands[key].Name}");
enginesRoot.AddEngine(_customCommands[key]);
}
}

View file

@ -6,6 +6,8 @@ using System.Threading.Tasks;
using Svelto.ECS;
using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Events
{
/// <summary>
@ -91,10 +93,12 @@ namespace GamecraftModdingAPI.Events
var entityFactory = enginesRoot.GenerateEntityFactory();
foreach (var key in _eventHandlers.Keys)
{
Logging.MetaDebugLog($"Registering IEventHandlerEngine {_eventHandlers[key].Name}");
enginesRoot.AddEngine(_eventHandlers[key]);
}
foreach (var key in _eventEmitters.Keys)
{
Logging.MetaDebugLog($"Registering IEventEmitterEngine {_eventEmitters[key].Name}");
_eventEmitters[key].Factory = entityFactory;
enginesRoot.AddEngine(_eventEmitters[key]);
}

View file

@ -20,7 +20,7 @@ namespace GamecraftModdingAPI.Events
{
private static bool firstLoad = true;
public static void Postfix(ref EnginesRoot ____frontEndEnginesRoot)
public static void Postfix(ref EnginesRoot ____frontEndEnginesRoot, FullGameCompositionRoot __instance)
{
// register custom menu engines
MenuEngineManager.RegisterEngines(____frontEndEnginesRoot);
@ -30,6 +30,7 @@ namespace GamecraftModdingAPI.Events
if (firstLoad)
{
firstLoad = false;
FullGameFields.Init(__instance);
Logging.Log("Dispatching App Init event");
EventManager.GetEventEmitter("GamecraftModdingAPIApplicationInitializedEventEmitter").Emit();
}

View file

@ -20,39 +20,51 @@ namespace GamecraftModdingAPI
{
private static HarmonyInstance harmony;
public static bool IsInitialized {
get { return harmony != null; }
}
/// <summary>
/// Initializes the GamecraftModdingAPI.
/// This should be called ASAP after Gamecraft starts up.
/// Call this as soon as possible after Gamecraft starts up.
/// Ideally, this should be called from your main Plugin class's OnApplicationStart() method.
/// </summary>
public static void Init()
{
var currentAssembly = Assembly.GetExecutingAssembly();
if (harmony == null)
if (IsInitialized)
{
harmony = HarmonyInstance.Create(currentAssembly.GetName().Name);
harmony.PatchAll(currentAssembly);
Logging.LogWarning("GamecraftModdingAPI.Main.Init() called but API is already initialized!");
return;
}
Logging.MetaDebugLog($"Patching Gamecraft");
var currentAssembly = Assembly.GetExecutingAssembly();
harmony = HarmonyInstance.Create(currentAssembly.GetName().Name);
harmony.PatchAll(currentAssembly);
// create default event emitters
Logging.MetaDebugLog($"Initializing Events");
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.ApplicationInitialized, "GamecraftModdingAPIApplicationInitializedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Menu, "GamecraftModdingAPIMenuActivatedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.MenuSwitchedTo, "GamecraftModdingAPIMenuSwitchedToEventEmitter", false)); // TODO
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.MenuSwitchedTo, "GamecraftModdingAPIMenuSwitchedToEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Game, "GamecraftModdingAPIGameActivatedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameReloaded, "GamecraftModdingAPIGameReloadedEventEmitter", false)); // TODO
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameSwitchedTo, "GamecraftModdingAPIGameSwitchedToEventEmitter", false)); // TODO
Logging.Log($"{currentAssembly.GetName().Name} {currentAssembly.GetName().Version} init & patch complete");
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameReloaded, "GamecraftModdingAPIGameReloadedEventEmitter", false));
EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameSwitchedTo, "GamecraftModdingAPIGameSwitchedToEventEmitter", false));
// init block implementors
Logging.MetaDebugLog($"Initializing Blocks");
Blocks.Movement.Init();
Blocks.Rotation.Init();
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
}
/// <summary>
/// Shuts down & cleans up the GamecraftModdingAPI.
/// This should be called ASAP before Gamecraft quits.
/// Call this as late as possible before Gamecraft quits.
/// Ideally, this should be called from your main Plugin class's OnApplicationQuit() method.
/// </summary>
public static void Shutdown()
{
var currentAssembly = Assembly.GetExecutingAssembly();
harmony.UnpatchAll(currentAssembly.GetName().Name);
Logging.Log($"{currentAssembly.GetName().Name} {currentAssembly.GetName().Version} shutdown & unpatch complete");
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
}
}
}

View file

@ -19,7 +19,7 @@ namespace GamecraftModdingAPI.Tests
: IllusionPlugin.IEnhancedPlugin
#endif
{
public static HarmonyInstance harmony { get; protected set; }
private static HarmonyInstance harmony { get; set; }
public string[] Filter { get; } = new string[] { "Gamecraft" };
@ -64,6 +64,16 @@ namespace GamecraftModdingAPI.Tests
"Exit", "Close Gamecraft without any prompts"));
CommandManager.AddCommand(new SimpleCustomCommandEngine<float>((float d) => { UnityEngine.Camera.main.fieldOfView = d; },
"SetFOV", "Set the player camera's field of view"));
CommandManager.AddCommand(new SimpleCustomCommandEngine<float, float, float>(
(x,y,z) => {
bool success = GamecraftModdingAPI.Blocks.Movement.MoveConnectedBlocks(
GamecraftModdingAPI.Blocks.BlockIdentifiers.LatestBlockID,
new Unity.Mathematics.float3(x, y, z));
if (!success)
{
GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!");
}
}, "MoveLastBlock", "Move the most-recently-placed block, and any connected blocks by the given offset"));
}

View file

@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataLoader;
using Harmony;
using RobocraftX;
using RobocraftX.Blocks.GUI;
using RobocraftX.Common.Utilities;
using RobocraftX.GUI;
using RobocraftX.Multiplayer;
using RobocraftX.Rendering;
using Svelto.Context;
using Svelto.ECS;
using Svelto.ECS.Schedulers.Unity;
using UnityEngine;
using Unity.Entities;
using Unity.Physics.Systems;
namespace GamecraftModdingAPI.Utility
{
/// <summary>
/// Public access to the private variables in RobocraftX.FullGameCompositionRoot
/// </summary>
public static class FullGameFields
{
public static MultiplayerInitParameters _multiplayerParams
{ get
{
return (MultiplayerInitParameters)fgcr?.Field("_multiplayerParams").GetValue();
}
}
public static EnginesRoot _frontEndEnginesRoot
{
get
{
return (EnginesRoot)fgcr?.Field("_frontEndEnginesRoot").GetValue();
}
}
public static EnginesRoot _mainGameEnginesRoot
{
get
{
return (EnginesRoot)fgcr?.Field("_mainGameEnginesRoot").GetValue();
}
}
public static World _physicsWorld
{
get
{
return (World)fgcr?.Field("_physicsWorld").GetValue();
}
}
public static World _renderingWorld
{
get
{
return (World)fgcr?.Field("_renderingWorld").GetValue();
}
}
public static SimpleSubmissionEntityViewScheduler _mainGameSubmissionScheduler
{
get
{
return (SimpleSubmissionEntityViewScheduler)fgcr?.Field("_mainGameSubmissionScheduler").GetValue();
}
}
public static BuildPhysicsWorld _physicsWorldSystem
{
get
{
return (BuildPhysicsWorld)fgcr?.Field("_physicsWorldSystem").GetValue();
}
}
public static UnityContext<FullGameCompositionRoot> _contextHolder
{
get
{
return (UnityContext<FullGameCompositionRoot>)fgcr?.Field("_contextHolder").GetValue();
}
}
public static GameObject _frontEndGo
{
get
{
return (GameObject)fgcr?.Field("_frontEndGo").GetValue();
}
}
public static GameObject _mainGameGo
{
get
{
return (GameObject)fgcr?.Field("_mainGameGo").GetValue();
}
}
public static PhysicsUtility _physicsUtility
{
get
{
return (PhysicsUtility)fgcr?.Field("_physicsUtility").GetValue();
}
}
public static UnityEntitySubmissionScheduler _frontEndSubmissionScheduler
{
get
{
return (UnityEntitySubmissionScheduler)fgcr?.Field("_frontEndSubmissionScheduler").GetValue();
}
}
public static LoadingScreenImplementer _loadingScreen
{
get
{
return (LoadingScreenImplementer)fgcr?.Field("_loadingScreen").GetValue();
}
}
public static IDataDB _dataDb
{
get
{
return (IDataDB)fgcr?.Field("_dataDb").GetValue();
}
}
public static LabelResourceManager _textBlockLabelResourceManager
{
get
{
return (LabelResourceManager)fgcr?.Field("_textBlockLabelResourceManager").GetValue();
}
}
public static LabelResourceManager _labelResourceManager
{
get
{
return (LabelResourceManager)fgcr?.Field("_labelResourceManager").GetValue();
}
}
public static ECSGameObjectResourceManager _eCsGameObjectResourceManager
{
get
{
return (ECSGameObjectResourceManager)fgcr?.Field("_eCsGameObjectResourceManager").GetValue();
}
}
public static bool _isQuitting
{
get
{
return (bool)fgcr?.Field("_isQuitting").GetValue();
}
}
private static Traverse fgcr;
public static void Init(FullGameCompositionRoot instance)
{
fgcr = new Traverse(instance);
}
}
}

View file

@ -8,6 +8,9 @@ using Svelto.ECS;
namespace GamecraftModdingAPI.Utility
{
/// <summary>
/// Keeps track of custom game-modifying engines
/// </summary>
public static class GameEngineManager
{
private static Dictionary<string, IApiEngine> _gameEngines = new Dictionary<string, IApiEngine>();
@ -46,6 +49,7 @@ namespace GamecraftModdingAPI.Utility
{
foreach (var key in _gameEngines.Keys)
{
Logging.MetaDebugLog($"Registering Game IApiEngine {_gameEngines[key].Name}");
enginesRoot.AddEngine(_gameEngines[key]);
}
}

View file

@ -50,6 +50,7 @@ namespace GamecraftModdingAPI.Utility
{
foreach (var key in _menuEngines.Keys)
{
Logging.MetaDebugLog($"Registering Menu IApiEngine {_menuEngines[key].Name}");
enginesRoot.AddEngine(_menuEngines[key]);
}
}