diff --git a/Automation/gen_csproj.py b/Automation/gen_csproj.py index 1180d71..9d5dfbc 100755 --- a/Automation/gen_csproj.py +++ b/Automation/gen_csproj.py @@ -3,15 +3,22 @@ import argparse from pathlib import Path, PurePath import re +import os DLL_EXCLUSIONS_REGEX = r"(System|Microsoft|Mono|IronPython|DiscordRPC)\." def getAssemblyReferences(path): asmDir = Path(path) result = list() + addedPath = "" + if not asmDir.exists(): + addedPath = "../" + asmDir = Path(addedPath + path) for child in asmDir.iterdir(): if child.is_file() and re.search(DLL_EXCLUSIONS_REGEX, str(child), re.I) is None and str(child).lower().endswith(".dll"): - result.append(str(child).replace("\\", "/")) + childstr = str(child) + childstr = os.path.relpath(childstr, addedPath).replace("\\", "/") + result.append(childstr) return result def buildReferencesXml(path): @@ -27,16 +34,16 @@ def buildReferencesXml(path): return "\n \n" + "".join(result) + " \n" if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Generate GamecraftModdingAPI.csproj") + parser = argparse.ArgumentParser(description="Generate TechbloxModdingAPI.csproj") # TODO (maybe?): add params for custom csproj read and write locations args = parser.parse_args() print("Building Assembly references") - asmXml = buildReferencesXml("../ref/GamecraftPreview_Data/Managed") + asmXml = buildReferencesXml("../ref/TechbloxPreview_Data/Managed") # print(asmXml) - with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "r") as xmlFile: - print("Parsing GamecraftModdingAPI.csproj") + with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile: + print("Parsing TechbloxModdingAPI.csproj") fileStr = xmlFile.read() # print(fileStr) depsStart = re.search(r"\ + + diff --git a/CodeGenerator/Program.cs b/CodeGenerator/Program.cs new file mode 100644 index 0000000..0e9dea5 --- /dev/null +++ b/CodeGenerator/Program.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using RobocraftX.Blocks; +using RobocraftX.PilotSeat; +using Techblox.EngineBlock; +using Techblox.WheelRigBlock; + +namespace CodeGenerator +{ + internal class Program + { + public static void Main(string[] args) + { + var bcg = new BlockClassGenerator(); + bcg.Generate("Engine", null, new Dictionary + { + { "engineOn", "On" } + }, typeof(EngineBlockComponent), // Simulation time properties + typeof(EngineBlockTweakableComponent), typeof(EngineBlockReadonlyComponent)); + bcg.Generate("DampedSpring", "DAMPEDSPRING_BLOCK_GROUP", new Dictionary + { + {"maxExtent", "MaxExtension"} + }, + typeof(TweakableJointDampingComponent), typeof(DampedSpringReadOnlyStruct)); + bcg.Generate("LogicGate", "LOGIC_BLOCK_GROUP"); + bcg.Generate("Servo", types: typeof(ServoReadOnlyStruct), renames: new Dictionary + { + {"minDeviation", "MinimumAngle"}, + {"maxDeviation", "MaximumAngle"}, + {"servoVelocity", "MaximumForce"} + }); + bcg.Generate("WheelRig", "WHEELRIG_BLOCK_BUILD_GROUP", null, + typeof(WheelRigTweakableStruct), typeof(WheelRigReadOnlyStruct), + typeof(WheelRigSteerableTweakableStruct), typeof(WheelRigSteerableReadOnlyStruct)); + bcg.Generate("Seat", "RobocraftX.PilotSeat.SeatGroups.PILOTSEAT_BLOCK_BUILD_GROUP", null, typeof(SeatFollowCamComponent), typeof(SeatReadOnlySettingsComponent)); + bcg.Generate("Piston", null, new Dictionary + { + {"pistonVelocity", "MaximumForce"} + }, typeof(PistonReadOnlyStruct)); + bcg.Generate("Motor", null, null, typeof(MotorReadOnlyStruct)); + } + } +} \ No newline at end of file diff --git a/CodeGenerator/Properties/AssemblyInfo.cs b/CodeGenerator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..18681a4 --- /dev/null +++ b/CodeGenerator/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodeGenerator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CodeGenerator")] +[assembly: AssemblyCopyright("Copyright © ExMods 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 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("0EBB6400-95A7-4A3D-B2ED-BF31E364CC10")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/GamecraftModdingAPI/App/AppEngine.cs b/GamecraftModdingAPI/App/AppEngine.cs deleted file mode 100644 index 9cc454d..0000000 --- a/GamecraftModdingAPI/App/AppEngine.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; - -using RobocraftX.GUI.MyGamesScreen; -using RobocraftX.GUI; -using Svelto.ECS; - -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.App -{ - public class AppEngine : IFactoryEngine - { - public event EventHandler EnterMenu; - - public event EventHandler ExitMenu; - - public IEntityFactory Factory { set; private get; } - - public string Name => "GamecraftModdingAPIAppEngine"; - - public bool isRemovable => false; - - public EntitiesDB entitiesDB { set; private get; } - - public void Dispose() - { - IsInMenu = false; - ExceptionUtil.InvokeEvent(ExitMenu, this, new MenuEventArgs { }); - } - - public void Ready() - { - IsInMenu = true; - ExceptionUtil.InvokeEvent(EnterMenu, this, new MenuEventArgs { }); - } - - // app functionality - - public bool IsInMenu - { - get; - private set; - } = false; - - public Game[] GetMyGames() - { - EntityCollection mgsevs = entitiesDB.QueryEntities(MyGamesScreenExclusiveGroups.MyGames); - var mgsevsB = mgsevs.ToBuffer().buffer; - Game[] games = new Game[mgsevs.count]; - for (int i = 0; i < mgsevs.count; i++) - { - Utility.Logging.MetaDebugLog($"Found game named {mgsevsB[i].GameName}"); - games[i] = new Game(mgsevsB[i].ID); - } - return games; - } - } - - public struct MenuEventArgs - { - - } -} diff --git a/GamecraftModdingAPI/App/GameGameEngine.cs b/GamecraftModdingAPI/App/GameGameEngine.cs deleted file mode 100644 index 85fb672..0000000 --- a/GamecraftModdingAPI/App/GameGameEngine.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using HarmonyLib; - -using RobocraftX; -using RobocraftX.Common; -using RobocraftX.Schedulers; -using RobocraftX.SimulationModeState; -using Svelto.ECS; -using Svelto.Tasks; -using Svelto.Tasks.Lean; - -using GamecraftModdingAPI.Blocks; -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.App -{ - public class GameGameEngine : IApiEngine - { - public event EventHandler EnterGame; - - public event EventHandler ExitGame; - - public string Name => "GamecraftModdingAPIGameInfoMenuEngine"; - - public bool isRemovable => false; - - public EntitiesDB entitiesDB { set; private get; } - - public void Dispose() - { - ExceptionUtil.InvokeEvent(ExitGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); - IsInGame = false; - } - - public void Ready() - { - ExceptionUtil.InvokeEvent(EnterGame, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); - IsInGame = true; - } - - // game functionality - - public bool IsInGame - { - get; - private set; - } = false; - - public void ExitCurrentGame(bool async = false) - { - if (async) - { - ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_TimeRunningAndStopped); - } - else - { - entitiesDB.QueryEntity(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true; - entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID); - } - - } - - public IEnumerator ExitCurrentGameAsync() - { - /* - while (Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING.isStopping) { yield return Yield.It; } - AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu").Invoke(FullGameFields.Instance, new object[0]);*/ - yield return Yield.It; - entitiesDB.QueryEntity(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true; - entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID); - } - - public void SaveCurrentGame() - { - ref GameSceneEntityStruct gses = ref entitiesDB.QueryEntity(CommonExclusiveGroups.GameSceneEGID); - gses.LoadAfterSaving = false; - gses.SaveNow = true; - entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID); - } - - public bool IsTimeRunningMode() - { - return TimeRunningModeUtil.IsTimeRunningMode(entitiesDB); - } - - public bool IsTimeStoppedMode() - { - return TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB); - } - - public void ToggleTimeMode() - { - TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB); - } - - public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid) - { - var allBlocks = entitiesDB.QueryEntities(); - List blockEGIDs = new List(); - if (filter == BlockIDs.Invalid) - { - foreach (var (blocks, _) in allBlocks) - { - var buffer = blocks.ToBuffer().buffer; - for (int i = 0; i < buffer.capacity; i++) - blockEGIDs.Add(buffer[i].ID); - } - - return blockEGIDs.ToArray(); - } - else - { - foreach (var (blocks, _) in allBlocks) - { - var array = blocks.ToBuffer().buffer; - for (var index = 0; index < array.capacity; index++) - { - var block = array[index]; - if (block.DBID == (ulong) filter) - blockEGIDs.Add(block.ID); - } - } - - return blockEGIDs.ToArray(); - } - } - } -} diff --git a/GamecraftModdingAPI/App/GameMenuEngine.cs b/GamecraftModdingAPI/App/GameMenuEngine.cs deleted file mode 100644 index efcb73e..0000000 --- a/GamecraftModdingAPI/App/GameMenuEngine.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System; -using HarmonyLib; - -using RobocraftX; -using RobocraftX.Common; -using RobocraftX.GUI; -using RobocraftX.GUI.MyGamesScreen; -using Svelto.ECS; -using Svelto.ECS.Experimental; - -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Utility; -using Svelto.DataStructures; - -namespace GamecraftModdingAPI.App -{ - public class GameMenuEngine : IFactoryEngine - { - public IEntityFactory Factory { set; private get; } - - public string Name => "GamecraftModdingAPIGameInfoGameEngine"; - - public bool isRemovable => false; - - public EntitiesDB entitiesDB { set; private get; } - - public void Dispose() - { - IsInMenu = false; - } - - public void Ready() - { - IsInMenu = true; - } - - // game functionality - - public bool IsInMenu - { - get; - private set; - } = false; - - public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L) - { - EntityComponentInitializer eci = Factory.BuildEntity(id); - eci.Init(new MyGameDataEntityStruct - { - SavedGamePath = new ECSString(path), - ThumbnailId = thumbnailId, - GameName = new ECSString(gameName), - CreatorName = new ECSString(creatorName), - GameDescription = new ECSString(description), - CreatedDate = createdDate, - }); - // entitiesDB.PublishEntityChange(id); // this will always fail - return true; - } - - public uint HighestID() - { - EntityCollection games = entitiesDB.QueryEntities(MyGamesScreenExclusiveGroups.MyGames); - var gamesB = games.ToBuffer().buffer; - uint max = 0; - for (int i = 0; i < games.count; i++) - { - if (gamesB[i].ID.entityID > max) - { - max = gamesB[i].ID.entityID; - } - } - return max; - } - - public bool EnterGame(EGID id) - { - if (!ExistsGameInfo(id)) return false; - ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id); - return EnterGame(mgdes.GameName, mgdes.SavedGamePath); - } - - public bool EnterGame(string gameName, string path, ulong workshopId = 0uL, bool autoEnterSim = false) - { - GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build; - GameMode.SaveGameDetails = new SaveGameDetails(gameName, path, workshopId); - // the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason - AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[0]); - return true; - } - - public bool SetGameName(EGID id, string name) - { - if (!ExistsGameInfo(id)) return false; - GetGameInfo(id).GameName.Set(name); - GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name); - return true; - } - - public bool SetGameDescription(EGID id, string name) - { - if (!ExistsGameInfo(id)) return false; - GetGameInfo(id).GameDescription.Set(name); - GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name); - return true; - } - - public bool ExistsGameInfo(EGID id) - { - return entitiesDB.Exists(id); - } - - public ref MyGameDataEntityStruct GetGameInfo(EGID id) - { - return ref GetComponent(id); - } - - public ref MyGamesSlotEntityViewStruct GetGameViewInfo(EGID id) - { - EntityCollection entities = - entitiesDB.QueryEntities(MyGamesScreenExclusiveGroups.GameSlotGuiEntities); - var entitiesB = entities.ToBuffer().buffer; - for (int i = 0; i < entities.count; i++) - { - if (entitiesB[i].ID.entityID == id.entityID) - { - return ref entitiesB[i]; - } - } - MyGamesSlotEntityViewStruct[] defRef = new MyGamesSlotEntityViewStruct[1]; - return ref defRef[0]; - } - - public ref T GetComponent(EGID id) where T: unmanaged, IEntityComponent - { - return ref entitiesDB.QueryEntity(id); - } - } - - internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor { } -} diff --git a/GamecraftModdingAPI/App/StateSyncRegPatch.cs b/GamecraftModdingAPI/App/StateSyncRegPatch.cs deleted file mode 100644 index 9c2ce68..0000000 --- a/GamecraftModdingAPI/App/StateSyncRegPatch.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Reflection; - -using RobocraftX.CR.MainGame; -using RobocraftX.StateSync; - -using HarmonyLib; - -namespace GamecraftModdingAPI.App -{ - [HarmonyPatch] - class StateSyncRegPatch - { - public static void Postfix(StateSyncRegistrationHelper stateSyncReg) - { - // register sim/build events engines - Game.InitDeterministic(stateSyncReg); - } - - [HarmonyTargetMethod] - public static MethodBase Target() - { - return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose").MakeGenericMethod(typeof(object)); - } - } -} diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs deleted file mode 100644 index 33a1623..0000000 --- a/GamecraftModdingAPI/Block.cs +++ /dev/null @@ -1,501 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection.Emit; - -using Gamecraft.Blocks.BlockGroups; -using Svelto.ECS; -using Svelto.ECS.EntityStructs; -using RobocraftX.Common; -using RobocraftX.Blocks; -using Unity.Mathematics; -using Gamecraft.Blocks.GUI; - -using GamecraftModdingAPI.Blocks; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI -{ - /// - /// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored. - /// For specific block type operations, use the specialised block classes in the GamecraftModdingAPI.Blocks namespace. - /// - public class Block : IEquatable, IEquatable - { - protected static readonly PlacementEngine PlacementEngine = new PlacementEngine(); - protected static readonly MovementEngine MovementEngine = new MovementEngine(); - protected static readonly RotationEngine RotationEngine = new RotationEngine(); - protected static readonly RemovalEngine RemovalEngine = new RemovalEngine(); - protected static readonly SignalEngine SignalEngine = new SignalEngine(); - protected static readonly BlockEventsEngine BlockEventsEngine = new BlockEventsEngine(); - protected static readonly ScalingEngine ScalingEngine = new ScalingEngine(); - - protected internal static readonly BlockEngine BlockEngine = new BlockEngine(); - - /// - /// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position. - /// Place blocks next to each other to connect them. - /// The placed block will be a complete block with a placement grid and collision which will be saved along with the game. - /// - /// The block's type - /// The block's color - /// The block color's darkness (0-9) - 0 is default color - /// The block's position - default block size is 0.2 - /// The block's rotation in degrees - /// The block's uniform scale - default scale is 1 (with 0.2 width) - /// The block's non-uniform scale - 0 means is used - /// The player who placed the block - /// The placed block or null if failed - public static Block PlaceNew(BlockIDs block, float3 position, - float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0, - int uscale = 1, float3 scale = default, Player player = null) - { - return PlaceNew(block, position, rotation, color, darkness, uscale, scale, player); - } - - /// - /// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position. - /// Place blocks next to each other to connect them. - /// The placed block will be a complete block with a placement grid and collision which will be saved along with the game. - /// - /// The block's type - /// The block's color - /// The block color's darkness (0-9) - 0 is default color - /// The block's position - default block size is 0.2 - /// The block's rotation in degrees - /// The block's uniform scale - default scale is 1 (with 0.2 width) - /// The block's non-uniform scale - 0 means is used - /// The player who placed the block - /// The placed block or null if failed - public static T PlaceNew(BlockIDs block, float3 position, - float3 rotation = default, BlockColors color = BlockColors.Default, byte darkness = 0, - int uscale = 1, float3 scale = default, Player player = null) where T : Block - { - if (PlacementEngine.IsInGame && GameState.IsBuildMode()) - { - var egid = PlacementEngine.PlaceBlock(block, color, darkness, - position, uscale, scale, player, rotation, out var initializer); - var bl = New(egid.entityID, egid.groupID); - bl.InitData.Group = BlockEngine.InitGroup(initializer); - Placed += bl.OnPlacedInit; - return bl; - } - - return null; - } - - /// - /// Returns the most recently placed block. - /// - /// The block object - public static Block GetLastPlacedBlock() - { - return New(BlockIdentifiers.LatestBlockID); - } - - /// - /// An event that fires each time a block is placed. - /// - public static event EventHandler Placed - { - add => BlockEventsEngine.Placed += value; - remove => BlockEventsEngine.Placed -= value; - } - - /// - /// An event that fires each time a block is removed. - /// - public static event EventHandler Removed - { - add => BlockEventsEngine.Removed += value; - remove => BlockEventsEngine.Removed -= value; - } - - private static Dictionary> initializers = new Dictionary>(); - - private static Dictionary typeToGroup = - new Dictionary - { - {typeof(ConsoleBlock), new[] {CommonExclusiveGroups.CONSOLE_BLOCK_GROUP}}, - {typeof(LogicGate), new [] {CommonExclusiveGroups.LOGIC_BLOCK_GROUP}}, - {typeof(Motor), new[] {CommonExclusiveGroups.MOTOR_BLOCK_GROUP}}, - {typeof(MusicBlock), new[] {CommonExclusiveGroups.MUSIC_BLOCK_GROUP}}, - {typeof(ObjectIdentifier), new[]{CommonExclusiveGroups.OBJID_BLOCK_GROUP}}, - {typeof(Piston), new[] {CommonExclusiveGroups.PISTON_BLOCK_GROUP}}, - {typeof(Servo), new[] {CommonExclusiveGroups.SERVO_BLOCK_GROUP}}, - { - typeof(SpawnPoint), - new[] - { - CommonExclusiveGroups.SPAWNPOINT_BLOCK_GROUP, - CommonExclusiveGroups.BUILDINGSPAWN_BLOCK_GROUP - } - }, - { - typeof(SfxBlock), - new[] - { - CommonExclusiveGroups.SIMPLESFX_BLOCK_GROUP, - CommonExclusiveGroups.LOOPEDSFX_BLOCK_GROUP - } - }, - {typeof(DampedSpring), new [] {CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP}}, - {typeof(TextBlock), new[] {CommonExclusiveGroups.TEXT_BLOCK_GROUP}}, - {typeof(Timer), new[] {CommonExclusiveGroups.TIMER_BLOCK_GROUP}} - }; - - /// - /// Constructs a new instance of T with the given ID and group using dynamically created delegates. - /// It's equivalent to new T(EGID) with a minimal overhead thanks to caching the created delegates. - /// - /// The block ID - /// The block group - /// The block's type or Block itself - /// An instance of the provided type - /// The block group doesn't match or cannot be found - /// The block class doesn't have the needed constructor - private static T New(uint id, ExclusiveGroupStruct? group = null) where T : Block - { - var type = typeof(T); - EGID egid; - if (!group.HasValue) - { - if (typeToGroup.TryGetValue(type, out var gr) && gr.Length == 1) - egid = new EGID(id, gr[0]); - else - egid = BlockEngine.FindBlockEGID(id) ?? throw new BlockTypeException("Could not find block group!"); - } - else - { - egid = new EGID(id, group.Value); - if (typeToGroup.TryGetValue(type, out var gr) - && gr.All(egs => egs != group.Value)) //If this subclass has a specific group, then use that - so Block should still work - throw new BlockTypeException($"Incompatible block type! Type {type.Name} belongs to group {gr.Select(g => g.ToString()).Aggregate((a, b) => a + ", " + b)} instead of {group.Value}"); - } - - if (initializers.TryGetValue(type, out var func)) - { - var bl = (T) func(egid); - return bl; - } - - //https://stackoverflow.com/a/10593806/2703239 - var ctor = type.GetConstructor(new[] {typeof(EGID)}); - if (ctor == null) - throw new MissingMethodException("There is no constructor with an EGID parameter for this object"); - DynamicMethod dynamic = new DynamicMethod(string.Empty, - type, - new[] {typeof(EGID)}, - type); - ILGenerator il = dynamic.GetILGenerator(); - - //il.DeclareLocal(type); - il.Emit(OpCodes.Ldarg_0); //Load EGID and pass to constructor - il.Emit(OpCodes.Newobj, ctor); //Call constructor - //il.Emit(OpCodes.Stloc_0); - doesn't seem like we need these - //il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - - func = (Func) dynamic.CreateDelegate(typeof(Func)); - initializers.Add(type, func); - var block = (T) func(egid); - return block; - } - - public Block(EGID id) - { - Id = id; - var type = GetType(); - if (typeToGroup.TryGetValue(type, out var groups)) - { - if (groups.All(gr => gr != id.groupID)) - throw new BlockTypeException("The block has the wrong group! The type is " + GetType() + - " while the group is " + id.groupID); - } - else if (type != typeof(Block)) - Logging.LogWarning($"Unknown block type! Add {type} to the dictionary."); - } - - /// - /// This overload searches for the correct group the block is in. - /// It will throw an exception if the block doesn't exist. - /// Use the EGID constructor where possible or subclasses of Block as those specify the group. - /// - public Block(uint id) - { - Id = BlockEngine.FindBlockEGID(id) ?? throw new BlockTypeException("Could not find the appropriate group for the block. The block probably doesn't exist or hasn't been submitted."); - } - - public EGID Id { get; } - - internal BlockEngine.BlockInitData InitData; - - /// - /// The block's current position or zero if the block no longer exists. - /// A block is 0.2 wide by default in terms of position. - /// - public float3 Position - { - get => MovementEngine.GetPosition(Id, InitData); - set - { - MovementEngine.MoveBlock(Id, InitData, value); - if (blockGroup != null) - blockGroup.PosAndRotCalculated = false; - } - } - - /// - /// The block's current rotation in degrees or zero if the block doesn't exist. - /// - public float3 Rotation - { - get => RotationEngine.GetRotation(Id, InitData); - set - { - RotationEngine.RotateBlock(Id, InitData, value); - if (blockGroup != null) - blockGroup.PosAndRotCalculated = false; - } - } - - /// - /// The block's non-uniform scale or zero if the block's invalid. Independent of the uniform scaling. - /// The default scale of 1 means 0.2 in terms of position. - /// - public float3 Scale - { - get => BlockEngine.GetBlockInfo(this, (ScalingEntityStruct st) => st.scale); - set - { - BlockEngine.SetBlockInfo(this, (ref ScalingEntityStruct st, float3 val) => st.scale = val, value); - if (!Exists) return; //UpdateCollision needs the block to exist - ScalingEngine.UpdateCollision(Id); - } - } - - /// - /// The block's uniform scale or zero if the block's invalid. Also sets the non-uniform scale. - /// The default scale of 1 means 0.2 in terms of position. - /// - public int UniformScale - { - get => BlockEngine.GetBlockInfo(this, (UniformBlockScaleEntityStruct st) => st.scaleFactor); - set - { - BlockEngine.SetBlockInfo(this, (ref UniformBlockScaleEntityStruct st, int val) => st.scaleFactor = val, - value); - Scale = new float3(value, value, value); - } - } - - /// - /// The block's type (ID). Returns BlockIDs.Invalid if the block doesn't exist anymore. - /// - public BlockIDs Type - { - get - { - return BlockEngine.GetBlockInfo(this, (DBEntityStruct st) => (BlockIDs) st.DBID, BlockIDs.Invalid); - } - } - - /// - /// The block's color. Returns BlockColors.Default if the block no longer exists. - /// - public BlockColor Color - { - get - { - byte index = BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.indexInPalette, - byte.MaxValue); - return new BlockColor(index); - } - set - { - BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, BlockColor val) => - { - color.indexInPalette = (byte) (val.Color + val.Darkness * 10); - //color.overridePaletteColour = false; - //color.needsUpdate = true; - color.hasNetworkChange = true; - color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette); - }, value); - } - } - - /// - /// The block's exact color. Gets reset to the palette color (Color property) after reentering the game. - /// - public float4 CustomColor - { - get => BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.paletteColour); - set - { - BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, float4 val) => - { - color.paletteColour = val; - //color.overridePaletteColour = true; - //color.needsUpdate = true; - color.hasNetworkChange = true; - }, value); - } - } - - /// - /// The text displayed on the block if applicable, or null. - /// Setting it is temporary to the session, it won't be saved. - /// - public string Label - { - get => BlockEngine.GetBlockInfoViewStruct(this, (TextLabelEntityViewStruct st) => st.textLabelComponent?.text); - set - { - BlockEngine.SetBlockInfoViewStruct(this, (ref TextLabelEntityViewStruct text, string val) => - { - if (text.textLabelComponent != null) text.textLabelComponent.text = val; - }, value); - } - } - - private BlockGroup blockGroup; - /// - /// Returns the block group this block is a part of. Block groups can be placed using blueprints. - /// Returns null if not part of a group.
- /// Setting the group after the block has been initialized will not update everything properly. - /// You should only set this property on blocks newly placed by your code. - ///
- public BlockGroup BlockGroup - { - get - { - if (blockGroup != null) return blockGroup; - return blockGroup = BlockEngine.GetBlockInfo(this, - (BlockGroupEntityComponent bgec) => - bgec.currentBlockGroup == -1 ? null : new BlockGroup(bgec.currentBlockGroup, this)); - } - set - { - blockGroup?.RemoveInternal(this); - BlockEngine.SetBlockInfo(this, - (ref BlockGroupEntityComponent bgec, BlockGroup val) => bgec.currentBlockGroup = val?.Id ?? -1, - value); - value?.AddInternal(this); - blockGroup = value; - } - } - - /// - /// Whether the block exists. The other properties will return a default value if the block doesn't exist. - /// If the block was just placed, then this will also return false but the properties will work correctly. - /// - public bool Exists => BlockEngine.BlockExists(Id); - - /// - /// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist. - /// - public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id); - - /// - /// Removes this block. - /// - /// True if the block exists and could be removed. - public bool Remove() => RemovalEngine.RemoveBlock(Id); - - /// - /// Returns the rigid body of the chunk of blocks this one belongs to during simulation. - /// Can be used to apply forces or move the block around while the simulation is running. - /// - /// The SimBody of the chunk or null if the block doesn't exist or not in simulation mode. - public SimBody GetSimBody() - { - return BlockEngine.GetBlockInfo(this, - (GridConnectionsEntityStruct st) => st.machineRigidBodyId != uint.MaxValue - ? new SimBody(st.machineRigidBodyId, st.clusterId) - : null); - } - - private void OnPlacedInit(object sender, BlockPlacedRemovedEventArgs e) - { //Member method instead of lambda to avoid constantly creating delegates - if (e.ID != Id) return; - Placed -= OnPlacedInit; //And we can reference it - InitData = default; //Remove initializer as it's no longer valid - if the block gets removed it shouldn't be used again - } - - public override string ToString() - { - return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Type)}: {Type}, {nameof(Color)}: {Color}, {nameof(Exists)}: {Exists}"; - } - - public bool Equals(Block other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Id.Equals(other.Id); - } - - public bool Equals(EGID other) - { - return Id.Equals(other); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Block) obj); - } - - public override int GetHashCode() - { - return Id.GetHashCode(); - } - - public static void Init() - { - GameEngineManager.AddGameEngine(PlacementEngine); - GameEngineManager.AddGameEngine(MovementEngine); - GameEngineManager.AddGameEngine(RotationEngine); - GameEngineManager.AddGameEngine(RemovalEngine); - GameEngineManager.AddGameEngine(BlockEngine); - GameEngineManager.AddGameEngine(BlockEventsEngine); - GameEngineManager.AddGameEngine(ScalingEngine); - GameEngineManager.AddGameEngine(SignalEngine); - Wire.signalEngine = SignalEngine; // requires same functionality, no need to duplicate the engine - } - - /// - /// Convert the block to a specialised block class. - /// - /// The block. - /// The specialised block type. - public T Specialise() where T : Block - { - // What have I gotten myself into? - // C# can't cast to a child of Block unless the object was originally that child type - // And C# doesn't let me make implicit cast operators for child types - // So thanks to Microsoft, we've got this horrible implementation using reflection - - //Lets improve that using delegates - var block = New(Id.entityID, Id.groupID); - if (this.InitData.Group != null) - { - block.InitData = this.InitData; - Placed += block.OnPlacedInit; //Reset InitData of new object - } - - return block; - } - -#if DEBUG - public static EntitiesDB entitiesDB - { - get - { - return BlockEngine.GetEntitiesDB(); - } - } -#endif - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockColor.cs b/GamecraftModdingAPI/Blocks/BlockColor.cs deleted file mode 100644 index bf22090..0000000 --- a/GamecraftModdingAPI/Blocks/BlockColor.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using Unity.Mathematics; - -namespace GamecraftModdingAPI.Blocks -{ - public struct BlockColor - { - public BlockColors Color; - public byte Darkness; - - public byte Index => Color == BlockColors.Default - ? byte.MaxValue - : (byte) (Darkness * 10 + Color); - - public BlockColor(byte index) - { - if (index == byte.MaxValue) - { - Color = BlockColors.Default; - Darkness = 0; - } - else - { - if (index > 99) - throw new ArgumentOutOfRangeException(nameof(index), "Invalid color index. Must be 0-90 or 255."); - Color = (BlockColors) (index % 10); - Darkness = (byte) (index / 10); - } - } - - public BlockColor(BlockColors color, byte darkness) - { - if (darkness > 9) - throw new ArgumentOutOfRangeException(nameof(darkness), "Darkness must be 0-9 where 0 is default."); - Color = color; - Darkness = darkness; - } - - public float4 RGBA => Block.BlockEngine.ConvertBlockColor(Index); - - public override string ToString() - { - return $"{nameof(Color)}: {Color}, {nameof(Darkness)}: {Darkness}"; - } - } - - /// - /// Preset block colours - /// - public enum BlockColors - { - Default = byte.MaxValue, - White = 0, - Pink, - Purple, - Blue, - Aqua, - Green, - Lime, - Yellow, - Orange, - Red - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockEngine.cs b/GamecraftModdingAPI/Blocks/BlockEngine.cs deleted file mode 100644 index 9ce6d62..0000000 --- a/GamecraftModdingAPI/Blocks/BlockEngine.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using Gamecraft.ColourPalette; -using Gamecraft.TimeRunning; -using Gamecraft.Wires; -using RobocraftX.Blocks; -using RobocraftX.Common; -using RobocraftX.Physics; -using RobocraftX.Scene.Simulation; -using Svelto.DataStructures; -using Svelto.ECS; -using Svelto.ECS.Hybrid; -using Unity.Mathematics; - -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Blocks -{ - /// - /// Engine for executing general block actions - /// - public partial class BlockEngine : IApiEngine - { - public string Name { get; } = "GamecraftModdingAPIBlockGameEngine"; - - public EntitiesDB entitiesDB { set; private get; } - - public bool isRemovable => false; - - public void Dispose() - { - } - - public void Ready() - { - } - - public Block[] GetConnectedBlocks(EGID blockID) - { - if (!BlockExists(blockID)) return new Block[0]; - Stack cubeStack = new Stack(); - FasterList cubes = new FasterList(10); - var coll = entitiesDB.QueryEntities(); - foreach (var (ecoll, _) in coll) - { - var ecollB = ecoll.ToBuffer(); - for(int i = 0; i < ecoll.count; i++) - { - ref var conn = ref ecollB.buffer[i]; - conn.isProcessed = false; - } - } - - ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes, - (in GridConnectionsEntityStruct g) => { return false; }); - - var ret = new Block[cubes.count]; - for (int i = 0; i < cubes.count; i++) - ret[i] = new Block(cubes[i]); - return ret; - } - - public float4 ConvertBlockColor(byte index) => index == byte.MaxValue - ? new float4(-1f, -1f, -1f, -1f) - : entitiesDB.QueryEntity(index, - CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour; - - public ref T GetBlockInfo(EGID blockID) where T : unmanaged, IEntityComponent - { - if (entitiesDB.Exists(blockID)) - return ref entitiesDB.QueryEntity(blockID); - T[] structHolder = new T[1]; //Create something that can be referenced - return ref structHolder[0]; //Gets a default value automatically - } - - public ref T GetBlockInfoViewStruct(EGID blockID) where T : struct, INeedEGID, IEntityViewComponent - { - if (entitiesDB.Exists(blockID)) - { - // TODO: optimize by using EntitiesDB internal calls instead of iterating over everything - BT> entities = entitiesDB.QueryEntities(blockID.groupID).ToBuffer(); - for (int i = 0; i < entities.count; i++) - { - if (entities.buffer[i].ID == blockID) - { - return ref entities.buffer[i]; - } - } - } - T[] structHolder = new T[1]; //Create something that can be referenced - return ref structHolder[0]; //Gets a default value automatically - } - - public U GetBlockInfo(Block block, Func getter, - U def = default) where T : unmanaged, IEntityComponent - { - if (entitiesDB.Exists(block.Id)) - return getter(entitiesDB.QueryEntity(block.Id)); - return GetBlockInitInfo(block, getter, def); - } - - public U GetBlockInfoViewStruct(Block block, Func getter, - U def = default) where T : struct, IEntityViewComponent - { - if (entitiesDB.Exists(block.Id)) - return getter(entitiesDB.QueryEntity(block.Id)); - return GetBlockInitInfo(block, getter, def); - } - - private U GetBlockInitInfo(Block block, Func getter, U def) where T : struct, IEntityComponent - { - if (block.InitData.Group == null) return def; - var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group); - if (initializer.Has()) - return getter(initializer.Get()); - return def; - } - - public delegate void Setter(ref T component, U value) where T : struct, IEntityComponent; - - public void SetBlockInfoViewStruct(Block block, Setter setter, U value) where T : struct, IEntityViewComponent - { - if (entitiesDB.Exists(block.Id)) - setter(ref entitiesDB.QueryEntity(block.Id), value); - else - SetBlockInitInfo(block, setter, value); - } - - public void SetBlockInfo(Block block, Setter setter, U value) where T : unmanaged, IEntityComponent - { - if (entitiesDB.Exists(block.Id)) - setter(ref entitiesDB.QueryEntity(block.Id), value); - else - SetBlockInitInfo(block, setter, value); - } - - private void SetBlockInitInfo(Block block, Setter setter, U value) - where T : struct, IEntityComponent - { - if (block.InitData.Group != null) - { - var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group); - T component = initializer.Has() ? initializer.Get() : default; - ref T structRef = ref component; - setter(ref structRef, value); - initializer.Init(structRef); - } - } - - public bool BlockExists(EGID blockID) - { - return entitiesDB.Exists(blockID); - } - - public bool GetBlockInfoExists(Block block) where T : struct, IEntityComponent - { - if (entitiesDB.Exists(block.Id)) - return true; - if (block.InitData.Group == null) - return false; - var init = new EntityComponentInitializer(block.Id, block.InitData.Group); - return init.Has(); - } - - public SimBody[] GetSimBodiesFromID(byte id) - { - var ret = new FasterList(4); - if (!entitiesDB.HasAny(CommonExclusiveGroups.OBJID_BLOCK_GROUP)) - return new SimBody[0]; - var oids = entitiesDB.QueryEntities(CommonExclusiveGroups.OBJID_BLOCK_GROUP).ToBuffer(); - var connections = entitiesDB.QueryMappedEntities(CommonExclusiveGroups.OBJID_BLOCK_GROUP); - for (int i = 0; i < oids.count; i++) - { - ref ObjectIdEntityStruct oid = ref oids.buffer[i]; - if (oid.objectId != id) continue; - var rid = connections.Entity(oid.ID.entityID).machineRigidBodyId; - foreach (var rb in ret) - { - if (rb.Id.entityID == rid) - goto DUPLICATE; //Multiple Object Identifiers on one rigid body - } - ret.Add(new SimBody(rid)); - DUPLICATE: ; - } - return ret.ToArray(); - } - - public ObjectIdentifier[] GetObjectIDsFromID(byte id, bool sim) - { - var ret = new FasterList(4); - if (!entitiesDB.HasAny(CommonExclusiveGroups.OBJID_BLOCK_GROUP)) - return new ObjectIdentifier[0]; - var oids = entitiesDB.QueryEntities(CommonExclusiveGroups.OBJID_BLOCK_GROUP).ToBuffer(); - for (int i = 0; i < oids.count; i++) - { - ref ObjectIdEntityStruct oid = ref oids.buffer[i]; - if (sim ? oid.simObjectId == id : oid.objectId == id) - ret.Add(new ObjectIdentifier(oid.ID)); - } - - return ret.ToArray(); - } - - public SimBody[] GetConnectedSimBodies(uint id) - { - var joints = entitiesDB.QueryEntities(MachineSimulationGroups.JOINTS_GROUP).ToBuffer(); - var list = new FasterList(4); - for (int i = 0; i < joints.count; i++) - { - ref var joint = ref joints.buffer[i]; - if (joint.jointState == JointState.Broken) continue; - if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); - else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA)); - } - - return list.ToArray(); - } - - public SimBody[] GetClusterBodies(uint cid) - { - var groups = entitiesDB.QueryEntities(); - var bodies = new HashSet(); - foreach (var (coll, _) in groups) - { - var array = coll.ToBuffer().buffer; - for (var index = 0; index < array.capacity; index++) - { - var conn = array[index]; - if (conn.clusterId == cid) - bodies.Add(conn.machineRigidBodyId); - } - } - - return bodies.Select(id => new SimBody(id, cid)).ToArray(); - } - - public EGID? FindBlockEGID(uint id) - { - var groups = entitiesDB.FindGroups(); - foreach (ExclusiveGroupStruct group in groups) - { - if (entitiesDB.Exists(id, group)) - return new EGID(id, group); - } - - return null; - } - - public Cluster GetCluster(uint sbid) - { - var groups = entitiesDB.QueryEntities(); - foreach (var (coll, _) in groups) - { - var array = coll.ToBuffer().buffer; - for (var index = 0; index < array.capacity; index++) - { - var conn = array[index]; - //Static blocks don't have a cluster ID but the cluster destruction manager should have one - if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue) - return new Cluster(conn.clusterId); - } - } - - return null; - } - - public Block[] GetBodyBlocks(uint sbid) - { - var groups = entitiesDB.QueryEntities(); - var set = new HashSet(); - foreach (var (coll, _) in groups) - { - var array = coll.ToBuffer().buffer; - for (var index = 0; index < array.capacity; index++) - { - var conn = array[index]; - if (conn.machineRigidBodyId == sbid) - set.Add(new Block(conn.ID)); - } - } - - return set.ToArray(); - } - -#if DEBUG - public EntitiesDB GetEntitiesDB() - { - return entitiesDB; - } -#endif - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockEngineInit.cs b/GamecraftModdingAPI/Blocks/BlockEngineInit.cs deleted file mode 100644 index 1bf6c15..0000000 --- a/GamecraftModdingAPI/Blocks/BlockEngineInit.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Linq.Expressions; - -using Svelto.DataStructures; -using Svelto.ECS; -using Svelto.ECS.Internal; - -namespace GamecraftModdingAPI.Blocks -{ - public partial class BlockEngine - { - /// - /// Holds information needed to construct a component initializer - /// - internal struct BlockInitData - { - public FasterDictionary Group; - } - - internal delegate FasterDictionary GetInitGroup( - EntityComponentInitializer initializer); - - /// - /// Accesses the group field of the initializer - /// - internal GetInitGroup InitGroup = CreateAccessor("_group"); - - //https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection - internal static TDelegate CreateAccessor(string memberName) where TDelegate : Delegate - { - var invokeMethod = typeof(TDelegate).GetMethod("Invoke"); - if (invokeMethod == null) - throw new InvalidOperationException($"{typeof(TDelegate)} signature could not be determined."); - - var delegateParameters = invokeMethod.GetParameters(); - if (delegateParameters.Length != 1) - throw new InvalidOperationException("Delegate must have a single parameter."); - - var paramType = delegateParameters[0].ParameterType; - - var objParam = Expression.Parameter(paramType, "obj"); - var memberExpr = Expression.PropertyOrField(objParam, memberName); - Expression returnExpr = memberExpr; - if (invokeMethod.ReturnType != memberExpr.Type) - returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType); - - var lambda = - Expression.Lambda(returnExpr, $"Access{paramType.Name}_{memberName}", new[] {objParam}); - return lambda.Compile(); - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockIDs.cs b/GamecraftModdingAPI/Blocks/BlockIDs.cs deleted file mode 100644 index 1d55b29..0000000 --- a/GamecraftModdingAPI/Blocks/BlockIDs.cs +++ /dev/null @@ -1,356 +0,0 @@ -namespace GamecraftModdingAPI.Blocks -{ - /// - /// Possible block types - /// - public enum BlockIDs : ushort - { - /// - /// Called "nothing" in Gamecraft. (DBID.NOTHING) - /// - Invalid = ushort.MaxValue, - AluminiumCube = 0, - AxleS, - HingeS = 3, - MotorS, - HingeM, - MotorM, - TyreM, - AxleM, - IronCube, - RubberCube, - OiledCube, - AluminiumConeSegment, //12 - AluminiumCorner, - AluminiumRoundedCorner, - AluminiumSlicedCube, - AluminiumRoundedSlicedCube, - AluminiumCylinder, - AluminiumPyramidSegment, - AluminiumSlope, - AluminiumRoundedSlope, - AluminiumSphere, - RubberConeSegment, //22 - RubberCorner, - RubberRoundedCorner, - RubberSlicedCube, - RubberRoundedSlicedCube, - RubberCylinder, - RubberPyramidSegment, - RubberSlope, - RubberRoundedSlope, - RubberSphere, - OiledConeSegment, //32 - OiledCorner, - OiledRoundedCorner, - OiledSlicedCube, - OiledRoundedSlicedCube, - OiledCylinder, - OiledPyramidSegment, - OiledSlope, - OiledRoundedSlope, - OiledSphere, - IronConeSegment, //42 - IronCorner, - IronRoundedCorner, - IronSlicedCube, - IronRoundedSlicedCube, - IronCylinder, - IronPyramidSegment, - IronSlope, - IronRoundedSlope, - IronSphere, - GlassCube, //52 - GlassSlicedCube, - GlassSlope, - GlassCorner, - GlassPyramidSegment, - GlassRoundedSlicedCube, - GlassRoundedSlope, - GlassRoundedCorner, - GlassConeSegment, - GlassCylinder, - GlassSphere, - Lever, //63 - two IDs skipped - PlayerSpawn = 66, //Crashes without special handling - SmallSpawn, - MediumSpawn, - LargeSpawn, - BallJoint, - UniversalJoint, - ServoAxle, - ServoHinge, - StepperAxle, - StepperHinge, - TelescopicJoint, - DampedSpring, - ServoPiston, - StepperPiston, - PneumaticPiston, - PneumaticHinge, - PneumaticAxle, //82 - PilotSeat = 90, //Might crash - PassengerSeat, - PilotControls, - GrassCube, - DirtCube, - GrassConeSegment, - GrassCorner, - GrassRoundedCorner, - GrassSlicedCube, - GrassRoundedSlicedCube, - GrassPyramidSegment, - GrassSlope, - GrassRoundedSlope, - DirtConeSegment, - DirtCorner, - DirtRoundedCorner, - DirtSlicedCube, - DirtRoundedSlicedCube, - DirtPyramidSegment, - DirtSlope, - DirtRoundedSlope, - RubberHemisphere, - AluminiumHemisphere, - GrassInnerCornerBulged, - DirtInnerCornerBulged, - IronHemisphere, - OiledHemisphere, - GlassHemisphere, - TyreS, - ThreeWaySwitch, - Dial, //120 - CharacterOnEnterTrigger, //Probably crashes - CharacterOnLeaveTrigger, - CharacterOnStayTrigger, - ObjectOnEnterTrigger, - ObjectOnLeaveTrigger, - ObjectOnStayTrigger, - Button, - Switch, - TextBlock, //Brings up a screen - ConsoleBlock, //Brings up a screen - Door, - GlassDoor, - PoweredDoor, - PoweredGlassDoor, - AluminiumTubeCorner, - IronTubeCorner, - WoodCube, - WoodSlicedCube, - WoodSlope, - WoodCorner, - WoodPyramidSegment, - WoodConeSegment, - WoodRoundedSlicedCube, - WoodRoundedSlope, - WoodRoundedCorner, - WoodCylinder, - WoodHemisphere, - WoodSphere, - BrickCube, //149 - BrickSlicedCube = 151, - BrickSlope, - BrickCorner, - ConcreteCube, - ConcreteSlicedCube, - ConcreteSlope, - ConcreteCorner, - RoadCarTyre, - OffRoadCarTyre, - RacingCarTyre, - BicycleTyre, - FrontBikeTyre, - RearBikeTyre, - ChopperBikeTyre, - TractorTyre, - MonsterTruckTyre, - MotocrossBikeTyre, - CartTyre, //168 - ObjectIdentifier, - ANDLogicBlock, - NANDLogicBlock, - NORLogicBlock, - NOTLogicBlock, - ORLogicBlock, - XNORLogicBlock, - XORLogicBlock, - AbsoluteMathsBlock, - AdderMathsBlock, - DividerMathsBlock, - SignMathsBlock, //180 - MaxMathsBlock, - MinMathsBlock, - MultiplierMathsBlock, - SubtractorMathsBlock, - SimpleConnector, - MeanMathsBlock, - Bit, - Counter, - Timer, - ObjectFilter, - PlayerFilter, - TeamFilter, - Number2Text, //193 - DestructionManager = 260, - ChunkHealthModifier, - ClusterHealthModifier, //262 - BeachTree1 = 200, - BeachTree2, - BeachTree3, - Rock1, - Rock2, - Rock3, - Rock4, - BirchTree1, - BirchTree2, - BirchTree3, - PineTree1, - PineTree2, - PineTree3, - Flower1, - Flower2, - Flower3, - Shrub1, - Shrub2, - Shrub3, - CliffCube, - CliffSlicedCorner, - CliffCornerA, - CliffCornerB, - CliffSlopeA, - CliffSlopeB, - GrassEdge, - GrassEdgeInnerCorner, - GrassEdgeCorner, - GrassEdgeSlope, - CentreHUD, - ObjectiveHUD, - GameStatsHUD, //231 - GameOverBlock, - SFXBlockGameplay = 240, - SFXBlock8Bit, - SFXBlockInstrument, - SFXBlockSciFi, - SFXBlockLoops, - SFXBlockVocal, - MovementConstrainer, //246 - RotationConstrainer, - AdvancedMovementDampener, - AdvancedRotationDampener, - Mover = 250, - Rotator, - MovementDampener, - RotationDampener, - AdvancedMover, - AdvancedRotator, - MusicBlock, //256 - PlasmaCannonBlock, - QuantumRiflePickup = 300, - QuantumRifleAmmoPickup, - AluminiumSlicedFraction, - AluminiumSlicedSlope, - AluminiumHalfPyramidLeft = 305, - AluminiumHalfPyramidRight, - AluminiumPyramidSliced, - AluminiumTubeCross, - AluminiumTubeT, - AluminiumPlateSquare, - AluminiumPlateCircle, - AluminiumPlateTriangle, //312 - OiledSlicedFraction = 314, - OiledSlicedSlope, - OiledHalfPyramidLeft, - OiledHalfPyramidRight, - OiledPyramidSliced, - GlassSlicedFraction, - GlassSlicedSlope, - GlassHalfPyramidLeft, - GlassHalfPyramidRight, - GlassPyramidSliced, - RubberSlicedFraction, - RubberSlicedSlope, - RubberHalfPyramidLeft, - RubberHalfPyramidRight, - RubberPyramidSliced, - WoodSlicedFraction, - WoodSlicedSlope, //330 - WoodHalfPyramidLeft, - WoodHalfPyramidRight, - WoodPyramidSliced, - HexNetSlicedFraction, - HexNetSlicedSlope, - HexNetHalfPyramidLeft, - HexNetHalfPyramidRight, - HexNetPyramidSliced, - OiledTubeCross, - OiledTubeT, //340 - GlassTubeCross, - GlassTubeT, - RubberTubeCross, - RubberTubeT, - WoodTubeCross, - WoodTubeT, - HexNetTubeCross, - HexNetTubeT, - BouncyCube, - BouncySlicedCube, //350 - BouncySlope, - BouncyCorner, - OiledTubeCorner, - GlassTubeCorner, - RubberTubeCorner, - WoodTubeCorner, - Basketball, - BowlingBall, - SoccerBall, - GolfBall, //360 - HockeyPuck, - PoolBall, - BouncyBall, - TennisBall, - UnlitCube, - IronSlicedFraction, - IronSlicedSlope, - IronHalfPyramidLeft, - IronHalfPyramidRight, - IronPyramidSliced, //370 - IronTubeCross, - IronTubeT, - SFXBlockMob = 374, - PointLight, - SpotLight, - SunLight, - AmbientLight, - UnlitGlowCube = 381, - PointLightInvisible, - SpotLightInvisible, - UnlitSlope, - UnlitGlowSlope, - Fog, - Sky, - MagmaRockCube = 777, - MagmaRockCubeSliced, - MagmaRockSlope, - MagmaRockCorner, - MagmaRockPyramidSegment, - MagmaRockConeSegment, - MagmaRockSlicedRounded, - MagmaRockSlopeRounded, - MagmaRockCornerRounded, - HexNetCube, - HexNetCubeSliced, - HexNetSlope, - HexNetCorner, - HexNetPyramidSegment, - HexNetConeSegment, - HexNetSlicedRounded, - HexNetSlopeRounded, - HexNetCornerRounded, //794 - MagmaRockBulgedInner, - HexNetCylinder = 797, - HexNetHemisphere, - HexNetSphere, - HexNetTubeCorner //800 - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockIdentifiers.cs b/GamecraftModdingAPI/Blocks/BlockIdentifiers.cs deleted file mode 100644 index 0c3222b..0000000 --- a/GamecraftModdingAPI/Blocks/BlockIdentifiers.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Svelto.ECS; -using RobocraftX.Common; - -using HarmonyLib; - -namespace GamecraftModdingAPI.Blocks -{ - /// - /// ExclusiveGroups and IDs used with blocks - /// - public static class BlockIdentifiers - { - /// - /// Blocks placed by the player - /// - TODO - //public static ExclusiveGroup OWNED_BLOCKS { get { return CommonExclusiveGroups.REAL_BLOCKS_GROUPS_DON_T_USE_IN_NEW_CODE; } } - - /// - /// Extra parts used in functional blocks - /// - public static ExclusiveGroup FUNCTIONAL_BLOCK_PARTS { get { return CommonExclusiveGroups.FUNCTIONAL_BLOCK_PART_GROUP; } } - - /// - /// Blocks which are disabled in Simulation mode - /// - public static ExclusiveGroup SIM_BLOCKS_DISABLED { get { return CommonExclusiveGroups.DISABLED_JOINTS_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; } } - - /// - /// The ID of the most recently placed block - /// - public static uint LatestBlockID { - get - { //Need the private field as the property increments itself - return ((uint) AccessTools.Field(typeof(CommonExclusiveGroups), "_nextBlockEntityID").GetValue(null)) - 1; - } - } - } -} diff --git a/GamecraftModdingAPI/Blocks/BlockTests.cs b/GamecraftModdingAPI/Blocks/BlockTests.cs deleted file mode 100644 index 5447f6c..0000000 --- a/GamecraftModdingAPI/Blocks/BlockTests.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; - -using Gamecraft.Wires; - -using GamecraftModdingAPI; -using GamecraftModdingAPI.Tests; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ -#if TEST - /// - /// Block test cases. Not accessible in release versions. - /// - [APITestClass] - public static class BlockTests - { - [APITestCase(TestType.EditMode)] - public static void TestPlaceNew() - { - Block newBlock = Block.PlaceNew(BlockIDs.AluminiumCube, Unity.Mathematics.float3.zero); - Assert.NotNull(newBlock.Id, "Newly placed block is missing Id. This should be populated when the block is placed.", "Newly placed block Id is not null, block successfully placed."); - } - - [APITestCase(TestType.EditMode)] - public static void TestInitProperty() - { - Block newBlock = Block.PlaceNew(BlockIDs.AluminiumCube, Unity.Mathematics.float3.zero + 2); - if (!Assert.CloseTo(newBlock.Position, (Unity.Mathematics.float3.zero + 2), $"Newly placed block at {newBlock.Position} is expected at {Unity.Mathematics.float3.zero + 2}.", "Newly placed block position matches.")) return; - //Assert.Equal(newBlock.Exists, true, "Newly placed block does not exist, possibly because Sync() skipped/missed/failed.", "Newly placed block exists, Sync() successful."); - } - - [APITestCase(TestType.EditMode)] - public static void TestTextBlock() - { - TextBlock textBlock = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler - Assert.Errorless(() => { textBlock = Block.PlaceNew(BlockIDs.TextBlock, Unity.Mathematics.float3.zero + 1); }, "Block.PlaceNew() raised an exception: ", "Block.PlaceNew() completed without issue."); - if (!Assert.NotNull(textBlock, "Block.PlaceNew() returned null, possibly because it failed silently.", "Specialized TextBlock is not null.")) return; - if (!Assert.NotNull(textBlock.Text, "TextBlock.Text is null, possibly because it failed silently.", "TextBlock.Text is not null.")) return; - if (!Assert.NotNull(textBlock.TextBlockId, "TextBlock.TextBlockId is null, possibly because it failed silently.", "TextBlock.TextBlockId is not null.")) return; - } - - [APITestCase(TestType.EditMode)] - public static void TestMotor() - { - Block newBlock = Block.PlaceNew(BlockIDs.MotorS, Unity.Mathematics.float3.zero + 1); - Motor b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler - Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); - if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized Motor is not null.")) return; - if (!Assert.CloseTo(b.Torque, 75f, $"Motor.Torque {b.Torque} does not equal default value, possibly because it failed silently.", "Motor.Torque close enough to default.")) return; - if (!Assert.CloseTo(b.TopSpeed, 30f, $"Motor.TopSpeed {b.TopSpeed} does not equal default value, possibly because it failed silently.", "Motor.Torque is close enough to default.")) return; - if (!Assert.Equal(b.Reverse, false, $"Motor.Reverse {b.Reverse} does not equal default value, possibly because it failed silently.", "Motor.Reverse is default.")) return; - } - - [APITestCase(TestType.EditMode)] - public static void TestPiston() - { - Block newBlock = Block.PlaceNew(BlockIDs.PneumaticPiston, Unity.Mathematics.float3.zero + 1); - Piston b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler - Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); - if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized Piston is not null.")) return; - if (!Assert.CloseTo(b.MaximumExtension, 1.01f, $"Piston.MaximumExtension {b.MaximumExtension} does not equal default value, possibly because it failed silently.", "Piston.MaximumExtension is close enough to default.")) return; - if (!Assert.CloseTo(b.MaximumForce, 750f, $"Piston.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Piston.MaximumForce is close enough to default.")) return; - } - - [APITestCase(TestType.EditMode)] - public static void TestServo() - { - Block newBlock = Block.PlaceNew(BlockIDs.ServoAxle, Unity.Mathematics.float3.zero + 1); - Servo b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler - Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); - if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized Servo is not null.")) return; - if (!Assert.CloseTo(b.MaximumAngle, 180f, $"Servo.MaximumAngle {b.MaximumAngle} does not equal default value, possibly because it failed silently.", "Servo.MaximumAngle is close enough to default.")) return; - if (!Assert.CloseTo(b.MinimumAngle, -180f, $"Servo.MinimumAngle {b.MinimumAngle} does not equal default value, possibly because it failed silently.", "Servo.MinimumAngle is close enough to default.")) return; - if (!Assert.CloseTo(b.MaximumForce, 750f, $"Servo.MaximumForce {b.MaximumForce} does not equal default value, possibly because it failed silently.", "Servo.MaximumForce is close enough to default.")) return; - } - - [APITestCase(TestType.Game)] - public static void TestMusicBlock1() - { - Block newBlock = Block.PlaceNew(BlockIDs.MusicBlock, Unity.Mathematics.float3.zero + 2); - MusicBlock b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler - Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); - if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return; - if (!Assert.CloseTo(b.Volume, 100f, $"MusicBlock.Volume {b.Volume} does not equal default value, possibly because it failed silently.", "MusicBlock.Volume is close enough to default.")) return; - if (!Assert.Equal(b.TrackIndex, 0, $"MusicBlock.TrackIndex {b.TrackIndex} does not equal default value, possibly because it failed silently.", "MusicBlock.TrackIndex is equal to default.")) return; - _musicBlock = b; - } - - private static MusicBlock _musicBlock; - - [APITestCase(TestType.EditMode)] - public static void TestMusicBlock2() - { - //Block newBlock = Block.GetLastPlacedBlock(); - var b = _musicBlock; - if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return; - b.IsPlaying = true; // play sfx - if (!Assert.Equal(b.IsPlaying, true, $"MusicBlock.IsPlaying {b.IsPlaying} does not equal true, possibly because it failed silently.", "MusicBlock.IsPlaying is set properly.")) return; - if (!Assert.Equal(b.ChannelType, ChannelType.None, $"MusicBlock.ChannelType {b.ChannelType} does not equal default value, possibly because it failed silently.", "MusicBlock.ChannelType is equal to default.")) return; - //Assert.Log(b.Track.ToString()); - if (!Assert.Equal(b.Track.ToString(), new Guid("3237ff8f-f5f2-4f84-8144-496ca280f8c0").ToString(), $"MusicBlock.Track {b.Track} does not equal default value, possibly because it failed silently.", "MusicBlock.Track is equal to default.")) return; - } - - [APITestCase(TestType.EditMode)] - public static void TestLogicGate() - { - Block newBlock = Block.PlaceNew(BlockIDs.NOTLogicBlock, Unity.Mathematics.float3.zero + 1); - LogicGate b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler - Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); - if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return; - if (!Assert.Equal(b.InputCount, 1u, $"LogicGate.InputCount {b.InputCount} does not equal default value, possibly because it failed silently.", "LogicGate.InputCount is default.")) return; - if (!Assert.Equal(b.OutputCount, 1u, $"LogicGate.OutputCount {b.OutputCount} does not equal default value, possibly because it failed silently.", "LogicGate.OutputCount is default.")) return; - if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return; - //if (!Assert.Equal(b.PortName(0, true), "Input", $"LogicGate.PortName(0, input:true) {b.PortName(0, true)} does not equal default value, possibly because it failed silently.", "LogicGate.PortName(0, input:true) is close enough to default.")) return; - LogicGate target = null; - if (!Assert.Errorless(() => { target = Block.PlaceNew(BlockIDs.ANDLogicBlock, Unity.Mathematics.float3.zero + 2); })) return; - Wire newWire = null; - if (!Assert.Errorless(() => { newWire = b.Connect(0, target, 0);})) return; - if (!Assert.NotNull(newWire, "SignalingBlock.Connect(...) returned null, possible because it failed silently.", "SignalingBlock.Connect(...) returned a non-null value.")) return; - } - } -#endif -} diff --git a/GamecraftModdingAPI/Blocks/ConsoleBlock.cs b/GamecraftModdingAPI/Blocks/ConsoleBlock.cs deleted file mode 100644 index e696fca..0000000 --- a/GamecraftModdingAPI/Blocks/ConsoleBlock.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; - -using RobocraftX.Blocks; -using RobocraftX.Common; -using Svelto.ECS; -using Unity.Mathematics; - -using GamecraftModdingAPI; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ - public class ConsoleBlock : SignalingBlock - { - public ConsoleBlock(EGID id): base(id) - { - } - - public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.CONSOLE_BLOCK_GROUP)) - { - } - - // custom console block properties - - /// - /// Setting a nonexistent command will crash the game when switching to simulation - /// - public string Command - { - get - { - return BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.commandName); - } - - set - { - BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.commandName.Set(val), - value); - } - } - - public string Arg1 - { - get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg1); - - set - { - BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg1.Set(val), - value); - } - } - - public string Arg2 - { - get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg2); - - set - { - BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg2.Set(val), - value); - } - } - - public string Arg3 - { - get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg3); - - set - { - BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg3.Set(val), - value); - } - } - } -} diff --git a/GamecraftModdingAPI/Blocks/DampedSpring.cs b/GamecraftModdingAPI/Blocks/DampedSpring.cs deleted file mode 100644 index 5a5a936..0000000 --- a/GamecraftModdingAPI/Blocks/DampedSpring.cs +++ /dev/null @@ -1,48 +0,0 @@ -using RobocraftX.Blocks; -using RobocraftX.Common; -using Svelto.ECS; - -namespace GamecraftModdingAPI.Blocks -{ - public class DampedSpring : Block - { - public DampedSpring(EGID id) : base(id) - { - } - - public DampedSpring(uint id) : base(new EGID(id, CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP)) - { - } - - /// - /// The spring's maximum force. This is known as Stiffness in-game - /// - public float MaxForce - { - get => BlockEngine.GetBlockInfo(this, (DampedSpringReadOnlyStruct dsrs) => dsrs.maxForce); - - set => BlockEngine.SetBlockInfo(this, - (ref DampedSpringReadOnlyStruct dsrs, float val) => dsrs.maxForce = val, value); - } - - /// - /// Alias of MaxForce. - /// - public float Stiffness - { - get => MaxForce; - set => MaxForce = value; - } - - /// - /// The spring's maximum damping force. - /// - public float Damping - { - get => BlockEngine.GetBlockInfo(this, (LinearJointForcesReadOnlyStruct ljf) => ljf.dampingForceMagnitude); - - set => BlockEngine.SetBlockInfo(this, - (ref LinearJointForcesReadOnlyStruct ljf, float val) => ljf.dampingForceMagnitude = val, value); - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/LogicGate.cs b/GamecraftModdingAPI/Blocks/LogicGate.cs deleted file mode 100644 index 2ec4cef..0000000 --- a/GamecraftModdingAPI/Blocks/LogicGate.cs +++ /dev/null @@ -1,16 +0,0 @@ -using RobocraftX.Common; -using Svelto.ECS; - -namespace GamecraftModdingAPI.Blocks -{ - public class LogicGate : SignalingBlock - { - public LogicGate(EGID id) : base(id) - { - } - - public LogicGate(uint id) : base(new EGID(id, CommonExclusiveGroups.LOGIC_BLOCK_GROUP)) - { - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/Motor.cs b/GamecraftModdingAPI/Blocks/Motor.cs deleted file mode 100644 index 0a69d27..0000000 --- a/GamecraftModdingAPI/Blocks/Motor.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; - -using RobocraftX.Blocks; -using RobocraftX.Common; -using Svelto.ECS; -using Unity.Mathematics; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ - public class Motor : SignalingBlock - { - public Motor(EGID id) : base(id) - { - } - - public Motor(uint id): base(new EGID(id, CommonExclusiveGroups.MOTOR_BLOCK_GROUP)) - { - } - - // custom motor properties - - /// - /// The motor's maximum rotational velocity. - /// - public float TopSpeed - { - get - { - return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxVelocity); - } - - set - { - BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxVelocity = val, value); - } - } - - /// - /// The motor's maximum rotational force. - /// - public float Torque - { - get - { - return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.maxForce); - } - - set - { - BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, float val) => st.maxForce = val, value); - } - } - - /// - /// The motor's direction. - /// - public bool Reverse - { - get - { - return BlockEngine.GetBlockInfo(this, (MotorReadOnlyStruct st) => st.reverse); - } - - set - { - BlockEngine.SetBlockInfo(this, (ref MotorReadOnlyStruct st, bool val) => st.reverse = val, value); - } - } - } -} diff --git a/GamecraftModdingAPI/Blocks/MovementEngine.cs b/GamecraftModdingAPI/Blocks/MovementEngine.cs deleted file mode 100644 index a4ac0fa..0000000 --- a/GamecraftModdingAPI/Blocks/MovementEngine.cs +++ /dev/null @@ -1,80 +0,0 @@ -using RobocraftX.Common; -using RobocraftX.UECS; -using Svelto.ECS; -using Svelto.ECS.EntityStructs; -using Unity.Transforms; -using Unity.Mathematics; - -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Blocks -{ - /// - /// Engine which executes block movement actions - /// - public class MovementEngine : IApiEngine - { - public string Name { get; } = "GamecraftModdingAPIMovementGameEngine"; - - public EntitiesDB entitiesDB { set; private get; } - - public bool isRemovable => false; - - public bool IsInGame = false; - - public void Dispose() - { - IsInGame = false; - } - - public void Ready() - { - IsInGame = true; - } - - // implementations for Movement static class - - internal float3 MoveBlock(EGID blockID, BlockEngine.BlockInitData data, float3 vector) - { - if (!entitiesDB.Exists(blockID)) - { - if (data.Group == null) return float3.zero; - var init = new EntityComponentInitializer(blockID, data.Group); - init.Init(new PositionEntityStruct {position = vector}); - init.Init(new GridRotationStruct {position = vector}); - init.Init(new LocalTransformEntityStruct {position = vector}); - return vector; - } - ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity(blockID); - ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity(blockID); - ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity(blockID); - ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity(blockID); - // 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 - }); - entitiesDB.QueryEntity(blockID).isProcessed = false; - return posStruct.position; - } - - internal float3 GetPosition(EGID blockID, BlockEngine.BlockInitData data) - { - if (!entitiesDB.Exists(blockID)) - { - if (data.Group == null) return float3.zero; - var init = new EntityComponentInitializer(blockID, data.Group); - return init.Has() ? init.Get().position : float3.zero; - } - ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity(blockID); - return posStruct.position; - } - } -} diff --git a/GamecraftModdingAPI/Blocks/MusicBlock.cs b/GamecraftModdingAPI/Blocks/MusicBlock.cs deleted file mode 100644 index 6b99b6f..0000000 --- a/GamecraftModdingAPI/Blocks/MusicBlock.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; - -using FMOD.Studio; -using FMODUnity; -using Gamecraft.Wires; -using RobocraftX.Common; -using RobocraftX.Blocks; -using Svelto.ECS; -using Unity.Mathematics; - -using GamecraftModdingAPI; -using GamecraftModdingAPI.Tests; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ - public class MusicBlock : SignalingBlock - { - public MusicBlock(EGID id) : base(id) - { - } - - public MusicBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.MUSIC_BLOCK_GROUP)) - { - } - - public byte TrackIndex - { - get - { - return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct st) => st.trackIndx); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref MusicBlockDataEntityStruct msdes, byte val) => msdes.trackIndx = val, value); - } - } - - public Guid Track - { - get - { - return BlockEngine.GetBlockInfo(this, - (MusicBlockDataEntityStruct msdes) => msdes.fmod2DEventPaths.Get(msdes.trackIndx)); - } - - set - { - BlockEngine.SetBlockInfo(this, (ref MusicBlockDataEntityStruct msdes, Guid val) => - { - for (byte i = 0; i < msdes.fmod2DEventPaths.Count(); i++) - { - Guid track = msdes.fmod2DEventPaths.Get(i); - if (track == val) - { - msdes.trackIndx = i; - break; - } - } - }, value); - } - } - - public Guid[] Tracks - { - get - { - return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct msdes) => - { - Guid[] tracks = new Guid[msdes.fmod2DEventPaths.Count()]; - for (byte i = 0; i < tracks.Length; i++) - { - tracks[i] = msdes.fmod2DEventPaths.Get(i); - } - return tracks; - }); - } - } - - public float Volume - { - get - { - return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct msdes) => msdes.tweakableVolume); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref MusicBlockDataEntityStruct msdes, float val) => msdes.tweakableVolume = val, value); - } - } - - public ChannelType ChannelType - { - get - { - //Assert.Log("Block exists: " + Exists); - return BlockEngine.GetBlockInfo(this, - (MusicBlockDataEntityStruct msdes) => (ChannelType) msdes.channelType); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref MusicBlockDataEntityStruct msdes, ChannelType val) => msdes.channelType = (byte) val, value); - } - } - - public bool IsPlaying - { - get - { - return BlockEngine.GetBlockInfo(this, - (MusicBlockDataEntityStruct msdes) => msdes.isPlaying); - } - - set - { - BlockEngine.SetBlockInfo(this, (ref MusicBlockDataEntityStruct msdes, bool val) => - { - if (msdes.isPlaying == val) return; - if (val) - { - // start playing - EventInstance inst = RuntimeManager.CreateInstance(msdes.fmod2DEventPaths.Get(msdes.trackIndx)); - inst.setVolume(msdes.tweakableVolume / 100f); - inst.start(); - msdes.eventHandle = inst.handle; - } - else - { - // stop playing - EventInstance inst = default(EventInstance); - inst.handle = msdes.eventHandle; - inst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT); - inst.release(); - } - msdes.isPlaying = val; - }, value); - } - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/ObjectIdentifier.cs b/GamecraftModdingAPI/Blocks/ObjectIdentifier.cs deleted file mode 100644 index 1233343..0000000 --- a/GamecraftModdingAPI/Blocks/ObjectIdentifier.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Gamecraft.Wires; -using RobocraftX.Common; -using Svelto.ECS; - -namespace GamecraftModdingAPI.Blocks -{ - public class ObjectIdentifier : Block - { - public ObjectIdentifier(EGID id) : base(id) - { - } - - public ObjectIdentifier(uint id) : base(new EGID(id, CommonExclusiveGroups.OBJID_BLOCK_GROUP)) - { - } - - public char Identifier - { - get => (char) BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.objectId + 'A'); - set - { - BlockEngine.SetBlockInfo(this, (ref ObjectIdEntityStruct st, char val) => - { - st.objectId = (byte) (val - 'A'); - Label = val + ""; //The label isn't updated automatically - }, value); - } - } - - /// - /// Simulation-time ID. Assigned by the game starting from 0. - /// - public byte SimID - { - get => BlockEngine.GetBlockInfo(this, (ObjectIdEntityStruct st) => st.simObjectId); - } - - /// - /// Finds the identifier blocks with the given ID. - /// - /// The ID to look for - /// An array that may be empty - public static ObjectIdentifier[] GetByID(char id) => BlockEngine.GetObjectIDsFromID((byte) (id - 'A'), false); - - /// - /// Finds the identifier blocks with the given simulation-time ID. This ID is assigned by the game starting from 0. - /// - /// - /// - public static ObjectIdentifier[] GetBySimID(byte id) => BlockEngine.GetObjectIDsFromID(id, true); - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/Piston.cs b/GamecraftModdingAPI/Blocks/Piston.cs deleted file mode 100644 index 04b3aeb..0000000 --- a/GamecraftModdingAPI/Blocks/Piston.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; - -using RobocraftX.Blocks; -using Svelto.ECS; -using Unity.Mathematics; - -using GamecraftModdingAPI.Utility; -using RobocraftX.Common; - -namespace GamecraftModdingAPI.Blocks -{ - public class Piston : SignalingBlock - { - public Piston(EGID id) : base(id) - { - } - - public Piston(uint id) : base(new EGID(id, CommonExclusiveGroups.PISTON_BLOCK_GROUP)) - { - } - - // custom piston properties - - /// - /// The piston's max extension distance. - /// - public float MaximumExtension - { - get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxDeviation); - - set - { - BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxDeviation = val, - value); - } - } - - /// - /// The piston's max extension force. - /// - public float MaximumForce - { - get => BlockEngine.GetBlockInfo(this, (PistonReadOnlyStruct st) => st.maxForce); - - set - { - BlockEngine.SetBlockInfo(this, (ref PistonReadOnlyStruct st, float val) => st.maxForce = val, value); - } - } - } -} diff --git a/GamecraftModdingAPI/Blocks/PlacementEngine.cs b/GamecraftModdingAPI/Blocks/PlacementEngine.cs deleted file mode 100644 index 5357e7f..0000000 --- a/GamecraftModdingAPI/Blocks/PlacementEngine.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Reflection; - -using DataLoader; -using HarmonyLib; -using RobocraftX.Blocks; -using RobocraftX.Blocks.Scaling; -using RobocraftX.Character; -using RobocraftX.Common; -using RobocraftX.CR.MachineEditing; -using Svelto.ECS; -using Svelto.ECS.EntityStructs; -using Unity.Mathematics; -using UnityEngine; - -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Players; -using RobocraftX.Rendering.GPUI; - -namespace GamecraftModdingAPI.Blocks -{ - /// - /// Engine which executes block placement actions - /// - public class PlacementEngine : IApiEngine - { - public bool IsInGame; - - public void Dispose() - { - IsInGame = false; - } - - public void Ready() - { - IsInGame = true; - } - - public EntitiesDB entitiesDB { get; set; } - private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceBlockEngine - - public EGID PlaceBlock(BlockIDs block, BlockColors color, byte darkness, float3 position, int uscale, - float3 scale, Player player, float3 rotation, out EntityComponentInitializer initializer) - { //It appears that only the non-uniform scale has any visible effect, but if that's not given here it will be set to the uniform one - if (darkness > 9) - throw new Exception("That is too dark. Make sure to use 0-9 as darkness. (0 is default.)"); - initializer = BuildBlock((ushort) block, (byte) (color + darkness * 10), position, uscale, scale, rotation, - (player ?? new Player(PlayerType.Local)).Id); - return initializer.EGID; - } - - private EntityComponentInitializer BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId) - { - if (_blockEntityFactory == null) - throw new BlockException("The factory is null."); - if (uscale < 1) - throw new BlockException("Scale needs to be at least 1"); - if (scale.x < 4e-5) scale.x = uscale; - if (scale.y < 4e-5) scale.y = uscale; - if (scale.z < 4e-5) scale.z = uscale; - uint dbid = block; - if (!PrefabsID.HasPrefabRegistered(dbid, 0)) - throw new BlockException("Block with ID " + dbid + " not found!"); - //RobocraftX.CR.MachineEditing.PlaceBlockEngine - ScalingEntityStruct scaling = new ScalingEntityStruct {scale = scale}; - Quaternion rotQ = Quaternion.Euler(rot); - RotationEntityStruct rotation = new RotationEntityStruct {rotation = rotQ}; - GridRotationStruct gridRotation = new GridRotationStruct - {position = position, rotation = rotQ}; - DBEntityStruct dbEntity = new DBEntityStruct {DBID = dbid}; - BlockPlacementScaleEntityStruct placementScale = new BlockPlacementScaleEntityStruct - { - blockPlacementHeight = uscale, blockPlacementWidth = uscale, desiredScaleFactor = uscale - }; - EquippedColourStruct colour = new EquippedColourStruct {indexInPalette = color}; - - EntityComponentInitializer - structInitializer = - _blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, dbid); //The ghost block index is only used for triggers - if (colour.indexInPalette != byte.MaxValue) - structInitializer.Init(new ColourParameterEntityStruct - { - indexInPalette = colour.indexInPalette, - hasNetworkChange = true - }); - uint prefabId = PrefabsID.GetPrefabId(dbid, 0); - structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId)); - structInitializer.Init(new PhysicsPrefabEntityStruct(prefabId)); - structInitializer.Init(dbEntity); - structInitializer.Init(new PositionEntityStruct {position = position}); - structInitializer.Init(rotation); - structInitializer.Init(scaling); - structInitializer.Init(gridRotation); - structInitializer.Init(new UniformBlockScaleEntityStruct - { - scaleFactor = placementScale.desiredScaleFactor - }); - structInitializer.Init(new BlockPlacementInfoStruct() - { - loadedFromDisk = false, - placedBy = playerId - }); - PrimaryRotationUtility.InitialisePrimaryDirection(rotation.rotation, ref structInitializer); - EGID playerEGID = new EGID(playerId, CharacterExclusiveGroups.OnFootGroup); - ref PickedBlockExtraDataStruct pickedBlock = ref entitiesDB.QueryEntity(playerEGID); - pickedBlock.placedBlockEntityID = structInitializer.EGID; - pickedBlock.placedBlockWasAPickedBlock = false; - return structInitializer; - } - - public string Name { get; } = "GamecraftModdingAPIPlacementGameEngine"; - - public bool isRemovable => false; - - [HarmonyPatch] - public class FactoryObtainerPatch - { - static void Postfix(BlockEntityFactory blockEntityFactory) - { - _blockEntityFactory = blockEntityFactory; - Logging.MetaDebugLog("Block entity factory injected."); - } - - static MethodBase TargetMethod(Harmony instance) - { - return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlockEngine").GetConstructors()[0]; - } - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/RotationEngine.cs b/GamecraftModdingAPI/Blocks/RotationEngine.cs deleted file mode 100644 index ca97874..0000000 --- a/GamecraftModdingAPI/Blocks/RotationEngine.cs +++ /dev/null @@ -1,90 +0,0 @@ -using RobocraftX.Common; -using RobocraftX.UECS; -using Svelto.ECS; -using Svelto.ECS.EntityStructs; -using Unity.Mathematics; -using UnityEngine; - -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Blocks -{ - /// - /// Engine which executes block movement actions - /// - public class RotationEngine : IApiEngine - { - public string Name { get; } = "GamecraftModdingAPIRotationGameEngine"; - - public EntitiesDB entitiesDB { set; private get; } - - public bool isRemovable => false; - - public bool IsInGame = false; - - public void Dispose() - { - IsInGame = false; - } - - public void Ready() - { - IsInGame = true; - } - - // implementations for Rotation static class - - internal float3 RotateBlock(EGID blockID, BlockEngine.BlockInitData data, Vector3 vector) - { - if (!entitiesDB.Exists(blockID)) - { - if (data.Group == null) return float3.zero; - var init = new EntityComponentInitializer(blockID, data.Group); - init.Init(new RotationEntityStruct {rotation = new Quaternion {eulerAngles = vector}}); - init.Init(new GridRotationStruct {rotation = new Quaternion {eulerAngles = vector}}); - init.Init(new LocalTransformEntityStruct {rotation = new Quaternion {eulerAngles = vector}}); - return vector; - } - ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity(blockID); - ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntity(blockID); - ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntity(blockID); - ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntity(blockID); - // main (persistent) position - Quaternion newRotation = rotStruct.rotation; - newRotation.eulerAngles = vector; - rotStruct.rotation = newRotation; - // placement grid rotation - Quaternion newGridRotation = gridStruct.rotation; - newGridRotation.eulerAngles = vector; - gridStruct.rotation = newGridRotation; - // rendered position - Quaternion newTransRotation = rotStruct.rotation; - newTransRotation.eulerAngles = vector; - transStruct.rotation = newTransRotation; - // collision position - FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Unity.Transforms.Rotation - { - Value = rotStruct.rotation - }); - entitiesDB.QueryEntity(blockID).isProcessed = false; - return ((Quaternion)rotStruct.rotation).eulerAngles; - - } - - internal float3 GetRotation(EGID blockID, BlockEngine.BlockInitData data) - { - if (!entitiesDB.Exists(blockID)) - { - if (data.Group == null) return float3.zero; - var init = new EntityComponentInitializer(blockID, data.Group); - return init.Has() - ? (float3) ((Quaternion) init.Get().rotation).eulerAngles - : float3.zero; - } - - ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntity(blockID); - return ((Quaternion) rotStruct.rotation).eulerAngles; - } - } -} diff --git a/GamecraftModdingAPI/Blocks/Servo.cs b/GamecraftModdingAPI/Blocks/Servo.cs deleted file mode 100644 index 606a48a..0000000 --- a/GamecraftModdingAPI/Blocks/Servo.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; - -using RobocraftX.Blocks; -using RobocraftX.Common; -using Svelto.ECS; -using Unity.Mathematics; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ - public class Servo : SignalingBlock - { - public Servo(EGID id) : base(id) - { - } - - public Servo(uint id) : base(new EGID(id, CommonExclusiveGroups.SERVO_BLOCK_GROUP)) - { - } - - // custom servo properties - - /// - /// The servo's minimum angle. - /// - public float MinimumAngle - { - get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.minDeviation); - - set - { - BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.minDeviation = val, value); - } - } - - /// - /// The servo's maximum angle. - /// - public float MaximumAngle - { - get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxDeviation); - - set - { - BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxDeviation = val, value); - } - } - - /// - /// The servo's maximum force. - /// - public float MaximumForce - { - get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.maxForce); - - set - { - BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, float val) => st.maxForce = val, value); - } - } - - /// - /// The servo's direction. - /// - public bool Reverse - { - get => BlockEngine.GetBlockInfo(this, (ServoReadOnlyStruct st) => st.reverse); - - set - { - BlockEngine.SetBlockInfo(this, (ref ServoReadOnlyStruct st, bool val) => st.reverse = val, value); - } - } - } -} diff --git a/GamecraftModdingAPI/Blocks/SfxBlock.cs b/GamecraftModdingAPI/Blocks/SfxBlock.cs deleted file mode 100644 index 88d69d2..0000000 --- a/GamecraftModdingAPI/Blocks/SfxBlock.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using FMOD.Studio; -using FMODUnity; -using Gamecraft.Wires; -using RobocraftX.Blocks; -using RobocraftX.Common; -using Svelto.ECS; - -namespace GamecraftModdingAPI.Blocks -{ - public class SfxBlock : SignalingBlock - { - public SfxBlock(EGID id) : base(id) - { - } - - public SfxBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.SIMPLESFX_BLOCK_GROUP /* This could also be BUILD_LOOPEDSFX_BLOCK_GROUP */)) - { - } - - public float Volume - { - get - { - return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.tweakableVolume); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref SoundSfxBlockDataEntityStruct obj, float val) => obj.tweakableVolume = val, value); - } - } - - public float Pitch - { - get - { - return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.tweakablePitch); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref SoundSfxBlockDataEntityStruct obj, float val) => obj.tweakablePitch = val, value); - } - } - - public bool Is3D - { - get - { - return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.is3D); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref SoundSfxBlockDataEntityStruct obj, bool val) => obj.is3D = val, value); - } - } - - public ChannelType ChannelType - { - get - { - return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => (ChannelType)obj.channelType); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref SoundSfxBlockDataEntityStruct obj, ChannelType val) => obj.tweakableVolume = (byte) val, value); - } - } - - public byte TrackIndex - { - get - { - return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.soundEffectIndex); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref SoundSfxBlockDataEntityStruct obj, byte val) => obj.soundEffectIndex = val, value); - } - } - - // track - public Guid Track - { - get - { - return BlockEngine.GetBlockInfo(this, - (SoundSfxBlockDataEntityStruct obj) => obj.is3D ? obj.fmod3DEventPaths.Get(obj.soundEffectIndex) : obj.fmod2DEventPaths.Get(obj.soundEffectIndex)); - } - - set - { - BlockEngine.SetBlockInfo(this, (ref SoundSfxBlockDataEntityStruct obj, Guid val) => - { - for (byte i = 0; i < obj.fmod2DEventPaths.Count(); i++) - { - Guid track = obj.fmod2DEventPaths.Get(i); - if (track == val) - { - obj.soundEffectIndex = i; - obj.is3D = false; - return; - } - } - for (byte i = 0; i < obj.fmod3DEventPaths.Count(); i++) - { - Guid track = obj.fmod3DEventPaths.Get(i); - if (track == val) - { - obj.soundEffectIndex = i; - obj.is3D = true; - return; - } - } - }, value); - } - } - - // all tracks - public Guid[] Tracks2D - { - get - { - return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => - { - Guid[] tracks = new Guid[obj.fmod2DEventPaths.Count()]; - for (byte i = 0; i < tracks.Length; i++) - { - tracks[i] = obj.fmod2DEventPaths.Get(i); - } - return tracks; - }); - } - } - - public Guid[] Tracks3D - { - get - { - return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => - { - Guid[] tracks = new Guid[obj.fmod3DEventPaths.Count()]; - for (byte i = 0; i < tracks.Length; i++) - { - tracks[i] = obj.fmod2DEventPaths.Get(i); - } - return tracks; - }); - } - } - - public bool IsLooped - { - get - { - return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.isLoopedBlock); - } - - set - { - BlockEngine.SetBlockInfo(this, - (ref SoundSfxBlockDataEntityStruct obj, bool val) => obj.isLoopedBlock = val, value); - } - } - - public bool IsPlaying - { - get - { - return BlockEngine.GetBlockInfo(this, - (SoundSfxBlockDataEntityStruct obj) => obj.isPlaying); - } - - set - { - BlockEngine.SetBlockInfo(this, (ref SoundSfxBlockDataEntityStruct obj, bool val) => - { - if (obj.isPlaying == val) return; - if (val) - { - // start playing - EventInstance inst = RuntimeManager.CreateInstance(obj.is3D ? obj.fmod3DEventPaths.Get(obj.soundEffectIndex) : obj.fmod2DEventPaths.Get(obj.soundEffectIndex)); - inst.setVolume(obj.tweakableVolume / 100f); - inst.start(); - obj.eventHandle = inst.handle; - } - else - { - // stop playing - EventInstance inst = default(EventInstance); - inst.handle = obj.eventHandle; - inst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT); - inst.release(); - } - obj.isPlaying = val; - }, value); - } - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/SpawnPoint.cs b/GamecraftModdingAPI/Blocks/SpawnPoint.cs deleted file mode 100644 index 17bffd9..0000000 --- a/GamecraftModdingAPI/Blocks/SpawnPoint.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; - -using RobocraftX.Blocks; -using RobocraftX.Common; -using Gamecraft.CharacterVulnerability; -using Svelto.ECS; -using Unity.Mathematics; - -using GamecraftModdingAPI; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ - public class SpawnPoint : Block - { - public SpawnPoint(EGID id) : base(id) - { - } - - public SpawnPoint(uint id) : base(new EGID(id, CommonExclusiveGroups.SPAWNPOINT_BLOCK_GROUP)) - { - } - - // custom spawn point properties - - /// - /// The lives the player spawns in with. - /// - public uint Lives - { - get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.lives); - - set - { - BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, uint val) => st.lives = val, value); - } - } - - /// - /// Whether the spawned player can take damage. - /// - public bool Damageable - { - get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.canTakeDamage); - - set - { - BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.canTakeDamage = val, value); - } - } - - /// - /// Whether the game over screen will be displayed - /// - public bool GameOverEnabled - { - get => BlockEngine.GetBlockInfo(this, (SpawnPointStatsEntityStruct st) => st.gameOverScreen); - - set - { - BlockEngine.SetBlockInfo(this, (ref SpawnPointStatsEntityStruct st, bool val) => st.gameOverScreen = val, value); - } - } - - /// - /// The team id for players who spawn here. - /// - public byte Team - { - get => BlockEngine.GetBlockInfo(this, (SpawnPointIdsEntityStruct st) => st.teamId); - - set - { - BlockEngine.SetBlockInfo(this, (ref SpawnPointIdsEntityStruct st, byte val) => st.teamId = val, value); - } - } - } -} diff --git a/GamecraftModdingAPI/Blocks/TextBlock.cs b/GamecraftModdingAPI/Blocks/TextBlock.cs deleted file mode 100644 index 6fa5487..0000000 --- a/GamecraftModdingAPI/Blocks/TextBlock.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; - -using Gamecraft.Blocks.GUI; -using RobocraftX.Common; -using Svelto.ECS; -using Unity.Mathematics; - -using GamecraftModdingAPI; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ - public class TextBlock : SignalingBlock - { - public TextBlock(EGID id) : base(id) - { - } - - public TextBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.TEXT_BLOCK_GROUP)) - { - } - - // custom text block properties - - /// - /// The text block's current text. - /// - public string Text - { - get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textCurrent); - - set - { - if (value == null) value = ""; - BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) => - { - if (val == null) val = ""; - tbds.textCurrent.Set(val); - tbds.textStored.Set(val); - }, value); - BlockEngine.SetBlockInfo(this, - (ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockStringContent.Set(val), value); - } - } - - /// - /// The text block's current text block ID (used in ChangeTextBlockCommand). - /// - public string TextBlockId - { - get => BlockEngine.GetBlockInfo(this, (TextBlockDataStruct st) => st.textBlockID); - - set - { - if (value == null) value = ""; - BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) => - tbds.textBlockID.Set(val), value); - BlockEngine.SetBlockInfo(this, - (ref TextBlockNetworkDataStruct st, string val) => st.newTextBlockID.Set(val), value); - } - } - } -} diff --git a/GamecraftModdingAPI/Blocks/Timer.cs b/GamecraftModdingAPI/Blocks/Timer.cs deleted file mode 100644 index 0bbd302..0000000 --- a/GamecraftModdingAPI/Blocks/Timer.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; - -using RobocraftX.Blocks; -using RobocraftX.Common; -using Gamecraft.Blocks.TimerBlock; -using Svelto.ECS; -using Unity.Mathematics; - -using GamecraftModdingAPI; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ - public class Timer : SignalingBlock - { - public Timer(EGID id) : base(id) - { - } - - public Timer(uint id) : base(new EGID(id, CommonExclusiveGroups.TIMER_BLOCK_GROUP)) - { - } - - // custom timer properties - - /// - /// The player-specified start time. - /// - public float Start - { - get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.startTime); - - set - { - BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.startTime = val, - value); - } - } - - /// - /// The player-specified end time. - /// - public float End - { - get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.endTime); - - set - { - BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, float val) => tbds.endTime = val, - value); - } - } - - /// - /// Whether to display time with millisecond precision. - /// - public bool DisplayMilliseconds - { - get => BlockEngine.GetBlockInfo(this, (TimerBlockDataStruct st) => st.outputFormatHasMS); - - set - { - BlockEngine.SetBlockInfo(this, (ref TimerBlockDataStruct tbds, bool val) => tbds.outputFormatHasMS = val, - value); - } - } - - /// - /// Current time (as of the last video frame), in milliseconds. - /// - public int CurrentTime - { - get => BlockEngine.GetBlockInfo(this, (TimerBlockLabelCacheEntityStruct st) => st.timeLastRenderFrameMS); - - set - { - BlockEngine.SetBlockInfo(this, (ref TimerBlockLabelCacheEntityStruct tbds, int val) => tbds.timeLastRenderFrameMS = val, - value); - } - } - } -} diff --git a/GamecraftModdingAPI/Blocks/Wire.cs b/GamecraftModdingAPI/Blocks/Wire.cs deleted file mode 100644 index e58a625..0000000 --- a/GamecraftModdingAPI/Blocks/Wire.cs +++ /dev/null @@ -1,355 +0,0 @@ -using System; - -using Gamecraft.Wires; -using Svelto.ECS; -using Svelto.ECS.Experimental; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Blocks -{ - public class Wire - { - internal static SignalEngine signalEngine; - - protected EGID startPortEGID; - - protected EGID endPortEGID; - - protected EGID startBlockEGID; - - protected EGID endBlockEGID; - - protected EGID wireEGID; - - protected bool inputToOutput; - - protected byte startPort; - - protected byte endPort; - - public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort) - { - WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort); - return new Wire(wire, start, end); - } - - /// - /// An existing wire connection ending at the specified input. - /// If multiple exist, this will return the first one found. - /// - /// Destination block. - /// Port number. - /// The wire, where the end of the wire is the block port specified, or null if does not exist. - public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort) - { - EGID port = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists); - if (!exists) return null; - WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out exists); - if (exists) - { - return new Wire(new Block(wire.sourceBlockEGID), end, wire.sourcePortUsage, endPort); - } - return null; - } - - /// - /// An existing wire connection starting at the specified output. - /// If multiple exist, this will return the first one found. - /// - /// Source block entity ID. - /// Port number. - /// The wire, where the start of the wire is the block port specified, or null if does not exist. - public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort) - { - EGID port = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists); - if (!exists) return null; - WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out exists); - if (exists) - { - return new Wire(start, new Block(wire.destinationBlockEGID), startPort, wire.destinationPortUsage); - } - return null; - } - - /// - /// Construct a wire object from an existing connection. - /// - /// Starting block ID. - /// Ending block ID. - /// Starting port number, or guess if omitted. - /// Ending port number, or guess if omitted. - /// Guessing failed or wire does not exist. - public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue) - { - startBlockEGID = start.Id; - endBlockEGID = end.Id; - // find block ports - WireEntityStruct wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, out bool exists, startPort, endPort); - if (exists) - { - wireEGID = wire.ID; - endPortEGID = signalEngine.MatchBlockInputToPort(end, wire.destinationPortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockOutputToPort(start, wire.sourcePortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - inputToOutput = false; - endPort = wire.destinationPortUsage; - startPort = wire.sourcePortUsage; - } - else - { - // flip I/O around and try again - wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, out exists, endPort, startPort); - if (exists) - { - wireEGID = wire.ID; - endPortEGID = signalEngine.MatchBlockOutputToPort(end, wire.sourcePortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockInputToPort(start, wire.destinationPortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - inputToOutput = true; // end is actually the source - // NB: start and end are handled exactly as they're received as params. - // This makes wire traversal easier, but makes logic in this class a bit more complex - endPort = wire.sourcePortUsage; - startPort = wire.destinationPortUsage; - } - else - { - throw new WireInvalidException("Wire not found"); - } - } - } - - /// - /// Construct a wire object from an existing wire connection. - /// - /// Starting block ID. - /// Ending block ID. - /// Starting port number. - /// Ending port number. - /// The wire ID. - /// Whether the wire direction goes input -> output (true) or output -> input (false, preferred). - public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput) - { - this.startBlockEGID = start.Id; - this.endBlockEGID = end.Id; - this.inputToOutput = inputToOutput; - this.wireEGID = wire; - if (inputToOutput) - { - endPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out bool exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - } - else - { - endPortEGID = signalEngine.MatchBlockInputToPort(end, endPort, out bool exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockOutputToPort(start, startPort, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - } - this.startPort = startPort; - this.endPort = endPort; - } - - /// - /// Construct a wire object from an existing wire connection. - /// - /// The wire ID. - public Wire(EGID wireEgid) - { - this.wireEGID = wireEgid; - WireEntityStruct wire = signalEngine.GetWire(wireEGID); - this.startBlockEGID = wire.sourceBlockEGID; - this.endBlockEGID = wire.destinationBlockEGID; - this.inputToOutput = false; - endPortEGID = signalEngine.MatchBlockInputToPort(wire.destinationBlockEGID, wire.destinationPortUsage, out bool exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockOutputToPort(wire.sourceBlockEGID, wire.sourcePortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - this.endPort = wire.destinationPortUsage; - this.startPort = wire.sourcePortUsage; - } - - internal Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest) - { - this.wireEGID = wire.ID; - this.startBlockEGID = wire.sourceBlockEGID; - this.endBlockEGID = wire.destinationBlockEGID; - inputToOutput = false; - endPortEGID = signalEngine.MatchBlockInputToPort(dest, wire.destinationPortUsage, out bool exists); - if (!exists) throw new WireInvalidException("Wire end port not found"); - startPortEGID = signalEngine.MatchBlockOutputToPort(src, wire.sourcePortUsage, out exists); - if (!exists) throw new WireInvalidException("Wire start port not found"); - this.endPort = wire.destinationPortUsage; - this.startPort = wire.sourcePortUsage; - } - - /// - /// The wire's in-game id. - /// - public EGID Id - { - get => wireEGID; - } - - /// - /// The wire's signal value, as a float. - /// - public float Float - { - get - { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return 0f; - return cds.valueAsFloat; - } - - set - { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return; - cds.valueAsFloat = value; - } - } - - /// - /// The wire's string signal. - /// - public string String - { - get - { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return ""; - return cds.valueAsEcsString; - } - - set - { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return; - cds.valueAsEcsString.Set(value); - } - } - - /// - /// The wire's raw string signal. - /// - public ECSString ECSString - { - get - { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return default; - return cds.valueAsEcsString; - } - - set - { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return; - cds.valueAsEcsString = value; - } - } - - /// - /// The wire's signal id. - /// I'm 50% sure this is useless. - /// - public uint SignalId - { - get - { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return uint.MaxValue; - return cds.valueAsID; - } - - set - { - ref ChannelDataStruct cds = ref signalEngine.GetChannelDataStruct(startPortEGID, out bool exists); - if (!exists) return; - cds.valueAsID = value; - } - } - - /// - /// The block at the beginning of the wire. - /// - public SignalingBlock Start - { - get => new SignalingBlock(startBlockEGID); - } - - /// - /// The port number that the beginning of the wire connects to. - /// - public byte StartPort - { - get => startPort; - } - - /// - /// The block at the end of the wire. - /// - public SignalingBlock End - { - get => new SignalingBlock(endBlockEGID); - } - - /// - /// The port number that the end of the wire connects to. - /// - public byte EndPort - { - get => endPort; - } - - /// - /// Create a copy of the wire object where the direction of the wire is guaranteed to be from a block output to a block input. - /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input). - /// - /// A copy of the wire object. - public Wire OutputToInputCopy() - { - return new Wire(wireEGID); - } - - /// - /// Convert the wire object to the direction the signal flows. - /// Signals on wires always flow from a block output port to a block input port. - /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input). - /// - public void OutputToInputInPlace() - { - if (inputToOutput) - { - inputToOutput = false; - // swap inputs and outputs - EGID temp = endBlockEGID; - endBlockEGID = startBlockEGID; - startBlockEGID = temp; - temp = endPortEGID; - endPortEGID = startPortEGID; - startPortEGID = temp; - byte tempPortNumber = endPort; - endPort = startPort; - startPort = tempPortNumber; - } - } - - public override string ToString() - { - if (signalEngine.Exists(wireEGID)) - { - return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {Start.PortName(StartPort, inputToOutput)}) -> ({End.Type}::{EndPort} aka {End.PortName(EndPort, !inputToOutput)})"; - } - return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})"; - } - - internal static void Init() { } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Commands/CommandPatch.cs b/GamecraftModdingAPI/Commands/CommandPatch.cs deleted file mode 100644 index ab90e6e..0000000 --- a/GamecraftModdingAPI/Commands/CommandPatch.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Reflection; - -using HarmonyLib; -using Svelto.Context; -using Svelto.ECS; -using RobocraftX; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Commands -{ - /// - /// Patch of RobocraftX.GUI.CommandLine.CommandLineCompositionRoot.Compose() - /// - // TODO: fix - [HarmonyPatch] - //[HarmonyPatch(typeof(RobocraftX.GUI.CommandLine.CommandLineCompositionRoot))] - //[HarmonyPatch("Compose")] - //[HarmonyPatch("Compose", new Type[] { typeof(UnityContext), typeof(EnginesRoot), typeof(World), typeof(Action), typeof(MultiplayerInitParameters), typeof(StateSyncRegistrationHelper)})] - static class CommandPatch - { - public static void Postfix(EnginesRoot enginesRoot) - { - // When a game is loaded, register the command engines - CommandManager.RegisterEngines(enginesRoot); - } - - public static MethodBase TargetMethod(Harmony instance) - { - return typeof(RobocraftX.GUI.CommandLine.CommandLineCompositionRoot).GetMethod("Compose").MakeGenericMethod(typeof(object)); - //return func.Method; - } - } -} diff --git a/GamecraftModdingAPI/Commands/ExistingCommands.cs b/GamecraftModdingAPI/Commands/ExistingCommands.cs deleted file mode 100644 index 60bd900..0000000 --- a/GamecraftModdingAPI/Commands/ExistingCommands.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; - -using uREPL; - -namespace GamecraftModdingAPI.Commands -{ - public static class ExistingCommands - { - public static void Call(string commandName) - { - RuntimeCommands.Call(commandName); - } - - public static void Call(string commandName, Arg0 arg0) - { - RuntimeCommands.Call(commandName, arg0); - } - - public static void Call(string commandName, Arg0 arg0, Arg1 arg1) - { - RuntimeCommands.Call(commandName, arg0, arg1); - } - - public static void Call(string commandName, Arg0 arg0, Arg1 arg1, Arg2 arg2) - { - RuntimeCommands.Call(commandName, arg0, arg1, arg2); - } - - public static bool Exists(string commandName) - { - return RuntimeCommands.HasRegistered(commandName); - } - - public static string[] GetCommandNames() - { - var keys = RuntimeCommands.table.Keys; - string[] res = new string[keys.Count]; - keys.CopyTo(res, 0); - return res; - } - } -} diff --git a/GamecraftModdingAPI/Events/DeterministicStepComposeEngineGroupsPatch.cs b/GamecraftModdingAPI/Events/DeterministicStepComposeEngineGroupsPatch.cs deleted file mode 100644 index 9f16955..0000000 --- a/GamecraftModdingAPI/Events/DeterministicStepComposeEngineGroupsPatch.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -using HarmonyLib; -using Svelto.ECS; -using RobocraftX.Common; -using RobocraftX.StateSync; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Patch of RobocraftX.StateSync.DeterministicStepCompositionRoot.ComposeEnginesGroups(...) - /// - //[HarmonyPatch(typeof(DeterministicStepCompositionRoot), "DeterministicCompose")] - [Obsolete] - [HarmonyPatch] - class GameHostTransitionDeterministicGroupEnginePatch - { - - public static readonly GameStateBuildEmitterEngine buildEngine = new GameStateBuildEmitterEngine(); - - public static readonly GameStateSimulationEmitterEngine simEngine = new GameStateSimulationEmitterEngine(); - - public static void Postfix() - { - //stateSyncReg.buildModeInitializationEngines.Add(buildEngine); - //stateSyncReg.simulationModeInitializationEngines.Add(simEngine); - //enginesRoot.AddEngine(buildEngine); - //enginesRoot.AddEngine(simEngine); - buildEngine.EmitIfBuildMode(); - simEngine.EmitIfSimMode(); - } - - [HarmonyTargetMethod] - public static MethodBase TargetMethod(Harmony harmonyInstance) - { - return AccessTools.Method(AccessTools.TypeByName("RobocraftX.StateSync.GameHostTransitionDeterministicGroupEngine"), "EndTransition"); - //.MakeGenericMethod(typeof(CosmeticEnginesSequenceBuildOrder), typeof(CosmeticEnginesSequenceSimOrder), typeof(DeterministicToCosmeticSyncBuildOrder), typeof(DeterministicToCosmeticSyncSimOrder)); - } - } -} diff --git a/GamecraftModdingAPI/Events/EmitterBuilder.cs b/GamecraftModdingAPI/Events/EmitterBuilder.cs deleted file mode 100644 index c6a6879..0000000 --- a/GamecraftModdingAPI/Events/EmitterBuilder.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; - -using Svelto.ECS; - -namespace GamecraftModdingAPI.Events -{ - [Obsolete] - public class EmitterBuilder - { - private string name; - - private int? type; - - /// - /// Create a new event emitter builder. - /// - public EmitterBuilder() - { - } - - /// - /// Create a new event emitter builder. - /// This is equivalent to new EmitterBuilder().Name(name) - /// - /// The emitter name. - public EmitterBuilder(string name) - { - this.name = name; - } - - /// - /// Create and return an event emitter builder. - /// - /// The builder. - public static EmitterBuilder Builder() - { - return new EmitterBuilder(); - } - - /// - /// Create and return an event emitter builder. - /// This is equivalent to Builder().Name(name) - /// - /// The builder. - /// The emitter name. - public static EmitterBuilder Builder(string name) - { - return new EmitterBuilder(name); - } - - /// - /// Name the event emitter. - /// - /// The builder. - /// The event emitter name. - public EmitterBuilder Name(string name) - { - this.name = name; - return this; - } - - /// - /// Set the type of event to handle. - /// - /// The builder. - /// The event type. - public EmitterBuilder Handle(EventType eventType) - { - return Handle((int)eventType); - } - - /// - /// Set the type of event to handle. - /// - /// The builder. - /// The event type. - public EmitterBuilder Handle(int eventType) - { - this.type = eventType; - return this; - } - - /// - /// Build the event emitter. - /// - /// The event emitter. - /// Automatically register the event emitter with EventManager.AddEventemitter(). - public IEventEmitterEngine Build(bool register = true) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new EventParameterMissingException("Event emitter name must be defined before Build() is called"); - } - if (!type.HasValue) - { - throw new EventParameterMissingException("Event emitter event type must be defined before Build() is called"); - } - SimpleEventEmitterEngine result = new SimpleEventEmitterEngine(type.Value, name); - if (register) - { - EventManager.AddEventEmitter(result); - } - return result; - } - } -} diff --git a/GamecraftModdingAPI/Events/EventEngineFactory.cs b/GamecraftModdingAPI/Events/EventEngineFactory.cs deleted file mode 100644 index 7981303..0000000 --- a/GamecraftModdingAPI/Events/EventEngineFactory.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Svelto.ECS; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Convenient factories for mod event engines - /// - [Obsolete] - public static class EventEngineFactory - { - /// - /// Factory method which automatically adds the SimpleEventHandlerEngine to the Manager - /// - /// The name of the engine - /// The type of event to handle - /// The operation to do when the event is created - /// The operation to do when the event is destroyed (if applicable) - /// The created object - public static SimpleEventHandlerEngine CreateAddSimpleHandler(string name, int type, Action onActivated, Action onDestroyed) - { - var engine = new SimpleEventHandlerEngine(onActivated, onDestroyed, type, name); - EventManager.AddEventHandler(engine); - return engine; - } - - /// - /// Factory method which automatically adds the SimpleEventHandlerEngine to the Manager - /// - /// The name of the engine - /// The type of event to handle - /// The operation to do when the event is created - /// The operation to do when the event is destroyed (if applicable) - /// The created object - public static SimpleEventHandlerEngine CreateAddSimpleHandler(string name, int type, Action onActivated, Action onDestroyed) - { - var engine = new SimpleEventHandlerEngine(onActivated, onDestroyed, type, name); - EventManager.AddEventHandler(engine); - return engine; - } - - /// - /// Factory method which automatically adds the SimpleEventEmitterEngine to the Manager - /// - /// The name of the engine - /// The type of event to emit - /// Will removing this engine not break your code? - /// The created object - public static SimpleEventEmitterEngine CreateAddSimpleEmitter(string name, int type, bool isRemovable = true) - { - var engine = new SimpleEventEmitterEngine(type, name, isRemovable); - EventManager.AddEventEmitter(engine); - return engine; - } - } -} diff --git a/GamecraftModdingAPI/Events/EventExceptions.cs b/GamecraftModdingAPI/Events/EventExceptions.cs deleted file mode 100644 index b4458bc..0000000 --- a/GamecraftModdingAPI/Events/EventExceptions.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -namespace GamecraftModdingAPI.Events -{ - public class EventException : GamecraftModdingAPIException - { - public EventException() - { - } - - public EventException(string message) : base(message) - { - } - - public EventException(string message, Exception innerException) : base(message, innerException) - { - } - } - - public class EventNotFoundException : EventException - { - public EventNotFoundException() - { - } - - public EventNotFoundException(string message) : base(message) - { - } - } - - public class EventAlreadyExistsException : EventException - { - public EventAlreadyExistsException() - { - } - - public EventAlreadyExistsException(string message) : base(message) - { - } - } - - public class EventRuntimeException : EventException - { - public EventRuntimeException() - { - } - - public EventRuntimeException(string message) : base(message) - { - } - - public EventRuntimeException(string message, Exception innerException) : base(message, innerException) - { - } - } - - public class EventParameterMissingException : EventException - { - public EventParameterMissingException() - { - } - - public EventParameterMissingException(string message) : base(message) - { - } - } -} diff --git a/GamecraftModdingAPI/Events/EventManager.cs b/GamecraftModdingAPI/Events/EventManager.cs deleted file mode 100644 index f021e9f..0000000 --- a/GamecraftModdingAPI/Events/EventManager.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Svelto.ECS; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Keeps track of event handlers and emitters. - /// This is used to add, remove and get API event handlers and emitters. - /// - [Obsolete("This will be removed in an upcoming update. Use the new C# event architecture from GamecraftModdingAPI.App")] - public static class EventManager - { - private static Dictionary _eventEmitters = new Dictionary(); - - private static Dictionary _eventHandlers = new Dictionary(); - - private static EnginesRoot _lastEngineRoot; - - // event handler management - - public static void AddEventHandler(IEventHandlerEngine engine) - { - if (ExistsEventHandler(engine)) - { - throw new EventAlreadyExistsException($"IEventHandlerEngine {engine.Name} already exists"); - } - _eventHandlers[engine.Name] = engine; - if (_lastEngineRoot != null) - { - Logging.MetaDebugLog($"Registering IEventHandlerEngine {engine.Name}"); - _lastEngineRoot.AddEngine(engine); - } - } - - public static bool ExistsEventHandler(string name) - { - return _eventHandlers.ContainsKey(name); - } - - public static bool ExistsEventHandler(IEventHandlerEngine engine) - { - return ExistsEventHandler(engine.Name); - } - - public static IEventHandlerEngine GetEventHandler(string name) - { - return _eventHandlers[name]; - } - - public static string[] GetEventHandlerNames() - { - return _eventHandlers.Keys.ToArray(); - } - - public static void RemoveEventHandler(string name) - { - _eventHandlers.Remove(name); - } - - // event emitter management - - public static void AddEventEmitter(IEventEmitterEngine engine) - { - if (ExistsEventEmitter(engine)) - { - throw new EventAlreadyExistsException($"IEventEmitterEngine {engine.Name} already exists"); - } - _eventEmitters[engine.Name] = engine; - if (_lastEngineRoot != null) - { - Logging.MetaDebugLog($"Registering IEventEmitterEngine {engine.Name}"); - _lastEngineRoot.AddEngine(engine); - } - } - - public static bool ExistsEventEmitter(string name) - { - return _eventEmitters.ContainsKey(name); - } - - public static bool ExistsEventEmitter(IEventEmitterEngine engine) - { - return ExistsEventEmitter(engine.Name); - } - - public static IEventEmitterEngine GetEventEmitter(string name) - { - return _eventEmitters[name]; - } - - public static string[] GetEventEmitterNames() - { - return _eventEmitters.Keys.ToArray(); - } - - public static void RemoveEventEmitter(string name) - { - if (_eventEmitters[name].isRemovable) - { - _eventEmitters.Remove(name); - } - } - - public static void RegisterEngines(EnginesRoot enginesRoot) - { - _lastEngineRoot = enginesRoot; - // Register handlers before emitters so no events are missed - 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]); - } - } - } -} diff --git a/GamecraftModdingAPI/Events/EventType.cs b/GamecraftModdingAPI/Events/EventType.cs deleted file mode 100644 index 468e214..0000000 --- a/GamecraftModdingAPI/Events/EventType.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Built-in event types. - /// These are configured to fire when the API is initialized. - /// - public enum EventType - { - ApplicationInitialized, - Menu, - MenuSwitchedTo, - Game, - GameReloaded, - GameSwitchedTo, - SimulationSwitchedTo, - BuildSwitchedTo - } -} diff --git a/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs b/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs deleted file mode 100644 index c3a5fb5..0000000 --- a/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -using HarmonyLib; -using RobocraftX.CR.MainGame; -using Svelto.ECS; -using Unity.Entities; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Patch of RobocraftX.FullGameCompositionRoot.ActivateGame() - /// - [Obsolete] - [HarmonyPatch] - class GameActivatedComposePatch - { - public static bool IsGameSwitching = false; - - public static bool IsGameReloading = false; - - public static void Postfix(ref object contextHolder, ref EnginesRoot enginesRoot, World physicsWorld) - { - // register custom game engines - GameEngineManager.RegisterEngines(enginesRoot); - // initialize AsyncUtils - AsyncUtils.Setup(enginesRoot); - // A new EnginesRoot is always created when ActivateGame is called - // so all event emitters and handlers must be re-registered. - EventManager.RegisterEngines(enginesRoot); - Logging.Log("Dispatching Game Activated event"); - EventManager.GetEventEmitter("GamecraftModdingAPIGameActivatedEventEmitter").Emit(); - if (IsGameSwitching) - { - IsGameSwitching = false; - Logging.Log("Dispatching Game Switched To event"); - EventManager.GetEventEmitter("GamecraftModdingAPIGameSwitchedToEventEmitter").Emit(); - } - if (IsGameReloading) - { - IsGameReloading = false; - Logging.Log("Dispatching Game Reloaded event"); - EventManager.GetEventEmitter("GamecraftModdingAPIGameReloadedEventEmitter").Emit(); - } - } - - public static MethodBase TargetMethod() - { - return typeof(MainGameCompositionRoot).GetMethods().First(m => m.Name == "Compose") - .MakeGenericMethod(typeof(object)); - } - } -} diff --git a/GamecraftModdingAPI/Events/GameReloadedPatch.cs b/GamecraftModdingAPI/Events/GameReloadedPatch.cs deleted file mode 100644 index 7228084..0000000 --- a/GamecraftModdingAPI/Events/GameReloadedPatch.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using HarmonyLib; -using RobocraftX; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Patch of RobocraftX.FullGameCompositionRoot.ReloadGame() - /// - [Obsolete] - [HarmonyPatch(typeof(FullGameCompositionRoot), "ReloadGame")] - class GameReloadedPatch - { - public static void Postfix() - { - GameActivatedComposePatch.IsGameReloading = true; - } - } -} diff --git a/GamecraftModdingAPI/Events/GameStateBuildEmitterEngine.cs b/GamecraftModdingAPI/Events/GameStateBuildEmitterEngine.cs deleted file mode 100644 index 725f544..0000000 --- a/GamecraftModdingAPI/Events/GameStateBuildEmitterEngine.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; - -using Unity.Jobs; -using RobocraftX.SimulationModeState; -using RobocraftX.StateSync; -using Svelto.ECS; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Event emitter engine for switching to to build mode. - /// - [Obsolete] - public class GameStateBuildEmitterEngine : IEventEmitterEngine, IUnorderedInitializeOnTimeStoppedModeEntered - { - public string Name { get; } = "GamecraftModdingAPIGameStateBuildEventEmitter" ; - - public EntitiesDB entitiesDB { set; private get; } - - public int type { get; } = (int)EventType.BuildSwitchedTo; - - public bool isRemovable { get; } = false; - - public IEntityFactory Factory { set; private get; } - - public void Dispose() { } - - public void Emit() - { - Logging.Log("Dispatching Build Switched To event"); - if (Factory == null) { return; } - Factory.BuildEntity(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup) - .Init(new ModEventEntityStruct { type = type }); - } - - public void EmitIfBuildMode() - { - //Logging.MetaDebugLog($"nextSimulationMode: {entitiesDB.QueryUniqueEntity(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode}"); - if (entitiesDB.QueryUniqueEntity(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode == SimulationMode.TimeStopped) - { - Emit(); - } - } - - public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps) - { - Emit(); - return inputDeps; - } - - public void Ready() { } - } -} diff --git a/GamecraftModdingAPI/Events/GameStateSimulationEmitterEngine.cs b/GamecraftModdingAPI/Events/GameStateSimulationEmitterEngine.cs deleted file mode 100644 index 6e6e2ce..0000000 --- a/GamecraftModdingAPI/Events/GameStateSimulationEmitterEngine.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -using Unity.Jobs; -using RobocraftX.SimulationModeState; -using RobocraftX.StateSync; -using Svelto.ECS; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Event emitter engine for switching to simulation mode. - /// - [Obsolete] - public class GameStateSimulationEmitterEngine : IEventEmitterEngine, IUnorderedInitializeOnTimeRunningModeEntered - { - public string Name { get; } = "GamecraftModdingAPIGameStateSimulationEventEmitter" ; - - public EntitiesDB entitiesDB { set; private get; } - - public int type { get; } = (int)EventType.SimulationSwitchedTo; - - public bool isRemovable { get; } = false; - - public IEntityFactory Factory { set; private get; } - - public void Dispose() { } - - public void Emit() - { - Logging.Log("Dispatching Simulation Switched To event"); - if (Factory == null) { return; } - Factory.BuildEntity(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup) - .Init(new ModEventEntityStruct { type = type }); - } - - public void EmitIfSimMode() - { - if (entitiesDB.QueryUniqueEntity(SimulationModeStateExclusiveGroups.GAME_STATE_GROUP).nextSimulationMode == SimulationMode.TimeRunning) - { - Emit(); - } - } - - public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps) - { - Emit(); - return inputDeps; - } - - public void Ready() { } - } -} diff --git a/GamecraftModdingAPI/Events/GameSwitchedToPatch.cs b/GamecraftModdingAPI/Events/GameSwitchedToPatch.cs deleted file mode 100644 index dbd63c0..0000000 --- a/GamecraftModdingAPI/Events/GameSwitchedToPatch.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Reflection; - -using HarmonyLib; -using RobocraftX; -using RobocraftX.CR.MainGame; -using Svelto.ECS; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Patch of RobocraftX.FullGameCompositionRoot.ActivateGame() - /// (scheduled for execution during RobocraftX.FullGameCompositionRoot.SwitchToGame()) - /// - [Obsolete] - [HarmonyPatch(typeof(FullGameCompositionRoot), "SwitchToGame")] - class GameSwitchedToPatch - { - public static void Prefix() - { - GameActivatedComposePatch.IsGameSwitching = true; - } - } -} diff --git a/GamecraftModdingAPI/Events/HandlerBuilder.cs b/GamecraftModdingAPI/Events/HandlerBuilder.cs deleted file mode 100644 index 10f3290..0000000 --- a/GamecraftModdingAPI/Events/HandlerBuilder.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; - -using Svelto.ECS; - -namespace GamecraftModdingAPI.Events -{ - [Obsolete] - public class HandlerBuilder - { - private string name; - - private int? type; - - private Action activated; - - private Action destroyed; - - /// - /// Create a new event handler builder. - /// - public HandlerBuilder() - { - } - - /// - /// Create a new event handler builder. - /// This is equivalent to new HandlerBuilder().Name(name) - /// - /// The handler name. - public HandlerBuilder(string name) - { - this.name = name; - } - - /// - /// Create and return an event handler builder. - /// - /// The builder. - public static HandlerBuilder Builder() - { - return new HandlerBuilder(); - } - - /// - /// Create and return an event handler builder. - /// This is equivalent to Builder().Name(name) - /// - /// The builder. - /// The handler name. - public static HandlerBuilder Builder(string name) - { - return new HandlerBuilder(name); - } - - /// - /// Name the event handler. - /// - /// The builder. - /// The event handler name. - public HandlerBuilder Name(string name) - { - this.name = name; - return this; - } - - /// - /// Set the action to perform on when the activated event occurs. - /// - /// The builder. - /// The activated event action. - public HandlerBuilder OnActivation(Action action) - { - return OnActivation((_) => { action(); }); - } - - /// - /// Set the action to perform on when the activated event occurs. - /// - /// The builder. - /// The activated event action. - public HandlerBuilder OnActivation(Action action) - { - this.activated = action; - return this; - } - - /// - /// Set the action to perform when the destroyed event occurs. - /// - /// The builder. - /// The destroyed event action. - public HandlerBuilder OnDestruction(Action action) - { - return OnDestruction((_) => { action(); }); - } - - /// - /// Set the action to perform when the destroyed event occurs. - /// - /// The builder. - /// The destroyed event action. - public HandlerBuilder OnDestruction(Action action) - { - this.destroyed = action; - return this; - } - - /// - /// Set the type of event to handle. - /// - /// The builder. - /// The event type. - public HandlerBuilder Handle(EventType eventType) - { - return Handle((int)eventType); - } - - /// - /// Set the type of event to handle. - /// - /// The builder. - /// The event type. - public HandlerBuilder Handle(int eventType) - { - this.type = eventType; - return this; - } - - /// - /// Build the event handler. - /// - /// The event handler. - /// Automatically register the event handler with EventManager.AddEventHandler(). - public IEventHandlerEngine Build(bool register = true) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new EventParameterMissingException("Event handler name must be defined before Build() is called"); - } - if (activated == null && destroyed == null) - { - throw new EventParameterMissingException("Event handler destruction or activated event action must be defined before Build() is called"); - } - if (!type.HasValue) - { - throw new EventParameterMissingException("Event handler event type must be defined before Build() is called"); - } - Action validActivated = activated; - if (validActivated == null) - { - validActivated = (_) => { }; - } - Action validDestroyed = destroyed; - if (validDestroyed == null) - { - validDestroyed = (_) => { }; - } - SimpleEventHandlerEngine result = new SimpleEventHandlerEngine(validActivated, validDestroyed, type.Value, name); - if (register) - { - EventManager.AddEventHandler(result); - } - return result; - } - } -} diff --git a/GamecraftModdingAPI/Events/IEventEmitterEngine.cs b/GamecraftModdingAPI/Events/IEventEmitterEngine.cs deleted file mode 100644 index 8917cef..0000000 --- a/GamecraftModdingAPI/Events/IEventEmitterEngine.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Svelto.ECS; - -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Engine interface to create a ModEventEntityStruct in entitiesDB when a specific event occurs. - /// - [Obsolete] - public interface IEventEmitterEngine : IFactoryEngine - { - /// - /// Emit the event. (Optional) - /// - void Emit(); - } -} diff --git a/GamecraftModdingAPI/Events/IEventHandlerEngine.cs b/GamecraftModdingAPI/Events/IEventHandlerEngine.cs deleted file mode 100644 index 228adb8..0000000 --- a/GamecraftModdingAPI/Events/IEventHandlerEngine.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Svelto.ECS; -using Svelto.ECS.Internal; - -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines. - /// - [Obsolete] - public interface IEventHandlerEngine : IReactionaryEngine - { - } -} diff --git a/GamecraftModdingAPI/Events/MenuActivatedPatch.cs b/GamecraftModdingAPI/Events/MenuActivatedPatch.cs deleted file mode 100644 index 382bac4..0000000 --- a/GamecraftModdingAPI/Events/MenuActivatedPatch.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using HarmonyLib; -using RobocraftX; -using Svelto.ECS; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Patch of RobocraftX.FullGameCompositionRoot.ActivateMenu() - /// - [Obsolete] - [HarmonyPatch(typeof(FullGameCompositionRoot), "ActivateMenu")] - class MenuActivatedPatch - { - - private static bool firstLoad = true; - public static void Postfix(ref EnginesRoot ____frontEndEnginesRoot, FullGameCompositionRoot __instance) - { - // register custom menu engines - MenuEngineManager.RegisterEngines(____frontEndEnginesRoot); - // A new EnginesRoot is always created when ActivateMenu is called - // so all event emitters and handlers must be re-registered. - EventManager.RegisterEngines(____frontEndEnginesRoot); - if (firstLoad) - { - firstLoad = false; - FullGameFields.Init(__instance); - //Application.Application.SetFullGameCompositionRoot(__instance); - Logging.Log("Dispatching App Init event"); - EventManager.GetEventEmitter("GamecraftModdingAPIApplicationInitializedEventEmitter").Emit(); - } - Logging.Log("Dispatching Menu Activated event"); - EventManager.GetEventEmitter("GamecraftModdingAPIMenuActivatedEventEmitter").Emit(); - } - } -} diff --git a/GamecraftModdingAPI/Events/MenuSwitchedToPatch.cs b/GamecraftModdingAPI/Events/MenuSwitchedToPatch.cs deleted file mode 100644 index 30b84da..0000000 --- a/GamecraftModdingAPI/Events/MenuSwitchedToPatch.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using HarmonyLib; -using RobocraftX; -using Svelto.ECS; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// Patch of RobocraftX.FullGameCompositionRoot.SwitchToMenu() - /// - [Obsolete] - [HarmonyPatch(typeof(FullGameCompositionRoot), "SwitchToMenu")] - class MenuSwitchedToPatch - { - public static void Postfix() - { - // Event emitters and handlers should already be registered by MenuActivated event - Logging.Log("Dispatching Menu Switched To event"); - EventManager.GetEventEmitter("GamecraftModdingAPIMenuSwitchedToEventEmitter").Emit(); - } - } -} diff --git a/GamecraftModdingAPI/Events/ModEventEntityDescriptor.cs b/GamecraftModdingAPI/Events/ModEventEntityDescriptor.cs deleted file mode 100644 index 5172d9c..0000000 --- a/GamecraftModdingAPI/Events/ModEventEntityDescriptor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Svelto.ECS; - -namespace GamecraftModdingAPI.Events -{ - /// - /// EntityDescriptor for creating ModEventEntityStructs - /// - public class ModEventEntityDescriptor : GenericEntityDescriptor - { - } -} diff --git a/GamecraftModdingAPI/Events/ModEventEntityStruct.cs b/GamecraftModdingAPI/Events/ModEventEntityStruct.cs deleted file mode 100644 index cc945ec..0000000 --- a/GamecraftModdingAPI/Events/ModEventEntityStruct.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Svelto.ECS; - -namespace GamecraftModdingAPI.Events -{ - /// - /// The event entity struct - /// - public struct ModEventEntityStruct : IEntityComponent, INeedEGID - { - /// - /// The type of event that has been emitted - /// - public int type; - - public EGID ID { get; set; } - } -} diff --git a/GamecraftModdingAPI/Events/SimpleEventEmitterEngine.cs b/GamecraftModdingAPI/Events/SimpleEventEmitterEngine.cs deleted file mode 100644 index 0ea8170..0000000 --- a/GamecraftModdingAPI/Events/SimpleEventEmitterEngine.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Svelto.ECS; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// A simple implementation of IEventEmitterEngine sufficient for most uses - /// - [Obsolete] - public class SimpleEventEmitterEngine : IEventEmitterEngine - { - public string Name { get; set; } - public int type { get; set; } - - public bool isRemovable { get; } - - public IEntityFactory Factory { private get; set; } - - public EntitiesDB entitiesDB { set; private get; } - - public void Ready() { } - - /// - /// Emit the event - /// - public void Emit() - { - Factory.BuildEntity(ApiExclusiveGroups.eventID++, ApiExclusiveGroups.eventsExclusiveGroup) - .Init(new ModEventEntityStruct { type = type }); - } - - public void Dispose() { } - - /// - /// Construct the engine - /// - /// The EventType to use for ModEventEntityStruct.type - /// The name of this engine - /// Will removing this engine not break your code? - public SimpleEventEmitterEngine(EventType type, string name, bool isRemovable = true) - { - this.type = (int)type; - this.Name = name; - this.isRemovable = isRemovable; - } - - /// - /// Construct the engine - /// - /// The object to use for ModEventEntityStruct.type - /// The name of this engine - /// Will removing this engine not break your code? - public SimpleEventEmitterEngine(int type, string name, bool isRemovable = true) - { - this.type = type; - this.Name = name; - this.isRemovable = isRemovable; - } - } -} diff --git a/GamecraftModdingAPI/Events/SimpleEventHandlerEngine.cs b/GamecraftModdingAPI/Events/SimpleEventHandlerEngine.cs deleted file mode 100644 index ebce21d..0000000 --- a/GamecraftModdingAPI/Events/SimpleEventHandlerEngine.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Svelto.ECS; - -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Events -{ - /// - /// A simple implementation of IEventHandlerEngine sufficient for most uses - /// - [Obsolete] - public class SimpleEventHandlerEngine : IEventHandlerEngine - { - public int type { get; set; } - public string Name { get; set; } - - private bool isActivated = false; - - private bool jankActivateFix = false; - - private bool jankDestroyFix = false; - - private readonly Action onActivated; - - private readonly Action onDestroyed; - - public EntitiesDB entitiesDB { set; private get; } - - public bool isRemovable => true; - - public void Add(ref ModEventEntityStruct entityView, EGID egid) - { - if (entityView.type.Equals(this.type)) - { - jankActivateFix = !jankActivateFix; - if (jankActivateFix) return; - isActivated = true; - onActivatedInvokeCatchError(entitiesDB); - } - } - - /// - /// Manually activate the EventHandler. - /// Once activated, the next remove event will not be ignored. - /// - /// Whether to invoke the activated action - public void Activate(bool handle = false) - { - isActivated = true; - if (handle && entitiesDB != null) - { - onActivatedInvokeCatchError(entitiesDB); - } - } - - public void Deactivate() - { - isActivated = false; - } - - public void Ready() { } - - public void Remove(ref ModEventEntityStruct entityView, EGID egid) - { - if (entityView.type.Equals(this.type) && isActivated) - { - jankDestroyFix = !jankDestroyFix; - if (jankDestroyFix) return; - isActivated = false; - onDestroyedInvokeCatchError(entitiesDB); - } - } - - public void Dispose() - { - if (isActivated) - { - isActivated = false; - onDestroyedInvokeCatchError(entitiesDB); - } - } - - /// - /// Construct the engine - /// - /// The operation to do when the event is created - /// The operation to do when the event is destroyed (if applicable) - /// The type of event to handle - /// The name of the engine - /// A useless parameter to use to avoid Python overload resolution errors - public SimpleEventHandlerEngine(Action activated, Action removed, int type, string name, bool simple = true) - : this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, type, name) { } - - /// - /// Construct the engine - /// - /// The operation to do when the event is created - /// The operation to do when the event is destroyed (if applicable) - /// The type of event to handler - /// The name of the engine - public SimpleEventHandlerEngine(Action activated, Action removed, int type, string name) - { - this.type = type; - this.Name = name; - this.onActivated = activated; - this.onDestroyed = removed; - } - - private void onActivatedInvokeCatchError(EntitiesDB _entitiesDB) - { - try - { - onActivated.Invoke(_entitiesDB); - } - catch (Exception e) - { - EventRuntimeException wrappedException = new EventRuntimeException($"EventHandler {Name} threw an exception when activated", e); - Logging.LogWarning(wrappedException.ToString()); - } - } - - private void onDestroyedInvokeCatchError(EntitiesDB _entitiesDB) - { - try - { - onDestroyed.Invoke(_entitiesDB); - } - catch (Exception e) - { - EventRuntimeException wrappedException = new EventRuntimeException($"EventHandler {Name} threw an exception when destroyed", e); - Logging.LogWarning(wrappedException.ToString()); - } - } - - public SimpleEventHandlerEngine(Action activated, Action removed, EventType type, string name, bool simple = true) - : this((EntitiesDB _) => { activated.Invoke(); }, (EntitiesDB _) => { removed.Invoke(); }, (int)type, name) { } - - public SimpleEventHandlerEngine(Action activated, Action removed, EventType type, string name, bool simple = true) - : this(activated, removed, (int)type, name) { } - } -} diff --git a/GamecraftModdingAPI/GamecraftModdingAPI.csproj b/GamecraftModdingAPI/GamecraftModdingAPI.csproj deleted file mode 100644 index cdbb4d9..0000000 --- a/GamecraftModdingAPI/GamecraftModdingAPI.csproj +++ /dev/null @@ -1,1061 +0,0 @@ - - - net472 - true - 1.7.0 - Exmods - GNU General Public Licence 3+ - https://git.exmods.org/modtainers/GamecraftModdingAPI - en-CA - true - - - - - - - DEBUG;TEST;TRACE - - - - - - - - - - - - - - - ..\ref\GamecraftPreview_Data\Managed\IllusionInjector.dll - ..\..\ref\GamecraftPreview_Data\Managed\IllusionInjector.dll - - - ..\ref\GamecraftPreview_Data\Managed\IllusionPlugin.dll - ..\..\ref\GamecraftPreview_Data\Managed\IllusionPlugin.dll - - - ..\ref\GamecraftPreview_Data\Managed\Analytics.dll - ..\..\ref\GamecraftPreview_Data\Managed\Analytics.dll - - - ..\ref\GamecraftPreview_Data\Managed\Assembly-CSharp-firstpass.dll - ..\..\ref\GamecraftPreview_Data\Managed\Assembly-CSharp-firstpass.dll - - - ..\ref\GamecraftPreview_Data\Managed\Assembly-CSharp.dll - ..\..\ref\GamecraftPreview_Data\Managed\Assembly-CSharp.dll - - - ..\ref\GamecraftPreview_Data\Managed\Authentication.dll - ..\..\ref\GamecraftPreview_Data\Managed\Authentication.dll - - - ..\ref\GamecraftPreview_Data\Managed\Blocks.HUDFeedbackBlocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Blocks.HUDFeedbackBlocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\CommandLine.dll - ..\..\ref\GamecraftPreview_Data\Managed\CommandLine.dll - - - ..\ref\GamecraftPreview_Data\Managed\CommandLineCompositionRoot.dll - ..\..\ref\GamecraftPreview_Data\Managed\CommandLineCompositionRoot.dll - - - ..\ref\GamecraftPreview_Data\Managed\ConsoleBlockComposotionRoot.dll - ..\..\ref\GamecraftPreview_Data\Managed\ConsoleBlockComposotionRoot.dll - - - ..\ref\GamecraftPreview_Data\Managed\ConsoleCommand.dll - ..\..\ref\GamecraftPreview_Data\Managed\ConsoleCommand.dll - - - ..\ref\GamecraftPreview_Data\Managed\DataLoader.dll - ..\..\ref\GamecraftPreview_Data\Managed\DataLoader.dll - - - ..\ref\GamecraftPreview_Data\Managed\DDNA.dll - ..\..\ref\GamecraftPreview_Data\Managed\DDNA.dll - - - ..\ref\GamecraftPreview_Data\Managed\Facepunch.Steamworks.Win64.dll - ..\..\ref\GamecraftPreview_Data\Managed\Facepunch.Steamworks.Win64.dll - - - ..\ref\GamecraftPreview_Data\Managed\FMOD.dll - ..\..\ref\GamecraftPreview_Data\Managed\FMOD.dll - - - ..\ref\GamecraftPreview_Data\Managed\FullGame.dll - ..\..\ref\GamecraftPreview_Data\Managed\FullGame.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.AudioBlocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.AudioBlocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.BlockEntityFactory.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.BlockEntityFactory.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.BlockGroups.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.BlockGroups.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.ConsoleBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.ConsoleBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.DamagingSurfaceBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.DamagingSurfaceBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.DestructionBlocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.DestructionBlocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.LightBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.LightBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.LogicBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.LogicBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\GameCraft.Blocks.ProjectileBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\GameCraft.Blocks.ProjectileBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.TextBlock.CompositionRoot.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.TextBlock.CompositionRoot.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.TimerBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Blocks.TimerBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.BlocksEntityDescriptors.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.BlocksEntityDescriptors.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.CharacterVulnerability.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.CharacterVulnerability.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.ColourPalette.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.ColourPalette.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Damage.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Damage.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Effects.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Effects.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.ExplosionFragments.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.ExplosionFragments.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GraphicsSettings.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GraphicsSettings.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.BlueprintInventory.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.BlueprintInventory.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.BlueprintInventoryMock.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.BlueprintInventoryMock.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Blueprints.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Blueprints.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.BlueprintSets.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.BlueprintSets.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.ConsoleBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.ConsoleBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.GameOptionsScreen.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.GameOptionsScreen.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Hotbar.Blocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Hotbar.Blocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Hotbar.Colours.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Hotbar.Colours.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.ModeBar.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.ModeBar.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.OptionsScreen.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.OptionsScreen.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TabsBar.Blocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TabsBar.Blocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TabsBar.Blueprints.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TabsBar.Blueprints.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TabsBar.Colours.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TabsBar.Colours.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TabsBar.Common.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TabsBar.Common.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TimeModeClock.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.TimeModeClock.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Tweaks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Tweaks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Wires.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Wires.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Wires.Mockup.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.Wires.Mockup.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUIs.Hotbar.BlueprintsHotbar.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.GUIs.Hotbar.BlueprintsHotbar.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.InventoryTimeRunning.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.InventoryTimeRunning.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.JointBlocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.JointBlocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Music.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Music.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.PerformanceWarnings.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.PerformanceWarnings.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.PickupBlck.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.PickupBlck.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.PickupsCommon.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.PickupsCommon.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.PopupMessage.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.PopupMessage.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Projectiles.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Projectiles.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Tweaks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Tweaks.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Tweaks.Mockup.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Tweaks.Mockup.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.VisualEffects.Decals.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.VisualEffects.Decals.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.VisualEffects.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.VisualEffects.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Wires.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Wires.dll - - - ..\ref\GamecraftPreview_Data\Managed\Gamecraft.Wires.Mockup.dll - ..\..\ref\GamecraftPreview_Data\Managed\Gamecraft.Wires.Mockup.dll - - - ..\ref\GamecraftPreview_Data\Managed\GameState.dll - ..\..\ref\GamecraftPreview_Data\Managed\GameState.dll - - - ..\ref\GamecraftPreview_Data\Managed\GhostShark.Outline.dll - ..\..\ref\GamecraftPreview_Data\Managed\GhostShark.Outline.dll - - - ..\ref\GamecraftPreview_Data\Managed\GPUInstancer.dll - ..\..\ref\GamecraftPreview_Data\Managed\GPUInstancer.dll - - - ..\ref\GamecraftPreview_Data\Managed\Havok.Physics.dll - ..\..\ref\GamecraftPreview_Data\Managed\Havok.Physics.dll - - - ..\ref\GamecraftPreview_Data\Managed\Havok.Physics.Hybrid.dll - ..\..\ref\GamecraftPreview_Data\Managed\Havok.Physics.Hybrid.dll - - - ..\ref\GamecraftPreview_Data\Managed\LZ4.dll - ..\..\ref\GamecraftPreview_Data\Managed\LZ4.dll - - - ..\ref\GamecraftPreview_Data\Managed\mscorlib.dll - ..\..\ref\GamecraftPreview_Data\Managed\mscorlib.dll - - - ..\ref\GamecraftPreview_Data\Managed\MultiplayerNetworking.dll - ..\..\ref\GamecraftPreview_Data\Managed\MultiplayerNetworking.dll - - - ..\ref\GamecraftPreview_Data\Managed\MultiplayerTest.dll - ..\..\ref\GamecraftPreview_Data\Managed\MultiplayerTest.dll - - - ..\ref\GamecraftPreview_Data\Managed\RCX.ScreenshotTaker.dll - ..\..\ref\GamecraftPreview_Data\Managed\RCX.ScreenshotTaker.dll - - - ..\ref\GamecraftPreview_Data\Managed\Rewired_Core.dll - ..\..\ref\GamecraftPreview_Data\Managed\Rewired_Core.dll - - - ..\ref\GamecraftPreview_Data\Managed\Rewired_Windows.dll - ..\..\ref\GamecraftPreview_Data\Managed\Rewired_Windows.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftECS.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftECS.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.AccountPreferences.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.AccountPreferences.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Blocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Blocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Blocks.Ghost.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Blocks.Ghost.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Blocks.Triggers.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Blocks.Triggers.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Building.BoxSelect.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Building.BoxSelect.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Building.Jobs.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Building.Jobs.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Character.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Character.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.ClusterToWireConversion.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.ClusterToWireConversion.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Common.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Common.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.ControlsScreen.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.ControlsScreen.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Crosshair.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Crosshair.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.FrontEnd.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.FrontEnd.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.BlockLabel.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.BlockLabel.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.DebugDisplay.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.DebugDisplay.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.Hotbar.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.Hotbar.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.Inventory.BlocksInventory.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.Inventory.BlocksInventory.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.Inventory.ColourInventory.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.Inventory.ColourInventory.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.Inventory.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.Inventory.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.RemoveBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.RemoveBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.ScaleGhost.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.ScaleGhost.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.TabsBar.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUI.TabsBar.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUIs.WorkshopPrefabs.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.GUIs.WorkshopPrefabs.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Input.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Input.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.MachineEditor.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.MachineEditor.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.MainGame.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.MainGame.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.MainSimulation.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.MainSimulation.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.MockCharacter.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.MockCharacter.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Multiplayer.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Multiplayer.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Multiplayer.GUI.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Multiplayer.GUI.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.MultiplayerInput.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.MultiplayerInput.dll - - - ..\ref\GamecraftPreview_Data\Managed\Robocraftx.ObjectIdBlocks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Robocraftx.ObjectIdBlocks.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Party.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Party.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.PartyGui.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.PartyGui.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Physics.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Physics.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.PilotSeat.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.PilotSeat.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Player.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Player.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Rendering.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Rendering.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Rendering.Mock.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Rendering.Mock.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.SaveAndLoad.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.SaveAndLoad.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.SaveGameDialog.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.SaveGameDialog.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Serializers.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Serializers.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.Services.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.Services.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.SignalHandling.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.SignalHandling.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX.StateSync.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX.StateSync.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX_SpawnPoints.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX_SpawnPoints.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocraftX_TextBlock.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocraftX_TextBlock.dll - - - ..\ref\GamecraftPreview_Data\Managed\RobocratX.SimulationMockCompositionRoot.dll - ..\..\ref\GamecraftPreview_Data\Managed\RobocratX.SimulationMockCompositionRoot.dll - - - ..\ref\GamecraftPreview_Data\Managed\SpawningPointCompositionRoot.dll - ..\..\ref\GamecraftPreview_Data\Managed\SpawningPointCompositionRoot.dll - - - ..\ref\GamecraftPreview_Data\Managed\SpecializedDescriptors.dll - ..\..\ref\GamecraftPreview_Data\Managed\SpecializedDescriptors.dll - - - ..\ref\GamecraftPreview_Data\Managed\StringFormatter.dll - ..\..\ref\GamecraftPreview_Data\Managed\StringFormatter.dll - - - ..\ref\GamecraftPreview_Data\Managed\Svelto.Common.dll - ..\..\ref\GamecraftPreview_Data\Managed\Svelto.Common.dll - - - ..\ref\GamecraftPreview_Data\Managed\Svelto.ECS.dll - ..\..\ref\GamecraftPreview_Data\Managed\Svelto.ECS.dll - - - ..\ref\GamecraftPreview_Data\Managed\Svelto.Services.dll - ..\..\ref\GamecraftPreview_Data\Managed\Svelto.Services.dll - - - ..\ref\GamecraftPreview_Data\Managed\Svelto.Tasks.dll - ..\..\ref\GamecraftPreview_Data\Managed\Svelto.Tasks.dll - - - ..\ref\GamecraftPreview_Data\Managed\UltimateDecals.dll - ..\..\ref\GamecraftPreview_Data\Managed\UltimateDecals.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Addressables.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Addressables.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Animation.Curves.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Animation.Curves.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Animation.Curves.Hybrid.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Animation.Curves.Hybrid.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Animation.DefaultGraphPipeline.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Animation.DefaultGraphPipeline.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Animation.DefaultGraphPipeline.Hybrid.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Animation.DefaultGraphPipeline.Hybrid.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Animation.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Animation.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Animation.Graph.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Animation.Graph.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Animation.Hybrid.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Animation.Hybrid.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Build.SlimPlayerRuntime.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Build.SlimPlayerRuntime.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Burst.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Burst.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Collections.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Collections.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Collections.LowLevel.ILSupport.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Collections.LowLevel.ILSupport.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.DataFlowGraph.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.DataFlowGraph.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Deformations.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Deformations.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Entities.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Entities.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Entities.Hybrid.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Entities.Hybrid.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.InternalAPIEngineBridge.002.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.InternalAPIEngineBridge.002.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Jobs.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Jobs.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Mathematics.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Mathematics.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Mathematics.Extensions.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Mathematics.Extensions.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Mathematics.Extensions.Hybrid.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Mathematics.Extensions.Hybrid.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.MemoryProfiler.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.MemoryProfiler.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Physics.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Physics.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Physics.Hybrid.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Physics.Hybrid.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Platforms.Common.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Platforms.Common.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Postprocessing.Runtime.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Postprocessing.Runtime.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Properties.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Properties.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Properties.Reflection.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Properties.Reflection.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Properties.UI.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Properties.UI.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipeline.Universal.ShaderLibrary.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipeline.Universal.ShaderLibrary.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.Universal.Runtime.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.Universal.Runtime.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.Universal.Shaders.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.RenderPipelines.Universal.Shaders.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.ResourceManager.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.ResourceManager.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Scenes.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Scenes.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.ScriptableBuildPipeline.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.ScriptableBuildPipeline.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Serialization.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Serialization.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.TextMeshPro.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.TextMeshPro.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Timeline.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Timeline.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Transforms.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Transforms.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Transforms.Hybrid.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Transforms.Hybrid.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.VisualEffectGraph.Runtime.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.VisualEffectGraph.Runtime.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.AccessibilityModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.AccessibilityModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.AIModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.AIModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.AndroidJNIModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.AndroidJNIModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.AnimationModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.AnimationModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.ARModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.ARModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.AssetBundleModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.AssetBundleModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.AudioModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.AudioModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.ClothModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.ClothModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.ClusterInputModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.ClusterInputModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.ClusterRendererModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.ClusterRendererModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.CoreModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.CoreModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.CrashReportingModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.CrashReportingModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.DirectorModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.DirectorModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.DSPGraphModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.DSPGraphModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.GameCenterModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.GameCenterModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.GridModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.GridModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.HotReloadModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.HotReloadModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.ImageConversionModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.ImageConversionModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.IMGUIModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.IMGUIModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.InputLegacyModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.InputLegacyModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.InputModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.InputModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.JSONSerializeModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.JSONSerializeModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.LocalizationModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.LocalizationModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.ParticleSystemModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.ParticleSystemModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.PerformanceReportingModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.PerformanceReportingModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.Physics2DModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.Physics2DModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.PhysicsModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.PhysicsModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.ProfilerModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.ProfilerModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.ScreenCaptureModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.ScreenCaptureModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.SharedInternalsModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.SharedInternalsModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.SpriteMaskModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.SpriteMaskModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.SpriteShapeModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.SpriteShapeModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.StreamingModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.StreamingModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.SubstanceModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.SubstanceModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.SubsystemsModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.SubsystemsModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.TerrainModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.TerrainModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.TerrainPhysicsModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.TerrainPhysicsModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.TextCoreModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.TextCoreModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.TextRenderingModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.TextRenderingModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.TilemapModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.TilemapModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.TLSModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.TLSModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UI.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UI.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UIElementsModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UIElementsModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UIElementsNativeModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UIElementsNativeModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UIModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UIModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UmbraModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UmbraModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UNETModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UNETModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityAnalyticsModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityAnalyticsModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityConnectModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityConnectModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityTestProtocolModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityTestProtocolModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.VehiclesModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.VehiclesModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.VFXModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.VFXModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.VideoModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.VideoModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.VirtualTexturingModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.VirtualTexturingModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.VRModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.VRModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.WindModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.WindModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\UnityEngine.XRModule.dll - ..\..\ref\GamecraftPreview_Data\Managed\UnityEngine.XRModule.dll - - - ..\ref\GamecraftPreview_Data\Managed\uREPL.dll - ..\..\ref\GamecraftPreview_Data\Managed\uREPL.dll - - - ..\ref\GamecraftPreview_Data\Managed\VisualProfiler.dll - ..\..\ref\GamecraftPreview_Data\Managed\VisualProfiler.dll - - - ..\ref\GamecraftPreview_Data\Managed\Accessibility.dll - ..\..\ref\GamecraftPreview_Data\Managed\Accessibility.dll - - - ..\ref\GamecraftPreview_Data\Managed\JWT.dll - ..\..\ref\GamecraftPreview_Data\Managed\JWT.dll - - - ..\ref\GamecraftPreview_Data\Managed\netstandard.dll - ..\..\ref\GamecraftPreview_Data\Managed\netstandard.dll - - - ..\ref\GamecraftPreview_Data\Managed\Newtonsoft.Json.dll - ..\..\ref\GamecraftPreview_Data\Managed\Newtonsoft.Json.dll - - - ..\ref\GamecraftPreview_Data\Managed\Novell.Directory.Ldap.dll - ..\..\ref\GamecraftPreview_Data\Managed\Novell.Directory.Ldap.dll - - - ..\ref\GamecraftPreview_Data\Managed\Unity.Burst.Unsafe.dll - ..\..\ref\GamecraftPreview_Data\Managed\Unity.Burst.Unsafe.dll - - - - \ No newline at end of file diff --git a/GamecraftModdingAPI/GamecraftModdingAPIException.cs b/GamecraftModdingAPI/GamecraftModdingAPIException.cs deleted file mode 100644 index bc944aa..0000000 --- a/GamecraftModdingAPI/GamecraftModdingAPIException.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace GamecraftModdingAPI -{ - public class GamecraftModdingAPIException : Exception - { - public GamecraftModdingAPIException() - { - } - - public GamecraftModdingAPIException(string message) : base(message) - { - } - - public GamecraftModdingAPIException(string message, Exception innerException) : base(message, innerException) - { - } - - protected GamecraftModdingAPIException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - } -} diff --git a/GamecraftModdingAPI/Input/FakeInputEngine.cs b/GamecraftModdingAPI/Input/FakeInputEngine.cs deleted file mode 100644 index 11d5f3a..0000000 --- a/GamecraftModdingAPI/Input/FakeInputEngine.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; - -using RobocraftX.Common.Input; -using RobocraftX.Players; -using Svelto.ECS; - -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Input -{ - public class FakeInputEngine : IApiEngine - { - public string Name { get; } = "GamecraftModdingAPIFakeInputEngine"; - - public EntitiesDB entitiesDB { set; private get; } - - public bool isRemovable => false; - - public bool IsReady = false; - - public void Dispose() - { - IsReady = false; - } - - public void Ready() - { - IsReady = true; - } - - public bool SendCustomInput(LocalInputEntityStruct input, uint playerID, bool remote = false) - { - EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers); - if (entitiesDB.Exists(egid)) - { - ref LocalInputEntityStruct ies = ref entitiesDB.QueryEntity(egid); - ies = input; - return true; - } - else return false; - } - - public LocalInputEntityStruct GetInput(uint playerID, bool remote = false) - { - EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers); - if (entitiesDB.Exists(egid)) - { - return entitiesDB.QueryEntity(egid); - } - else return default(LocalInputEntityStruct); - } - - public ref LocalInputEntityStruct GetInputRef(uint playerID, bool remote = false) - { - EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers); - return ref entitiesDB.QueryEntity(egid); - } - - public uint GetLocalPlayerID() - { - return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB); - } - } -} diff --git a/GamecraftModdingAPI/Inventory/Hotbar.cs b/GamecraftModdingAPI/Inventory/Hotbar.cs deleted file mode 100644 index 838c554..0000000 --- a/GamecraftModdingAPI/Inventory/Hotbar.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; - -using RobocraftX.Common.Input; -using RobocraftX.Multiplayer.Input; - -using GamecraftModdingAPI.Blocks; -using GamecraftModdingAPI.Utility; -using HarmonyLib; - -namespace GamecraftModdingAPI.Inventory -{ - public static class Hotbar - { - private static readonly HotbarEngine hotbarEngine = new HotbarEngine(); - - /// - /// Switch the block in the player's hand - /// - /// The block to switch to. - /// The player. Omit this to use the local player. - public static void EquipBlock(BlockIDs block, uint playerID = uint.MaxValue) - { - if (playerID == uint.MaxValue) - { - playerID = hotbarEngine.GetLocalPlayerID(); - } - hotbarEngine.SelectBlock((int) block, playerID); - // cubeSelectedByPick = true will crash the game - // (this would be equivalent to mouse middle click pick block action) - // reason: the game expects a Dictionary entry for the tweaked stats - } - - /// - /// Gets the block in the player's hand - /// - /// The equipped block. - public static BlockIDs GetEquippedBlock() - { - return HotbarSlotSelectionHandlerEnginePatch.EquippedPartID; - } - - public static void Init() - { - GameEngineManager.AddGameEngine(hotbarEngine); - } - } -} diff --git a/GamecraftModdingAPI/Inventory/HotbarEngine.cs b/GamecraftModdingAPI/Inventory/HotbarEngine.cs deleted file mode 100644 index 34cdf12..0000000 --- a/GamecraftModdingAPI/Inventory/HotbarEngine.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; - -using RobocraftX.Character; -using RobocraftX.GUI.Hotbar; -using RobocraftX.Players; -using RobocraftX.Common; -using RobocraftX.Common.Input; -using RobocraftX.Common.Players; -using Svelto.ECS; - -using GamecraftModdingAPI.Blocks; -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Inventory -{ - public class HotbarEngine : IApiEngine - { - public string Name { get; } = "GamecraftModdingAPIHotbarGameEngine"; - - public EntitiesDB entitiesDB { set; private get; } - - public bool isRemovable => false; - - public bool IsInGame = false; - - public void Dispose() - { - IsInGame = false; - } - - public void Ready() - { - IsInGame = true; - } - - public bool SelectBlock(int block, uint playerID, bool cubeSelectedByPick = false) - { - var inputs = entitiesDB.QueryEntities(InputExclusiveGroups.LocalPlayers).ToBuffer(); - if (inputs.count == 0) return false; - for (int i = 0; i < inputs.count; i++) - { - if (inputs.buffer[i].ID.entityID == playerID) { - inputs.buffer[i].cubeSelectedByPick = cubeSelectedByPick; - inputs.buffer[i].selectedCube = block; - return true; - } - } - // TODO: expose the rest of the input functionality - return false; - } - - public uint GetLocalPlayerID() - { - return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB); - } - } -} diff --git a/GamecraftModdingAPI/Player.cs b/GamecraftModdingAPI/Player.cs deleted file mode 100644 index 14892a3..0000000 --- a/GamecraftModdingAPI/Player.cs +++ /dev/null @@ -1,446 +0,0 @@ -using System; -using Gamecraft.GUI.Blueprints; -using Unity.Mathematics; -using RobocraftX.Common; -using RobocraftX.Common.Players; -using Svelto.ECS; - -using GamecraftModdingAPI.Players; -using GamecraftModdingAPI.Blocks; - -namespace GamecraftModdingAPI -{ - /// - /// An in-game player character. Any Leo you see is a player. - /// - public class Player : IEquatable, IEquatable - { - // static functionality - private static PlayerEngine playerEngine = new PlayerEngine(); - private static Player localPlayer; - - /// - /// Checks if the specified player exists. - /// - /// Whether the player exists. - /// Player type. - public static bool Exists(PlayerType player) - { - switch (player) - { - case PlayerType.Remote: - return playerEngine.GetRemotePlayer() != uint.MaxValue; - case PlayerType.Local: - return playerEngine.GetLocalPlayer() != uint.MaxValue; - } - return false; - } - - /// - /// Checks if the specified player exists. - /// - /// Whether the player exists. - /// The player's unique identifier. - public static bool Exists(uint player) - { - return playerEngine.ExistsById(player); - } - - /// - /// The amount of Players in the current game. - /// - /// The count. - public static uint Count() - { - return (uint) playerEngine.GetAllPlayerCount(); - } - - /// - /// Returns the current player belonging to this client. - /// - public static Player LocalPlayer - { - get - { - if (localPlayer == null || localPlayer.Id != playerEngine.GetLocalPlayer()) - localPlayer = new Player(PlayerType.Local); - return localPlayer; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The player's unique identifier. - public Player(uint id) - { - this.Id = id; - if (!Exists(id)) - { - throw new PlayerNotFoundException($"No player with id {id} exists"); - } - this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote; - } - - /// - /// Initializes a new instance of the class. - /// - /// The player type. Chooses the first available player matching the criteria. - public Player(PlayerType player) - { - switch (player) - { - case PlayerType.Local: - this.Id = playerEngine.GetLocalPlayer(); - break; - case PlayerType.Remote: - this.Id = playerEngine.GetRemotePlayer(); - break; - } - if (this.Id == uint.MaxValue) - { - throw new PlayerNotFoundException($"No player of {player} type exists"); - } - this.Type = player; - } - - // object fields & properties - - /// - /// The player's type. - /// The player type is always relative to the current client, not the game host. - /// - /// The enumerated player type. - public PlayerType Type { get; } - - /// - /// The player's unique identifier. - /// - /// The identifier. - public uint Id { get; } - - /// - /// The player's current position. - /// - /// The position. - public float3 Position - { - get - { - return playerEngine.GetLocation(Id); - } - - set - { - playerEngine.SetLocation(Id, value, false); - } - } - - /// - /// The player's current rotation. - /// - /// The rotation. - public float3 Rotation - { - get - { - return playerEngine.GetRotation(Id); - } - - set - { - playerEngine.SetRotation(Id, value); - } - } - - /// - /// The player's current velocity. - /// - /// The velocity. - public float3 Velocity - { - get - { - return playerEngine.GetLinearVelocity(Id); - } - - set - { - playerEngine.SetLinearVelocity(Id, value); - } - } - - /// - /// The player's current angular velocity. - /// - /// The angular velocity. - public float3 AngularVelocity - { - get - { - return playerEngine.GetAngularVelocity(Id); - } - - set - { - playerEngine.SetAngularVelocity(Id, value); - } - } - - /// - /// The player's mass. - /// - /// The mass. - public float Mass - { - get - { - return 1f / playerEngine.GetMass(Id).InverseMass; - } - - // FIXME: Setting mass doesn't do anything - /*set - { - playerEngine.SetInverseMass(Id, 1f / value); - }*/ - } - - private float _ping = -1f; - - /// - /// The player's latest network ping time. - /// - /// The ping (s). - public float Ping - { - get - { - float? temp = playerEngine.GetLastPingTime(Id, Type); - if (temp.HasValue) - { - _ping = temp.Value; - } - return _ping; - } - } - - /// - /// The player's initial health when entering Simulation (aka Time Running) mode. - /// - /// The initial health. - public float InitialHealth - { - get => playerEngine.GetInitialHealth(Id); - - set - { - playerEngine.SetInitialHealth(Id, value); - } - } - - /// - /// The player's current health in Simulation (aka Time Running) mode. - /// - /// The current health. - public float CurrentHealth - { - get => playerEngine.GetCurrentHealth(Id); - - set - { - playerEngine.DamagePlayer(Id, CurrentHealth - value); - } - } - - /// - /// Whether this is damageable. - /// - /// true if damageable; otherwise, false. - public bool Damageable - { - get => playerEngine.GetDamageable(Id); - - set - { - playerEngine.SetDamageable(Id, value); - } - } - - /// - /// The player's lives when initially entering Simulation (aka Time Running) mode. - /// - /// The initial lives. - public uint InitialLives - { - get => playerEngine.GetInitialLives(Id); - - set => playerEngine.SetInitialLives(Id, value); - } - - /// - /// The player's current lives in Simulation (aka Time Running) mode. - /// - /// The current lives. - public uint CurrentLives - { - get => playerEngine.GetCurrentLives(Id); - - set => playerEngine.SetCurrentLives(Id, value); - } - - /// - /// Whether the Game Over screen is displayed for the player. - /// - /// true if game over; otherwise, false. - public bool GameOver - { - get => playerEngine.GetGameOverScreen(Id); - } - - /// - /// Whether the player is dead. - /// If true, hopefully it was quick. - /// - /// true if dead; otherwise, false. - public bool Dead - { - get => playerEngine.IsDead(Id); - } - - /// - /// The player's selected block ID in their hand. - /// - /// The selected block. - public BlockIDs SelectedBlock - { - get - { - return (BlockIDs)playerEngine.GetSelectedBlock(Id); - } - } - - /// - /// The player's selected block color in their hand. - /// - /// The selected block's color. - public BlockColor SelectedColor - { - get - { - return new BlockColor(playerEngine.GetSelectedColor(Id)); - } - } - - /// - /// The player's selected block colour in their hand. - /// - /// The selected block's colour. - public BlockColor SelectedColour - { - get - { - return new BlockColor(playerEngine.GetSelectedColor(Id)); - } - } - - /// - /// The player's selected blueprint in their hand. Set to null to clear. Dispose after usage. - /// - public Blueprint SelectedBlueprint - { - get => playerEngine.GetPlayerStruct(Id, out BlueprintInventoryItemEntityStruct biies) - ? new Blueprint(biies.blueprintResourceId) - : null; - set => BlockGroup._engine.SelectBlueprint(value?.Id ?? uint.MaxValue); - } - - // object methods - - /// - /// Teleport the player to the specified coordinates. - /// - /// The x coordinate. - /// The y coordinate. - /// The z coordinate. - /// If set to true teleport relative to the player's current position. - /// If set to true exit any seat the player is in. - public void Teleport(float x, float y, float z, bool relative = true, bool exitSeat = true) - { - float3 location = new float3(x, y, z); - if (relative) - { - location += playerEngine.GetLocation(Id); - } - playerEngine.SetLocation(Id, location, exitSeat: exitSeat); - } - - /// - /// Returns the block the player is currently looking at in build mode. - /// - /// The maximum distance from the player (default is the player's building reach) - /// The block or null if not found - public Block GetBlockLookedAt(float maxDistance = -1f) - { - var egid = playerEngine.GetThingLookedAt(Id, maxDistance); - return egid.HasValue && egid.Value.groupID != CommonExclusiveGroups.SIMULATION_BODIES_GROUP - ? new Block(egid.Value) - : null; - } - - /// - /// Returns the rigid body the player is currently looking at during simulation. - /// - /// The maximum distance from the player (default is the player's building reach) - /// The block or null if not found - public SimBody GetSimBodyLookedAt(float maxDistance = -1f) - { - var egid = playerEngine.GetThingLookedAt(Id, maxDistance); - return egid.HasValue && egid.Value.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP - ? new SimBody(egid.Value) - : null; - } - - /// - /// Returns the blocks that are in the player's current selection. - /// - /// An array of blocks or an empty array - public Block[] GetSelectedBlocks() - { - return playerEngine.GetSelectedBlocks(Id); - } - - public bool Equals(Player other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Id == other.Id; - } - - public bool Equals(EGID other) - { - return Id == other.entityID && other.groupID == (Type == PlayerType.Local - ? PlayersExclusiveGroups.LocalPlayers - : PlayersExclusiveGroups.RemotePlayers); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((Player) obj); - } - - public override int GetHashCode() - { - return (int) Id; - } - - // internal methods - - internal static void Init() - { - Utility.GameEngineManager.AddGameEngine(playerEngine); - } - } -} diff --git a/GamecraftModdingAPI/Players/PlayerEngine.cs b/GamecraftModdingAPI/Players/PlayerEngine.cs deleted file mode 100644 index e253eac..0000000 --- a/GamecraftModdingAPI/Players/PlayerEngine.cs +++ /dev/null @@ -1,479 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.CompilerServices; - -using RobocraftX.Character; -using RobocraftX.Character.Movement; -using RobocraftX.Common.Players; -using RobocraftX.Common.Input; -using RobocraftX.CR.MachineEditing.BoxSelect; -using RobocraftX.Physics; -using RobocraftX.Blocks.Ghost; -using RobocraftX.Character.Camera; -using RobocraftX.Character.Factories; -using Gamecraft.GUI.HUDFeedbackBlocks; -using Svelto.ECS; -using Unity.Mathematics; -using Unity.Physics; -using UnityEngine; - -using GamecraftModdingAPI.Engines; -using HarmonyLib; -using RobocraftX.Common; -using Svelto.ECS.DataStructures; - -namespace GamecraftModdingAPI.Players -{ - internal class PlayerEngine : IApiEngine, IFactoryEngine - { - public string Name { get; } = "GamecraftModdingAPIPlayerGameEngine"; - - public EntitiesDB entitiesDB { set; private get; } - - public bool isRemovable => false; - - public IEntityFactory Factory { set; private get; } - - private bool isReady = false; - - public void Dispose() - { - isReady = false; - } - - public void Ready() - { - isReady = true; - } - - public uint GetLocalPlayer() - { - if (!isReady) return uint.MaxValue; - var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.LocalPlayers).ToBuffer(); - if (localPlayers.count > 0) - { - return localPlayers.buffer[0].ID.entityID; - } - return uint.MaxValue; - } - - public uint GetRemotePlayer() - { - if (!isReady) return uint.MaxValue; - var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.RemotePlayers).ToBuffer(); - if (localPlayers.count > 0) - { - return localPlayers.buffer[0].ID.entityID; - } - return uint.MaxValue; - } - - public long GetAllPlayerCount() - { - if (entitiesDB == null) return 0; - long count = 0; - foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers) - { - count += entitiesDB.Count(eg); - } - return count; - } - - public long GetLocalPlayerCount() - { - if (entitiesDB == null) return 0; - return entitiesDB.Count(PlayersExclusiveGroups.LocalPlayers); - } - - public long GetRemotePlayerCount() - { - if (entitiesDB == null) return 0; - return entitiesDB.Count(PlayersExclusiveGroups.RemotePlayers); - } - - public bool ExistsById(uint playerId) - { - if (entitiesDB == null) return false; - return entitiesDB.Exists(playerId, PlayersExclusiveGroups.LocalPlayers) - || entitiesDB.Exists(playerId, PlayersExclusiveGroups.RemotePlayers); - } - - public float3 GetLocation(uint playerId) - { - if (entitiesDB == null) return float3.zero; - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return rbes.position; - } - return float3.zero; - } - - public bool SetLocation(uint playerId, float3 location, bool exitSeat = true) - { - if (entitiesDB == null) return false; - var characterGroups = CharacterExclusiveGroups.AllCharacters; - for (int i = 0; i < characterGroups.count; i++) - { - EGID egid = new EGID(playerId, characterGroups[i]); - if (entitiesDB.Exists(egid)) - { - ref RigidBodyEntityStruct rbes = ref entitiesDB.QueryEntity(egid); - if (characterGroups[i] == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat) - { - entitiesDB.QueryEntity(egid).instantExit = true; - entitiesDB.PublishEntityChange(egid); - } - rbes.position = location; - return true; - } - } - return false; - } - - public float3 GetRotation(uint playerId) - { - if (entitiesDB == null) return float3.zero; - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return ((Quaternion) rbes.rotation).eulerAngles; - } - return default(float3); - } - - public bool SetRotation(uint playerId, float3 value) - { - if (entitiesDB == null) return false; - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - Quaternion q = rbes.rotation; - q.eulerAngles = value; - rbes.rotation = q; - return true; - } - return false; - } - - public float3 GetLinearVelocity(uint playerId) - { - if (entitiesDB == null) return float3.zero; - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return rbes.velocity; - } - return float3.zero; - } - - public bool SetLinearVelocity(uint playerId, float3 value) - { - if (entitiesDB == null) return false; - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - rbes.velocity = value; - return true; - } - return false; - } - - public float3 GetAngularVelocity(uint playerId) - { - if (entitiesDB == null) return float3.zero; - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return rbes.angularVelocity; - } - return float3.zero; - } - - public bool SetAngularVelocity(uint playerId, float3 value) - { - if (entitiesDB == null) return false; - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - rbes.angularVelocity = value; - return true; - } - return false; - } - - public PhysicsMass GetMass(uint playerId) - { - if (entitiesDB == null) return default(PhysicsMass); - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return rbes.physicsMass; - } - return default(PhysicsMass); - } - - public bool SetInverseMass(uint playerId, float inverseMass) - { - if (entitiesDB == null) return false; - ref var rbes = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - rbes.physicsMass.InverseInertia = inverseMass; - return true; - } - return false; - } - - public float? GetLastPingTime(uint playerId, PlayerType type) - { - if (entitiesDB == null) return null; - EGID egid = new EGID(playerId, PlayerGroupFromEnum(type)); - if (entitiesDB.Exists(egid)) - { - return entitiesDB.QueryEntity(egid).lastPingTimeSinceLevelLoad; - } - return null; - } - - public float GetInitialHealth(uint playerId) - { - if (entitiesDB == null) return 0; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return c.initialHealth; - } - return -1f; - } - - public bool SetInitialHealth(uint playerId, float val) - { - if (entitiesDB == null) return false; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - c.initialHealth = val; - return true; - } - return false; - } - - public float GetCurrentHealth(uint playerId) - { - if (entitiesDB == null) return 0; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return c.currentHealth; - } - return -1f; - } - - public bool SetCurrentHealth(uint playerId, float val) - { - if (entitiesDB == null) return false; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - c.currentHealth = val; - return true; - } - return false; - } - - public bool DamagePlayer(uint playerId, float amount) - { - if (entitiesDB == null) return false; - return SetCurrentHealth(playerId, GetCurrentHealth(playerId) - amount); - } - - public bool GetDamageable(uint playerId) - { - if (entitiesDB == null) return false; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return c.canTakeDamageStat; - } - return false; - } - - public bool SetDamageable(uint playerId, bool val) - { - if (entitiesDB == null) return false; - ref var ches = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - ches.canTakeDamage = val; - ches.canTakeDamage = val; - return true; - } - return false; - } - - public uint GetInitialLives(uint playerId) - { - if (entitiesDB == null) return 0; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return c.initialLives; - } - return uint.MaxValue; - } - - public bool SetInitialLives(uint playerId, uint val) - { - if (entitiesDB == null) return false; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - c.initialLives = val; - return true; - } - return false; - } - - public uint GetCurrentLives(uint playerId) - { - if (entitiesDB == null) return 0; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return c.currentLives; - } - return uint.MaxValue; - } - - public bool SetCurrentLives(uint playerId, uint val) - { - if (entitiesDB == null) return false; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - c.currentLives = val; - return true; - } - return false; - } - - public bool GetGameOverScreen(uint playerId) - { - if (entitiesDB == null) return false; - ref HudActivatedBlocksEntityStruct habes = ref entitiesDB.QueryEntity(HUDFeedbackBlocksGUIExclusiveGroups.GameOverHudEgid); - NativeDynamicArrayCast nativeDynamicArrayCast = new NativeDynamicArrayCast(habes.activatedBlocksOrdered); - return nativeDynamicArrayCast.count > 0; - } - - public bool IsDead(uint playerId) - { - if (entitiesDB == null) return true; - return entitiesDB.Exists(playerId, CharacterExclusiveGroups.DeadCharacters); - } - - public int GetSelectedBlock(uint playerId) - { - if (entitiesDB == null) return 0; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return c.SelectedDBPartID; - } - return ushort.MaxValue; - } - - public byte GetSelectedColor(uint playerId) - { - if (entitiesDB == null) return 0; - ref var c = ref GetCharacterStruct(playerId, out bool exists); - if (exists) - { - return c.indexInPalette; - } - return 255; - } - - // reusable methods - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ExclusiveGroup PlayerGroupFromEnum(PlayerType type) - { - return type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T GetCharacterStruct(uint playerId, out bool exists) where T : unmanaged, IEntityComponent - { - var characterGroups = CharacterExclusiveGroups.AllCharacters; - for (int i = 0; i < characterGroups.count; i++) - { - EGID egid = new EGID(playerId, characterGroups[i]); - if (entitiesDB.Exists(egid)) - { - exists = true; - return ref entitiesDB.QueryEntity(egid); - } - } - - exists = false; - T[] arr = new T[1]; - return ref arr[0]; //Return default value - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool GetPlayerStruct(uint playerId, out T s) where T : unmanaged, IEntityComponent - { - var playerGroups = PlayersExclusiveGroups.AllPlayers; - for (int i = 0; i < playerGroups.count; i++) - { - EGID egid = new EGID(playerId, playerGroups[i]); - if (entitiesDB.Exists(egid)) - { - s = entitiesDB.QueryEntity(egid); - return true; - } - } - s = default; - return false; - } - - public EGID? GetThingLookedAt(uint playerId, float maxDistance = -1f) - { - if (!entitiesDB.TryQueryMappedEntities( - CameraExclusiveGroups.CameraGroup, out var mapper)) - return null; - mapper.TryGetEntity(playerId, out CharacterCameraRayCastEntityStruct rayCast); - float distance = maxDistance < 0 - ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast, - GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize) - : maxDistance; - if (rayCast.hit && rayCast.distance <= distance) - return rayCast.hitEgid; - - return null; - } - - public unsafe Block[] GetSelectedBlocks(uint playerid) - { - if (!entitiesDB.Exists(playerid, - BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)) - return new Block[0]; - var state = entitiesDB.QueryEntity(playerid, - BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup); - var blocks = entitiesDB.QueryEntity(playerid, - BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup); - if (!state.active) return new Block[0]; - var pointer = (EGID*) blocks.selectedBlocks.ToPointer(); - var ret = new Block[blocks.count]; - for (int j = 0; j < blocks.count; j++) - { - var egid = pointer[j]; - ret[j] = new Block(egid); - } - - return ret; - } - } -} diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs deleted file mode 100644 index c904ef7..0000000 --- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs +++ /dev/null @@ -1,497 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; - -using HarmonyLib; -using IllusionInjector; -// test -using GPUInstancer; -using Svelto.ECS; -using RobocraftX.Blocks; -using RobocraftX.Common; -using RobocraftX.SimulationModeState; -using RobocraftX.FrontEnd; -using Unity.Mathematics; -using UnityEngine; - -using GamecraftModdingAPI.Commands; -using GamecraftModdingAPI.Events; -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Blocks; -using GamecraftModdingAPI.Players; -using EventType = GamecraftModdingAPI.Events.EventType; - -namespace GamecraftModdingAPI.Tests -{ -#if DEBUG - // unused by design - /// - /// Modding API implemented as a standalone IPA Plugin. - /// Ideally, GamecraftModdingAPI should be loaded by another mod; not itself - /// - public class GamecraftModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin - { - - private static Harmony harmony { get; set; } - - public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name; - - public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - - public string HarmonyID { get; } = "org.git.exmods.modtainers.gamecraftmoddingapi"; - - public override void OnApplicationQuit() - { - GamecraftModdingAPI.Main.Shutdown(); - } - - public override void OnApplicationStart() - { - FileLog.Reset(); - Harmony.DEBUG = true; - GamecraftModdingAPI.Main.Init(); - Logging.MetaDebugLog($"Version group id {(uint)ApiExclusiveGroups.versionGroup}"); - // in case Steam is not installed/running - // this will crash the game slightly later during startup - //SteamInitPatch.ForcePassSteamCheck = true; - // in case running in a VM - //MinimumSpecsCheckPatch.ForcePassMinimumSpecCheck = true; - // disable some Gamecraft analytics - //AnalyticsDisablerPatch.DisableAnalytics = true; - // disable background music - Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers())); - //AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :( - - //Utility.VersionTracking.Enable();//(very) unstable - - // debug/test handlers -#pragma warning disable 0612 - HandlerBuilder.Builder() - .Name("appinit API debug") - .Handle(EventType.ApplicationInitialized) - .OnActivation(() => { Logging.Log("App Inited event!"); }) - .Build(); - - HandlerBuilder.Builder("menuact API debug") - .Handle(EventType.Menu) - .OnActivation(() => { Logging.Log("Menu Activated event!"); }) - .OnDestruction(() => { Logging.Log("Menu Destroyed event!"); }) - .Build(); - - HandlerBuilder.Builder("menuswitch API debug") - .Handle(EventType.MenuSwitchedTo) - .OnActivation(() => { Logging.Log("Menu Switched To event!"); }) - .Build(); - - HandlerBuilder.Builder("gameact API debug") - .Handle(EventType.Menu) - .OnActivation(() => { Logging.Log("Game Activated event!"); }) - .OnDestruction(() => { Logging.Log("Game Destroyed event!"); }) - .Build(); - - HandlerBuilder.Builder("gamerel API debug") - .Handle(EventType.GameReloaded) - .OnActivation(() => { Logging.Log("Game Reloaded event!"); }) - .Build(); - - HandlerBuilder.Builder("gameswitch API debug") - .Handle(EventType.GameSwitchedTo) - .OnActivation(() => { Logging.Log("Game Switched To event!"); }) - .Build(); - - HandlerBuilder.Builder("simulationswitch API debug") - .Handle(EventType.SimulationSwitchedTo) - .OnActivation(() => { Logging.Log("Game Mode Simulation Switched To event!"); }) - .Build(); - - HandlerBuilder.Builder("buildswitch API debug") - .Handle(EventType.BuildSwitchedTo) - .OnActivation(() => { Logging.Log("Game Mode Build Switched To event!"); }) - .Build(); - - HandlerBuilder.Builder("menu activated API error thrower test") - .Handle(EventType.Menu) - .OnActivation(() => { throw new Exception("Event Handler always throws an exception!"); }) - .Build(); -#pragma warning restore 0612 - /*HandlerBuilder.Builder("enter game from menu test") - .Handle(EventType.Menu) - .OnActivation(() => - { - Tasks.Scheduler.Schedule(new Tasks.Repeatable(enterGame, shouldRetry, 0.2f)); - }) - .Build();*/ - - // debug/test commands - if (Dependency.Hell("ExtraCommands")) - { - CommandBuilder.Builder() - .Name("Exit") - .Description("Close Gamecraft immediately, without any prompts") - .Action(() => { UnityEngine.Application.Quit(); }) - .Build(); - - CommandBuilder.Builder() - .Name("SetFOV") - .Description("Set the player camera's field of view") - .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; }) - .Build(); - - CommandBuilder.Builder() - .Name("MoveLastBlock") - .Description("Move the most-recently-placed block, and any connected blocks by the given offset") - .Action((float x, float y, float z) => - { - if (GameState.IsBuildMode()) - foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes()) - block.Position += new Unity.Mathematics.float3(x, y, z); - else - GamecraftModdingAPI.Utility.Logging.CommandLogError("Blocks can only be moved in Build mode!"); - }).Build(); - - CommandBuilder.Builder() - .Name("PlaceAluminium") - .Description("Place a block of aluminium at the given coordinates") - .Action((float x, float y, float z) => - { - var block = Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x, y, z)); - Logging.CommandLog("Block placed with type: " + block.Type); - }) - .Build(); - - CommandBuilder.Builder() - .Name("PlaceAluminiumLots") - .Description("Place a lot of blocks of aluminium at the given coordinates") - .Action((float x, float y, float z) => - { - Logging.CommandLog("Starting..."); - var sw = Stopwatch.StartNew(); - for (int i = 0; i < 100; i++) - for (int j = 0; j < 100; j++) - Block.PlaceNew(BlockIDs.AluminiumCube, new float3(x + i, y, z + j)); - //Block.Sync(); - sw.Stop(); - Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms"); - }) - .Build(); - - Block b = null; - CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up") - .Action(() => - { - if (b == null) - { - b = new Player(PlayerType.Local).GetBlockLookedAt(); - Logging.CommandLog("Block saved: " + b); - } - else - Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0))); - }).Build(); - - CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.") - .Action(() => { throw new Exception("Error Command always throws an error"); }) - .Build(); - - CommandBuilder.Builder("ColorBlock", - "Change color of the block looked at if there's any.") - .Action(str => - { - if (!Enum.TryParse(str, out BlockColors color)) - { - Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values."); - var s = str.Split(' '); - new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]), - float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3])); - return; - } - new Player(PlayerType.Local).GetBlockLookedAt().Color = - new BlockColor { Color = color }; - Logging.CommandLog("Colored block to " + color); - - }).Build(); - - CommandBuilder.Builder("GetBlockByID", "Gets a block based on its object identifier and teleports it up.") - .Action(ch => - { - foreach (var body in SimBody.GetFromObjectID(ch)) - { - Logging.CommandLog("SimBody: " + body); - body.Position += new float3(0, 10, 0); - foreach (var bodyConnectedBody in body.GetConnectedBodies()) - { - Logging.CommandLog("Moving " + bodyConnectedBody); - bodyConnectedBody.Position += new float3(0, 10, 0); - } - } - }).Build(); - - CommandBuilder.Builder() - .Name("PlaceConsole") - .Description("Place a bunch of console block with a given text - entering simulation with them crashes the game as the cmd doesn't exist") - .Action((float x, float y, float z) => - { - Stopwatch sw = new Stopwatch(); - sw.Start(); - for (int i = 0; i < 100; i++) - { - for (int j = 0; j < 100; j++) - { - var block = Block.PlaceNew(BlockIDs.ConsoleBlock, - new float3(x + i, y, z + j)); - block.Command = "test_command"; - } - } - - sw.Stop(); - Logging.CommandLog($"Blocks placed in {sw.ElapsedMilliseconds} ms"); - }) - .Build(); - - CommandBuilder.Builder() - .Name("WireTest") - .Description("Place two blocks and then wire them together") - .Action(() => - { - LogicGate notBlock = Block.PlaceNew(BlockIDs.NOTLogicBlock, new float3(1, 2, 0)); - LogicGate andBlock = Block.PlaceNew(BlockIDs.ANDLogicBlock, new float3(2, 2, 0)); - // connect NOT Gate output to AND Gate input #2 (ports are zero-indexed, so 1 is 2nd position and 0 is 1st position) - Wire conn = notBlock.Connect(0, andBlock, 1); - Logging.CommandLog(conn.ToString()); - }) - .Build(); - - CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.") - .Action((float val, float max) => - { - var body = new Player(PlayerType.Local).GetSimBodyLookedAt(); - if (body == null) return; - body.CurrentHealth = val; - body.InitialHealth = max; - Logging.CommandLog("Health set to: " + val); - }).Build(); - - CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group") - .Action((float x, float y, float z) => - { - var pos = new float3(x, y, z); - var group = BlockGroup.Create(Block.PlaceNew(BlockIDs.AluminiumCube, pos, - color: BlockColors.Aqua)); - Block.PlaceNew(BlockIDs.AluminiumCube, pos += new float3(1, 0, 0), color: BlockColors.Blue) - .BlockGroup = group; - Block.PlaceNew(BlockIDs.AluminiumCube, pos += new float3(1, 0, 0), color: BlockColors.Green) - .BlockGroup = group; - Block.PlaceNew(BlockIDs.AluminiumCube, pos += new float3(1, 0, 0), color: BlockColors.Lime) - .BlockGroup = group; - }).Build(); - - GameClient.SetDebugInfo("InstalledMods", InstalledMods); - Block.Placed += (sender, args) => - Logging.MetaDebugLog("Placed block " + args.Type + " with ID " + args.ID); - Block.Removed += (sender, args) => - Logging.MetaDebugLog("Removed block " + args.Type + " with ID " + args.ID); - - /* - CommandManager.AddCommand(new SimpleCustomCommandEngine((float d) => { UnityEngine.Camera.main.fieldOfView = d; }, - "SetFOV", "Set the player camera's field of view")); - CommandManager.AddCommand(new SimpleCustomCommandEngine( - (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")); - CommandManager.AddCommand(new SimpleCustomCommandEngine( - (x, y, z) => { Blocks.Placement.PlaceBlock(Blocks.BlockIDs.AluminiumCube, new Unity.Mathematics.float3(x, y, z)); }, - "PlaceAluminium", "Place a block of aluminium at the given coordinates")); - System.Random random = new System.Random(); // for command below - CommandManager.AddCommand(new SimpleCustomCommandEngine( - () => { - if (!GameState.IsSimulationMode()) - { - Logging.CommandLogError("You must be in simulation mode for this to work!"); - return; - } - Tasks.Repeatable task = new Tasks.Repeatable(() => { - uint count = 0; - EGID[] eBlocks = Blocks.Signals.GetElectricBlocks(); - for (uint i = 0u; i < eBlocks.Length; i++) - { - uint[] ids = Blocks.Signals.GetSignalIDs(eBlocks[i]); - for (uint j = 0u; j < ids.Length; j++) - { - Blocks.Signals.SetSignalByID(ids[j], (float)random.NextDouble()); - count++; - } - } - Logging.MetaDebugLog($"Did the thing on {count} inputs"); - }, - () => { return GameState.IsSimulationMode(); }); - Tasks.Scheduler.Schedule(task); - }, "RandomizeSignalsInputs", "Do the thing")); - */ - } - - // dependency test - if (Dependency.Hell("GamecraftScripting", new Version("0.0.1.0"))) - { - Logging.LogWarning("You're in GamecraftScripting dependency hell"); - } - else - { - Logging.Log("Compatible GamecraftScripting detected"); - } - -#if TEST - TestRoot.RunTests(); -#endif - } - - private string modsString; - private string InstalledMods() - { - if (modsString != null) return modsString; - StringBuilder sb = new StringBuilder("Installed mods:"); - foreach (var plugin in PluginManager.Plugins) - sb.Append("\n" + plugin.Name + " - " + plugin.Version); - return modsString = sb.ToString(); - } - - private bool retry = true; - - private bool shouldRetry() - { - return retry; - } - - private void enterGame() - { - App.Client app = new App.Client(); - App.Game[] myGames = app.MyGames; - Logging.MetaDebugLog($"MyGames count {myGames.Length}"); - if (myGames.Length != 0) - { - Logging.MetaDebugLog($"MyGames[0] EGID {myGames[0].EGID}"); - retry = false; - try - { - //myGames[0].Description = "test msg pls ignore"; // make sure game exists first - Logging.MetaDebugLog($"Entering game {myGames[0].Name}"); - myGames[0].EnterGame(); - } - catch (Exception e) - { - Logging.MetaDebugLog($"Failed to enter game; exception: {e}"); - retry = true; - } - } - else - { - Logging.MetaDebugLog("MyGames not populated yet :("); - } - } - - [HarmonyPatch] - public class MinimumSpecsPatch - { - public static bool Prefix() - { - return false; - } - - public static MethodInfo TargetMethod() - { - return ((Action) MinimumSpecsCheck.CheckRequirementsMet).Method; - } - } - - [HarmonyPatch] - public class BugHuntPatch - { - public static MethodInfo method = - SymbolExtensions.GetMethodInfo(str => Console.WriteLine(str)); - - public static IEnumerable Transpiler(IEnumerable instructions) - { - int i = 0; - foreach (var instruction in instructions) - { - i++; - yield return instruction; //Return the instruction first - //stloc, dup, callvirt - if (instruction.opcode.Name.ToLower().StartsWith("stloc") - || instruction.opcode == OpCodes.Dup - || instruction.opcode == OpCodes.Callvirt) - { - yield return new CodeInstruction(OpCodes.Ldstr, - "Just ran the " + i + ". instruction ending with " + instruction.opcode.Name); - yield return new CodeInstruction(OpCodes.Call, method); - } - } - } - - public static MethodInfo TargetMethod() - { - return AccessTools.Method("RobocraftX.CR.MachineEditing.BoxSelect.CopySelectionEngine:GenerateThumbnail"); - } - } - - [HarmonyPatch] - public class BugHuntPatch2 - { - public static void Prefix(int width, float fieldOfView, Vector3 cameraDirection, Vector3 lightDirection) - { - Console.WriteLine("TakeThumbnail invoked with parameters: " + width + ", " + fieldOfView + ", " + - cameraDirection + ", " + lightDirection); - - GPUInstancerManager manager = GPUInstancerAPI.GetActiveManagers().Find(m => m is GPUInstancerPrefabManager); - Bounds instancesBounds = manager.ComputeInstancesBounds(2); - Console.WriteLine("Bounds: " + instancesBounds); - Console.WriteLine("Size: " + instancesBounds.size); - Console.WriteLine("Size.x < 0: " + (instancesBounds.size.x < 0)); - } - - public static void Postfix(Texture2D __result) - { - Console.WriteLine("TakeThumbnail returned: " + (__result == null ? null : __result.name)); - } - - private delegate Texture2D TakeThumbnailDel(int width, float fieldOfView, Vector3 cameraDirection, - Vector3 lightDirection); - - public static MethodInfo TargetMethod() - { - return ((TakeThumbnailDel) ThumbnailUtility.TakeThumbnail).Method; - } - } - - [HarmonyPatch] - public class BugHuntPatch3 - { - public static void Prefix(int width, int filterLayerMask, GPUInstancerManager manager, - Vector3 cameraPosition, Quaternion cameraRotation, float cameraFov, Vector3 lightDirection, - int cullingLayer) - { - Console.WriteLine("Inner TakeThumbnail invoked with parameters: " + width + ", " + filterLayerMask + - ", " + (manager != null ? manager.name : null) + ", " + cameraPosition + ", " + - cameraRotation + ", " + cameraFov + ", " + lightDirection + ", " + cullingLayer); - } - - private delegate Texture2D TakeThumbnailDel(int width, int filterLayerMask, GPUInstancerManager manager, - Vector3 cameraPosition, Quaternion cameraRotation, float cameraFov, Vector3 lightDirection, - int cullingLayer); - - public static MethodInfo TargetMethod() - { - return ((TakeThumbnailDel) ThumbnailUtility.TakeThumbnail).Method; - } - } - } -#endif -} diff --git a/GamecraftModdingAPI/Utility/AsyncUtils.cs b/GamecraftModdingAPI/Utility/AsyncUtils.cs deleted file mode 100644 index 8fed9ee..0000000 --- a/GamecraftModdingAPI/Utility/AsyncUtils.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Threading.Tasks; - -using Svelto.ECS; - -namespace GamecraftModdingAPI.Utility -{ - public static class AsyncUtils - { - private static AsyncUtilsEngine gameEngine = new AsyncUtilsEngine(); - - /// - /// Waits for entity submission asynchronously. - /// Use after placing a block or otherwise creating things in the game to access their properties. - /// - public static async Task WaitForSubmission() - { - await gameEngine.WaitForSubmission(); - } - - public static async Task WaitForNextFrame() - { - await gameEngine.WaitForNextFrame(); - } - - public static void Setup(EnginesRoot enginesRoot) - { - gameEngine.Setup(enginesRoot.GenerateEntityFunctions(), enginesRoot.GenerateEntityFactory()); - } - - public static void Init() - { - GameEngineManager.AddGameEngine(gameEngine); - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/AsyncUtilsEngine.cs b/GamecraftModdingAPI/Utility/AsyncUtilsEngine.cs deleted file mode 100644 index 56aab89..0000000 --- a/GamecraftModdingAPI/Utility/AsyncUtilsEngine.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections; -using System.Threading.Tasks; - -using RobocraftX.Schedulers; -using Svelto.ECS; -using Svelto.Tasks.ExtraLean; - -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Utility -{ - public class AsyncUtilsEngine : IApiEngine - { - private IEntityFunctions _efu; - private IEntityFactory _efa; - private IEnumerator WaitForSubmissionInternal(IEntityFunctions efu, IEntityFactory efa, - EntitiesDB entitiesDB, TaskCompletionSource task) - { - var waitEnumerator = new WaitForSubmissionEnumerator(efu, efa, entitiesDB); - while (waitEnumerator.MoveNext()) - yield return null; - task.SetResult(null); - } - - private IEnumerator WaitForNextFrameInternal(TaskCompletionSource task) - { - yield return null; - task.SetResult(null); - } - - public Task WaitForSubmission() - { - var task = new TaskCompletionSource(); - WaitForSubmissionInternal(_efu, _efa, entitiesDB, task).RunOn(ExtraLean.EveryFrameStepRunner_TimeStopped); - return task.Task; - } - - public Task WaitForNextFrame() - { - var task = new TaskCompletionSource(); - WaitForNextFrameInternal(task).RunOn(ExtraLean.EveryFrameStepRunner_TimeStopped); - return task.Task; - } - - public void Setup(IEntityFunctions efu, IEntityFactory efa) - { - _efu = efu; - _efa = efa; - } - - public void Ready() - { - } - - public EntitiesDB entitiesDB { get; set; } - public void Dispose() - { - } - - public string Name { get; } = "GamecraftModdingAPIAsyncUtilsGameEngine"; - public bool isRemovable { get; } = false; - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/ExceptionUtil.cs b/GamecraftModdingAPI/Utility/ExceptionUtil.cs deleted file mode 100644 index 63d0fef..0000000 --- a/GamecraftModdingAPI/Utility/ExceptionUtil.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using GamecraftModdingAPI.Events; - -namespace GamecraftModdingAPI.Utility -{ - public static class ExceptionUtil - { - /// - /// Invokes an event in a try-catch block to avoid propagating exceptions. - /// - /// The event to emit, can be null - /// Event sender - /// Event arguments - /// Type of the event arguments - public static void InvokeEvent(EventHandler handler, object sender, T args) - { - try - { - handler?.Invoke(sender, args); - } - catch (Exception e) - { - EventRuntimeException wrappedException = - new EventRuntimeException($"EventHandler with arg type {typeof(T).Name} threw an exception", e); - Logging.LogWarning(wrappedException.ToString()); - } - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/GameClient.cs b/GamecraftModdingAPI/Utility/GameClient.cs deleted file mode 100644 index 98d8ccb..0000000 --- a/GamecraftModdingAPI/Utility/GameClient.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using GamecraftModdingAPI.Blocks; - -namespace GamecraftModdingAPI.Utility -{ - public static class GameClient - { - private static DebugInterfaceEngine _engine = new DebugInterfaceEngine(); - - /// - /// Saves the extra information to be displayed on the debug view. - /// The provided getter function is called each time the view updates so make sure it returns quickly. - /// - /// A global ID for the custom information - /// A function that returns the current information - public static void SetDebugInfo(string id, Func contentGetter) => _engine.SetInfo(id, contentGetter); - - /// - /// Removes an information provided by a plugin. - /// - /// The ID of the custom information - /// - public static bool RemoveDebugInfo(string id) => _engine.RemoveInfo(id); - - public static void Init() - { - GameEngineManager.AddGameEngine(_engine); - } - } -} \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/VersionTracking.cs b/GamecraftModdingAPI/Utility/VersionTracking.cs deleted file mode 100644 index 32b86dc..0000000 --- a/GamecraftModdingAPI/Utility/VersionTracking.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Reflection; - -using RobocraftX.Common; -using Svelto.ECS; -using Svelto.ECS.Serialization; - -using GamecraftModdingAPI.Persistence; -using GamecraftModdingAPI.Events; - -namespace GamecraftModdingAPI.Utility -{ - /// - /// Tracks the API version the current game was built for. - /// For compatibility reasons, this must be enabled before it will work. - /// - [Obsolete] - public static class VersionTracking - { - private static readonly VersionTrackingEngine versionEngine = new VersionTrackingEngine(); - - private static bool isEnabled = false; - - /// - /// Gets the API version saved in the current game. - /// - /// The version. - public static uint GetVersion() - { - if (!isEnabled) return 0u; - return versionEngine.GetGameVersion(); - } - - /// - /// Enable API version tracking. - /// - public static void Enable() - { - if (!SerializerManager.ExistsSerializer(typeof(ModVersionStruct).FullName)) - { - SerializerManager.AddSerializer(new SimpleEntitySerializer( - (_) => { return new EGID[1] { new EGID(0u, ApiExclusiveGroups.versionGroup) }; } - )); - } - EventManager.AddEventEmitter(versionEngine); - isEnabled = true; - } - - /// - /// Disable API version tracking. - /// - public static void Disable() - { - EventManager.AddEventEmitter(versionEngine); - isEnabled = false; - } - - public static void Init() { } - - } - - [Obsolete] - internal class VersionTrackingEngine : IEventEmitterEngine - { - public string Name { get; } = "GamecraftModdingAPIVersionTrackingGameEngine"; - - public EntitiesDB entitiesDB { set; private get; } - - public int type => -1; - - public bool isRemovable => false; - - public IEntityFactory Factory { set; private get; } - - public void Dispose() { } - - public void Ready() - { - EGID egid = new EGID(0u, ApiExclusiveGroups.versionGroup); - if (!entitiesDB.Exists(egid)) - { - Version currentVersion = Assembly.GetExecutingAssembly().GetName().Version; - int v = (currentVersion.Major * 1000) + (currentVersion.Minor); - Factory.BuildEntity(egid).Init(new ModVersionStruct - { - version = (uint)v - }); - } - } - - public uint GetGameVersion() - { - return entitiesDB.QueryUniqueEntity(ApiExclusiveGroups.versionGroup).version; - } - - public void Emit() { } - } - - [Obsolete] - public struct ModVersionStruct : IEntityComponent - { - public uint version; - } - - [Obsolete] - public class ModVersionDescriptor: SerializableEntityDescriptor - { - [HashName("GamecraftModdingAPIVersionV0")] - public class _ModVersionDescriptor : IEntityDescriptor - { - public IComponentBuilder[] componentsToBuild { get; } = new IComponentBuilder[]{ - new SerializableComponentBuilder(((int)SerializationType.Network, new DefaultSerializer()), ((int)SerializationType.Storage, new DefaultSerializer())), - }; - } - } -} diff --git a/README.md b/README.md index fd3bf5a..a4c0457 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# GamecraftModdingAPI +# TechbloxModdingAPI -Unofficial Gamecraft modding API for interfacing Gamecraft from mods. +Unofficial Techblox modding API for interfacing Techblox from mods. -The GamecraftModdingAPI aims to simplify the mods in two ways: +The TechbloxModdingAPI aims to simplify the mods in two ways: - *Ease-of-Use* The API provides convenient ways to do common tasks such as moving blocks and adding commands. All of the Harmony patching is done for you, so you can focus on writing your mod instead of reading swathes of undocumented code. - *Stability* The API aims to be reliable and consistent between versions. -This means your code won't break when the GamecraftModdingAPI or Gamecraft updates. +This means your code won't break when the TechbloxModdingAPI or Techblox updates. For more info, please check out the [official documentation](https://mod.exmods.org). @@ -16,16 +16,16 @@ For more support, join the ExMods [Discord](https://discord.exmods.org). [Please follow the official mod installation guide](https://www.exmods.org/guides/install.html) or use GCMM. ## Development -To get started, create a symbolic link called `ref` in the root of the project, or one folder higher, linking to the Gamecraft install folder. -This will allow your IDE to resolve references to Gamecraft files for building and IDE tools. +To get started, create a symbolic link called `ref` in the root of the project, or one folder higher, linking to the Techblox install folder. +This will allow your IDE to resolve references to Techblox files for building and IDE tools. -GamecraftModdingAPI version numbers follow the [Semantic Versioning guidelines](https://semver.org/). +TechbloxModdingAPI version numbers follow the [Semantic Versioning guidelines](https://semver.org/). ## External Libraries -GamecraftModdingAPI includes [Harmony](https://github.com/pardeike/Harmony) to modify the behaviour of existing Gamecraft code. +TechbloxModdingAPI includes [Harmony](https://github.com/pardeike/Harmony) to modify the behaviour of existing Techblox code. # Disclaimer -This API is an unofficial modification of Gamecraft software, and is not endorsed or supported by FreeJam or Gamecraft. -The GamecraftModdingAPI developer(s) claim no rights on the Gamecraft code referenced within this project. -All code contained in this project is licensed under the [GNU Public License v3](https://git.exmods.org/modtainers/GamecraftModdingAPI/src/branch/master/LICENSE). +This API is an unofficial modification of Techblox software, and is not endorsed or supported by FreeJam or Techblox. +The TechbloxModdingAPI developer(s) claim no rights on the Techblox code referenced within this project. +All code contained in this project is licensed under the [GNU Public License v3](https://git.exmods.org/modtainers/TechbloxModdingAPI/src/branch/master/LICENSE). diff --git a/GamecraftModdingAPI.sln b/TechbloxModdingAPI.sln similarity index 58% rename from GamecraftModdingAPI.sln rename to TechbloxModdingAPI.sln index 6482776..999cac5 100644 --- a/GamecraftModdingAPI.sln +++ b/TechbloxModdingAPI.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29411.108 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GamecraftModdingAPI", "GamecraftModdingAPI\GamecraftModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechbloxModdingAPI", "TechbloxModdingAPI\TechbloxModdingAPI.csproj", "{7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -18,6 +20,12 @@ Global {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Release|Any CPU.Build.0 = Release|Any CPU {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.ActiveCfg = Test|Any CPU {7FD5A7D8-4F3E-426A-B07D-7DC70442A4DF}.Test|Any CPU.Build.0 = Test|Any CPU + {0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Release|Any CPU.Build.0 = Release|Any CPU + {0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Test|Any CPU.ActiveCfg = Debug|Any CPU + {0EBB6400-95A7-4A3D-B2ED-BF31E364CC10}.Test|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GamecraftModdingAPI/App/AppCallbacksTest.cs b/TechbloxModdingAPI/App/AppCallbacksTest.cs similarity index 93% rename from GamecraftModdingAPI/App/AppCallbacksTest.cs rename to TechbloxModdingAPI/App/AppCallbacksTest.cs index 041ac50..c1e184b 100644 --- a/GamecraftModdingAPI/App/AppCallbacksTest.cs +++ b/TechbloxModdingAPI/App/AppCallbacksTest.cs @@ -1,8 +1,8 @@ using System; -using GamecraftModdingAPI.Tests; +using TechbloxModdingAPI.Tests; -namespace GamecraftModdingAPI.App +namespace TechbloxModdingAPI.App { #if TEST /// diff --git a/TechbloxModdingAPI/App/AppEngine.cs b/TechbloxModdingAPI/App/AppEngine.cs new file mode 100644 index 0000000..e4f5dcb --- /dev/null +++ b/TechbloxModdingAPI/App/AppEngine.cs @@ -0,0 +1,44 @@ +using System; + +using RobocraftX.GUI.MyGamesScreen; +using Svelto.ECS; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.App +{ + public class AppEngine : IFactoryEngine + { + public WrappedHandler EnterMenu; + + public WrappedHandler ExitMenu; + + public IEntityFactory Factory { set; private get; } + + public string Name => "TechbloxModdingAPIAppEngine"; + + public bool isRemovable => false; + + public EntitiesDB entitiesDB { set; private get; } + + public void Dispose() + { + IsInMenu = false; + ExitMenu.Invoke(this, new MenuEventArgs { }); + } + + public void Ready() + { + IsInMenu = true; + EnterMenu.Invoke(this, new MenuEventArgs { }); + } + + // app functionality + + public bool IsInMenu + { + get; + private set; + } = false; + } +} diff --git a/GamecraftModdingAPI/App/AppExceptions.cs b/TechbloxModdingAPI/App/AppExceptions.cs similarity index 86% rename from GamecraftModdingAPI/App/AppExceptions.cs rename to TechbloxModdingAPI/App/AppExceptions.cs index 2e67695..dfe3afe 100644 --- a/GamecraftModdingAPI/App/AppExceptions.cs +++ b/TechbloxModdingAPI/App/AppExceptions.cs @@ -1,9 +1,9 @@ using System; using System.Runtime.Serialization; -namespace GamecraftModdingAPI.App +namespace TechbloxModdingAPI.App { - public class AppException : GamecraftModdingAPIException + public class AppException : TechbloxModdingAPIException { public AppException() { diff --git a/GamecraftModdingAPI/App/Client.cs b/TechbloxModdingAPI/App/Client.cs similarity index 81% rename from GamecraftModdingAPI/App/Client.cs rename to TechbloxModdingAPI/App/Client.cs index ca5dcfa..6fe4b4e 100644 --- a/GamecraftModdingAPI/App/Client.cs +++ b/TechbloxModdingAPI/App/Client.cs @@ -4,33 +4,29 @@ using HarmonyLib; using RobocraftX.Services; using UnityEngine; - -using GamecraftModdingAPI.Utility; using RobocraftX.Common; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.App +namespace TechbloxModdingAPI.App { /// - /// The Gamecraft application that is running this code right now. + /// The Techblox application that is running this code right now. /// public class Client { - // extensible engine - protected static AppEngine appEngine = new AppEngine(); - - protected static Func ErrorHandlerInstanceGetter; + protected static Func ErrorHandlerInstanceGetter; protected static Action EnqueueError; protected static Action HandleErrorClosed; - /// + /// /// An event that fires whenever the main menu is loaded. /// public static event EventHandler EnterMenu - { - add => appEngine.EnterMenu += value; - remove => appEngine.EnterMenu -= value; + { + add => Game.menuEngine.EnterMenu += value; + remove => Game.menuEngine.EnterMenu -= value; } /// @@ -38,12 +34,12 @@ namespace GamecraftModdingAPI.App /// public static event EventHandler ExitMenu { - add => appEngine.ExitMenu += value; - remove => appEngine.ExitMenu -= value; + add => Game.menuEngine.ExitMenu += value; + remove => Game.menuEngine.ExitMenu -= value; } /// - /// Gamecraft build version string. + /// Techblox build version string. /// Usually this is in the form YYYY.mm.DD.HH.MM.SS /// /// The version. @@ -70,23 +66,23 @@ namespace GamecraftModdingAPI.App { get { - if (!appEngine.IsInMenu) return new Game[0]; - return appEngine.GetMyGames(); + if (!Game.menuEngine.IsInMenu) return Array.Empty(); + return Game.menuEngine.GetMyGames(); } } /// - /// Whether Gamecraft is in the Main Menu + /// Whether Techblox is in the Main Menu /// /// true if in menu; false when loading or in a game. public bool InMenu { - get => appEngine.IsInMenu; + get => Game.menuEngine.IsInMenu; } /// /// Open a popup which prompts the user to click a button. - /// This reuses Gamecraft's error dialog popup + /// This reuses Techblox's error dialog popup /// /// The popup to display. Use an instance of SingleChoicePrompt or DualChoicePrompt. public void PromptUser(Error popup) @@ -111,17 +107,15 @@ namespace GamecraftModdingAPI.App // this would have been so much simpler if this didn't involve a bunch of internal fields & classes Type errorHandler = AccessTools.TypeByName("RobocraftX.Services.ErrorHandler"); Type errorHandle = AccessTools.TypeByName("RobocraftX.Services.ErrorHandle"); - ErrorHandlerInstanceGetter = (Func) AccessTools.Method("GamecraftModdingAPI.App.Client:GenInstanceGetter") + ErrorHandlerInstanceGetter = (Func) AccessTools.Method("TechbloxModdingAPI.App.Client:GenInstanceGetter") .MakeGenericMethod(errorHandler) .Invoke(null, new object[0]); - EnqueueError = (Action) AccessTools.Method("GamecraftModdingAPI.App.Client:GenEnqueueError") + EnqueueError = (Action) AccessTools.Method("TechbloxModdingAPI.App.Client:GenEnqueueError") .MakeGenericMethod(errorHandler, errorHandle) .Invoke(null, new object[0]); - /*HandleErrorClosed = (Action) AccessTools.Method("GamecraftModdingAPI.App.Client:GenHandlePopupClosed") + /*HandleErrorClosed = (Action) AccessTools.Method("TechbloxModdingAPI.App.Client:GenHandlePopupClosed") .MakeGenericMethod(errorHandler) .Invoke(null, new object[0]);*/ - // register engines - MenuEngineManager.AddMenuEngine(appEngine); } // Creating delegates once is faster than reflection every time diff --git a/GamecraftModdingAPI/App/ClientAlertTest.cs b/TechbloxModdingAPI/App/ClientAlertTest.cs similarity index 95% rename from GamecraftModdingAPI/App/ClientAlertTest.cs rename to TechbloxModdingAPI/App/ClientAlertTest.cs index 23ced76..20d8201 100644 --- a/GamecraftModdingAPI/App/ClientAlertTest.cs +++ b/TechbloxModdingAPI/App/ClientAlertTest.cs @@ -3,9 +3,9 @@ using HarmonyLib; using RobocraftX.Services; -using GamecraftModdingAPI.Tests; +using TechbloxModdingAPI.Tests; -namespace GamecraftModdingAPI.App +namespace TechbloxModdingAPI.App { #if TEST /// diff --git a/TechbloxModdingAPI/App/CurrentGameMode.cs b/TechbloxModdingAPI/App/CurrentGameMode.cs new file mode 100644 index 0000000..85b3e09 --- /dev/null +++ b/TechbloxModdingAPI/App/CurrentGameMode.cs @@ -0,0 +1,23 @@ +namespace TechbloxModdingAPI.App +{ + public enum CurrentGameMode + { + None, + /// + /// Building a game + /// + Build, + /// + /// Playing a game + /// + Play, + /// + /// Viewing a prefab + /// + View, + /// + /// Viewing a tutorial + /// + Tutorial + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/App/Game.cs b/TechbloxModdingAPI/App/Game.cs similarity index 75% rename from GamecraftModdingAPI/App/Game.cs rename to TechbloxModdingAPI/App/Game.cs index d6a4eff..7f52965 100644 --- a/GamecraftModdingAPI/App/Game.cs +++ b/TechbloxModdingAPI/App/Game.cs @@ -7,12 +7,12 @@ using RobocraftX.GUI.MyGamesScreen; using RobocraftX.StateSync; using Svelto.ECS; -using GamecraftModdingAPI; -using GamecraftModdingAPI.Blocks; -using GamecraftModdingAPI.Tasks; -using GamecraftModdingAPI.Utility; +using TechbloxModdingAPI; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Tasks; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.App +namespace TechbloxModdingAPI.App { /// /// An in-game save. @@ -23,7 +23,7 @@ namespace GamecraftModdingAPI.App { // extensible engines protected static GameGameEngine gameEngine = new GameGameEngine(); - protected static GameMenuEngine menuEngine = new GameMenuEngine(); + protected internal static GameMenuEngine menuEngine = new GameMenuEngine(); protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine(); protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine(); @@ -33,7 +33,7 @@ namespace GamecraftModdingAPI.App private bool hasId = false; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Menu identifier. public Game(uint id) : this(new EGID(id, MyGamesScreenExclusiveGroups.MyGames)) @@ -41,7 +41,7 @@ namespace GamecraftModdingAPI.App } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Menu identifier. public Game(EGID id) @@ -54,7 +54,7 @@ namespace GamecraftModdingAPI.App } /// - /// Initializes a new instance of the class without id. + /// Initializes a new instance of the class without id. /// This is assumed to be the current game. /// public Game() @@ -118,7 +118,7 @@ namespace GamecraftModdingAPI.App /// /// An event that fires right before a game returns to the main menu. - /// At this point, Gamecraft is transitioning state so many things are invalid/unstable here. + /// At this point, Techblox is transitioning state so many things are invalid/unstable here. /// public static event EventHandler Exit { @@ -165,7 +165,7 @@ namespace GamecraftModdingAPI.App { if (!VerifyMode()) return null; if (menuMode) return menuEngine.GetGameInfo(EGID).GameName; - return GameMode.SaveGameDetails.Name; + return gameEngine.GetGameData().saveName; } set @@ -174,11 +174,7 @@ namespace GamecraftModdingAPI.App if (menuMode) { menuEngine.SetGameName(EGID, value); - } - else - { - GameMode.SaveGameDetails.Name = value; - } + } // Save details are directly saved from user input or not changed at all when in game } } @@ -201,11 +197,7 @@ namespace GamecraftModdingAPI.App if (menuMode) { menuEngine.SetGameDescription(EGID, value); - } - else - { - // No description exists in-game - } + } // No description exists in-game } } @@ -219,7 +211,7 @@ namespace GamecraftModdingAPI.App { if (!VerifyMode()) return null; if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath; - return GameMode.SaveGameDetails.Folder; + return gameEngine.GetGameData().gameID; } set @@ -229,11 +221,6 @@ namespace GamecraftModdingAPI.App { menuEngine.GetGameInfo(EGID).SavedGamePath.Set(value); } - else - { - // this likely breaks things - GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, value, GameMode.SaveGameDetails.WorkshopId); - } } } @@ -242,28 +229,16 @@ namespace GamecraftModdingAPI.App /// In most cases this is invalid and returns 0, so this can be ignored. /// /// The workshop identifier. + [Obsolete] public ulong WorkshopId { get { - if (!VerifyMode()) return 0uL; - if (menuMode) return 0uL; // MyGames don't have workshop IDs - return GameMode.SaveGameDetails.WorkshopId; + return 0uL; // Not supported anymore } set { - VerifyMode(); - if (menuMode) - { - // MyGames don't have workshop IDs - // menuEngine.GetGameInfo(EGID).GameName.Set(value); - } - else - { - // this likely breaks things - GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, GameMode.SaveGameDetails.Folder, value); - } } } @@ -335,6 +310,18 @@ namespace GamecraftModdingAPI.App gameEngine.ToggleTimeMode(); } + /// + /// The mode of the game. + /// + public CurrentGameMode Mode + { + get + { + if (menuMode || !VerifyMode()) return CurrentGameMode.None; + return (CurrentGameMode) GameMode.CurrentMode; + } + } + /// /// Load the game save. /// This happens asynchronously, so when this method returns the game not loaded yet. @@ -384,19 +371,20 @@ namespace GamecraftModdingAPI.App /// /// Add information to the in-game debug display. - /// When this object is garbage collected, this debug info is automatically removed. + /// When this object is garbage collected, this debug info is automatically removed. + /// The provided getter function is called each frame so make sure it returns quickly. /// /// Debug info identifier. - /// Content getter. - public void AddDebugInfo(string id, Func contentGetter) + /// A function that returns the current information. + public void AddDebugInfo(string id, Func contentGetter) { - if (!VerifyMode()) return; - if (menuMode) - { - throw new GameNotFoundException("Game object references a menu item but AddDebugInfo only works on the currently-loaded game"); - } - debugOverlayEngine.SetInfo(id, contentGetter); - debugIds.Add(id); + if (!VerifyMode()) return; + if (menuMode) + { + throw new GameNotFoundException("Game object references a menu item but AddDebugInfo only works on the currently-loaded game"); + } + debugOverlayEngine.SetInfo("game_" + id, contentGetter); + debugIds.Add(id); } /// @@ -406,14 +394,36 @@ namespace GamecraftModdingAPI.App /// Debug info identifier. public bool RemoveDebugInfo(string id) { - if (!VerifyMode()) return false; - if (menuMode) - { - throw new GameNotFoundException("Game object references a menu item but RemoveDebugInfo only works on the currently-loaded game"); - } - if (!debugIds.Contains(id)) return false; - debugOverlayEngine.RemoveInfo(id); - return debugIds.Remove(id); + if (!VerifyMode()) return false; + if (menuMode) + { + throw new GameNotFoundException("Game object references a menu item but RemoveDebugInfo only works on the currently-loaded game"); + } + if (!debugIds.Contains(id)) return false; + debugOverlayEngine.RemoveInfo("game_" + id); + return debugIds.Remove(id); + } + + /// + /// Add information to the in-game debug display. + /// This debug info will be present for all games until it is manually removed. + /// The provided getter function is called each frame so make sure it returns quickly. + /// + /// Debug info identifier. + /// A function that returns the current information. + public static void AddPersistentDebugInfo(string id, Func contentGetter) + { + debugOverlayEngine.SetInfo("persistent_" + id, contentGetter); + } + + /// + /// Remove persistent information from the in-game debug display. + /// + /// true, if debug info was removed, false otherwise. + /// Debug info identifier. + public static bool RemovePersistentDebugInfo(string id) + { + return debugOverlayEngine.RemoveInfo("persistent_" + id); } /// @@ -433,11 +443,20 @@ namespace GamecraftModdingAPI.App Block[] blocks = new Block[blockEGIDs.Length]; for (int b = 0; b < blockEGIDs.Length; b++) { - blocks[b] = new Block(blockEGIDs[b]); + blocks[b] = Block.New(blockEGIDs[b]); } return blocks; } + /// + /// Enable the screenshot taker for updating the game's screenshot. Breaks the pause menu in a new save. + /// + public void EnableScreenshotTaker() + { + if (!VerifyMode()) return; + gameEngine.EnableScreenshotTaker(); + } + ~Game() { foreach (string id in debugIds) @@ -466,12 +485,8 @@ namespace GamecraftModdingAPI.App { GameEngineManager.AddGameEngine(gameEngine); GameEngineManager.AddGameEngine(debugOverlayEngine); + GameEngineManager.AddGameEngine(buildSimEventEngine); MenuEngineManager.AddMenuEngine(menuEngine); } - - internal static void InitDeterministic(StateSyncRegistrationHelper stateSyncReg) - { - stateSyncReg.AddDeterministicEngine(buildSimEventEngine); - } } } diff --git a/GamecraftModdingAPI/App/GameBuildSimEventEngine.cs b/TechbloxModdingAPI/App/GameBuildSimEventEngine.cs similarity index 53% rename from GamecraftModdingAPI/App/GameBuildSimEventEngine.cs rename to TechbloxModdingAPI/App/GameBuildSimEventEngine.cs index 4c9a536..67e6769 100644 --- a/GamecraftModdingAPI/App/GameBuildSimEventEngine.cs +++ b/TechbloxModdingAPI/App/GameBuildSimEventEngine.cs @@ -4,19 +4,18 @@ using RobocraftX.Common; using RobocraftX.StateSync; using Svelto.ECS; using Unity.Jobs; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.App +namespace TechbloxModdingAPI.App { public class GameBuildSimEventEngine : IApiEngine, IUnorderedInitializeOnTimeRunningModeEntered, IUnorderedInitializeOnTimeStoppedModeEntered { - public event EventHandler SimulationMode; + public WrappedHandler SimulationMode; - public event EventHandler BuildMode; + public WrappedHandler BuildMode; - public string Name => "GamecraftModdingAPIBuildSimEventGameEngine"; + public string Name => "TechbloxModdingAPIBuildSimEventGameEngine"; public bool isRemovable => false; @@ -28,13 +27,13 @@ namespace GamecraftModdingAPI.App public JobHandle OnInitializeTimeRunningMode(JobHandle inputDeps) { - ExceptionUtil.InvokeEvent(SimulationMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); + SimulationMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); // TODO return inputDeps; } public JobHandle OnInitializeTimeStoppedMode(JobHandle inputDeps) { - ExceptionUtil.InvokeEvent(BuildMode, this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); + BuildMode.Invoke(this, new GameEventArgs { GameName = "", GamePath = "" }); return inputDeps; } } diff --git a/TechbloxModdingAPI/App/GameGameEngine.cs b/TechbloxModdingAPI/App/GameGameEngine.cs new file mode 100644 index 0000000..2bed201 --- /dev/null +++ b/TechbloxModdingAPI/App/GameGameEngine.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; + +using RobocraftX.Common; +using RobocraftX.Schedulers; +using RobocraftX.SimulationModeState; +using Svelto.ECS; +using Svelto.Tasks; +using Svelto.Tasks.Lean; +using RobocraftX.Blocks; +using RobocraftX.Common.Loading; +using RobocraftX.ScreenshotTaker; +using Techblox.Environment.Transition; +using Techblox.GameSelection; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.App +{ + public class GameGameEngine : IApiEngine, IReactOnAddAndRemove + { + public WrappedHandler EnterGame; + + public WrappedHandler ExitGame; + + public string Name => "TechbloxModdingAPIGameInfoMenuEngine"; + + public bool isRemovable => false; + + public EntitiesDB entitiesDB { set; private get; } + + private bool enteredGame; + + public void Dispose() + { + ExitGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID }); + IsInGame = false; + } + + public void Ready() + { + enteredGame = true; + } + + // game functionality + + public bool IsInGame + { + get; + private set; + } = false; + + public void ExitCurrentGame(bool async = false) + { + if (async) + { + ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_TimeRunningAndStopped); + } + else + { + entitiesDB.QueryEntity(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true; + entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID); + } + + } + + public IEnumerator ExitCurrentGameAsync() + { + /* + while (Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING.isStopping) { yield return Yield.It; } + AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToMenu").Invoke(FullGameFields.Instance, new object[0]);*/ + yield return Yield.It; + entitiesDB.QueryEntity(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true; + entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID); + } + + public void SaveCurrentGame() + { + ref GameSceneEntityStruct gses = ref entitiesDB.QueryEntity(CommonExclusiveGroups.GameSceneEGID); + gses.LoadAfterSaving = false; + gses.SaveNow = true; + entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID); + } + + public bool IsTimeRunningMode() + { + return TimeRunningModeUtil.IsTimeRunningMode(entitiesDB); + } + + public bool IsTimeStoppedMode() + { + return TimeRunningModeUtil.IsTimeStoppedMode(entitiesDB); + } + + public void ToggleTimeMode() + { + if (!entitiesDB.FoundInGroups()) + throw new AppStateException("At least one block must exist in the world to enter simulation"); + SwitchAnimationUtil.Start(entitiesDB); + } + + public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid) + { + var allBlocks = entitiesDB.QueryEntities(); + List blockEGIDs = new List(); + foreach (var (blocks, _) in allBlocks) + { + var (buffer, count) = blocks.ToBuffer(); + for (int i = 0; i < count; i++) + { + uint dbid; + if (filter == BlockIDs.Invalid) + dbid = (uint)filter; + else + dbid = entitiesDB.QueryEntity(buffer[i].ID).DBID; + if (dbid == (ulong)filter) + blockEGIDs.Add(buffer[i].ID); + } + } + + return blockEGIDs.ToArray(); + } + + public void EnableScreenshotTaker() + { + ref var local = ref entitiesDB.QueryEntity(ScreenshotTakerEgids.ScreenshotTaker); + if (local.enabled) + return; + local.enabled = true; + entitiesDB.PublishEntityChange(ScreenshotTakerEgids.ScreenshotTaker); + } + + public GameSelectionComponent GetGameData() + { + return entitiesDB.QueryEntity(GameSelectionConstants.GameSelectionEGID); + } + + public void Add(ref LoadingActionEntityStruct entityComponent, EGID egid) + { + } + + public void Remove(ref LoadingActionEntityStruct entityComponent, EGID egid) + { // Finished loading + if (!enteredGame) return; + EnterGame.Invoke(this, new GameEventArgs { GameName = GetGameData().saveName, GamePath = GetGameData().gameID }); + IsInGame = true; + enteredGame = false; + } + } +} diff --git a/TechbloxModdingAPI/App/GameMenuEngine.cs b/TechbloxModdingAPI/App/GameMenuEngine.cs new file mode 100644 index 0000000..ac24e3a --- /dev/null +++ b/TechbloxModdingAPI/App/GameMenuEngine.cs @@ -0,0 +1,204 @@ +using System; +using System.Reflection; +using HarmonyLib; + +using RobocraftX; +using RobocraftX.Common; +using RobocraftX.FrontEnd; +using RobocraftX.GUI; +using RobocraftX.GUI.MyGamesScreen; +using Svelto.ECS; +using Svelto.ECS.Experimental; +using Techblox.GameSelection; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; +using GameMode = RobocraftX.Common.GameMode; + +namespace TechbloxModdingAPI.App +{ + public class GameMenuEngine : IFactoryEngine + { + public WrappedHandler EnterMenu; + + public WrappedHandler ExitMenu; + public IEntityFactory Factory { set; private get; } + + public string Name => "TechbloxModdingAPIGameInfoGameEngine"; + + public bool isRemovable => false; + + public EntitiesDB entitiesDB { set; private get; } + + public GameMenuEngine() + { + MenuEnteredEnginePatch.EnteredExitedMenu = () => + { + if (IsInMenu) + EnterMenu.Invoke(this, new MenuEventArgs { }); + else + ExitMenu.Invoke(this, new MenuEventArgs { }); + }; + } + + public void Dispose() + { + } + + public void Ready() + { + MenuEnteredEnginePatch.IsInMenu = true; // At first it uses ActivateMenu(), then GoToMenu() which is patched + MenuEnteredEnginePatch.EnteredExitedMenu(); + } + + // game functionality + + public bool IsInMenu => MenuEnteredEnginePatch.IsInMenu; + + public Game[] GetMyGames() + { + EntityCollection mgsevs = entitiesDB.QueryEntities(MyGamesScreenExclusiveGroups.MyGames); + var mgsevsB = mgsevs.ToBuffer().buffer; + Game[] games = new Game[mgsevs.count]; + for (int i = 0; i < mgsevs.count; i++) + { + Utility.Logging.MetaDebugLog($"Found game named {mgsevsB[i].GameName}"); + games[i] = new Game(mgsevsB[i].ID); + } + return games; + } + + public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L) + { + EntityInitializer eci = Factory.BuildEntity(id); + eci.Init(new MyGameDataEntityStruct + { + SavedGamePath = new ECSString(path), + ThumbnailId = thumbnailId, + GameName = new ECSString(gameName), + CreatorName = new ECSString(creatorName), + GameDescription = new ECSString(description), + CreatedDate = createdDate, + }); + // entitiesDB.PublishEntityChange(id); // this will always fail + return true; + } + + public uint HighestID() + { + EntityCollection games = entitiesDB.QueryEntities(MyGamesScreenExclusiveGroups.MyGames); + var gamesB = games.ToBuffer().buffer; + uint max = 0; + for (int i = 0; i < games.count; i++) + { + if (gamesB[i].ID.entityID > max) + { + max = gamesB[i].ID.entityID; + } + } + return max; + } + + public bool EnterGame(EGID id) + { + if (!ExistsGameInfo(id)) return false; + ref MyGameDataEntityStruct mgdes = ref GetGameInfo(id); + return EnterGame(mgdes.GameName, mgdes.FileId); + } + + public bool EnterGame(string gameName, string fileId, bool autoEnterSim = false) + { + GameMode.CurrentMode = autoEnterSim ? RCXMode.Play : RCXMode.Build; + var data = new GameSelectionData + { + gameMode = Techblox.GameSelection.GameMode.PlayGame, + gameType = GameType.MachineEditor, + saveName = gameName, + saveType = SaveType.ExistingSave, + gameID = "GAMEID_Road_Track", //TODO: Expose to the API + userContentID = fileId + }; + // the private FullGameCompositionRoot.SwitchToGame() method gets passed to menu items for this reason + AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame").Invoke(FullGameFields.Instance, new object[]{data}); + return true; + } + + public bool SetGameName(EGID id, string name) + { + if (!ExistsGameInfo(id)) return false; + GetGameInfo(id).GameName.Set(name); + GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name); + return true; + } + + public bool SetGameDescription(EGID id, string name) + { + if (!ExistsGameInfo(id)) return false; + GetGameInfo(id).GameDescription.Set(name); + GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name); + return true; + } + + public bool ExistsGameInfo(EGID id) + { + return entitiesDB.Exists(id); + } + + public ref MyGameDataEntityStruct GetGameInfo(EGID id) + { + return ref GetComponent(id); + } + + public dynamic GetGameViewInfo(EGID id) + { + dynamic structOptional = AccessTools.Method("TechbloxModdingAPI.Utility.NativeApiExtensions:QueryEntityOptional", new []{typeof(EntitiesDB), typeof(EGID)}) + .MakeGenericMethod(AccessTools.TypeByName("RobocraftX.GUI.MyGamesScreen.MyGamesSlotEntityViewStruct")) + .Invoke(null, new object[] {entitiesDB, new EGID(id.entityID, MyGamesScreenExclusiveGroups.GameSlotGuiEntities)}); + if (structOptional == null) throw new Exception("Could not get game slot entity"); + return structOptional ? structOptional : null; + } + + public ref T GetComponent(EGID id) where T: unmanaged, IEntityComponent + { + return ref entitiesDB.QueryEntity(id); + } + } + + internal class MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal : GenericEntityDescriptor { } + + [HarmonyPatch] + static class MenuEnteredEnginePatch + { + internal static bool IsInMenu; + internal static Action EnteredExitedMenu; + public static void Postfix() + { + IsInMenu = true; + EnteredExitedMenu(); + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method(typeof(FullGameCompositionRoot), "GoToMenu"); + } + } + + [HarmonyPatch] + static class MenuExitedEnginePatch + { + public static void Prefix() + { + MenuEnteredEnginePatch.IsInMenu = false; + MenuEnteredEnginePatch.EnteredExitedMenu(); + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method(typeof(FullGameCompositionRoot), "SwitchToGame"); + } + } + + public struct MenuEventArgs + { + + } +} diff --git a/GamecraftModdingAPI/App/UserPrompts.cs b/TechbloxModdingAPI/App/UserPrompts.cs similarity index 96% rename from GamecraftModdingAPI/App/UserPrompts.cs rename to TechbloxModdingAPI/App/UserPrompts.cs index 26a9395..7548055 100644 --- a/GamecraftModdingAPI/App/UserPrompts.cs +++ b/TechbloxModdingAPI/App/UserPrompts.cs @@ -3,7 +3,7 @@ using HarmonyLib; using RobocraftX.Services; -namespace GamecraftModdingAPI.App +namespace TechbloxModdingAPI.App { public class DualChoicePrompt : MultiChoiceError { diff --git a/TechbloxModdingAPI/Block.cs b/TechbloxModdingAPI/Block.cs new file mode 100644 index 0000000..ec1147a --- /dev/null +++ b/TechbloxModdingAPI/Block.cs @@ -0,0 +1,495 @@ +using System; +using System.Collections.Generic; + +using DataLoader; +using Gamecraft.Blocks.BlockGroups; +using Svelto.ECS; +using Svelto.ECS.EntityStructs; +using RobocraftX.Common; +using RobocraftX.Blocks; +using Unity.Mathematics; +using Gamecraft.Blocks.GUI; +using HarmonyLib; +using RobocraftX.PilotSeat; + +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Blocks.Engines; +using TechbloxModdingAPI.Tests; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI +{ + /// + /// A single (perhaps scaled) block. Properties may return default values if the block is removed and then setting them is ignored. + /// For specific block type operations, use the specialised block classes in the TechbloxModdingAPI.Blocks namespace. + /// + public class Block : EcsObjectBase, IEquatable, IEquatable + { + protected static readonly PlacementEngine PlacementEngine = new PlacementEngine(); + protected static readonly MovementEngine MovementEngine = new MovementEngine(); + protected static readonly RotationEngine RotationEngine = new RotationEngine(); + protected static readonly RemovalEngine RemovalEngine = new RemovalEngine(); + protected static readonly SignalEngine SignalEngine = new SignalEngine(); + protected static readonly BlockEventsEngine BlockEventsEngine = new BlockEventsEngine(); + protected static readonly ScalingEngine ScalingEngine = new ScalingEngine(); + protected static readonly BlockCloneEngine BlockCloneEngine = new BlockCloneEngine(); + + protected internal static readonly BlockEngine BlockEngine = new BlockEngine(); + + /// + /// Place a new block at the given position. If scaled, position means the center of the block. The default block size is 0.2 in terms of position. + /// Place blocks next to each other to connect them. + /// The placed block will be a complete block with a placement grid and collision which will be saved along with the game. + /// + /// The block's type + /// The block's position - default block size is 0.2 + /// Whether the block should be auto-wired (if functional) + /// The player who placed the block + /// + /// The placed block or null if failed + public static Block PlaceNew(BlockIDs block, float3 position, bool autoWire = false, Player player = null, + bool force = false) + { + if (PlacementEngine.IsInGame && (GameState.IsBuildMode() || force)) + { + var initializer = PlacementEngine.PlaceBlock(block, position, player, autoWire); + var egid = initializer.EGID; + var bl = New(egid); + bl.InitData = initializer; + Placed += bl.OnPlacedInit; + return bl; + } + + return null; + } + + /// + /// Returns the most recently placed block. + /// + /// The block object or null if doesn't exist + public static Block GetLastPlacedBlock() + { + uint lastBlockID = (uint) AccessTools.Field(typeof(CommonExclusiveGroups), "_nextBlockEntityID").GetValue(null) - 1; + EGID? egid = BlockEngine.FindBlockEGID(lastBlockID); + return egid.HasValue ? New(egid.Value) : null; + } + + /*public static Block CreateGhostBlock() + { + return BlockGroup._engine.BuildGhostChild(); + }*/ + + /// + /// An event that fires each time a block is placed. + /// + public static event EventHandler Placed + { //TODO: Rename and add instance version in 3.0 + add => BlockEventsEngine.Placed += value; + remove => BlockEventsEngine.Placed -= value; + } + + /// + /// An event that fires each time a block is removed. + /// + public static event EventHandler Removed + { + add => BlockEventsEngine.Removed += value; + remove => BlockEventsEngine.Removed -= value; + } + + private static readonly Dictionary Constructor, Type Type)> GroupToConstructor = + new Dictionary, Type)> + { + {CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP, (id => new DampedSpring(id), typeof(DampedSpring))}, + {CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP, (id => new Engine(id), typeof(Engine))}, + {CommonExclusiveGroups.LOGIC_BLOCK_GROUP, (id => new LogicGate(id), typeof(LogicGate))}, + {CommonExclusiveGroups.PISTON_BLOCK_GROUP, (id => new Piston(id), typeof(Piston))}, + {CommonExclusiveGroups.SERVO_BLOCK_GROUP, (id => new Servo(id), typeof(Servo))}, + {CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP, (id => new WheelRig(id), typeof(WheelRig))} + }; + + static Block() + { + foreach (var group in SeatGroups.SEATS_BLOCK_GROUPS) // Adds driver and passenger seats, occupied and unoccupied + GroupToConstructor.Add(group, (id => new Seat(id), typeof(Seat))); + } + + /// + /// Returns a correctly typed instance of this block. The instances are shared for a specific block. + /// If an instance is no longer referenced a new instance is returned. + /// + /// The EGID of the block + /// Whether the block is definitely a signaling block + /// + internal static Block New(EGID egid, bool signaling = false) + { + if (egid == default) return null; + if (GroupToConstructor.ContainsKey(egid.groupID)) + { + var (constructor, type) = GroupToConstructor[egid.groupID]; + return GetInstance(egid, constructor, type); + } + + return signaling + ? GetInstance(egid, e => new SignalingBlock(e)) + : GetInstance(egid, e => new Block(e)); + } + + public Block(EGID id) : base(id) + { + Type expectedType; + if (GroupToConstructor.ContainsKey(id.groupID) && + !GetType().IsAssignableFrom(expectedType = GroupToConstructor[id.groupID].Type)) + throw new BlockSpecializationException($"Incorrect block type! Expected: {expectedType} Actual: {GetType()}"); + } + + /// + /// This overload searches for the correct group the block is in. + /// It will throw an exception if the block doesn't exist. + /// Use the EGID constructor where possible or subclasses of Block as those specify the group. + /// + public Block(uint id) : this(BlockEngine.FindBlockEGID(id) + ?? throw new BlockTypeException( + "Could not find the appropriate group for the block." + + " The block probably doesn't exist or hasn't been submitted.")) + { + } + + /// + /// Places a new block in the world. + /// + /// The block's type + /// The block's position (a block is 0.2 wide in terms of position) + /// Whether the block should be auto-wired (if functional) + /// The player who placed the block + /// Place even if not in build mode + public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null, bool force = false) + : base(block => + { + if (!PlacementEngine.IsInGame || !GameState.IsBuildMode() && !force) + throw new BlockException("Blocks can only be placed in build mode."); + var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire); + block.InitData = initializer; + Placed += ((Block)block).OnPlacedInit; + return initializer.EGID; + }) + { + } + + private EGID copiedFrom; + + /// + /// The block's current position or zero if the block no longer exists. + /// A block is 0.2 wide by default in terms of position. + /// + public float3 Position + { + get => MovementEngine.GetPosition(this); + set + { + MovementEngine.MoveBlock(this, value); + if (blockGroup != null) + blockGroup.PosAndRotCalculated = false; + BlockEngine.UpdateDisplayedBlock(Id); + } + } + + /// + /// The block's current rotation in degrees or zero if the block doesn't exist. + /// + public float3 Rotation + { + get => RotationEngine.GetRotation(this); + set + { + RotationEngine.RotateBlock(this, value); + if (blockGroup != null) + blockGroup.PosAndRotCalculated = false; + BlockEngine.UpdateDisplayedBlock(Id); + } + } + + /// + /// The block's non-uniform scale or zero if the block's invalid. Independent of the uniform scaling. + /// The default scale of 1 means 0.2 in terms of position. + /// + public float3 Scale + { + get => BlockEngine.GetBlockInfo(this).scale; + set + { + int uscale = UniformScale; + if (value.x < 4e-5) value.x = uscale; + if (value.y < 4e-5) value.y = uscale; + if (value.z < 4e-5) value.z = uscale; + BlockEngine.GetBlockInfo(this).scale = value; + //BlockEngine.GetBlockInfo(this).gridScale = value - (int3) value + 1; + if (!Exists) return; //UpdateCollision needs the block to exist + ScalingEngine.UpdateCollision(Id); + BlockEngine.UpdateDisplayedBlock(Id); + } + } + + /// + /// The block's uniform scale or zero if the block's invalid. Also sets the non-uniform scale. + /// The default scale of 1 means 0.2 in terms of position. + /// + public int UniformScale + { + get => BlockEngine.GetBlockInfo(this).scaleFactor; + set + { + if (value < 1) value = 1; + BlockEngine.GetBlockInfo(this).scaleFactor = value; + Scale = new float3(value, value, value); + } + } + + /** + * Whether the block is flipped. + */ + public bool Flipped + { + get => BlockEngine.GetBlockInfo(this).scale.x < 0; + set + { + ref var st = ref BlockEngine.GetBlockInfo(this); + st.scale.x = math.abs(st.scale.x) * (value ? -1 : 1); + BlockEngine.UpdatePrefab(this, (byte) Material, value); + } + } + + /// + /// The block's type (ID). Returns BlockIDs.Invalid if the block doesn't exist anymore. + /// + public BlockIDs Type + { + get + { + var opt = BlockEngine.GetBlockInfoOptional(this); + return opt ? (BlockIDs) opt.Get().DBID : BlockIDs.Invalid; + } + } + + /// + /// The block's color. Returns BlockColors.Default if the block no longer exists. + /// + public BlockColor Color + { + get + { + var opt = BlockEngine.GetBlockInfoOptional(this); + return new BlockColor(opt ? opt.Get().indexInPalette : byte.MaxValue); + } + set + { + if (value.Color == BlockColors.Default) + value = new BlockColor(FullGameFields._dataDb.TryGetValue((int) Type, out CubeListData cld) + ? cld.DefaultColour + : throw new BlockTypeException("Unknown block type! Could not set default color.")); + ref var color = ref BlockEngine.GetBlockInfo(this); + color.indexInPalette = value.Index; + color.hasNetworkChange = true; + color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette); //Setting to 255 results in black + } + } + + /// + /// The block's exact color. Gets reset to the palette color (Color property) after reentering the game. + /// + public float4 CustomColor + { + get => BlockEngine.GetBlockInfo(this).paletteColour; + set + { + ref var color = ref BlockEngine.GetBlockInfo(this); + color.paletteColour = value; + color.hasNetworkChange = true; + } + } + + /** + * The block's material. + */ + public BlockMaterial Material + { + get + { + var opt = BlockEngine.GetBlockInfoOptional(this); + return opt ? (BlockMaterial) opt.Get().materialId : BlockMaterial.Default; + } + set + { + byte val = (byte) value; + if (value == BlockMaterial.Default) + val = FullGameFields._dataDb.TryGetValue((int) Type, out CubeListData cld) + ? cld.DefaultMaterialID + : throw new BlockTypeException("Unknown block type! Could not set default material."); + if (!FullGameFields._dataDb.ContainsKey(val)) + throw new BlockException($"Block material {value} does not exist!"); + ref var comp = ref BlockEngine.GetBlockInfo(this); + if (comp.materialId == val) + return; + comp.materialId = val; + BlockEngine.UpdatePrefab(this, val, Flipped); //The default causes the screen to go black + } + } + + /// + /// The text displayed on the block if applicable, or null. + /// Setting it is temporary to the session, it won't be saved. + /// + [TestValue(null)] + public string Label + { + get => BlockEngine.GetBlockInfoViewComponent(this).textLabelComponent?.text; + set + { + var comp = BlockEngine.GetBlockInfoViewComponent(this).textLabelComponent; + if (comp != null) comp.text = value; + } + } + + private BlockGroup blockGroup; + /// + /// Returns the block group this block is a part of. Block groups can also be placed using blueprints. + /// Returns null if not part of a group, although all blocks should have their own by default.
+ /// Setting the group after the block has been initialized will not update everything properly, + /// so you can only set this property on blocks newly placed by your code.
+ /// To set it for existing blocks, you can use the Copy() method and set the property on the resulting block + /// (and remove this block). + ///
+ public BlockGroup BlockGroup + { + get + { + if (blockGroup != null) return blockGroup; + var bgec = BlockEngine.GetBlockInfo(this); + return blockGroup = bgec.currentBlockGroup == -1 ? null : new BlockGroup(bgec.currentBlockGroup, this); + } + set + { + if (Exists) + { + Logging.LogWarning("Attempted to set group of existing block. This is not supported." + + " Copy the block and set the group of the resulting block."); + return; + } + blockGroup?.RemoveInternal(this); + if (!InitData.Valid) + return; + BlockEngine.GetBlockInfo(this).currentBlockGroup = (int?) value?.Id.entityID ?? -1; + value?.AddInternal(this); + blockGroup = value; + } + } + + /// + /// Whether the block should be static in simulation. If set, it cannot be moved. The effect is temporary, it will not be saved with the block. + /// + public bool Static + { + get => BlockEngine.GetBlockInfo(this).isStatic; + set => BlockEngine.GetBlockInfo(this).isStatic = value; + } + + /// + /// Whether the block exists. The other properties will return a default value if the block doesn't exist. + /// If the block was just placed, then this will also return false but the properties will work correctly. + /// + public bool Exists => BlockEngine.BlockExists(Id); + + /// + /// Returns an array of blocks that are connected to this one. Returns an empty array if the block doesn't exist. + /// + public Block[] GetConnectedCubes() => BlockEngine.GetConnectedBlocks(Id); + + /// + /// Removes this block. + /// + /// True if the block exists and could be removed. + public bool Remove() => RemovalEngine.RemoveBlock(Id); + + /// + /// Returns the rigid body of the chunk of blocks this one belongs to during simulation. + /// Can be used to apply forces or move the block around while the simulation is running. + /// + /// The SimBody of the chunk or null if the block doesn't exist or not in simulation mode. + public SimBody GetSimBody() + { + var st = BlockEngine.GetBlockInfo(this); + return st.machineRigidBodyId != uint.MaxValue + ? new SimBody(st.machineRigidBodyId, st.clusterId) + : null; + } + + /// + /// Creates a copy of the block in the game with the same properties, stats and wires. + /// + /// + public Block Copy() + { + var block = PlaceNew(Type, Position); + block.Rotation = Rotation; + block.Color = Color; + block.Material = Material; + block.UniformScale = UniformScale; + block.Scale = Scale; + block.copiedFrom = Id; + return block; + } + + private void OnPlacedInit(object sender, BlockPlacedRemovedEventArgs e) + { //Member method instead of lambda to avoid constantly creating delegates + if (e.ID != Id) return; + Placed -= OnPlacedInit; //And we can reference it + InitData = default; //Remove initializer as it's no longer valid - if the block gets removed it shouldn't be used again + if (copiedFrom != default) + BlockCloneEngine.CopyBlockStats(copiedFrom, Id); + } + + public override string ToString() + { + return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Type)}: {Type}, {nameof(Color)}: {Color}, {nameof(Exists)}: {Exists}"; + } + + public bool Equals(Block other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Id.Equals(other.Id); + } + + public bool Equals(EGID other) + { + return Id.Equals(other); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Block) obj); + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + public static void Init() + { + GameEngineManager.AddGameEngine(PlacementEngine); + GameEngineManager.AddGameEngine(MovementEngine); + GameEngineManager.AddGameEngine(RotationEngine); + GameEngineManager.AddGameEngine(RemovalEngine); + GameEngineManager.AddGameEngine(BlockEngine); + GameEngineManager.AddGameEngine(BlockEventsEngine); + GameEngineManager.AddGameEngine(ScalingEngine); + GameEngineManager.AddGameEngine(SignalEngine); + GameEngineManager.AddGameEngine(BlockCloneEngine); + Wire.signalEngine = SignalEngine; // requires same functionality, no need to duplicate the engine + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/BlockGroup.cs b/TechbloxModdingAPI/BlockGroup.cs similarity index 71% rename from GamecraftModdingAPI/BlockGroup.cs rename to TechbloxModdingAPI/BlockGroup.cs index d660236..82f0be0 100644 --- a/GamecraftModdingAPI/BlockGroup.cs +++ b/TechbloxModdingAPI/BlockGroup.cs @@ -1,39 +1,62 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using Gamecraft.Blocks.BlockGroups; +using Svelto.ECS; using Unity.Mathematics; using UnityEngine; -using GamecraftModdingAPI.Blocks; -using GamecraftModdingAPI.Utility; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Blocks.Engines; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI +namespace TechbloxModdingAPI { /// - /// A group of blocks that can be selected together. The placed version of blueprints. + /// A group of blocks that can be selected together. The placed version of blueprints. Dispose after usage. /// - public class BlockGroup : ICollection + public class BlockGroup : EcsObjectBase, ICollection, IDisposable { internal static BlueprintEngine _engine = new BlueprintEngine(); - public int Id { get; } private readonly Block sourceBlock; private readonly List blocks; private float3 position, rotation; internal bool PosAndRotCalculated; - internal BlockGroup(int id, Block block) + internal BlockGroup(int id, Block block) : base(new EGID((uint)id, + BlockGroupExclusiveGroups.BlockGroupEntityGroup)) { if (id == BlockGroupUtility.GROUP_UNASSIGNED) throw new BlockException("Cannot create a block group for blocks without a group!"); - Id = id; sourceBlock = block; blocks = new List(GetBlocks()); + Block.Removed += OnBlockRemoved; } - + + private void OnBlockRemoved(object sender, BlockPlacedRemovedEventArgs e) + { + //blocks.RemoveAll(block => block.Id == e.ID); - Allocation heavy + int index = -1; + for (int i = 0; i < blocks.Count; i++) + { + if (blocks[i].Id == e.ID) + { + index = i; + break; + } + } + + if (index != -1) blocks.RemoveAt(index); + } + + public void Dispose() + { + Block.Removed -= OnBlockRemoved; + } + /// - /// The position of the block group (center). Recalculated if blocks have been added/removed since the last query. + /// The position of the block group (center). Can only be used after initialization is complete. /// public float3 Position { @@ -56,7 +79,7 @@ namespace GamecraftModdingAPI } /// - /// The rotation of the block group. Recalculated if blocks have been added/removed since the last query. + /// The rotation of the block group. Can only be used after initialization is complete. /// public float3 Rotation { @@ -93,13 +116,15 @@ namespace GamecraftModdingAPI /// /// Creates a new block group consisting of a single block. /// You can add more blocks using the Add() method or by setting the BlockGroup property of the blocks.
- /// Note that only newly placed blocks should be added to groups. + /// Note that only newly placed blocks can be added to groups. ///
/// The block to add /// A new block group containing the given block public static BlockGroup Create(Block block) { - return new BlockGroup(_engine.CreateBlockGroup(default, default), block); + var bg = new BlockGroup(_engine.CreateBlockGroup(block.Position, Quaternion.Euler(block.Rotation)), block); + block.BlockGroup = bg; + return bg; } /// @@ -131,7 +156,7 @@ namespace GamecraftModdingAPI IEnumerator IEnumerable.GetEnumerator() => blocks.GetEnumerator(); /// - /// Adds a block to the group. You should only add newly placed blocks + /// Adds a block to the group. You can only add newly placed blocks /// so that the game initializes the group membership properly. /// /// @@ -142,11 +167,15 @@ namespace GamecraftModdingAPI item.BlockGroup = this; //Calls AddInternal } - internal void AddInternal(Block item) => blocks.Add(item); + internal void AddInternal(Block item) + { + blocks.Add(item); + _engine.AddBlockToGroup(item.Id, (int) Id.entityID); + } /// /// Removes all blocks from this group. - /// You should not remove blocks that have been initialized, only those that you placed recently. + /// You cannot remove blocks that have been initialized, only those that you placed recently. /// public void Clear() { @@ -159,7 +188,7 @@ namespace GamecraftModdingAPI /// /// Removes a block from this group. - /// You should not remove blocks that have been initialized, only those that you placed recently. + /// You cannot remove blocks that have been initialized, only those that you placed recently. /// /// /// @@ -179,5 +208,10 @@ namespace GamecraftModdingAPI public bool IsReadOnly { get; } = false; public Block this[int index] => blocks[index]; //Setting is not supported, since the order doesn't matter + + public override string ToString() + { + return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}, {nameof(Count)}: {Count}"; + } } } \ No newline at end of file diff --git a/TechbloxModdingAPI/Blocks/BlockColor.cs b/TechbloxModdingAPI/Blocks/BlockColor.cs new file mode 100644 index 0000000..42e1f9b --- /dev/null +++ b/TechbloxModdingAPI/Blocks/BlockColor.cs @@ -0,0 +1,64 @@ +using System; +using Unity.Mathematics; + +namespace TechbloxModdingAPI.Blocks +{ + public struct BlockColor + { + public BlockColors Color => Index == byte.MaxValue + ? BlockColors.Default + : (BlockColors) (Index % 10); + + public byte Darkness => (byte) (Index == byte.MaxValue + ? 0 + : Index / 10); + + public byte Index { get; } + + public BlockColor(byte index) + { + if (index > 99 && index != byte.MaxValue) + throw new ArgumentOutOfRangeException(nameof(index), "Invalid color index. Must be 0-90 or 255."); + Index = index; + } + + public BlockColor(BlockColors color, byte darkness = 0) + { + if (darkness > 9) + throw new ArgumentOutOfRangeException(nameof(darkness), "Darkness must be 0-9 where 0 is default."); + if (color > BlockColors.Red && color != BlockColors.Default) //Last valid color + throw new ArgumentOutOfRangeException(nameof(color), "Invalid color!"); + Index = (byte) (darkness * 10 + (byte) color); + } + + public static implicit operator BlockColor(BlockColors color) + { + return new BlockColor(color); + } + + public float4 RGBA => Block.BlockEngine.ConvertBlockColor(Index); + + public override string ToString() + { + return $"{nameof(Color)}: {Color}, {nameof(Darkness)}: {Darkness}"; + } + } + + /// + /// Preset block colours + /// + public enum BlockColors : byte + { + Default = byte.MaxValue, + White = 0, + Pink, + Purple, + Blue, + Aqua, + Green, + Lime, + Yellow, + Orange, + Red + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockExceptions.cs b/TechbloxModdingAPI/Blocks/BlockExceptions.cs similarity index 88% rename from GamecraftModdingAPI/Blocks/BlockExceptions.cs rename to TechbloxModdingAPI/Blocks/BlockExceptions.cs index 9949424..4029185 100644 --- a/GamecraftModdingAPI/Blocks/BlockExceptions.cs +++ b/TechbloxModdingAPI/Blocks/BlockExceptions.cs @@ -1,10 +1,10 @@ using System; -using GamecraftModdingAPI; +using TechbloxModdingAPI; -namespace GamecraftModdingAPI.Blocks +namespace TechbloxModdingAPI.Blocks { - public class BlockException : GamecraftModdingAPIException + public class BlockException : TechbloxModdingAPIException { public BlockException() { diff --git a/TechbloxModdingAPI/Blocks/BlockIDs.cs b/TechbloxModdingAPI/Blocks/BlockIDs.cs new file mode 100644 index 0000000..e454c40 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/BlockIDs.cs @@ -0,0 +1,278 @@ +namespace TechbloxModdingAPI.Blocks +{ + /// + /// Possible block types + /// + public enum BlockIDs : ushort + { + /// + /// Called "nothing" in Techblox. (DBID.NOTHING) + /// + Invalid = ushort.MaxValue, + Cube = 0, + Wedge, + QuarterPyramid, + Tetrahedron, + RoundedWedge, + RoundedQuarterPyramid, + RoundedTetrahedron, + NegativeQuarterPyramid, + NegativeTetrahedron, + RoundedNegativeQuarterPyramid, + RoundedNegativeTetrahedron, + Plate, + PlateWedge, + PlateQuarterPyramid, + PlateTetrahedron, + Sphere, + CarWheelArch = 47, + CarArchSmallFlare, + CarArchFlare, + CarArchExtrudedFlare, + Axle = 100, + Hinge, + BallJoint, + UniversalJoint, + TelescopicJoint, + DampedHingeSpring, + DampedAxleSpring, + DampedSpring, + WheelRigNoSteering, + WheelRigWithSteering, + PlateTriangle = 130, + PlateCircle, + PlateQuarterCircle, + PlateRoundedWedge, + PlateRoundedTetrahedron, + Cone, + ConeSegment, + DoubleSliced, + HalfDoubleSliced, + EighthPyramid, + Hemisphere, + WideCylinder, + WideCylinderBend, + WideCylinderT, + WideCylinderCross, + WideCylinderCorner, + NarrowCylinder, + NarrowCylinderBend, + NarrowCylinderT, + NarrowCylinderCross, + DriverSeat, + PassengerSeat, + Engine, + NarrowCylinderCorner, + PlateWideCylinder, + PlateNarrowCylinder, + PlateNegativeTetrahedron, + PlateNegativeQuarterPyramid, + PlateRoundedNegativeTetrahedron, + PlateRoundedNegativeQuarterPyramid, + HeadlampSquare, + HeadlampCircle, + HeadlampWedge, + WideCylinderDiagonal, + NarrowCylinderDiagonal, + HeadlampTetrahedron, + GoKartEngine, + Screen5X2Y2Z, + Screen5X2Y3Z, + Screen5X2Y5Z, + Screen9X2Y2Z, + Screen9X3Y2Z, + Screen9X2Y3Z, + Screen9X3Y3Z, + Screen9X2Y5Z, + Screen9X3Y5Z, + Screen11X3Y2Z, + Screen11X3Y3Z, + Screen11X3Y5Z, + Window6X2Y2Z, + Window6X3Y2Z, + Window6X2Y2ZS1, + Window6X3Y2ZS1, + Window6X2Y2ZS2, + Window6X3Y2ZS2, + Window6X2Y2ZS4, + Window6X3Y2ZS4, + FrameSquare, + FrameSkewedSquare, + FrameTriangle, + FrameSkewedTriangle, + GlassFrameSquare, + GlassFrameSkewedSquare, + GlassFrameTriangle, + GlassFrameSkewedTriangle, + GlassPlate, + GlassPlateTriangle, + GoKartWheelRigNoSteering, + GoKartWheelRigWithSteering, + GoKartSeat, + CarWheelWideProfile, + CarWheel, + GoKartWheelWideProfile, + GoKartWheel, + ANDLogicGate, + ORLogicGate, + NOTLogicGate, + NANDLogicGate, + NORLogicGate, + XORLogicGate, + XNORLogicGate, + AdderMathBlock, + SubtractorMathBlock, + MultiplierMathBlock, + DividerMathBlock, + InverterMathBlock, + AverageMathBlock, + AbsoluteMathBlock, + MinMathBlock, + MaxMathBlock, + SimpleConnector, + Motor, + AxleServo, + HingeServo, + Piston, + Button, + Switch, + Dial, + Lever, + ThreeWaySwitch, + EqualsMathBlock, + LessThanMathBlock, + LessThanOrEqualMathBlock, + GreaterThanMathBlock, + GreaterThanOrEqualMathBlock, + HatchbackWheelRigNoSteering, + HatchbackWheelRigWithSteering, + HatchbackEngine, + HatchbackWheel, + HatchbackWheelArch, + HatchbackArchSmallFlare, + HatchbackArchFlare, + CeilingStripLight, + CardboardBox, + BarrierRail, + BarrierRailEnd, + TruckWheel, + HatchbackWheelWideProfile, + TruckWheelRigWithSteering = 249, + TruckWheelRigNoSteering, + HatchbackDriverSeat, + HatchbackPassengerSeat, + FormulaEngine, + SmallGrass, + SmallGrassRoad, + GrassBridge, + SmallGrassTurn, + MediumGrassTurn, + LargeGrassTurn, + ExtraLargeGrassTurn, + TruckWheelDouble, + TruckWheelArch, + TruckArchSingleFlare, + WoodenDoorWithWindow, + TyreBarrierCorner, + TyreBarrierEdge, + TyreBarrierCenter, + AppleTree, + AppleForestTree, + FormulaWheel, + FormulaWheelRear, + AppleSapling, + GrassHill, + GrassHillInnerCorner, + GrassHillOuterCorner, + GrassRoadHill, + FormulaSeat, + SmallDirt, + SmallDirtRoad, + SmallDirtTurn, + MediumDirtTurn, + LargeDirtTurn, + ExtraLargeDirtTurn, + SmallGrid, + MonsterTruckWheel, + SmallGrassGridStart, + SmallGrassRumbleStripRoad, + SmallGrassRumbleStripEndRoad, + SmallGrassStartLine, + MonsterTruckEngine, + DirtHill, + DirtHillInnerCorner, + DirtHillOuterCorner, + BuildingWindowEdge, + BuildingWindowCorner, + BuildingWindowStraight, + BuildingWindowTJunction, + BuildingWindowCross, + BuildingWindowEdgeSill, + BuildingWindowCornerSill, + BuildingWindowTJunctionSill, + Broadleaf, + ForestBroadleaf, + AzaleaBush, + AzaleaFlowers1, + AzaleaFlowers2, + TreeStump1, + TreeStump2, + FieldJuniper, + ForestJuniper, + JuniperSapling, + JuniperSeedling, + FieldRedMaple, + RedMapleForest1, + RedMapleForest2, + RedMapleSapling, + FieldWhiteSpruce, + ForestWhiteSpruce, + WhiteSpruceSapling, + GirderBase, + GirderStraight, + GirderDiagonal, + GirderCorner, + PostBase, + PostStraight, + PostLShape, + PostTJunction, + PostCross, + PostCorner, + PostDiagonal, + DirtRock1, + DirtRock2, + DirtRock3, + DirtRock4, + DirtRoadHill, + WoodenPalette, + ElderberryBush, + BarrelCactus, + KnapweedFlower, + MarigoldFlowers, + TrampledBushyBluestep, + RoughGrass, + DogRose, + WesternSwordFern, + BackyardGrass, + ThickGrass, + FireExtinguisher, + DirtLowRamp, + DirtTabletopRamp, + MonsterTruckWheelRigNoSteering, + MonsterTruckWheelRigWithSteering, + MeadowCloudyDayAtmosphere, + BarrierRailDiagonal, + DirtHighRamp, + GrassRock1, + GrassRock2, + GrassRock3, + GrassRock4, + GreenFieldsSunnyDayAtmosphere, + RedMountainsDawnAtmosphere, + HighFantasySunriseAtmosphere, + /// + /// The grid block used by the world editor, named Small Grid like the other one + /// + SmallGridInWorldEditor + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Blocks/BlockMaterial.cs b/TechbloxModdingAPI/Blocks/BlockMaterial.cs new file mode 100644 index 0000000..777845a --- /dev/null +++ b/TechbloxModdingAPI/Blocks/BlockMaterial.cs @@ -0,0 +1,35 @@ +namespace TechbloxModdingAPI.Blocks +{ + public enum BlockMaterial : byte + { + Default = byte.MaxValue, + SteelBodywork = 0, + RigidSteel, + CarbonFiber, + Plastic, + Wood = 6, + RigidSteelPainted, + RigidSteelRustedPaint, + RigidSteelHeavyRust, + SteelBodyworkMetallicPaint, + SteelBodyworkRustedPaint, + SteelBodyworkHeavyRust, + WoodVarnishedDark, + Chrome, + FenceChainLink, + ConcreteUnpainted, + Grid9x9, + CeramicTileFloor, + PlasticBumpy, + PlasticDustySmeared, + AluminiumGarageDoor, + SteelRigidScratched, + AluminiumBrushedTinted, + AluminiumSheetStained, + ConcretePaintedGrooves, + PlasticSpecklySatin, + SteelBodyworkPaintedChipped, + WoodPainted, + WoodRoughGrungy, + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Blocks/BlockTests.cs b/TechbloxModdingAPI/Blocks/BlockTests.cs new file mode 100644 index 0000000..4bc29db --- /dev/null +++ b/TechbloxModdingAPI/Blocks/BlockTests.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using DataLoader; +using Svelto.Tasks; +using Svelto.Tasks.Enumerators; +using Unity.Mathematics; +using UnityEngine; + +using TechbloxModdingAPI.App; +using TechbloxModdingAPI.Tests; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Blocks +{ +#if TEST + /// + /// Block test cases. Not accessible in release versions. + /// + [APITestClass] + public static class BlockTests + { + [APITestCase(TestType.Game)] //At least one block must be placed for simulation to work + public static void TestPlaceNew() + { + Block newBlock = Block.PlaceNew(BlockIDs.Cube, float3.zero); + Assert.NotNull(newBlock.Id, "Newly placed block is missing Id. This should be populated when the block is placed.", "Newly placed block Id is not null, block successfully placed."); + } + + [APITestCase(TestType.EditMode)] + public static void TestInitProperty() + { + Block newBlock = Block.PlaceNew(BlockIDs.Cube, float3.zero + 2); + if (!Assert.CloseTo(newBlock.Position, (float3.zero + 2), $"Newly placed block at {newBlock.Position} is expected at {Unity.Mathematics.float3.zero + 2}.", "Newly placed block position matches.")) return; + //Assert.Equal(newBlock.Exists, true, "Newly placed block does not exist, possibly because Sync() skipped/missed/failed.", "Newly placed block exists, Sync() successful."); + } + + [APITestCase(TestType.EditMode)] + public static void TestBlockIDCoverage() + { + Assert.Equal( + FullGameFields._dataDb.GetValues().Keys.Select(ushort.Parse).OrderBy(id => id) + .SequenceEqual(Enum.GetValues(typeof(BlockIDs)).Cast().OrderBy(id => id) + .Except(new[] {(ushort) BlockIDs.Invalid})), true, + "Block ID enum is different than the known block types, update needed.", + "Block ID enum matches the known block types."); + } + + private static Block[] blocks; // Store placed blocks as some blocks are already present as the workshop and the game save + [APITestCase(TestType.EditMode)] + public static void TestBlockIDs() + { + float3 pos = new float3(); + var values = Enum.GetValues(typeof(BlockIDs)); + blocks = new Block[values.Length - 1]; // Minus the invalid ID + int i = 0; + foreach (BlockIDs id in values) + { + if (id == BlockIDs.Invalid) continue; + try + { + blocks[i++] = Block.PlaceNew(id, pos); + pos += 0.2f; + } + catch (Exception e) + { //Only print failed case + Assert.Fail($"Failed to place block type {id}: {e}"); + return; + } + } + + Assert.Pass("Placing all possible block types succeeded."); + } + + [APITestCase(TestType.EditMode)] + public static IEnumerator TestBlockProperties() + { //Uses the result of the previous test case + yield return Yield.It; + if (blocks is null) + yield break; + for (var index = 0; index < blocks.Length; index++) + { + if (index % 50 == 0) yield return Yield.It; //The material or flipped status can only be changed 130 times per submission + var block = blocks[index]; + if (!block.Exists) continue; + foreach (var property in block.GetType().GetProperties()) + { + if (property.Name == "Material" || property.Name == "Flipped") continue; // TODO: Crashes in game + if (property.Name == "Material" || property.Name == "Flipped") + { + Console.WriteLine("Block type: "+block.Type); + Console.WriteLine("Will set " + property.Name); + yield return new WaitForSecondsEnumerator(1).Continue(); + } + //Includes specialised block properties + if (property.SetMethod == null) continue; + var testValues = new (Type, object, Predicate)[] + { + //(type, default value, predicate or null for equality) + (typeof(long), 3, null), + (typeof(int), 4, null), + (typeof(double), 5.2f, obj => Math.Abs((double) obj - 5.2f) < float.Epsilon), + (typeof(float), 5.2f, obj => Math.Abs((float) obj - 5.2f) < float.Epsilon), + (typeof(bool), true, obj => (bool) obj), + (typeof(string), "Test", obj => (string) obj == "Test"), //String equality check + (typeof(float3), (float3) 2, obj => math.all((float3) obj - 2 < (float3) float.Epsilon)), + (typeof(BlockColor), new BlockColor(BlockColors.Aqua, 2), null), + (typeof(float4), (float4) 5, obj => math.all((float4) obj - 5 < (float4) float.Epsilon)) + }; + var propType = property.PropertyType; + if (!propType.IsValueType) continue; + (object valueToUse, Predicate predicateToUse) = (null, null); + foreach (var (type, value, predicate) in testValues) + { + if (type.IsAssignableFrom(propType)) + { + valueToUse = value; + predicateToUse = predicate ?? (obj => Equals(obj, value)); + break; + } + } + + if (propType.IsEnum) + { + var values = propType.GetEnumValues(); + valueToUse = values.GetValue(values.Length / 2); + predicateToUse = val => Equals(val, valueToUse); + } + + if (valueToUse == null) + { + Assert.Fail($"Property {block.GetType().Name}.{property.Name} has an unknown type {propType}, test needs fixing."); + yield break; + } + + try + { + property.SetValue(block, valueToUse); + } + catch (Exception e) + { + Assert.Fail($"Failed to set property {block.GetType().Name}.{property.Name} to {valueToUse}\n{e}"); + } + object got; + try + { + got = property.GetValue(block); + } + catch (Exception e) + { + Assert.Fail($"Failed to get property {block.GetType().Name}.{property.Name}\n{e}"); + continue; + } + var attr = property.GetCustomAttribute(); + if (!predicateToUse(got) && (attr == null || !Equals(attr.PossibleValue, got))) + { + Assert.Fail($"Property {block.GetType().Name}.{property.Name} value {got} does not equal {valueToUse} for block {block}."); + yield break; + } + } + } + + Assert.Pass("Setting all possible properties of all registered API block types succeeded."); + } + + [APITestCase(TestType.EditMode)] + public static IEnumerator TestDefaultValue() + { + for (int i = 0; i < 2; i++) + { //Tests shared defaults + var block = Block.PlaceNew(BlockIDs.Cube, 1); + while (!block.Exists) + yield return Yield.It; + block.Remove(); + while (block.Exists) + yield return Yield.It; + if(!Assert.Equal(block.Position, default, + $"Block position default value {block.Position} is incorrect, should be 0.", + $"Block position default value {block.Position} matches default.")) + yield break; + block.Position = 4; + } + } + + [APITestCase(TestType.EditMode)] + public static void TestDampedSpring() + { + Block newBlock = Block.PlaceNew(BlockIDs.DampedSpring, Unity.Mathematics.float3.zero + 1); + DampedSpring b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler + Assert.Errorless(() => { b = (DampedSpring) newBlock; }, "Casting block to DampedSpring raised an exception: ", "Casting block to DampedSpring completed without issue."); + if (!Assert.CloseTo(b.Stiffness, 1f, $"DampedSpring.Stiffness {b.Stiffness} does not equal default value, possibly because it failed silently.", "DampedSpring.Stiffness is close enough to default.")) return; + if (!Assert.CloseTo(b.Damping, 0.1f, $"DampedSpring.Damping {b.Damping} does not equal default value, possibly because it failed silently.", "DampedSpring.Damping is close enough to default.")) return; + if (!Assert.CloseTo(b.MaxExtension, 0.3f, $"DampedSpring.MaxExtension {b.MaxExtension} does not equal default value, possibly because it failed silently.", "DampedSpring.MaxExtension is close enough to default.")) return; + } + + /*[APITestCase(TestType.Game)] + public static void TestMusicBlock1() + { + Block newBlock = Block.PlaceNew(BlockIDs.MusicBlock, Unity.Mathematics.float3.zero + 2); + MusicBlock b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler + Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); + if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return; + if (!Assert.CloseTo(b.Volume, 100f, $"MusicBlock.Volume {b.Volume} does not equal default value, possibly because it failed silently.", "MusicBlock.Volume is close enough to default.")) return; + if (!Assert.Equal(b.TrackIndex, 0, $"MusicBlock.TrackIndex {b.TrackIndex} does not equal default value, possibly because it failed silently.", "MusicBlock.TrackIndex is equal to default.")) return; + _musicBlock = b; + } + + private static MusicBlock _musicBlock; + + [APITestCase(TestType.EditMode)] + public static void TestMusicBlock2() + { + //Block newBlock = Block.GetLastPlacedBlock(); + var b = _musicBlock; + if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized MusicBlock is not null.")) return; + b.IsPlaying = true; // play sfx + if (!Assert.Equal(b.IsPlaying, true, $"MusicBlock.IsPlaying {b.IsPlaying} does not equal true, possibly because it failed silently.", "MusicBlock.IsPlaying is set properly.")) return; + if (!Assert.Equal(b.ChannelType, ChannelType.None, $"MusicBlock.ChannelType {b.ChannelType} does not equal default value, possibly because it failed silently.", "MusicBlock.ChannelType is equal to default.")) return; + //Assert.Log(b.Track.ToString()); + if (!Assert.Equal(b.Track.ToString(), new Guid("3237ff8f-f5f2-4f84-8144-496ca280f8c0").ToString(), $"MusicBlock.Track {b.Track} does not equal default value, possibly because it failed silently.", "MusicBlock.Track is equal to default.")) return; + } + + [APITestCase(TestType.EditMode)] + public static void TestLogicGate() + { + Block newBlock = Block.PlaceNew(BlockIDs.NOTLogicBlock, Unity.Mathematics.float3.zero + 1); + LogicGate b = null; // Note: the assignment operation is a lambda, which slightly confuses the compiler + Assert.Errorless(() => { b = newBlock.Specialise(); }, "Block.Specialize() raised an exception: ", "Block.Specialize() completed without issue."); + if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return; + if (!Assert.Equal(b.InputCount, 1u, $"LogicGate.InputCount {b.InputCount} does not equal default value, possibly because it failed silently.", "LogicGate.InputCount is default.")) return; + if (!Assert.Equal(b.OutputCount, 1u, $"LogicGate.OutputCount {b.OutputCount} does not equal default value, possibly because it failed silently.", "LogicGate.OutputCount is default.")) return; + if (!Assert.NotNull(b, "Block.Specialize() returned null, possibly because it failed silently.", "Specialized LogicGate is not null.")) return; + //if (!Assert.Equal(b.PortName(0, true), "Input", $"LogicGate.PortName(0, input:true) {b.PortName(0, true)} does not equal default value, possibly because it failed silently.", "LogicGate.PortName(0, input:true) is close enough to default.")) return; + LogicGate target = null; + if (!Assert.Errorless(() => { target = Block.PlaceNew(BlockIDs.ANDLogicBlock, Unity.Mathematics.float3.zero + 2); })) return; + Wire newWire = null; + if (!Assert.Errorless(() => { newWire = b.Connect(0, target, 0);})) return; + if (!Assert.NotNull(newWire, "SignalingBlock.Connect(...) returned null, possible because it failed silently.", "SignalingBlock.Connect(...) returned a non-null value.")) return; + }*/ + + /*[APITestCase(TestType.EditMode)] + public static void TestSpecialiseError() + { + Block newBlock = Block.PlaceNew(BlockIDs.Bench, new float3(1, 1, 1)); + if (Assert.Errorful(() => newBlock.Specialise(), "Block.Specialise() was expected to error on a bench block.", "Block.Specialise() errored as expected for a bench block.")) return; + }*/ + } +#endif +} diff --git a/TechbloxModdingAPI/Blocks/DampedSpring.cs b/TechbloxModdingAPI/Blocks/DampedSpring.cs new file mode 100644 index 0000000..9408f31 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/DampedSpring.cs @@ -0,0 +1,71 @@ +namespace TechbloxModdingAPI.Blocks +{ + using RobocraftX.Common; + using Svelto.ECS; + + + public class DampedSpring : SignalingBlock + { + + /// + /// Constructs a(n) DampedSpring object representing an existing block. + /// + public DampedSpring(EGID egid) : + base(egid) + { + } + + /// + /// Constructs a(n) DampedSpring object representing an existing block. + /// + public DampedSpring(uint id) : + base(new EGID(id, CommonExclusiveGroups.DAMPEDSPRING_BLOCK_GROUP)) + { + } + + /// + /// Gets or sets the DampedSpring's Stiffness property. Tweakable stat. + /// + public float Stiffness + { + get + { + return BlockEngine.GetBlockInfo(this).stiffness; + } + set + { + BlockEngine.GetBlockInfo(this).stiffness = value; + } + } + + /// + /// Gets or sets the DampedSpring's Damping property. Tweakable stat. + /// + public float Damping + { + get + { + return BlockEngine.GetBlockInfo(this).damping; + } + set + { + BlockEngine.GetBlockInfo(this).damping = value; + } + } + + /// + /// Gets or sets the DampedSpring's MaxExtension property. Tweakable stat. + /// + public float MaxExtension + { + get + { + return BlockEngine.GetBlockInfo(this).maxExtent; + } + set + { + BlockEngine.GetBlockInfo(this).maxExtent = value; + } + } + } +} diff --git a/TechbloxModdingAPI/Blocks/Engine.cs b/TechbloxModdingAPI/Blocks/Engine.cs new file mode 100644 index 0000000..6140b11 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Engine.cs @@ -0,0 +1,382 @@ +namespace TechbloxModdingAPI.Blocks +{ + using RobocraftX.Common; + using Svelto.ECS; + + + public class Engine : SignalingBlock + { + + /// + /// Constructs a(n) Engine object representing an existing block. + /// + public Engine(EGID egid) : + base(egid) + { + } + + /// + /// Constructs a(n) Engine object representing an existing block. + /// + public Engine(uint id) : + base(new EGID(id, CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP)) + { + } + + /// + /// Gets or sets the Engine's On property. May not be saved. + /// + public bool On + { + get + { + return BlockEngine.GetBlockInfo(this).engineOn; + } + set + { + BlockEngine.GetBlockInfo(this).engineOn = value; + } + } + + /// + /// Gets or sets the Engine's CurrentGear property. May not be saved. + /// + public int CurrentGear + { + get + { + return BlockEngine.GetBlockInfo(this).currentGear; + } + set + { + BlockEngine.GetBlockInfo(this).currentGear = value; + } + } + + /// + /// Gets or sets the Engine's GearChangeCountdown property. May not be saved. + /// + public float GearChangeCountdown + { + get + { + return BlockEngine.GetBlockInfo(this).gearChangeCountdown; + } + set + { + BlockEngine.GetBlockInfo(this).gearChangeCountdown = value; + } + } + + /// + /// Gets or sets the Engine's CurrentRpmAV property. May not be saved. + /// + public float CurrentRpmAV + { + get + { + return BlockEngine.GetBlockInfo(this).currentRpmAV; + } + set + { + BlockEngine.GetBlockInfo(this).currentRpmAV = value; + } + } + + /// + /// Gets or sets the Engine's CurrentRpmLV property. May not be saved. + /// + public float CurrentRpmLV + { + get + { + return BlockEngine.GetBlockInfo(this).currentRpmLV; + } + set + { + BlockEngine.GetBlockInfo(this).currentRpmLV = value; + } + } + + /// + /// Gets or sets the Engine's TargetRpmAV property. May not be saved. + /// + public float TargetRpmAV + { + get + { + return BlockEngine.GetBlockInfo(this).targetRpmAV; + } + set + { + BlockEngine.GetBlockInfo(this).targetRpmAV = value; + } + } + + /// + /// Gets or sets the Engine's TargetRpmLV property. May not be saved. + /// + public float TargetRpmLV + { + get + { + return BlockEngine.GetBlockInfo(this).targetRpmLV; + } + set + { + BlockEngine.GetBlockInfo(this).targetRpmLV = value; + } + } + + /// + /// Gets or sets the Engine's CurrentTorque property. May not be saved. + /// + public float CurrentTorque + { + get + { + return BlockEngine.GetBlockInfo(this).currentTorque; + } + set + { + BlockEngine.GetBlockInfo(this).currentTorque = value; + } + } + + /// + /// Gets or sets the Engine's TotalWheelVelocityAV property. May not be saved. + /// + public float TotalWheelVelocityAV + { + get + { + return BlockEngine.GetBlockInfo(this).totalWheelVelocityAV; + } + set + { + BlockEngine.GetBlockInfo(this).totalWheelVelocityAV = value; + } + } + + /// + /// Gets or sets the Engine's TotalWheelVelocityLV property. May not be saved. + /// + public float TotalWheelVelocityLV + { + get + { + return BlockEngine.GetBlockInfo(this).totalWheelVelocityLV; + } + set + { + BlockEngine.GetBlockInfo(this).totalWheelVelocityLV = value; + } + } + + /// + /// Gets or sets the Engine's TotalWheelCount property. May not be saved. + /// + public int TotalWheelCount + { + get + { + return BlockEngine.GetBlockInfo(this).totalWheelCount; + } + set + { + BlockEngine.GetBlockInfo(this).totalWheelCount = value; + } + } + + /// + /// Gets or sets the Engine's LastGearUpInput property. May not be saved. + /// + public bool LastGearUpInput + { + get + { + return BlockEngine.GetBlockInfo(this).lastGearUpInput; + } + set + { + BlockEngine.GetBlockInfo(this).lastGearUpInput = value; + } + } + + /// + /// Gets or sets the Engine's LastGearDownInput property. May not be saved. + /// + public bool LastGearDownInput + { + get + { + return BlockEngine.GetBlockInfo(this).lastGearDownInput; + } + set + { + BlockEngine.GetBlockInfo(this).lastGearDownInput = value; + } + } + + /// + /// Gets or sets the Engine's ManualToAutoGearCoolOffCounter property. May not be saved. + /// + public float ManualToAutoGearCoolOffCounter + { + get + { + return BlockEngine.GetBlockInfo(this).manualToAutoGearCoolOffCounter; + } + set + { + BlockEngine.GetBlockInfo(this).manualToAutoGearCoolOffCounter = value; + } + } + + /// + /// Gets or sets the Engine's Load property. May not be saved. + /// + public float Load + { + get + { + return BlockEngine.GetBlockInfo(this).load; + } + set + { + BlockEngine.GetBlockInfo(this).load = value; + } + } + + /// + /// Gets or sets the Engine's Power property. Tweakable stat. + /// + public float Power + { + get + { + return BlockEngine.GetBlockInfo(this).power; + } + set + { + BlockEngine.GetBlockInfo(this).power = value; + } + } + + /// + /// Gets or sets the Engine's AutomaticGears property. Tweakable stat. + /// + public bool AutomaticGears + { + get + { + return BlockEngine.GetBlockInfo(this).automaticGears; + } + set + { + BlockEngine.GetBlockInfo(this).automaticGears = value; + } + } + + /// + /// Gets or sets the Engine's GearChangeTime property. May not be saved. + /// + public float GearChangeTime + { + get + { + return BlockEngine.GetBlockInfo(this).gearChangeTime; + } + set + { + BlockEngine.GetBlockInfo(this).gearChangeTime = value; + } + } + + /// + /// Gets or sets the Engine's MinRpm property. May not be saved. + /// + public float MinRpm + { + get + { + return BlockEngine.GetBlockInfo(this).minRpm; + } + set + { + BlockEngine.GetBlockInfo(this).minRpm = value; + } + } + + /// + /// Gets or sets the Engine's MaxRpm property. May not be saved. + /// + public float MaxRpm + { + get + { + return BlockEngine.GetBlockInfo(this).maxRpm; + } + set + { + BlockEngine.GetBlockInfo(this).maxRpm = value; + } + } + + /// + /// Gets the Engine's GearDownRpms property. May not be saved. + /// + public float[] GearDownRpms + { + get + { + return BlockEngine.GetBlockInfo(this).gearDownRpms.ToManagedArray(); + } + } + + /// + /// Gets or sets the Engine's GearUpRpm property. May not be saved. + /// + public float GearUpRpm + { + get + { + return BlockEngine.GetBlockInfo(this).gearUpRpm; + } + set + { + BlockEngine.GetBlockInfo(this).gearUpRpm = value; + } + } + + /// + /// Gets or sets the Engine's MaxRpmChange property. May not be saved. + /// + public float MaxRpmChange + { + get + { + return BlockEngine.GetBlockInfo(this).maxRpmChange; + } + set + { + BlockEngine.GetBlockInfo(this).maxRpmChange = value; + } + } + + /// + /// Gets or sets the Engine's ManualToAutoGearCoolOffTime property. May not be saved. + /// + public float ManualToAutoGearCoolOffTime + { + get + { + return BlockEngine.GetBlockInfo(this).manualToAutoGearCoolOffTime; + } + set + { + BlockEngine.GetBlockInfo(this).manualToAutoGearCoolOffTime = value; + } + } + } +} diff --git a/TechbloxModdingAPI/Blocks/Engines/BlockCloneEngine.cs b/TechbloxModdingAPI/Blocks/Engines/BlockCloneEngine.cs new file mode 100644 index 0000000..01f2d05 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Engines/BlockCloneEngine.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Gamecraft.Wires; +using HarmonyLib; +using RobocraftX.Blocks; +using RobocraftX.Character; +using RobocraftX.Common; +using Svelto.DataStructures; +using Svelto.ECS; +using TechbloxModdingAPI.Engines; + +namespace TechbloxModdingAPI.Blocks.Engines +{ + public class BlockCloneEngine : IApiEngine + { + private static Type copyEngineType = + AccessTools.TypeByName("Gamecraft.GUI.Tweaks.Engines.CopyTweaksOnPickEngine"); + private static Type copyWireEngineType = + AccessTools.TypeByName("Gamecraft.Wires.WireConnectionCopyOnPickEngine"); + private static Type createWireEngineType = + AccessTools.TypeByName("RobocraftX.GUI.Wires.WireConnectionCreateOnPlaceEngine"); + + private MethodBase copyFromBlock = AccessTools.Method(copyEngineType, "CopyTweaksFromBlock"); + private MethodBase copyToBlock = AccessTools.Method(copyEngineType, "ApplyTweaksToPlacedBlock"); + private MethodBase copyWireFromBlock = AccessTools.Method(copyWireEngineType, "CopyWireInputsAndOutputs"); + private MethodBase copyWireToBlock = AccessTools.Method(createWireEngineType, "PlaceWiresOnPlaceNewCube"); + + public void Ready() + { + } + + public EntitiesDB entitiesDB { get; set; } + + public void Dispose() + { + } + + public void CopyBlockStats(EGID sourceID, EGID targetID) + { + var allCharacters = (LocalFasterReadOnlyList) CharacterExclusiveGroups.AllCharacters; + foreach (var ((pickedBlockColl, count), _) in entitiesDB.QueryEntities(allCharacters)) + { + for (int i = 0; i < count; ++i) + { + ref PickedBlockExtraDataStruct pickedBlock = ref pickedBlockColl[i]; + var oldStruct = pickedBlock; + pickedBlock.pickedBlockEntityID = sourceID; + pickedBlock.placedBlockEntityID = targetID; + pickedBlock.placedBlockTweaksMustCopy = true; + if (entitiesDB.Exists(pickedBlock.pickedBlockEntityID) + && entitiesDB.Exists(pickedBlock.placedBlockEntityID)) + { + copyFromBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock}); + + uint playerID = Player.LocalPlayer.Id; + var parameters = new object[] {playerID, pickedBlock}; + copyWireFromBlock.Invoke(Patch.copyWireEngine, parameters); + pickedBlock = (PickedBlockExtraDataStruct) parameters[1]; //ref arg + + copyToBlock.Invoke(Patch.copyEngine, new object[] {pickedBlock.ID, pickedBlock}); + + ExclusiveGroupStruct group = WiresExclusiveGroups.WIRES_COPY_GROUP + playerID; + copyWireToBlock.Invoke(Patch.createWireEngine, new object[] {group, pickedBlock.ID}); + + pickedBlock.placedBlockTweaksMustCopy = false; + } + + pickedBlock = oldStruct; //Make sure to not interfere with the game - Although that might not be the case with the wire copying + } + } + } + + [HarmonyPatch] + private static class Patch + { + public static object copyEngine; + public static object copyWireEngine; + public static object createWireEngine; + + public static void Postfix(object __instance) + { + if (__instance.GetType() == copyEngineType) + copyEngine = __instance; + else if (__instance.GetType() == copyWireEngineType) + copyWireEngine = __instance; + else if (__instance.GetType() == createWireEngineType) + createWireEngine = __instance; + } + + public static IEnumerable TargetMethods() + { + return new[] + { + AccessTools.GetDeclaredConstructors(copyEngineType)[0], + AccessTools.GetDeclaredConstructors(copyWireEngineType)[0], + AccessTools.GetDeclaredConstructors(createWireEngineType)[0] + }; + } + } + + public string Name { get; } = "TechbloxModdingAPIBlockCloneGameEngine"; + public bool isRemovable { get; } = false; + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Blocks/Engines/BlockEngine.cs b/TechbloxModdingAPI/Blocks/Engines/BlockEngine.cs new file mode 100644 index 0000000..c7c84cf --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Engines/BlockEngine.cs @@ -0,0 +1,274 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using HarmonyLib; + +using Gamecraft.ColourPalette; +using Gamecraft.TimeRunning; +using Gamecraft.Wires; +using RobocraftX.Blocks; +using RobocraftX.Common; +using RobocraftX.Physics; +using RobocraftX.Rendering; +using RobocraftX.Rendering.GPUI; +using Svelto.DataStructures; +using Svelto.ECS; +using Svelto.ECS.Hybrid; +using Techblox.BuildingDrone; +using Unity.Mathematics; + +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Blocks.Engines +{ + /// + /// Engine for executing general block actions + /// + public partial class BlockEngine : IApiEngine + { + public string Name { get; } = "TechbloxModdingAPIBlockGameEngine"; + + public EntitiesDB entitiesDB { set; private get; } + + public bool isRemovable => false; + + public void Dispose() + { + } + + public void Ready() + { + } + + public Block[] GetConnectedBlocks(EGID blockID) + { + if (!BlockExists(blockID)) return new Block[0]; + Stack cubeStack = new Stack(); + FasterList cubes = new FasterList(10); + var coll = entitiesDB.QueryEntities(); + foreach (var (ecoll, _) in coll) + { + var ecollB = ecoll.ToBuffer(); + for(int i = 0; i < ecoll.count; i++) + { + ref var conn = ref ecollB.buffer[i]; + conn.isProcessed = false; + } + } + + ConnectedCubesUtility.TreeTraversal.GetConnectedCubes(entitiesDB, blockID, cubeStack, cubes, + (in GridConnectionsEntityStruct g) => { return false; }); + + var ret = new Block[cubes.count]; + for (int i = 0; i < cubes.count; i++) + ret[i] = Block.New(cubes[i]); + return ret; + } + + public float4 ConvertBlockColor(byte index) => index == byte.MaxValue + ? new float4(-1f, -1f, -1f, -1f) + : entitiesDB.QueryEntity(index, + CommonExclusiveGroups.COLOUR_PALETTE_GROUP).Colour; + + public OptionalRef GetBlockInfoOptional(Block block) where T : unmanaged, IEntityComponent + { + return entitiesDB.QueryEntityOptional(block); + } + + public ref T GetBlockInfo(Block block) where T : unmanaged, IEntityComponent + { + return ref entitiesDB.QueryEntityOrDefault(block); + } + + internal ref T GetBlockInfo(EcsObjectBase obj) where T : unmanaged, IEntityComponent + { + return ref entitiesDB.QueryEntityOrDefault(obj); + } + + public ref T GetBlockInfoViewComponent(Block block) where T : struct, IEntityViewComponent + { + return ref entitiesDB.QueryEntityOrDefault(block); + } + + public void UpdateDisplayedBlock(EGID id) + { + if (!BlockExists(id)) return; + RenderingPatch.UpdateBlocks(); + } + + internal void UpdatePrefab(Block block, byte material, bool flipped) + { + var prefabAssetIDOpt = entitiesDB.QueryEntityOptional(block); + uint prefabAssetID = prefabAssetIDOpt + ? prefabAssetIDOpt.Get().prefabAssetID + : uint.MaxValue; + if (prefabAssetID == uint.MaxValue) + { + if (entitiesDB.QueryEntityOptional(block)) //The block exists + throw new BlockException("Prefab asset ID not found for block " + block); //Set by the game + return; + } + + uint prefabId = + PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, material, 1, flipped); + entitiesDB.QueryEntityOrDefault(block).prefabID = prefabId; + if (block.Exists) + { + entitiesDB.PublishEntityChange(block.Id); + entitiesDB.PublishEntityChange(block.Id); + + ref BuildingActionComponent local = + ref entitiesDB.QueryEntity(BuildingDroneUtility + .GetLocalBuildingDrone(entitiesDB).ToEGID(entitiesDB)); + local.buildAction = BuildAction.ChangeMaterial; + local.targetPosition = block.Position; + this.entitiesDB.PublishEntityChange(local.ID); + } + //Phyiscs prefab: prefabAssetID, set on block creation from the CubeListData + } + + public bool BlockExists(EGID blockID) + { + return entitiesDB.Exists(blockID); + } + + public SimBody[] GetSimBodiesFromID(byte id) + { + var ret = new FasterList(4); + var oide = entitiesDB.QueryEntities(); + EGIDMapper? connections = null; + foreach (var ((oids, count), _) in oide) + { + for (int i = 0; i < count; i++) + { + ref ObjectIdEntityStruct oid = ref oids[i]; + if (oid.objectId != id) continue; + if (!connections.HasValue) //Would need reflection to get the group from the build group otherwise + connections = entitiesDB.QueryMappedEntities(oid.ID.groupID); + var rid = connections.Value.Entity(oid.ID.entityID).machineRigidBodyId; + foreach (var rb in ret) + { + if (rb.Id.entityID == rid) + goto DUPLICATE; //Multiple Object Identifiers on one rigid body + } + + ret.Add(new SimBody(rid)); + DUPLICATE: ; + } + } + + return ret.ToArray(); + } + + public SimBody[] GetConnectedSimBodies(uint id) + { + var joints = entitiesDB.QueryEntities(MachineSimulationGroups.JOINTS_GROUP).ToBuffer(); + var list = new FasterList(4); + for (int i = 0; i < joints.count; i++) + { + ref var joint = ref joints.buffer[i]; + if (joint.isBroken) continue; + if (joint.connectedEntityA == id) list.Add(new SimBody(joint.connectedEntityB)); + else if (joint.connectedEntityB == id) list.Add(new SimBody(joint.connectedEntityA)); + } + + return list.ToArray(); + } + + public SimBody[] GetClusterBodies(uint cid) + { + var groups = entitiesDB.QueryEntities(); + var bodies = new HashSet(); + foreach (var (coll, _) in groups) + { + var array = coll.ToBuffer().buffer; + for (var index = 0; index < array.capacity; index++) + { + var conn = array[index]; + if (conn.clusterId == cid) + bodies.Add(conn.machineRigidBodyId); + } + } + + return bodies.Select(id => new SimBody(id, cid)).ToArray(); + } + + public EGID? FindBlockEGID(uint id) + { + var groups = entitiesDB.FindGroups(); + foreach (ExclusiveGroupStruct group in groups) + { + if (entitiesDB.Exists(id, group)) + return new EGID(id, group); + } + + return null; + } + + public Cluster GetCluster(uint sbid) + { + var groups = entitiesDB.QueryEntities(); + foreach (var (coll, _) in groups) + { + var array = coll.ToBuffer().buffer; + for (var index = 0; index < array.capacity; index++) + { + var conn = array[index]; + //Static blocks don't have a cluster ID but the cluster destruction manager should have one + if (conn.machineRigidBodyId == sbid && conn.clusterId != uint.MaxValue) + return new Cluster(conn.clusterId); + } + } + + return null; + } + + public Block[] GetBodyBlocks(uint sbid) + { + var groups = entitiesDB.QueryEntities(); + var set = new HashSet(); + foreach (var (coll, _) in groups) + { + var array = coll.ToBuffer().buffer; + for (var index = 0; index < array.capacity; index++) + { + var conn = array[index]; + if (conn.machineRigidBodyId == sbid) + set.Add(Block.New(conn.ID)); + } + } + + return set.ToArray(); + } + +#if DEBUG + public EntitiesDB GetEntitiesDB() + { + return entitiesDB; + } +#endif + + [HarmonyPatch] + public static class RenderingPatch + { + private static ComputeRenderingEntitiesMatricesEngine Engine; + + public static void Postfix(ComputeRenderingEntitiesMatricesEngine __instance) + { + Engine = __instance; + } + + public static MethodBase TargetMethod() + { + return typeof(ComputeRenderingEntitiesMatricesEngine).GetConstructors()[0]; + } + + public static void UpdateBlocks() + { + var data = new RenderingDataStruct(); + Engine.Add(ref data, new EGID(0, CommonExclusiveGroups.BUTTON_BLOCK_GROUP)); + } + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs b/TechbloxModdingAPI/Blocks/Engines/BlockEventsEngine.cs similarity index 56% rename from GamecraftModdingAPI/Blocks/BlockEventsEngine.cs rename to TechbloxModdingAPI/Blocks/Engines/BlockEventsEngine.cs index d1c2639..4da3f65 100644 --- a/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs +++ b/TechbloxModdingAPI/Blocks/Engines/BlockEventsEngine.cs @@ -1,18 +1,17 @@ using System; -using RobocraftX.Common; +using RobocraftX.Blocks; using Svelto.ECS; -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Utility; -using RobocraftX.Blocks; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Blocks +namespace TechbloxModdingAPI.Blocks.Engines { public class BlockEventsEngine : IReactionaryEngine { - public event EventHandler Placed; - public event EventHandler Removed; + public WrappedHandler Placed; + public WrappedHandler Removed; public void Ready() { @@ -24,7 +23,7 @@ namespace GamecraftModdingAPI.Blocks { } - public string Name { get; } = "GamecraftModdingAPIBlockEventsEngine"; + public string Name { get; } = "TechbloxModdingAPIBlockEventsEngine"; public bool isRemovable { get; } = false; private bool shouldAddRemove; @@ -32,25 +31,22 @@ namespace GamecraftModdingAPI.Blocks { if (!(shouldAddRemove = !shouldAddRemove)) return; - ExceptionUtil.InvokeEvent(Placed, this, - new BlockPlacedRemovedEventArgs {ID = egid}); + Placed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid}); } public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid) { if (!(shouldAddRemove = !shouldAddRemove)) return; - ExceptionUtil.InvokeEvent(Removed, this, - new BlockPlacedRemovedEventArgs {ID = egid}); + Removed.Invoke(this, new BlockPlacedRemovedEventArgs {ID = egid}); } } public struct BlockPlacedRemovedEventArgs { public EGID ID; - public BlockIDs Type; private Block block; - public Block Block => block ?? (block = new Block(ID)); + public Block Block => block ??= Block.New(ID); } } \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlueprintEngine.cs b/TechbloxModdingAPI/Blocks/Engines/BlueprintEngine.cs similarity index 56% rename from GamecraftModdingAPI/Blocks/BlueprintEngine.cs rename to TechbloxModdingAPI/Blocks/Engines/BlueprintEngine.cs index e46ad50..2ba15ce 100644 --- a/GamecraftModdingAPI/Blocks/BlueprintEngine.cs +++ b/TechbloxModdingAPI/Blocks/Engines/BlueprintEngine.cs @@ -1,26 +1,32 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; using Gamecraft.Blocks.BlockGroups; using Gamecraft.GUI.Blueprints; -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Utility; using HarmonyLib; using RobocraftX.Blocks; +using RobocraftX.Blocks.Ghost; using RobocraftX.Common; using RobocraftX.CR.MachineEditing.BoxSelect; using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations; +using RobocraftX.Physics; +using RobocraftX.Rendering; +using RobocraftX.Rendering.GPUI; using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.DataStructures; using Svelto.ECS.EntityStructs; +using Svelto.ECS.Native; using Svelto.ECS.Serialization; +using Techblox.Blocks; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; using Unity.Collections; using Unity.Mathematics; using UnityEngine; using Allocator = Svelto.Common.Allocator; -namespace GamecraftModdingAPI.Blocks +namespace TechbloxModdingAPI.Blocks.Engines { public class BlueprintEngine : IFactoryEngine { @@ -29,13 +35,18 @@ namespace GamecraftModdingAPI.Blocks private NativeDynamicArray selectedBlocksInGroup; private NativeHashSet removedConnections = new NativeHashSet(); - + private int addingToBlockGroup = -1; + private static readonly Type PlaceBlueprintUtilityType = AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlueprintUtility"); private static readonly FieldInfo LocalBlockMap = AccessTools.DeclaredField(PlaceBlueprintUtilityType, "_localBlockMap"); private static readonly MethodInfo BuildBlock = AccessTools.Method(PlaceBlueprintUtilityType, "BuildBlock"); private static readonly MethodInfo BuildWires = AccessTools.Method(PlaceBlueprintUtilityType, "BuildWires"); + private static readonly Type SerializeGhostBlueprintType = + AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BoxSelect.SerializeGhostChildrenOnAddEngine"); + private static readonly MethodInfo SerializeGhostBlueprint = + AccessTools.Method(SerializeGhostBlueprintType, "SerializeClipboardGhostEntities"); private static NativeEntityRemove nativeRemove; private static MachineGraphConnectionEntityFactory connectionFactory; @@ -44,13 +55,16 @@ namespace GamecraftModdingAPI.Blocks private static IEntitySerialization entitySerialization; private static IEntityFactory entityFactory; private static FasterList globalBlockMap; - + private static object SerializeGhostBlueprintInstance; + private static GhostChildEntityFactory BuildGhostBlueprintFactory; + public void Ready() { selectedBlocksInGroup = NativeDynamicArray.Alloc(Allocator.Persistent); } public EntitiesDB entitiesDB { get; set; } + public void Dispose() { selectedBlocksInGroup.Dispose(); @@ -67,7 +81,7 @@ namespace GamecraftModdingAPI.Blocks int count = selectedBlocksInGroup.Count(); var ret = new Block[count]; for (uint i = 0; i < count; i++) - ret[i] = new Block(selectedBlocksInGroup.Get(i)); + ret[i] = Block.New(selectedBlocksInGroup.Get(i)); selectedBlocksInGroup.FastClear(); return ret; } @@ -90,17 +104,26 @@ namespace GamecraftModdingAPI.Blocks return nextFilterId; } + public void AddBlockToGroup(EGID blockID, int groupID) + { + if (globalBlockMap == null) + globalBlockMap = FullGameFields._deserialisedBlockMap; + if (groupID != addingToBlockGroup) + { + Logging.MetaDebugLog("Changing current block group from " + addingToBlockGroup + " to " + groupID); + addingToBlockGroup = groupID; + globalBlockMap.Clear(); + } + + globalBlockMap.Add(blockID); + } + public void SelectBlueprint(uint resourceID) { if (resourceID == uint.MaxValue) BlueprintUtil.UnselectBlueprint(entitiesDB); else - { - BlueprintUtil.SelectBlueprint(entitiesDB, new BlueprintInventoryItemEntityStruct - { - blueprintResourceId = resourceID, - }); - } + BlueprintUtil.SelectBlueprint(entitiesDB, resourceID, false, -1); } public uint CreateBlueprint() @@ -108,7 +131,7 @@ namespace GamecraftModdingAPI.Blocks uint index = clipboardManager.AllocateSerializationData(); return index; } - + public void ReplaceBlueprint(uint playerID, uint blueprintID, ICollection selected, float3 pos, quaternion rot) { var blockIDs = new EGID[selected.Count]; @@ -122,7 +145,7 @@ namespace GamecraftModdingAPI.Blocks } var serializationData = clipboardManager.GetSerializationData(blueprintID); - SelectionSerializationUtility.ClearClipboard(playerID, entitiesDB, entityFunctions, serializationData.blueprintData); + SelectionSerializationUtility.ClearClipboard(playerID, entitiesDB, entityFunctions, serializationData.blueprintData, -1); if (selected.Count == 0) return; //ref BlockGroupTransformEntityComponent groupTransform = ref EntityNativeDBExtensions.QueryEntity(entitiesDb, (uint) local1.currentBlockGroup, BlockGroupExclusiveGroups.BlockGroupEntityGroup); @@ -130,11 +153,28 @@ namespace GamecraftModdingAPI.Blocks //float3 bottomOffset = PlaceBlockUtility.GetBottomOffset(collider); //var rootPosition = math.mul(groupTransform.blockGroupGridRotation, bottomOffset) + groupTransform.blockGroupGridPosition; //var rootRotation = groupTransform.blockGroupGridRotation; - + clipboardManager.SetGhostSerialized(blueprintID, false); SelectionSerializationUtility.CopySelectionToClipboard(playerID, entitiesDB, serializationData.blueprintData, entitySerialization, entityFactory, blockIDs, - (uint) blockIDs.Length, pos, rot); + (uint) blockIDs.Length, pos, rot, -1); + BuildGhostBlueprint(selected, pos, rot, playerID); + SerializeGhostBlueprint.Invoke(SerializeGhostBlueprintInstance, new object[] {playerID, blueprintID}); + + } + + private void BuildGhostBlueprint(ICollection blocks, float3 pos, quaternion rot, uint playerID) + { + GhostChildUtility.ClearGhostChildren(playerID, entitiesDB, entityFunctions); + var bssesopt = entitiesDB.QueryEntityOptional(new EGID(playerID, + BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)); + if (!bssesopt) + return; + foreach (var block in blocks) + { + GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB, + BuildGhostBlueprintFactory, false, bssesopt.Get().buildingDroneReference); + } } public Block[] PlaceBlueprintBlocks(uint blueprintID, uint playerID, float3 pos, float3 rot) @@ -159,11 +199,12 @@ namespace GamecraftModdingAPI.Blocks } } int nextFilterId1 = BlockGroupUtility.NextFilterId; - entityFactory.BuildEntity(new EGID((uint) nextFilterId1, BlockGroupExclusiveGroups.BlockGroupEntityGroup)).Init(new BlockGroupTransformEntityComponent - { - blockGroupGridPosition = selectionPosition.position, - blockGroupGridRotation = selectionRotation.rotation - }); + entityFactory.BuildEntity(new EGID((uint) nextFilterId1, + BlockGroupExclusiveGroups.BlockGroupEntityGroup)).Init(new BlockGroupTransformEntityComponent + { + blockGroupGridPosition = selectionPosition.position, + blockGroupGridRotation = selectionRotation.rotation + }); var frot = Quaternion.Euler(rot); var grid = new GridRotationStruct {position = pos, rotation = frot}; var poss = new PositionEntityStruct {position = pos}; @@ -173,7 +214,7 @@ namespace GamecraftModdingAPI.Blocks new object[] { playerID, grid, poss, rots, selectionPosition, selectionRotation, blueprintData, - entitiesDB, entitySerialization, nextFilterId1 + entitySerialization, nextFilterId1 }); /* uint playerId, in GridRotationStruct ghostParentGrid, @@ -191,10 +232,22 @@ namespace GamecraftModdingAPI.Blocks new object[] {playerID, blueprintData, entitySerialization, entitiesDB, entityFactory}); var blocks = new Block[placedBlocks.count]; for (int i = 0; i < blocks.Length; i++) - blocks[i] = new Block(placedBlocks[i]); + blocks[i] = Block.New(placedBlocks[i]); return blocks; } + public void GetBlueprintInfo(uint blueprintID, out float3 pos, out quaternion rot, out uint selectionSize) + { + var serializationData = clipboardManager.GetSerializationData(blueprintID); + var blueprintData = serializationData.blueprintData; + blueprintData.dataPos = 0U; + BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out var posst, + out var rotst, out _); + blueprintData.dataPos = 0U; //Just to be sure, it gets reset when it's read anyway + pos = posst.position; + rot = rotst.rotation; + } + public void InitBlueprint(uint blueprintID) { clipboardManager.IncrementRefCount(blueprintID); @@ -204,8 +257,78 @@ namespace GamecraftModdingAPI.Blocks { clipboardManager.DecrementRefCount(blueprintID); } + - public string Name { get; } = "GamecraftModdingAPIBlueprintGameEngine"; + //GhostChildUtility.BuildGhostChild + public Block BuildGhostChild() + { + var sourceId = new EGID(Player.LocalPlayer.Id, GHOST_BLOCKS_ENABLED.Group); + var positionEntityStruct = entitiesDB.QueryEntity(sourceId); + var rotationEntityStruct = entitiesDB.QueryEntity(sourceId); + var scalingEntityStruct = entitiesDB.QueryEntity(sourceId); + var dbStruct = entitiesDB.QueryEntity(sourceId); + var colliderStruct = entitiesDB.QueryEntity(sourceId); + var colorStruct = entitiesDB.QueryEntity(sourceId); + uint ghostChildBlockId = CommonExclusiveGroups.GetNewGhostChildBlockID(); + var ghostEntityReference = GhostBlockUtils.GetGhostEntityReference(sourceId.entityID, entitiesDB); + var entityInitializer = BuildGhostBlueprintFactory.Build( + new EGID(ghostChildBlockId, BoxSelectExclusiveGroups.GhostChildEntitiesExclusiveGroup), /*dbStruct.DBID*/ (uint)BlockIDs.Cube); + entityInitializer.Init(dbStruct); + entityInitializer.Init(new GFXPrefabEntityStructGPUI( + PrefabsID.GetOrCreatePrefabID((ushort)entityInitializer.Get().prefabAssetID, + entitiesDB.QueryEntity(sourceId).materialId, 7, + FlippedBlockUtils.IsFlipped(in scalingEntityStruct.scale)), true)); + entityInitializer.Init(entitiesDB.QueryEntity(sourceId)); + entityInitializer.Init(new GhostParentEntityStruct + { + ghostBlockParentEntityReference = ghostEntityReference, + ownerMustSerializeOnAdd = false + }); + entityInitializer.Init(colorStruct); + entityInitializer.Init(colliderStruct); + entityInitializer.Init(new RigidBodyEntityStruct + { + position = positionEntityStruct.position, + rotation = rotationEntityStruct.rotation + }); + entityInitializer.Init(new ScalingEntityStruct + { + scale = scalingEntityStruct.scale + }); + entityInitializer.Init(new LocalTransformEntityStruct + { + position = positionEntityStruct.position, + rotation = rotationEntityStruct.rotation + }); + entityInitializer.Init(new RotationEntityStruct + { + rotation = rotationEntityStruct.rotation + }); + entityInitializer.Init(new PositionEntityStruct + { + position = positionEntityStruct.position + }); + entityInitializer.Init(new SkewComponent + { + skewMatrix = entitiesDB.QueryEntity(sourceId).skewMatrix + }); + entityInitializer.Init(new BlockPlacementInfoStruct + { + placedByBuildingDrone = entitiesDB + .QueryEntityOptional(new EGID(Player.LocalPlayer.Id, + BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)).Get().buildingDroneReference + }); + entityInitializer.Init(new GridRotationStruct + { + position = float3.zero, + rotation = quaternion.identity + }); + var block = Block.New(entityInitializer.EGID); + block.InitData = entityInitializer; + return block; + } + + public string Name { get; } = "TechbloxModdingAPIBlueprintGameEngine"; public bool isRemovable { get; } = false; [HarmonyPatch] @@ -243,6 +366,34 @@ namespace GamecraftModdingAPI.Blocks } } + [HarmonyPatch] + private static class SerializeGhostBlueprintPatch + { + public static void Postfix(object __instance) + { + SerializeGhostBlueprintInstance = __instance; + } + + public static MethodBase TargetMethod() + { + return AccessTools.GetDeclaredConstructors(SerializeGhostBlueprintType)[0]; + } + } + + [HarmonyPatch] + private static class BuildGhostBlueprintPatch + { + public static void Postfix(GhostChildEntityFactory ghostChildEntityFactory) + { + BuildGhostBlueprintFactory = ghostChildEntityFactory; + } + + public static MethodBase TargetMethod() + { + return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.BuildGhostChildForMultiblockPickEngine"))[0]; + } + } + public IEntityFactory Factory { get; set; } } } \ No newline at end of file diff --git a/TechbloxModdingAPI/Blocks/Engines/MovementEngine.cs b/TechbloxModdingAPI/Blocks/Engines/MovementEngine.cs new file mode 100644 index 0000000..9351623 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Engines/MovementEngine.cs @@ -0,0 +1,68 @@ +using RobocraftX.Common; +using RobocraftX.UECS; +using Svelto.ECS; +using Svelto.ECS.EntityStructs; +using Unity.Mathematics; +using Unity.Transforms; + +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Blocks.Engines +{ + /// + /// Engine which executes block movement actions + /// + public class MovementEngine : IApiEngine + { + public string Name { get; } = "TechbloxModdingAPIMovementGameEngine"; + + public EntitiesDB entitiesDB { set; private get; } + + public bool isRemovable => false; + + public bool IsInGame = false; + + public void Dispose() + { + IsInGame = false; + } + + public void Ready() + { + IsInGame = true; + } + + // implementations for Movement static class + + internal float3 MoveBlock(Block block, float3 vector) + { + ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntityOrDefault(block); + ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault(block); + ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault(block); + ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault(block); + // main (persistent) position + posStruct.position = vector; + // placement grid position + gridStruct.position = vector; + // rendered position + transStruct.position = vector; + // collision position + if (phyStruct.ID != default) + { //It exists + FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, new Translation + { + Value = posStruct.position + }); + } + + entitiesDB.QueryEntityOrDefault(block).isProcessed = false; + return posStruct.position; + } + + internal float3 GetPosition(Block block) + { + return entitiesDB.QueryEntityOrDefault(block).position; + } + } +} diff --git a/TechbloxModdingAPI/Blocks/Engines/PlacementEngine.cs b/TechbloxModdingAPI/Blocks/Engines/PlacementEngine.cs new file mode 100644 index 0000000..9ce7d84 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Engines/PlacementEngine.cs @@ -0,0 +1,130 @@ +using System.Reflection; + +using DataLoader; +using Gamecraft.Blocks.BlockGroups; +using Gamecraft.Wires; +using HarmonyLib; +using RobocraftX.Blocks; +using RobocraftX.Character; +using RobocraftX.Common; +using RobocraftX.CR.MachineEditing.BoxSelect; +using RobocraftX.Rendering; +using RobocraftX.Rendering.GPUI; +using Svelto.ECS; +using Svelto.ECS.EntityStructs; +using Unity.Mathematics; + +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Blocks.Engines +{ + /// + /// Engine which executes block placement actions + /// + public class PlacementEngine : IApiEngine + { + public bool IsInGame; + + public void Dispose() + { + IsInGame = false; + } + + public void Ready() + { + IsInGame = true; + } + + public EntitiesDB entitiesDB { get; set; } + private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceSingleBlockEngine + private static IEntityFactory _entityFactory; + + public EntityInitializer PlaceBlock(BlockIDs block, float3 position, Player player, bool autoWire) + { //It appears that only the non-uniform scale has any visible effect, but if that's not given here it will be set to the uniform one + return BuildBlock((ushort) block, position, autoWire, (player ?? Player.LocalPlayer).Id); + } + + private EntityInitializer BuildBlock(ushort block, float3 position, bool autoWire, uint playerId) + { + if (_blockEntityFactory == null) + throw new BlockException("The factory is null."); + if(!FullGameFields._dataDb.ContainsKey(block)) + throw new BlockException("Block with ID " + block + " not found!"); + //RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine + DBEntityStruct dbEntity = new DBEntityStruct {DBID = block}; + + EntityInitializer structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, block); //The ghost block index is only used for triggers + uint prefabAssetID = structInitializer.Has() + ? structInitializer.Get().prefabAssetID + : throw new BlockException("Prefab asset ID not found!"); //Set by the game + uint prefabId = PrefabsID.GetOrCreatePrefabID((ushort) prefabAssetID, (byte) BlockMaterial.SteelBodywork, 1, false); + structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId)); + structInitializer.Init(dbEntity); + structInitializer.Init(new PositionEntityStruct {position = position}); + structInitializer.Init(new RotationEntityStruct {rotation = quaternion.identity}); + structInitializer.Init(new ScalingEntityStruct {scale = new float3(1, 1, 1)}); + structInitializer.Init(new GridRotationStruct + { + position = position, + rotation = quaternion.identity + }); + structInitializer.Init(new UniformBlockScaleEntityStruct {scaleFactor = 1}); + structInitializer.Get().materialId = (byte) BlockMaterial.SteelBodywork; + var bssesopt = entitiesDB.QueryEntityOptional(new EGID(playerId, + BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)); + if (!bssesopt) + throw new BlockException("Invalid player ID specified for block placement"); + structInitializer.Init(new BlockPlacementInfoStruct + { + loadedFromDisk = false, + placedByBuildingDrone = bssesopt.Get().buildingDroneReference, + triggerAutoWiring = autoWire && structInitializer.Has() + }); + + int nextFilterId = BlockGroupUtility.NextFilterId; + structInitializer.Init(new BlockGroupEntityComponent + { + currentBlockGroup = nextFilterId + }); + _entityFactory.BuildEntity((uint) nextFilterId, + BlockGroupExclusiveGroups.BlockGroupEntityGroup) + .Init(new BlockGroupTransformEntityComponent + { + blockGroupGridRotation = quaternion.identity, + blockGroupGridPosition = position + }); + + foreach (var group in CharacterExclusiveGroups.AllCharacters) + { + EGID playerEGID = new EGID(playerId, group); + if (!entitiesDB.TryQueryEntitiesAndIndex(playerEGID, out uint index, + out var array)) continue; + ref PickedBlockExtraDataStruct pickedBlock = ref array[index]; + pickedBlock.placedBlockEntityID = structInitializer.EGID; + pickedBlock.placedBlockWasAPickedBlock = false; + } + return structInitializer; + } + + public string Name => "TechbloxModdingAPIPlacementGameEngine"; + + public bool isRemovable => false; + + [HarmonyPatch] + class FactoryObtainerPatch + { + static void Postfix(BlockEntityFactory blockEntityFactory, IEntityFactory entityFactory) + { + _blockEntityFactory = blockEntityFactory; + _entityFactory = entityFactory; + Logging.MetaDebugLog("Block entity factory injected."); + } + + static MethodBase TargetMethod(Harmony instance) + { + return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine").GetConstructors()[0]; + } + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/RemovalEngine.cs b/TechbloxModdingAPI/Blocks/Engines/RemovalEngine.cs similarity index 79% rename from GamecraftModdingAPI/Blocks/RemovalEngine.cs rename to TechbloxModdingAPI/Blocks/Engines/RemovalEngine.cs index c27c339..7999443 100644 --- a/GamecraftModdingAPI/Blocks/RemovalEngine.cs +++ b/TechbloxModdingAPI/Blocks/Engines/RemovalEngine.cs @@ -3,12 +3,14 @@ using System.Reflection; using HarmonyLib; using RobocraftX.Blocks; using RobocraftX.Common; +using Svelto.Common; using Svelto.ECS; +using Svelto.ECS.Native; -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Engines; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Blocks +namespace TechbloxModdingAPI.Blocks.Engines { public class RemovalEngine : IApiEngine { @@ -21,8 +23,8 @@ namespace GamecraftModdingAPI.Blocks return false; var connections = entitiesDB.QueryEntity(target); var groups = entitiesDB.FindGroups(); - var connStructMapper = - entitiesDB.QueryNativeMappedEntities(groups); + using var connStructMapper = //The allocator needs to be persistent because that's what is used in the Dispose() method + entitiesDB.QueryNativeMappedEntities(groups, Allocator.Persistent); for (int i = connections.connections.Count() - 1; i >= 0; i--) _connectionFactory.RemoveConnection(connections, i, connStructMapper); _entityFunctions.RemoveEntity(target); @@ -39,12 +41,12 @@ namespace GamecraftModdingAPI.Blocks { } - public string Name { get; } = "GamecraftModdingAPIRemovalGameEngine"; + public string Name { get; } = "TechbloxModdingAPIRemovalGameEngine"; public bool isRemovable => false; [HarmonyPatch] - public class FactoryObtainerPatch + class FactoryObtainerPatch { static void Postfix(IEntityFunctions entityFunctions, MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory) diff --git a/TechbloxModdingAPI/Blocks/Engines/RotationEngine.cs b/TechbloxModdingAPI/Blocks/Engines/RotationEngine.cs new file mode 100644 index 0000000..b2be508 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Engines/RotationEngine.cs @@ -0,0 +1,73 @@ +using RobocraftX.Common; +using RobocraftX.UECS; +using Svelto.ECS; +using Svelto.ECS.EntityStructs; +using Unity.Mathematics; +using UnityEngine; + +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Blocks.Engines +{ + /// + /// Engine which executes block movement actions + /// + public class RotationEngine : IApiEngine + { + public string Name { get; } = "TechbloxModdingAPIRotationGameEngine"; + + public EntitiesDB entitiesDB { set; private get; } + + public bool isRemovable => false; + + public bool IsInGame = false; + + public void Dispose() + { + IsInGame = false; + } + + public void Ready() + { + IsInGame = true; + } + + // implementations for Rotation static class + + internal float3 RotateBlock(Block block, Vector3 vector) + { + ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntityOrDefault(block); + ref GridRotationStruct gridStruct = ref this.entitiesDB.QueryEntityOrDefault(block); + ref LocalTransformEntityStruct transStruct = ref this.entitiesDB.QueryEntityOrDefault(block); + ref UECSPhysicsEntityStruct phyStruct = ref this.entitiesDB.QueryEntityOrDefault(block); + // main (persistent) rotation + Quaternion newRotation = rotStruct.rotation; + newRotation.eulerAngles = vector; + rotStruct.rotation = newRotation; + // placement grid rotation + gridStruct.rotation = newRotation; + // rendered rotation + transStruct.rotation = newRotation; + // collision rotation + if (phyStruct.ID != default) + { //It exists + FullGameFields._physicsWorld.EntityManager.SetComponentData(phyStruct.uecsEntity, + new Unity.Transforms.Rotation + { + Value = rotStruct.rotation + }); + } + + entitiesDB.QueryEntityOrDefault(block).isProcessed = false; + return ((Quaternion)rotStruct.rotation).eulerAngles; + + } + + internal float3 GetRotation(Block block) + { + ref RotationEntityStruct rotStruct = ref entitiesDB.QueryEntityOrDefault(block); + return ((Quaternion) rotStruct.rotation).eulerAngles; + } + } +} diff --git a/GamecraftModdingAPI/Blocks/ScalingEngine.cs b/TechbloxModdingAPI/Blocks/Engines/ScalingEngine.cs similarity index 87% rename from GamecraftModdingAPI/Blocks/ScalingEngine.cs rename to TechbloxModdingAPI/Blocks/Engines/ScalingEngine.cs index fcbf900..9ca77f0 100644 --- a/GamecraftModdingAPI/Blocks/ScalingEngine.cs +++ b/TechbloxModdingAPI/Blocks/Engines/ScalingEngine.cs @@ -6,10 +6,10 @@ using RobocraftX.UECS; using Svelto.ECS; using Unity.Entities; -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Utility; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Blocks +namespace TechbloxModdingAPI.Blocks.Engines { public class ScalingEngine : IApiEngine { @@ -24,7 +24,7 @@ namespace GamecraftModdingAPI.Blocks { } - public string Name { get; } = "GamecraftModdingAPIScalingEngine"; + public string Name { get; } = "TechbloxModdingAPIScalingEngine"; public bool isRemovable { get; } = false; private EntityManager _entityManager; //Unity entity manager @@ -41,7 +41,7 @@ namespace GamecraftModdingAPI.Blocks } [HarmonyPatch] - public class PhysicsEnginePatch + class PhysicsEnginePatch { static void Postfix(IReactOnAddAndRemove __instance) { diff --git a/GamecraftModdingAPI/Blocks/SignalEngine.cs b/TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs similarity index 78% rename from GamecraftModdingAPI/Blocks/SignalEngine.cs rename to TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs index 0980d79..3c2e1cb 100644 --- a/GamecraftModdingAPI/Blocks/SignalEngine.cs +++ b/TechbloxModdingAPI/Blocks/Engines/SignalEngine.cs @@ -1,11 +1,13 @@ using System; -using Svelto.ECS; -using Svelto.DataStructures; + using Gamecraft.Wires; +using Svelto.DataStructures; +using Svelto.ECS; -using GamecraftModdingAPI.Engines; +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Blocks +namespace TechbloxModdingAPI.Blocks.Engines { /// /// Engine which executes signal actions @@ -17,7 +19,7 @@ namespace GamecraftModdingAPI.Blocks public const float HIGH = 1.0f; public const float ZERO = 0.0f; - public string Name { get; } = "GamecraftModdingAPISignalGameEngine"; + public string Name { get; } = "TechbloxModdingAPISignalGameEngine"; public EntitiesDB entitiesDB { set; private get; } @@ -42,7 +44,7 @@ namespace GamecraftModdingAPI.Blocks public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort) { EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup.Group); - EntityComponentInitializer wireInitializer = Factory.BuildEntity(wireEGID); + EntityInitializer wireInitializer = Factory.BuildEntity(wireEGID); wireInitializer.Init(new WireEntityStruct { sourceBlockEGID = startBlock, @@ -88,8 +90,8 @@ namespace GamecraftModdingAPI.Blocks public ref PortEntityStruct GetPortByOffset(Block block, byte portNumber, bool input) { - BlockPortsStruct bps = GetFromDbOrInitData(block, block.Id, out bool exists); - if (!exists) + var bps = entitiesDB.QueryEntityOptional(block); + if (!bps) { throw new BlockException("Block does not exist"); } @@ -206,46 +208,35 @@ namespace GamecraftModdingAPI.Blocks } return outputs; } - - public EGID MatchBlockInputToPort(Block block, byte portUsage, out bool exists) + + public OptionalRef MatchBlockIOToPort(Block block, byte portUsage, bool output) { - BlockPortsStruct ports = GetFromDbOrInitData(block, block.Id, out exists); - return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup.Group); + return MatchBlockIOToPort(block.Id, portUsage, output); } - public EGID MatchBlockInputToPort(EGID block, byte portUsage, out bool exists) + public OptionalRef MatchBlockIOToPort(EGID block, byte portUsage, bool output) { if (!entitiesDB.Exists(block)) - { - exists = false; return default; - } - exists = true; + var group = output + ? NamedExclusiveGroup.Group + : NamedExclusiveGroup.Group; BlockPortsStruct ports = entitiesDB.QueryEntity(block); - return new EGID(ports.firstInputID + portUsage, NamedExclusiveGroup.Group); - } - - public EGID MatchBlockOutputToPort(Block block, byte portUsage, out bool exists) - { - BlockPortsStruct ports = GetFromDbOrInitData(block, block.Id, out exists); - return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup.Group); - } - - public EGID MatchBlockOutputToPort(EGID block, byte portUsage, out bool exists) - { - if (!entitiesDB.Exists(block)) - { - exists = false; + if (!entitiesDB.TryQueryMappedEntities(group, out var mapper)) return default; + for (uint i = 0; i < (output ? ports.outputCount : ports.inputCount); ++i) + { + uint entityID = (output ? ports.firstOutputID : ports.firstInputID) + i; + if (!mapper.TryGetArrayAndEntityIndex(entityID, out var index, out var array) || + array[index].usage != portUsage) continue; + return new OptionalRef(array, index); } - exists = true; - BlockPortsStruct ports = entitiesDB.QueryEntity(block); - return new EGID(ports.firstOutputID + portUsage, NamedExclusiveGroup.Group); + + return default; } - public ref WireEntityStruct MatchPortToWire(EGID portID, EGID blockID, out bool exists) + public ref WireEntityStruct MatchPortToWire(PortEntityStruct port, EGID blockID, out bool exists) { - ref PortEntityStruct port = ref entitiesDB.QueryEntity(portID); var wires = entitiesDB.QueryEntities(NamedExclusiveGroup.Group); var wiresB = wires.ToBuffer().buffer; for (uint i = 0; i < wires.count; i++) @@ -262,8 +253,7 @@ namespace GamecraftModdingAPI.Blocks return ref defRef[0]; } - public ref WireEntityStruct MatchBlocksToWire(EGID startBlock, EGID endBlock, out bool exists, byte startPort = byte.MaxValue, - byte endPort = byte.MaxValue) + public EGID MatchBlocksToWire(EGID startBlock, EGID endBlock, byte startPort = byte.MaxValue, byte endPort = byte.MaxValue) { EGID[] startPorts; if (startPort == byte.MaxValue) @@ -302,31 +292,23 @@ namespace GamecraftModdingAPI.Blocks if ((wiresB[w].destinationPortUsage == endPES.usage && wiresB[w].destinationBlockEGID == endBlock) && (wiresB[w].sourcePortUsage == startPES.usage && wiresB[w].sourceBlockEGID == startBlock)) { - exists = true; - return ref wiresB[w]; + return wiresB[w].ID; } } } } - - exists = false; - WireEntityStruct[] defRef = new WireEntityStruct[1]; - return ref defRef[0]; + + return default; } - public ref ChannelDataStruct GetChannelDataStruct(EGID portID, out bool exists) - { - ref PortEntityStruct port = ref entitiesDB.QueryEntity(portID); + public OptionalRef GetChannelDataStruct(EGID portID) + { + var port = GetPort(portID); var channels = entitiesDB.QueryEntities(NamedExclusiveGroup.Group); var channelsB = channels.ToBuffer(); - if (port.firstChannelIndexCachedInSim < channels.count) - { - exists = true; - return ref channelsB.buffer[port.firstChannelIndexCachedInSim]; - } - exists = false; - ChannelDataStruct[] defRef = new ChannelDataStruct[1]; - return ref defRef[0]; + return port.firstChannelIndexCachedInSim < channels.count + ? new OptionalRef(channelsB.buffer, port.firstChannelIndexCachedInSim) + : default; } public EGID[] GetElectricBlocks() @@ -386,29 +368,6 @@ namespace GamecraftModdingAPI.Blocks return results.ToArray(); } - private ref T GetFromDbOrInitData(Block block, EGID id, out bool exists) where T : unmanaged, IEntityComponent - { - T[] defRef = new T[1]; - if (entitiesDB.Exists(id)) - { - exists = true; - return ref entitiesDB.QueryEntity(id); - } - if (block == null || block.InitData.Group == null) - { - exists = false; - return ref defRef[0]; - } - EntityComponentInitializer initializer = new EntityComponentInitializer(block.Id, block.InitData.Group); - if (initializer.Has()) - { - exists = true; - return ref initializer.Get(); - } - exists = false; - return ref defRef[0]; - } - private EntityCollection GetSignalStruct(uint signalID, out uint index, bool input = true) { ExclusiveGroup group = input diff --git a/TechbloxModdingAPI/Blocks/LogicGate.cs b/TechbloxModdingAPI/Blocks/LogicGate.cs new file mode 100644 index 0000000..05cc3bf --- /dev/null +++ b/TechbloxModdingAPI/Blocks/LogicGate.cs @@ -0,0 +1,26 @@ +namespace TechbloxModdingAPI.Blocks +{ + using RobocraftX.Common; + using Svelto.ECS; + + + public class LogicGate : SignalingBlock + { + + /// + /// Constructs a(n) LogicGate object representing an existing block. + /// + public LogicGate(EGID egid) : + base(egid) + { + } + + /// + /// Constructs a(n) LogicGate object representing an existing block. + /// + public LogicGate(uint id) : + base(new EGID(id, CommonExclusiveGroups.LOGIC_BLOCK_GROUP)) + { + } + } +} diff --git a/TechbloxModdingAPI/Blocks/Motor.cs b/TechbloxModdingAPI/Blocks/Motor.cs new file mode 100644 index 0000000..233d5f6 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Motor.cs @@ -0,0 +1,71 @@ +namespace TechbloxModdingAPI.Blocks +{ + using RobocraftX.Common; + using Svelto.ECS; + + + public class Motor : SignalingBlock + { + + /// + /// Constructs a(n) Motor object representing an existing block. + /// + public Motor(EGID egid) : + base(egid) + { + } + + /// + /// Constructs a(n) Motor object representing an existing block. + /// + public Motor(uint id) : + base(new EGID(id, CommonExclusiveGroups.MOTOR_BLOCK_GROUP)) + { + } + + /// + /// Gets or sets the Motor's TopSpeed property. Tweakable stat. + /// + public float TopSpeed + { + get + { + return BlockEngine.GetBlockInfo(this).maxVelocity; + } + set + { + BlockEngine.GetBlockInfo(this).maxVelocity = value; + } + } + + /// + /// Gets or sets the Motor's Torque property. Tweakable stat. + /// + public float Torque + { + get + { + return BlockEngine.GetBlockInfo(this).maxForce; + } + set + { + BlockEngine.GetBlockInfo(this).maxForce = value; + } + } + + /// + /// Gets or sets the Motor's Reverse property. Tweakable stat. + /// + public bool Reverse + { + get + { + return BlockEngine.GetBlockInfo(this).reverse; + } + set + { + BlockEngine.GetBlockInfo(this).reverse = value; + } + } + } +} diff --git a/TechbloxModdingAPI/Blocks/Piston.cs b/TechbloxModdingAPI/Blocks/Piston.cs new file mode 100644 index 0000000..c351d59 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Piston.cs @@ -0,0 +1,71 @@ +namespace TechbloxModdingAPI.Blocks +{ + using RobocraftX.Common; + using Svelto.ECS; + + + public class Piston : SignalingBlock + { + + /// + /// Constructs a(n) Piston object representing an existing block. + /// + public Piston(EGID egid) : + base(egid) + { + } + + /// + /// Constructs a(n) Piston object representing an existing block. + /// + public Piston(uint id) : + base(new EGID(id, CommonExclusiveGroups.PISTON_BLOCK_GROUP)) + { + } + + /// + /// Gets or sets the Piston's MaximumForce property. Tweakable stat. + /// + public float MaximumForce + { + get + { + return BlockEngine.GetBlockInfo(this).pistonVelocity; + } + set + { + BlockEngine.GetBlockInfo(this).pistonVelocity = value; + } + } + + /// + /// Gets or sets the Piston's MaxExtension property. Tweakable stat. + /// + public float MaxExtension + { + get + { + return BlockEngine.GetBlockInfo(this).maxDeviation; + } + set + { + BlockEngine.GetBlockInfo(this).maxDeviation = value; + } + } + + /// + /// Gets or sets the Piston's InputIsExtension property. Tweakable stat. + /// + public bool InputIsExtension + { + get + { + return BlockEngine.GetBlockInfo(this).hasProportionalInput; + } + set + { + BlockEngine.GetBlockInfo(this).hasProportionalInput = value; + } + } + } +} diff --git a/TechbloxModdingAPI/Blocks/Seat.cs b/TechbloxModdingAPI/Blocks/Seat.cs new file mode 100644 index 0000000..e06077f --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Seat.cs @@ -0,0 +1,56 @@ +namespace TechbloxModdingAPI.Blocks +{ + using RobocraftX.Common; + using Svelto.ECS; + + + public class Seat : SignalingBlock + { + + /// + /// Constructs a(n) Seat object representing an existing block. + /// + public Seat(EGID egid) : + base(egid) + { + } + + /// + /// Constructs a(n) Seat object representing an existing block. + /// + public Seat(uint id) : + base(new EGID(id, RobocraftX.PilotSeat.SeatGroups.PILOTSEAT_BLOCK_BUILD_GROUP)) + { + } + + /// + /// Gets or sets the Seat's FollowCam property. Tweakable stat. + /// + public bool FollowCam + { + get + { + return BlockEngine.GetBlockInfo(this).followCam; + } + set + { + BlockEngine.GetBlockInfo(this).followCam = value; + } + } + + /// + /// Gets or sets the Seat's CharacterColliderHeight property. May not be saved. + /// + public float CharacterColliderHeight + { + get + { + return BlockEngine.GetBlockInfo(this).characterColliderHeight; + } + set + { + BlockEngine.GetBlockInfo(this).characterColliderHeight = value; + } + } + } +} diff --git a/TechbloxModdingAPI/Blocks/Servo.cs b/TechbloxModdingAPI/Blocks/Servo.cs new file mode 100644 index 0000000..89bd194 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Servo.cs @@ -0,0 +1,146 @@ +namespace TechbloxModdingAPI.Blocks +{ + using RobocraftX.Common; + using Svelto.ECS; + + + public class Servo : SignalingBlock + { + + /// + /// Constructs a(n) Servo object representing an existing block. + /// + public Servo(EGID egid) : + base(egid) + { + } + + /// + /// Constructs a(n) Servo object representing an existing block. + /// + public Servo(uint id) : + base(new EGID(id, CommonExclusiveGroups.SERVO_BLOCK_GROUP)) + { + } + + /// + /// Gets or sets the Servo's MaximumForce property. Tweakable stat. + /// + public float MaximumForce + { + get + { + return BlockEngine.GetBlockInfo(this).servoVelocity; + } + set + { + BlockEngine.GetBlockInfo(this).servoVelocity = value; + } + } + + /// + /// Gets or sets the Servo's MinimumAngle property. Tweakable stat. + /// + public float MinimumAngle + { + get + { + return BlockEngine.GetBlockInfo(this).minDeviation; + } + set + { + BlockEngine.GetBlockInfo(this).minDeviation = value; + } + } + + /// + /// Gets or sets the Servo's MaximumAngle property. Tweakable stat. + /// + public float MaximumAngle + { + get + { + return BlockEngine.GetBlockInfo(this).maxDeviation; + } + set + { + BlockEngine.GetBlockInfo(this).maxDeviation = value; + } + } + + /// + /// Gets or sets the Servo's Reverse property. Tweakable stat. + /// + public bool Reverse + { + get + { + return BlockEngine.GetBlockInfo(this).reverse; + } + set + { + BlockEngine.GetBlockInfo(this).reverse = value; + } + } + + /// + /// Gets or sets the Servo's InputIsAngle property. Tweakable stat. + /// + public bool InputIsAngle + { + get + { + return BlockEngine.GetBlockInfo(this).hasProportionalInput; + } + set + { + BlockEngine.GetBlockInfo(this).hasProportionalInput = value; + } + } + + /// + /// Gets or sets the Servo's DirectionVector property. May not be saved. + /// + public Unity.Mathematics.float3 DirectionVector + { + get + { + return BlockEngine.GetBlockInfo(this).directionVector; + } + set + { + BlockEngine.GetBlockInfo(this).directionVector = value; + } + } + + /// + /// Gets or sets the Servo's RotationAxis property. May not be saved. + /// + public Unity.Mathematics.float3 RotationAxis + { + get + { + return BlockEngine.GetBlockInfo(this).rotationAxis; + } + set + { + BlockEngine.GetBlockInfo(this).rotationAxis = value; + } + } + + /// + /// Gets or sets the Servo's ForceAxis property. May not be saved. + /// + public Unity.Mathematics.float3 ForceAxis + { + get + { + return BlockEngine.GetBlockInfo(this).forceAxis; + } + set + { + BlockEngine.GetBlockInfo(this).forceAxis = value; + } + } + } +} diff --git a/GamecraftModdingAPI/Blocks/SignalingBlock.cs b/TechbloxModdingAPI/Blocks/SignalingBlock.cs similarity index 87% rename from GamecraftModdingAPI/Blocks/SignalingBlock.cs rename to TechbloxModdingAPI/Blocks/SignalingBlock.cs index a137b98..7ae6e6a 100644 --- a/GamecraftModdingAPI/Blocks/SignalingBlock.cs +++ b/TechbloxModdingAPI/Blocks/SignalingBlock.cs @@ -4,10 +4,10 @@ using Gamecraft.Wires; using Svelto.ECS; using Unity.Mathematics; -using GamecraftModdingAPI; -using GamecraftModdingAPI.Utility; +using TechbloxModdingAPI; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Blocks +namespace TechbloxModdingAPI.Blocks { /// /// Common implementation for blocks that support wiring. @@ -46,9 +46,9 @@ namespace GamecraftModdingAPI.Blocks /// The connected wire. /// Port identifier. /// Whether the port has a wire connected to it. - protected ref WireEntityStruct GetConnectedWire(EGID portId, out bool connected) + protected ref WireEntityStruct GetConnectedWire(PortEntityStruct port, out bool connected) { - return ref SignalEngine.MatchPortToWire(portId, Id, out connected); + return ref SignalEngine.MatchPortToWire(port, Id, out connected); } /// @@ -56,10 +56,9 @@ namespace GamecraftModdingAPI.Blocks /// /// The channel data. /// Port identifier. - /// Whether the channel actually exists. - protected ref ChannelDataStruct GetChannelData(EGID portId, out bool exists) + protected OptionalRef GetChannelData(EGID portId) { - return ref SignalEngine.GetChannelDataStruct(portId, out exists); + return SignalEngine.GetChannelDataStruct(portId); } /// @@ -67,7 +66,7 @@ namespace GamecraftModdingAPI.Blocks /// public uint InputCount { - get => BlockEngine.GetBlockInfo(this, (BlockPortsStruct st) => st.inputCount); + get => BlockEngine.GetBlockInfo(this).inputCount; } /// @@ -75,7 +74,7 @@ namespace GamecraftModdingAPI.Blocks /// public uint OutputCount { - get => BlockEngine.GetBlockInfo(this, (BlockPortsStruct st) => st.outputCount); + get => BlockEngine.GetBlockInfo(this).outputCount; } /// @@ -109,7 +108,6 @@ namespace GamecraftModdingAPI.Blocks /// The localized port name. public string PortName(byte port, bool input) { - BlockPortsStruct bps = BlockEngine.GetBlockInfo(this, (BlockPortsStruct a) => a); PortEntityStruct pes = SignalEngine.GetPortByOffset(this, port, input); return pes.portNameLocalised; } diff --git a/TechbloxModdingAPI/Blocks/WheelRig.cs b/TechbloxModdingAPI/Blocks/WheelRig.cs new file mode 100644 index 0000000..28ba739 --- /dev/null +++ b/TechbloxModdingAPI/Blocks/WheelRig.cs @@ -0,0 +1,106 @@ +using TechbloxModdingAPI.Tests; + +namespace TechbloxModdingAPI.Blocks +{ + using RobocraftX.Common; + using Svelto.ECS; + + + public class WheelRig : SignalingBlock + { + + /// + /// Constructs a(n) WheelRig object representing an existing block. + /// + public WheelRig(EGID egid) : + base(egid) + { + } + + /// + /// Constructs a(n) WheelRig object representing an existing block. + /// + public WheelRig(uint id) : + base(new EGID(id, CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP)) + { + } + + /// + /// Gets or sets the WheelRig's BrakingStrength property. Tweakable stat. + /// + public float BrakingStrength + { + get + { + return BlockEngine.GetBlockInfo(this).brakingStrength; + } + set + { + BlockEngine.GetBlockInfo(this).brakingStrength = value; + } + } + + /// + /// Gets or sets the WheelRig's MaxVelocity property. May not be saved. + /// + public float MaxVelocity + { + get + { + return BlockEngine.GetBlockInfo(this).maxVelocity; + } + set + { + BlockEngine.GetBlockInfo(this).maxVelocity = value; + } + } + + /// + /// Gets or sets the WheelRig's SteerAngle property. Tweakable stat. + /// + [TestValue(0f)] // Can be 0 for no steer variant + public float SteerAngle + { + get + { + return BlockEngine.GetBlockInfo(this).steerAngle; + } + set + { + BlockEngine.GetBlockInfo(this).steerAngle = value; + } + } + + /// + /// Gets or sets the WheelRig's VelocityForMinAngle property. May not be saved. + /// + [TestValue(0f)] + public float VelocityForMinAngle + { + get + { + return BlockEngine.GetBlockInfo(this).velocityForMinAngle; + } + set + { + BlockEngine.GetBlockInfo(this).velocityForMinAngle = value; + } + } + + /// + /// Gets or sets the WheelRig's MinSteerAngleFactor property. May not be saved. + /// + [TestValue(0f)] + public float MinSteerAngleFactor + { + get + { + return BlockEngine.GetBlockInfo(this).minSteerAngleFactor; + } + set + { + BlockEngine.GetBlockInfo(this).minSteerAngleFactor = value; + } + } + } +} diff --git a/TechbloxModdingAPI/Blocks/Wire.cs b/TechbloxModdingAPI/Blocks/Wire.cs new file mode 100644 index 0000000..e59312d --- /dev/null +++ b/TechbloxModdingAPI/Blocks/Wire.cs @@ -0,0 +1,312 @@ +using System; + +using Gamecraft.Wires; +using Svelto.ECS; +using Svelto.ECS.Experimental; + +using TechbloxModdingAPI.Blocks.Engines; + +namespace TechbloxModdingAPI.Blocks +{ + public class Wire : EcsObjectBase + { + internal static SignalEngine signalEngine; + + protected EGID startPortEGID; + + protected EGID endPortEGID; + + protected EGID startBlockEGID; + + protected EGID endBlockEGID; + + protected EGID wireEGID; + + protected bool inputToOutput; + + protected byte startPort; + + protected byte endPort; + + public static Wire Connect(SignalingBlock start, byte startPort, SignalingBlock end, byte endPort) + { + WireEntityStruct wire = signalEngine.CreateNewWire(start.Id, startPort, end.Id, endPort); + return new Wire(wire, start, end); + } + + /// + /// An existing wire connection ending at the specified input. + /// If multiple exist, this will return the first one found. + /// + /// Destination block. + /// Port number. + /// The wire, where the end of the wire is the block port specified, or null if does not exist. + public static Wire ConnectedToInputPort(SignalingBlock end, byte endPort) + { + var port = signalEngine.MatchBlockIOToPort(end, endPort, false); + if (!port) return null; + WireEntityStruct wire = signalEngine.MatchPortToWire(port, end.Id, out var exists); + return exists + ? new Wire(wire.sourceBlockEGID, end.Id, wire.sourcePortUsage, endPort, wire.ID, false) + : null; + } + + /// + /// An existing wire connection starting at the specified output. + /// If multiple exist, this will return the first one found. + /// + /// Source block entity ID. + /// Port number. + /// The wire, where the start of the wire is the block port specified, or null if does not exist. + public static Wire ConnectedToOutputPort(SignalingBlock start, byte startPort) + { + var port = signalEngine.MatchBlockIOToPort(start, startPort, true); + if (!port) return null; + WireEntityStruct wire = signalEngine.MatchPortToWire(port, start.Id, out var exists); + return exists + ? new Wire(start.Id, wire.destinationBlockEGID, startPort, wire.destinationPortUsage, wire.ID, false) + : null; + } + + /// + /// Construct a wire object froam n existing connection. + /// + /// Starting block ID. + /// Ending block ID. + /// Starting port number, or guess if omitted. + /// Ending port number, or guess if omitted. + /// Guessing failed or wire does not exist. + public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue) : base(ecs => + { + var th = (Wire)ecs; + th.startBlockEGID = start.Id; + th.endBlockEGID = end.Id; + bool flipped = false; + // find block ports + EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort); + if (wire == default) + { + // flip I/O around and try again + wire = signalEngine.MatchBlocksToWire(end.Id, start.Id, endPort, startPort); + flipped = true; + // NB: start and end are handled exactly as they're received as params. + // This makes wire traversal easier, but makes logic in this class a bit more complex + } + + if (wire != default) + { + th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped); + } + else + { + throw new WireInvalidException("Wire not found"); + } + + return th.wireEGID; + }) + { + } + + /// + /// Construct a wire object from an existing wire connection. + /// + /// Starting block ID. + /// Ending block ID. + /// Starting port number. + /// Ending port number. + /// The wire ID. + /// Whether the wire direction goes input -> output (true) or output -> input (false, preferred). + public Wire(Block start, Block end, byte startPort, byte endPort, EGID wire, bool inputToOutput) + : this(start.Id, end.Id, startPort, endPort, wire, inputToOutput) + { + } + + private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire) + { + Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput); + } + + private void Construct(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) + { + this.startBlockEGID = startBlock; + this.endBlockEGID = endBlock; + this.inputToOutput = inputToOutput; + this.wireEGID = wire; + endPortEGID = signalEngine.MatchBlockIOToPort(startBlock, startPort, inputToOutput).Nullable()?.ID ?? default; + if (endPortEGID == default) throw new WireInvalidException("Wire end port not found"); + startPortEGID = signalEngine.MatchBlockIOToPort(endBlock, endPort, !inputToOutput).Nullable()?.ID ?? default; + if (startPortEGID == default) throw new WireInvalidException("Wire start port not found"); + this.startPort = startPort; + this.endPort = endPort; + } + + /// + /// Construct a wire object from an existing wire connection. + /// + /// The wire ID. + public Wire(EGID wireEgid) : base(wireEgid) + { + WireEntityStruct wire = signalEngine.GetWire(wireEGID); + Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage, + wireEgid, false); + } + + private Wire(WireEntityStruct wire, SignalingBlock src, SignalingBlock dest) + : this(src, dest, wire.sourcePortUsage, wire.destinationPortUsage, wire.ID, false) + { + } + + /// + /// The wire's signal value, as a float. + /// + public float Float + { + get + { + return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat; + } + + set + { + signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsFloat = value; + } + } + + /// + /// The wire's string signal. + /// + public string String + { + get + { + return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString; + } + + set + { + signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString.Set(value); + } + } + + /// + /// The wire's raw string signal. + /// + public ECSString ECSString + { + get + { + return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString; + } + + set + { + signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsEcsString = value; + } + } + + /// + /// The wire's signal id. + /// I'm 50% sure this is useless. + /// + public uint SignalId + { + get + { + return signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID; + } + + set + { + signalEngine.GetChannelDataStruct(startPortEGID).Get().valueAsID = value; + } + } + + /// + /// The block at the beginning of the wire. + /// + public SignalingBlock Start + { + get => (SignalingBlock)Block.New(startBlockEGID); + } + + /// + /// The port number that the beginning of the wire connects to. + /// + public byte StartPort + { + get => startPort; + } + + /// + /// The display name of the start port. + /// + public string StartPortName + { + get => signalEngine.GetPort(startPortEGID).portNameLocalised; + } + + /// + /// The block at the end of the wire. + /// + public SignalingBlock End + { + get => (SignalingBlock)Block.New(endBlockEGID); + } + + /// + /// The port number that the end of the wire connects to. + /// + public byte EndPort + { + get => endPort; + } + + /// + /// The display name of the end port. + /// + public string EndPortName + { + get => signalEngine.GetPort(endPortEGID).portNameLocalised; + } + + /// + /// Create a copy of the wire object where the direction of the wire is guaranteed to be from a block output to a block input. + /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input). + /// + /// A copy of the wire object. + public Wire OutputToInputCopy() + { + return GetInstance(wireEGID, egid => new Wire(egid)); + } + + /// + /// Convert the wire object to the direction the signal flows. + /// Signals on wires always flow from a block output port to a block input port. + /// This is simply a different memory configuration and does not affect the in-game wire (which is always output -> input). + /// + public void OutputToInputInPlace() + { + if (inputToOutput) + { + inputToOutput = false; + // swap inputs and outputs + (endBlockEGID, startBlockEGID) = (startBlockEGID, endBlockEGID); + var tempPort = endPortEGID; + endPortEGID = startPortEGID; + startPortEGID = tempPort; + (endPort, startPort) = (startPort, endPort); + } + } + + public override string ToString() + { + if (signalEngine.Exists(wireEGID)) + { + return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} aka {(StartPort != byte.MaxValue ? Start.PortName(StartPort, inputToOutput) : "")}) -> ({End.Type}::{EndPort} aka {(EndPort != byte.MaxValue ? End.PortName(EndPort, !inputToOutput) : "")})"; + } + return $"{nameof(Id)}: {Id}, Start{nameof(Start.Id)}: {Start.Id}, End{nameof(End.Id)}: {End.Id}, ({Start.Type}::{StartPort} -> {End.Type}::{EndPort})"; + } + + internal static void Init() { } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blueprint.cs b/TechbloxModdingAPI/Blueprint.cs similarity index 63% rename from GamecraftModdingAPI/Blueprint.cs rename to TechbloxModdingAPI/Blueprint.cs index 458c56c..c49d598 100644 --- a/GamecraftModdingAPI/Blueprint.cs +++ b/TechbloxModdingAPI/Blueprint.cs @@ -1,8 +1,8 @@ -using System; +using System; using Unity.Mathematics; using UnityEngine; -namespace GamecraftModdingAPI +namespace TechbloxModdingAPI { /// /// Represents a blueprint in the inventory. When placed it becomes a block group. @@ -15,15 +15,23 @@ namespace GamecraftModdingAPI { Id = id; BlockGroup._engine.InitBlueprint(id); + Refresh(); } - - /*public static void SelectBlueprint(Blueprint blueprint) - { - BlueprintUtil.SelectBlueprint(null, new BlueprintInventoryItemEntityStruct - { - blueprintResourceId = blueprint.Id - }); - }*/ + + /// + /// The center of the blueprint. Can only be set using the StoreBlocks() method. + /// + public float3 Position { get; private set; } + + /// + /// The rotation of the blueprint. Can only be set using the StoreBlocks() method. + /// + public float3 Rotation { get; private set; } + + /// + /// The amount of blocks in the blueprint. Gan only be set using the StoreBlocks() method. + /// + public uint BlockCount { get; private set; } /// /// Creates a new, empty blueprint. It will be deleted on disposal unless the game holds a reference to it. @@ -39,12 +47,13 @@ namespace GamecraftModdingAPI /// Use the BlockGroup overload for automatically calculated position and rotation. /// /// The array of blocks to use - /// The anchor position of the blueprint + /// The anchor (center) position of the blueprint /// The base rotation of the blueprint public void StoreBlocks(Block[] blocks, float3 position, float3 rotation) { BlockGroup._engine.ReplaceBlueprint(Player.LocalPlayer.Id, Id, blocks, position, quaternion.Euler(rotation)); + Refresh(); } /// @@ -55,6 +64,7 @@ namespace GamecraftModdingAPI { BlockGroup._engine.ReplaceBlueprint(Player.LocalPlayer.Id, Id, group, group.Position, Quaternion.Euler(group.Rotation)); + Refresh(); } /// @@ -68,9 +78,25 @@ namespace GamecraftModdingAPI return BlockGroup._engine.PlaceBlueprintBlocks(Id, Player.LocalPlayer.Id, position, rotation); } + /// + /// Updates the properties based on the blueprint data. Only necessary if the blueprint is changed from the game. + /// + public void Refresh() + { + BlockGroup._engine.GetBlueprintInfo(Id, out var pos, out var rot, out uint count); + Position = pos; + Rotation = ((Quaternion) rot).eulerAngles; + BlockCount = count; + } + public void Dispose() { BlockGroup._engine.DisposeBlueprint(Id); } + + public override string ToString() + { + return $"{nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}, {nameof(BlockCount)}: {BlockCount}"; + } } } \ No newline at end of file diff --git a/GamecraftModdingAPI/Cluster.cs b/TechbloxModdingAPI/Cluster.cs similarity index 85% rename from GamecraftModdingAPI/Cluster.cs rename to TechbloxModdingAPI/Cluster.cs index 79b5822..f88cec9 100644 --- a/GamecraftModdingAPI/Cluster.cs +++ b/TechbloxModdingAPI/Cluster.cs @@ -2,19 +2,16 @@ using RobocraftX.Common; using Svelto.ECS; -namespace GamecraftModdingAPI +namespace TechbloxModdingAPI { /// /// Represnts a cluster of blocks in time running mode, meaning blocks that are connected either directly or via joints. /// Only exists if a cluster destruction manager is present. Static blocks like grass and dirt aren't part of a cluster. /// - public class Cluster + public class Cluster : EcsObjectBase { - public EGID Id { get; } - - public Cluster(EGID id) + public Cluster(EGID id) : base(id) { - Id = id; } public Cluster(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP)) @@ -23,20 +20,20 @@ namespace GamecraftModdingAPI public float InitialHealth { - get => Block.BlockEngine.GetBlockInfo(Id).initialHealth; - set => Block.BlockEngine.GetBlockInfo(Id).initialHealth = value; + get => Block.BlockEngine.GetBlockInfo(this).initialHealth; + set => Block.BlockEngine.GetBlockInfo(this).initialHealth = value; } public float CurrentHealth { - get => Block.BlockEngine.GetBlockInfo(Id).currentHealth; - set => Block.BlockEngine.GetBlockInfo(Id).currentHealth = value; + get => Block.BlockEngine.GetBlockInfo(this).currentHealth; + set => Block.BlockEngine.GetBlockInfo(this).currentHealth = value; } public float HealthMultiplier { - get => Block.BlockEngine.GetBlockInfo(Id).healthMultiplier; - set => Block.BlockEngine.GetBlockInfo(Id).healthMultiplier = value; + get => Block.BlockEngine.GetBlockInfo(this).healthMultiplier; + set => Block.BlockEngine.GetBlockInfo(this).healthMultiplier = value; } /// diff --git a/GamecraftModdingAPI/Commands/CommandBuilder.cs b/TechbloxModdingAPI/Commands/CommandBuilder.cs similarity index 99% rename from GamecraftModdingAPI/Commands/CommandBuilder.cs rename to TechbloxModdingAPI/Commands/CommandBuilder.cs index 7bd5ed9..1a34f45 100644 --- a/GamecraftModdingAPI/Commands/CommandBuilder.cs +++ b/TechbloxModdingAPI/Commands/CommandBuilder.cs @@ -1,10 +1,9 @@ using System; using Svelto.ECS; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// /// Custom Command builder. diff --git a/GamecraftModdingAPI/Commands/CommandEngineFactory.cs b/TechbloxModdingAPI/Commands/CommandEngineFactory.cs similarity index 88% rename from GamecraftModdingAPI/Commands/CommandEngineFactory.cs rename to TechbloxModdingAPI/Commands/CommandEngineFactory.cs index ddcdcb8..07e9a67 100644 --- a/GamecraftModdingAPI/Commands/CommandEngineFactory.cs +++ b/TechbloxModdingAPI/Commands/CommandEngineFactory.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// /// UNIMPLEMENTED! diff --git a/GamecraftModdingAPI/Commands/CommandExceptions.cs b/TechbloxModdingAPI/Commands/CommandExceptions.cs similarity index 92% rename from GamecraftModdingAPI/Commands/CommandExceptions.cs rename to TechbloxModdingAPI/Commands/CommandExceptions.cs index 39502a7..b860093 100644 --- a/GamecraftModdingAPI/Commands/CommandExceptions.cs +++ b/TechbloxModdingAPI/Commands/CommandExceptions.cs @@ -1,7 +1,7 @@ using System; -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { - public class CommandException : GamecraftModdingAPIException + public class CommandException : TechbloxModdingAPIException { public CommandException() : base() {} diff --git a/GamecraftModdingAPI/Commands/CommandManager.cs b/TechbloxModdingAPI/Commands/CommandManager.cs similarity index 96% rename from GamecraftModdingAPI/Commands/CommandManager.cs rename to TechbloxModdingAPI/Commands/CommandManager.cs index 876d634..e275ace 100644 --- a/GamecraftModdingAPI/Commands/CommandManager.cs +++ b/TechbloxModdingAPI/Commands/CommandManager.cs @@ -5,10 +5,9 @@ using System.Text; using System.Threading.Tasks; using Svelto.ECS; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// /// Keeps track of custom commands diff --git a/TechbloxModdingAPI/Commands/CommandPatch.cs b/TechbloxModdingAPI/Commands/CommandPatch.cs new file mode 100644 index 0000000..b928c1d --- /dev/null +++ b/TechbloxModdingAPI/Commands/CommandPatch.cs @@ -0,0 +1,34 @@ +using System; +using System.Reflection; + +using HarmonyLib; +using Svelto.ECS; +using RobocraftX.CR.MainGame; +using RobocraftX.Multiplayer; +using RobocraftX.StateSync; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Commands +{ + /// + /// Patch of RobocraftX.CR.MainGame.MainGameCompositionRoot.DeterministicCompose() + /// Initializes custom commands + /// + [HarmonyPatch] + static class CommandPatch + { + public static void Postfix(StateSyncRegistrationHelper stateSyncReg) + { + /*CommandLineCompositionRoot.Compose(contextHolder, stateSyncReg.enginesRoot, reloadGame, multiplayerParameters, + stateSyncReg); - uREPL C# compilation not supported anymore */ + var enginesRoot = stateSyncReg.enginesRoot; + CommandManager.RegisterEngines(enginesRoot); + } + + public static MethodInfo TargetMethod() + { + return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose") + .MakeGenericMethod(typeof(object)); + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Commands/CommandRegistrationHelper.cs b/TechbloxModdingAPI/Commands/CommandRegistrationHelper.cs similarity index 61% rename from GamecraftModdingAPI/Commands/CommandRegistrationHelper.cs rename to TechbloxModdingAPI/Commands/CommandRegistrationHelper.cs index 5cb9069..e33b232 100644 --- a/GamecraftModdingAPI/Commands/CommandRegistrationHelper.cs +++ b/TechbloxModdingAPI/Commands/CommandRegistrationHelper.cs @@ -4,22 +4,17 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using uREPL; -using RobocraftX.CommandLine.Custom; - -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// - /// Convenient methods for registering commands to Gamecraft. + /// Convenient methods for registering commands to Techblox. /// All methods register to the command line and console block by default. /// public static class CommandRegistrationHelper { public static void Register(string name, Action action, string desc, bool noConsole = false) { - RuntimeCommands.Register(name, action, desc); - if (noConsole) { return; } - ConsoleCommands.Register(name, action, desc); + CustomCommands.Register(name, action, desc); } public static void Register(string name, Action action, string desc, bool noConsole = false) @@ -39,50 +34,42 @@ namespace GamecraftModdingAPI.Commands public static void Register(string name, Action action, string desc, bool noConsole = false) { - RuntimeCommands.Register(name, action, desc); - if (noConsole) { return; } - ConsoleCommands.Register(name, action, desc); + CustomCommands.Register(name, action, desc); } public static void Register(string name, Action action, string desc, bool noConsole = false) { - RuntimeCommands.Register(name, action, desc); - if (noConsole) { return; } - ConsoleCommands.Register(name, action, desc); + CustomCommands.Register(name, action, desc); } public static void Register(string name, Action action, string desc, bool noConsole = false) { - RuntimeCommands.Register(name, action, desc); - if (noConsole) { return; } - ConsoleCommands.Register(name, action, desc); + CustomCommands.Register(name, action, desc); } public static void Unregister(string name, bool noConsole = false) { - RuntimeCommands.Unregister(name); - if (noConsole) { return; } - ConsoleCommands.Unregister(name); + CustomCommands.Unregister(name); } public static void Call(string name) { - RuntimeCommands.Call(name); + CustomCommands.Call(name); } public static void Call(string name, Param0 param0) { - RuntimeCommands.Call(name, param0); + CustomCommands.Call(name, param0); } public static void Call(string name, Param0 param0, Param1 param1) { - RuntimeCommands.Call(name, param0, param1); + CustomCommands.Call(name, param0, param1); } public static void Call(string name, Param0 param0, Param1 param1, Param2 param2) { - RuntimeCommands.Call(name, param0, param1, param2); + CustomCommands.Call(name, param0, param1, param2); } } } diff --git a/TechbloxModdingAPI/Commands/CustomCommands.cs b/TechbloxModdingAPI/Commands/CustomCommands.cs new file mode 100644 index 0000000..0cacff8 --- /dev/null +++ b/TechbloxModdingAPI/Commands/CustomCommands.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.InteropServices; + +namespace TechbloxModdingAPI.Commands +{ + internal static class CustomCommands + { + public struct CommandData + { + public string Name; + public string Description; + public Delegate Action; + } + + private static Dictionary _commands = new Dictionary(); + public static void Register(string name, Delegate action, string desc) + { + _commands.Add(name, new CommandData + { + Name = name, + Description = desc, + Action = action + }); + } + + public static void Call(string name, params object[] args) + { + if (_commands.TryGetValue(name, out var command)) + { + var paramz = command.Action.Method.GetParameters(); + if (paramz.Length > args.Length) + throw new CommandParameterMissingException( + $"This command requires {paramz.Length} arguments, {args.Length} given"); + for (var index = 0; index < paramz.Length; index++) + { + args[index] = Convert.ChangeType(args[index], paramz[index].ParameterType); + } + + command.Action.DynamicInvoke(args); + } + else + throw new CommandNotFoundException($"Command {name} does not exist!"); + } + + public static void Unregister(string name) + { + _commands.Remove(name); + } + + public static bool Exists(string name) => _commands.ContainsKey(name); + + public static ReadOnlyDictionary GetAllCommandData() => + new ReadOnlyDictionary(_commands); + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Commands/ExistingCommands.cs b/TechbloxModdingAPI/Commands/ExistingCommands.cs new file mode 100644 index 0000000..dd61cc8 --- /dev/null +++ b/TechbloxModdingAPI/Commands/ExistingCommands.cs @@ -0,0 +1,37 @@ +using System.Linq; + +namespace TechbloxModdingAPI.Commands +{ + public static class ExistingCommands + { + public static void Call(string commandName) + { + CustomCommands.Call(commandName); + } + + public static void Call(string commandName, Arg0 arg0) + { + CustomCommands.Call(commandName, arg0); + } + + public static void Call(string commandName, Arg0 arg0, Arg1 arg1) + { + CustomCommands.Call(commandName, arg0, arg1); + } + + public static void Call(string commandName, Arg0 arg0, Arg1 arg1, Arg2 arg2) + { + CustomCommands.Call(commandName, arg0, arg1, arg2); + } + + public static bool Exists(string commandName) + { + return CustomCommands.Exists(commandName); + } + + public static (string Name, string Description)[] GetCommandNamesAndDescriptions() + { + return CustomCommands.GetAllCommandData().Values.Select(command => (command.Name, command.Description)).ToArray(); + } + } +} diff --git a/GamecraftModdingAPI/Commands/ICustomCommandEngine.cs b/TechbloxModdingAPI/Commands/ICustomCommandEngine.cs similarity index 85% rename from GamecraftModdingAPI/Commands/ICustomCommandEngine.cs rename to TechbloxModdingAPI/Commands/ICustomCommandEngine.cs index 1704e84..afb0237 100644 --- a/GamecraftModdingAPI/Commands/ICustomCommandEngine.cs +++ b/TechbloxModdingAPI/Commands/ICustomCommandEngine.cs @@ -6,10 +6,10 @@ using System.Threading.Tasks; using Svelto.ECS; -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Engines; +using TechbloxModdingAPI.Utility; +using TechbloxModdingAPI.Engines; -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// /// Engine interface to handle command operations. diff --git a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine.cs b/TechbloxModdingAPI/Commands/SimpleCustomCommandEngine.cs similarity index 87% rename from GamecraftModdingAPI/Commands/SimpleCustomCommandEngine.cs rename to TechbloxModdingAPI/Commands/SimpleCustomCommandEngine.cs index 2ce60a9..87e0960 100644 --- a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine.cs +++ b/TechbloxModdingAPI/Commands/SimpleCustomCommandEngine.cs @@ -5,10 +5,9 @@ using System.Text; using System.Threading.Tasks; using Svelto.ECS; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// /// A simple implementation of ICustomCommandEngine sufficient for most commands. @@ -37,13 +36,13 @@ namespace GamecraftModdingAPI.Commands public void Dispose() { - GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); + Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); CommandRegistrationHelper.Unregister(this.Name); } public void Ready() { - GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); + Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description); } diff --git a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine1.cs b/TechbloxModdingAPI/Commands/SimpleCustomCommandEngine1.cs similarity index 86% rename from GamecraftModdingAPI/Commands/SimpleCustomCommandEngine1.cs rename to TechbloxModdingAPI/Commands/SimpleCustomCommandEngine1.cs index bac16ef..ef3bb02 100644 --- a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine1.cs +++ b/TechbloxModdingAPI/Commands/SimpleCustomCommandEngine1.cs @@ -5,10 +5,9 @@ using System.Text; using System.Threading.Tasks; using Svelto.ECS; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// /// A simple implementation of ICustomCommandEngine sufficient for most commands. @@ -28,13 +27,13 @@ namespace GamecraftModdingAPI.Commands public void Dispose() { - GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); + Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); CommandRegistrationHelper.Unregister(this.Name); } public void Ready() { - GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); + Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description); } diff --git a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine2.cs b/TechbloxModdingAPI/Commands/SimpleCustomCommandEngine2.cs similarity index 86% rename from GamecraftModdingAPI/Commands/SimpleCustomCommandEngine2.cs rename to TechbloxModdingAPI/Commands/SimpleCustomCommandEngine2.cs index 4b2b38d..8127861 100644 --- a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine2.cs +++ b/TechbloxModdingAPI/Commands/SimpleCustomCommandEngine2.cs @@ -5,10 +5,9 @@ using System.Text; using System.Threading.Tasks; using Svelto.ECS; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// /// A simple implementation of ICustomCommandEngine sufficient for most commands. @@ -28,13 +27,13 @@ namespace GamecraftModdingAPI.Commands public void Dispose() { - GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); + Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); CommandRegistrationHelper.Unregister(this.Name); } public void Ready() { - GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); + Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description); } diff --git a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine3.cs b/TechbloxModdingAPI/Commands/SimpleCustomCommandEngine3.cs similarity index 86% rename from GamecraftModdingAPI/Commands/SimpleCustomCommandEngine3.cs rename to TechbloxModdingAPI/Commands/SimpleCustomCommandEngine3.cs index c9c942e..9b2f6b1 100644 --- a/GamecraftModdingAPI/Commands/SimpleCustomCommandEngine3.cs +++ b/TechbloxModdingAPI/Commands/SimpleCustomCommandEngine3.cs @@ -5,10 +5,9 @@ using System.Text; using System.Threading.Tasks; using Svelto.ECS; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Commands +namespace TechbloxModdingAPI.Commands { /// /// A simple implementation of ICustomCommandEngine sufficient for most commands. @@ -28,13 +27,13 @@ namespace GamecraftModdingAPI.Commands public void Dispose() { - GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); + Logging.MetaDebugLog($"Unregistering SimpleCustomCommandEngine {this.Name}"); CommandRegistrationHelper.Unregister(this.Name); } public void Ready() { - GamecraftModdingAPI.Utility.Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); + Logging.MetaDebugLog($"Registering SimpleCustomCommandEngine {this.Name}"); CommandRegistrationHelper.Register(this.Name, this.InvokeCatchError, this.Description); } diff --git a/TechbloxModdingAPI/EcsObjectBase.cs b/TechbloxModdingAPI/EcsObjectBase.cs new file mode 100644 index 0000000..9698eaa --- /dev/null +++ b/TechbloxModdingAPI/EcsObjectBase.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using Svelto.DataStructures; +using Svelto.ECS; +using Svelto.ECS.Internal; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI +{ + public abstract class EcsObjectBase + { + public EGID Id { get; } + + private static readonly Dictionary> _instances = + new Dictionary>(); + + private static readonly WeakDictionary _noInstance = + new WeakDictionary(); + + internal static WeakDictionary GetInstances(Type type) + { + return _instances.TryGetValue(type, out var dict) ? dict : null; + } + + /// + /// Returns a cached instance if there's an actively used instance of the object already. + /// Objects still get garbage collected and then they will be removed from the cache. + /// + /// The EGID of the entity + /// The constructor to construct the object + /// The object type + /// + internal static T GetInstance(EGID egid, Func constructor, Type type = null) where T : EcsObjectBase + { + var instances = GetInstances(type ?? typeof(T)); + if (instances == null || !instances.TryGetValue(egid, out var instance)) + return constructor(egid); // It will be added by the constructor + return (T)instance; + } + + protected EcsObjectBase(EGID id) + { + if (!_instances.TryGetValue(GetType(), out var dict)) + { + dict = new WeakDictionary(); + _instances.Add(GetType(), dict); + } + if (!dict.ContainsKey(id)) // Multiple instances may be created + dict.Add(id, this); + Id = id; + } + + protected EcsObjectBase(Func initializer) + { + if (!_instances.TryGetValue(GetType(), out var dict)) + { + dict = new WeakDictionary(); + _instances.Add(GetType(), dict); + } + + var id = initializer(this); + if (!dict.ContainsKey(id)) // Multiple instances may be created + dict.Add(id, this); + } + + #region ECS initializer stuff + + protected internal EcsInitData InitData; + + /// + /// Holds information needed to construct a component initializer + /// + protected internal struct EcsInitData + { + private FasterDictionary group; + private EntityReference reference; + + public static implicit operator EcsInitData(EntityInitializer initializer) => new EcsInitData + { group = GetInitGroup(initializer), reference = initializer.reference }; + + public EntityInitializer Initializer(EGID id) => new EntityInitializer(id, group, reference); + public bool Valid => group != null; + } + + private delegate FasterDictionary GetInitGroupFunc( + EntityInitializer initializer); + + /// + /// Accesses the group field of the initializer + /// + private static GetInitGroupFunc GetInitGroup = CreateAccessor("_group"); + + //https://stackoverflow.com/questions/55878525/unit-testing-ref-structs-with-private-fields-via-reflection + private static TDelegate CreateAccessor(string memberName) where TDelegate : Delegate + { + var invokeMethod = typeof(TDelegate).GetMethod("Invoke"); + if (invokeMethod == null) + throw new InvalidOperationException($"{typeof(TDelegate)} signature could not be determined."); + + var delegateParameters = invokeMethod.GetParameters(); + if (delegateParameters.Length != 1) + throw new InvalidOperationException("Delegate must have a single parameter."); + + var paramType = delegateParameters[0].ParameterType; + + var objParam = Expression.Parameter(paramType, "obj"); + var memberExpr = Expression.PropertyOrField(objParam, memberName); + Expression returnExpr = memberExpr; + if (invokeMethod.ReturnType != memberExpr.Type) + returnExpr = Expression.ConvertChecked(memberExpr, invokeMethod.ReturnType); + + var lambda = + Expression.Lambda(returnExpr, $"Access{paramType.Name}_{memberName}", new[] { objParam }); + return lambda.Compile(); + } + + #endregion + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Engines/EnginePatches.cs b/TechbloxModdingAPI/Engines/EnginePatches.cs new file mode 100644 index 0000000..8b6e6a5 --- /dev/null +++ b/TechbloxModdingAPI/Engines/EnginePatches.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using HarmonyLib; +using RobocraftX; +using RobocraftX.CR.MainGame; +using RobocraftX.FrontEnd; +using RobocraftX.StateSync; +using Svelto.ECS; +using Svelto.ECS.Schedulers; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Engines +{ + [HarmonyPatch] + class GameLoadedEnginePatch + { + public static EntitiesSubmissionScheduler Scheduler { get; private set; } + public static void Postfix(StateSyncRegistrationHelper stateSyncReg) + { + // register all game engines, including deterministic + GameEngineManager.RegisterEngines(stateSyncReg); + Scheduler = stateSyncReg.enginesRoot.scheduler; + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method(typeof(MainGameCompositionRoot), "DeterministicCompose").MakeGenericMethod(typeof(object)); + } + } + + [HarmonyPatch] + class MenuLoadedEnginePatch + { + public static void Postfix(EnginesRoot enginesRoot) + { + // register menu engines + MenuEngineManager.RegisterEngines(enginesRoot); + } + + public static MethodBase TargetMethod() + { + return AccessTools.Method(typeof(FrontEndCompositionRoot), "Compose").MakeGenericMethod(typeof(object)); + } + } + + [HarmonyPatch] + class FullGameCreatedEnginePatch + { + public static void Postfix(FullGameCompositionRoot __instance) + { + FullGameFields.Init(__instance); + } + + public static MethodBase TargetMethod() + { + return AccessTools.DeclaredConstructor(typeof(FullGameCompositionRoot)); + } + } +} diff --git a/GamecraftModdingAPI/Engines/IApiEngine.cs b/TechbloxModdingAPI/Engines/IApiEngine.cs similarity index 83% rename from GamecraftModdingAPI/Engines/IApiEngine.cs rename to TechbloxModdingAPI/Engines/IApiEngine.cs index 210c13d..6a8519a 100644 --- a/GamecraftModdingAPI/Engines/IApiEngine.cs +++ b/TechbloxModdingAPI/Engines/IApiEngine.cs @@ -6,10 +6,10 @@ using System.Threading.Tasks; using Svelto.ECS; -namespace GamecraftModdingAPI.Engines +namespace TechbloxModdingAPI.Engines { /// - /// Base engine interface used by all GamecraftModdingAPI engines + /// Base engine interface used by all TechbloxModdingAPI engines /// public interface IApiEngine : IEngine, IQueryingEntitiesEngine, IDisposable { diff --git a/GamecraftModdingAPI/Engines/IFactoryEngine.cs b/TechbloxModdingAPI/Engines/IFactoryEngine.cs similarity index 88% rename from GamecraftModdingAPI/Engines/IFactoryEngine.cs rename to TechbloxModdingAPI/Engines/IFactoryEngine.cs index 3e9c23e..d03b4e3 100644 --- a/GamecraftModdingAPI/Engines/IFactoryEngine.cs +++ b/TechbloxModdingAPI/Engines/IFactoryEngine.cs @@ -6,9 +6,9 @@ using System.Threading.Tasks; using Svelto.ECS; -using GamecraftModdingAPI.Utility; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Engines +namespace TechbloxModdingAPI.Engines { /// /// Engine interface to create a ModEventEntityStruct in entitiesDB when Emit() is called. diff --git a/TechbloxModdingAPI/Engines/IFunEngine.cs b/TechbloxModdingAPI/Engines/IFunEngine.cs new file mode 100644 index 0000000..d9ee7fe --- /dev/null +++ b/TechbloxModdingAPI/Engines/IFunEngine.cs @@ -0,0 +1,9 @@ +using Svelto.ECS; + +namespace TechbloxModdingAPI.Engines +{ + public interface IFunEngine : IApiEngine + { + public IEntityFunctions Functions { set; } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Engines/IReactionaryEngine.cs b/TechbloxModdingAPI/Engines/IReactionaryEngine.cs similarity index 86% rename from GamecraftModdingAPI/Engines/IReactionaryEngine.cs rename to TechbloxModdingAPI/Engines/IReactionaryEngine.cs index 9381605..d627866 100644 --- a/GamecraftModdingAPI/Engines/IReactionaryEngine.cs +++ b/TechbloxModdingAPI/Engines/IReactionaryEngine.cs @@ -7,9 +7,9 @@ using System.Threading.Tasks; using Svelto.ECS; using Svelto.ECS.Internal; -using GamecraftModdingAPI.Events; +using TechbloxModdingAPI.Events; -namespace GamecraftModdingAPI.Engines +namespace TechbloxModdingAPI.Engines { /// /// Engine interface to handle ModEventEntityStruct events emitted by IEventEmitterEngines. diff --git a/TechbloxModdingAPI/Events/EventExceptions.cs b/TechbloxModdingAPI/Events/EventExceptions.cs new file mode 100644 index 0000000..0273ace --- /dev/null +++ b/TechbloxModdingAPI/Events/EventExceptions.cs @@ -0,0 +1,33 @@ +using System; +namespace TechbloxModdingAPI.Events +{ + public class EventException : TechbloxModdingAPIException + { + public EventException() + { + } + + public EventException(string message) : base(message) + { + } + + public EventException(string message, Exception innerException) : base(message, innerException) + { + } + } + + public class EventRuntimeException : EventException + { + public EventRuntimeException() + { + } + + public EventRuntimeException(string message) : base(message) + { + } + + public EventRuntimeException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/GamecraftModdingAPI/Input/FakeInput.cs b/TechbloxModdingAPI/Input/FakeInput.cs similarity index 67% rename from GamecraftModdingAPI/Input/FakeInput.cs rename to TechbloxModdingAPI/Input/FakeInput.cs index 8cab6f5..a3d068a 100644 --- a/GamecraftModdingAPI/Input/FakeInput.cs +++ b/TechbloxModdingAPI/Input/FakeInput.cs @@ -3,63 +3,68 @@ using RobocraftX.Common; using RobocraftX.Common.Input; using Svelto.ECS; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Input +namespace TechbloxModdingAPI.Input { public static class FakeInput { private static readonly FakeInputEngine inputEngine = new FakeInputEngine(); - /// - /// Customize the player input. - /// - /// The custom input. - /// The player. Omit this to use the local player. - public static void CustomInput(LocalInputEntityStruct input, uint playerID = uint.MaxValue) + /// + /// Customize the local input. + /// + /// The custom input. + public static void CustomInput(LocalCosmeticInputEntityComponent input) + { + inputEngine.SendCustomInput(input); + } + + /// + /// Customize the player input. + /// + /// The custom input. + /// The player. Omit this to use the local player. + public static void CustomPlayerInput(LocalPlayerInputEntityStruct input, uint playerID = uint.MaxValue) { if (playerID == uint.MaxValue) - { - playerID = inputEngine.GetLocalPlayerID(); - } - inputEngine.SendCustomInput(input, playerID); + { + playerID = inputEngine.GetLocalPlayerID(); + } + inputEngine.SendCustomPlayerInput(input, playerID); + } + + public static LocalCosmeticInputEntityComponent GetInput() + { + return inputEngine.GetInput(); } - public static LocalInputEntityStruct GetInput(uint playerID = uint.MaxValue) + public static LocalPlayerInputEntityStruct GetPlayerInput(uint playerID = uint.MaxValue) { if (playerID == uint.MaxValue) - { - playerID = inputEngine.GetLocalPlayerID(); - } - return inputEngine.GetInput(playerID); + { + playerID = inputEngine.GetLocalPlayerID(); + } + return inputEngine.GetPlayerInput(playerID); } - /// - /// Fake a GUI input. + /// + /// Fake a GUI input. /// Omit any parameter you do not want to affect. /// Parameters that end with "?" don't do anything... yet. - /// - /// The player. Omit this to use the local player. - /// Select the hotbar slot by number. - /// Toggle the command line? - /// Open escape menu? - /// Page return? - /// Toggle debug display? - /// Select next? - /// Select previous? - /// Tab? - /// Toggle to hotbar colour mode? - /// Select the hotbar page by number? - /// Quicksave? - /// Paste? - public static void GuiInput(uint playerID = uint.MaxValue, int hotbar = -1, bool commandLine = false, bool escape = false, bool enter = false, bool debug = false, bool next = false, bool previous = false, bool tab = false, bool colour = false, int hotbarPage = -1, bool quickSave = false, bool paste = false) + /// + /// Select the hotbar slot by number. + /// Toggle the command line? + /// Open escape menu? + /// Page return? + /// Toggle debug display? + /// Toggle to hotbar colour mode? + /// Select the hotbar page by number? + /// Quicksave? + /// Paste? + public static void GuiInput(int hotbar = -1, bool escape = false, bool enter = false, bool debug = false, int hotbarPage = -1, bool quickSave = false, bool paste = false) { - if (playerID == uint.MaxValue) - { - playerID = inputEngine.GetLocalPlayerID(); - } - ref LocalInputEntityStruct currentInput = ref inputEngine.GetInputRef(playerID); + ref LocalCosmeticInputEntityComponent currentInput = ref inputEngine.GetInputRef(); //Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}"); // set inputs switch(hotbar) @@ -76,13 +81,9 @@ namespace GamecraftModdingAPI.Input case 9: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Hotbar_9; break; default: break; } - //if (commandLine) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.ToggleCommandLine; - TODO if (escape) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Escape; if (enter) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Return; if (debug) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.ToggleDebugDisplay; - if (next) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectNext; - if (previous) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectPrev; - if (tab) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.Tab; switch (hotbarPage) { case 1: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage1; break; @@ -95,10 +96,10 @@ namespace GamecraftModdingAPI.Input case 8: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage8; break; case 9: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage9; break; case 10: currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.HotbarPage10; break; - default: break; } + //RewiredConsts.Action if (quickSave) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.QuickSave; - if (paste) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.PasteSelection; + if (paste) currentInput.guiMask |= RobocraftX.Common.Input.GuiInput.SelectLastCopiedBlueprint; } public static void ActionInput(uint playerID = uint.MaxValue, bool toggleMode = false, bool forward = false, bool backward = false, bool up = false, bool down = false, bool left = false, bool right = false, bool sprint = false, bool toggleFly = false, bool alt = false, bool primary = false, bool secondary = false, bool tertiary = false, bool primaryHeld = false, bool secondaryHeld = false, bool toggleUnitGrid = false, bool ctrl = false, bool toggleColourMode = false, bool scaleBlockUp = false, bool scaleBlockDown = false, bool rotateBlockClockwise = false, bool rotateBlockCounterclockwise = false, bool cutSelection = false, bool copySelection = false, bool deleteSelection = false) @@ -107,8 +108,8 @@ namespace GamecraftModdingAPI.Input { playerID = inputEngine.GetLocalPlayerID(); } - ref LocalInputEntityStruct currentInput = ref inputEngine.GetInputRef(playerID); - //Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}"); + ref LocalPlayerInputEntityStruct currentInput = ref inputEngine.GetPlayerInputRef(playerID); + //Utility.Logging.CommandLog($"Current sim frame {currentInput.frame}"); // set inputs if (toggleMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleTimeRunningMode; if (forward) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Forward; @@ -118,8 +119,8 @@ namespace GamecraftModdingAPI.Input if (left) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Left; if (right) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Right; if (sprint) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.Sprint; - if (toggleFly) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode; - if (alt) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.AltAction; + //if (toggleFly) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SwitchFlyMode; + //if (alt) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.AltAction; if (primary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryAction; if (secondary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryAction; if (tertiary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.TertiaryAction; diff --git a/TechbloxModdingAPI/Input/FakeInputEngine.cs b/TechbloxModdingAPI/Input/FakeInputEngine.cs new file mode 100644 index 0000000..7b4528c --- /dev/null +++ b/TechbloxModdingAPI/Input/FakeInputEngine.cs @@ -0,0 +1,94 @@ +using System; + +using RobocraftX.Common; +using RobocraftX.Common.Input; +using RobocraftX.Players; +using Svelto.ECS; + +using TechbloxModdingAPI.Utility; +using TechbloxModdingAPI.Engines; + +namespace TechbloxModdingAPI.Input +{ + public class FakeInputEngine : IApiEngine + { + public string Name { get; } = "TechbloxModdingAPIFakeInputEngine"; + + public EntitiesDB entitiesDB { set; private get; } + + public bool isRemovable => false; + + public bool IsReady = false; + + public void Dispose() + { + IsReady = false; + } + + public void Ready() + { + IsReady = true; + } + + public bool SendCustomInput(LocalCosmeticInputEntityComponent input) + { + EGID egid = CommonExclusiveGroups.GameStateEGID; + if (entitiesDB.Exists(egid)) + { + ref LocalCosmeticInputEntityComponent ies = ref entitiesDB.QueryEntity(egid); + ies = input; + return true; + } + else return false; + } + + public bool SendCustomPlayerInput(LocalPlayerInputEntityStruct input, uint playerID, bool remote = false) + { + EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers); + if (entitiesDB.Exists(egid)) + { + ref LocalPlayerInputEntityStruct ies = ref entitiesDB.QueryEntity(egid); + ies = input; + return true; + } + else return false; + } + + public LocalCosmeticInputEntityComponent GetInput() + { + EGID egid = CommonExclusiveGroups.GameStateEGID; + if (entitiesDB.Exists(egid)) + { + return entitiesDB.QueryEntity(egid); + } + else return default(LocalCosmeticInputEntityComponent); + } + + public LocalPlayerInputEntityStruct GetPlayerInput(uint playerID, bool remote = false) + { + EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers); + if (entitiesDB.Exists(egid)) + { + return entitiesDB.QueryEntity(egid); + } + else return default; + } + + public ref LocalCosmeticInputEntityComponent GetInputRef() + { + EGID egid = CommonExclusiveGroups.GameStateEGID; + return ref entitiesDB.QueryEntity(egid); + } + + public ref LocalPlayerInputEntityStruct GetPlayerInputRef(uint playerID, bool remote = false) + { + EGID egid = new EGID(playerID, remote ? InputExclusiveGroups.RemotePlayers : InputExclusiveGroups.LocalPlayers); + return ref entitiesDB.QueryEntity(egid); + } + + public uint GetLocalPlayerID() + { + return LocalPlayerIDUtility.GetLocalPlayerID(entitiesDB); + } + } +} diff --git a/TechbloxModdingAPI/Interface/IMGUI/Button.cs b/TechbloxModdingAPI/Interface/IMGUI/Button.cs new file mode 100644 index 0000000..de8e7cc --- /dev/null +++ b/TechbloxModdingAPI/Interface/IMGUI/Button.cs @@ -0,0 +1,92 @@ +using System; +using Unity.Mathematics; +using UnityEngine; + +namespace TechbloxModdingAPI.Interface.IMGUI +{ + /// + /// A clickable button. + /// This wraps Unity's IMGUI Button. + /// + public class Button : UIElement + { + private bool automaticLayout = false; + + private string text; + + /// + /// The rectangular area that the button can use. + /// + public Rect Box { get; set; } = Rect.zero; + + /// + /// An event that fires when the button is clicked. + /// + public event EventHandler OnClick; + + public void OnGUI() + { + if (automaticLayout) + { + if (GUILayout.Button(text, Constants.Default.button)) + { + OnClick?.Invoke(this, true); + } + } + else + { + if (GUI.Button(Box, text, Constants.Default.button)) + { + OnClick?.Invoke(this, true); + } + } + } + + /// + /// The button's unique name. + /// + public string Name { get; private set; } + + /// + /// Whether to display the button. + /// + public bool Enabled { get; set; } = true; + + /// + /// Initialize a new button with automatic layout. + /// + /// The text to display on the button. + /// The button's name. + public Button(string text, string name = null) + { + automaticLayout = true; + this.text = text; + if (name == null) + { + this.Name = typeof(Button).FullName + "::" + text; + } + else + { + this.Name = name; + } + IMGUIManager.AddElement(this); + } + + /// + /// Initialize a new button. + /// + /// The text to display on the button. + /// Rectangular area for the button to use. + /// The button's name. + public Button(string text, Rect box, string name = null) : this(text, name) + { + automaticLayout = false; + this.Box = box; + } + + ~Button() + { + IMGUIManager.RemoveElement(this); + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Interface/IMGUI/Constants.cs b/TechbloxModdingAPI/Interface/IMGUI/Constants.cs new file mode 100644 index 0000000..ecb292a --- /dev/null +++ b/TechbloxModdingAPI/Interface/IMGUI/Constants.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using TechbloxModdingAPI.Utility; +using HarmonyLib; +using Svelto.Tasks; +using Svelto.Tasks.Lean; +using UnityEngine; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace TechbloxModdingAPI.Interface.IMGUI +{ + /// + /// Convenient IMGUI values. + /// + public static class Constants + { + private static byte _defaultCompletion = 0; + private static GUISkin _default = null; + + /// + /// Best-effort imitation of Techblox's UI style. + /// + public static GUISkin Default + { + get + { + if (_defaultCompletion != 0) _default = BuildDefaultGUISkin(); + return _default; + } + } + + private static Font _font = null; + + private static Texture2D _blueBackground = null; + private static Texture2D _grayBackground = null; + private static Texture2D _whiteBackground = null; + private static Texture2D _textInputBackground = null; + private static Texture2D _areaBackground = null; + + internal static void Init() + { + LoadGUIAssets(); + } + + private static GUISkin BuildDefaultGUISkin() + { + _defaultCompletion = 0; + //if (_font == null) return GUI.skin; + // build GUISkin + GUISkin gui = ScriptableObject.CreateInstance(); + gui.font = _font; + gui.settings.selectionColor = Color.white; + gui.settings.tripleClickSelectsLine = true; + Color textColour = new Color(0.706f, 0.706f, 0.706f); + // set properties off all UI elements + foreach (PropertyInfo p in typeof(GUISkin).GetProperties()) + { + // for a "scriptable" GUI system, it's ironic there's no better way to do this + if (p.GetValue(gui) is GUIStyle style) + { + style.richText = true; + style.alignment = TextAnchor.MiddleCenter; + style.fontSize = 16; + style.wordWrap = true; + style.border = new RectOffset(4, 4, 4, 4); + style.margin = new RectOffset(4, 4, 4, 4); + style.padding = new RectOffset(4, 4, 4, 4); + // normal state + style.normal.background = _grayBackground; + style.normal.textColor = textColour; + // hover state + style.hover.background = _grayBackground; + style.hover.textColor = textColour; + // focused + style.focused.background = _grayBackground; + style.focused.textColor = textColour; + // clicking state + style.active.background = _whiteBackground; + style.active.textColor = textColour; + + p.SetValue(gui, style); // probably unnecessary + } + } + // set element-specific styles + // label + gui.label.fontSize = 20; + gui.label.normal.background = null; + gui.label.hover.background = null; + gui.label.focused.background = null; + gui.label.active.background = null; + // text input + gui.textField.normal.background = _textInputBackground; + gui.textField.hover.background = _textInputBackground; + gui.textField.focused.background = _textInputBackground; + gui.textField.active.background = _textInputBackground; + // text area + gui.textArea.normal.background = _textInputBackground; + gui.textArea.hover.background = _textInputBackground; + gui.textArea.focused.background = _textInputBackground; + gui.textArea.active.background = _textInputBackground; + // window + gui.window.normal.background = _areaBackground; + gui.window.hover.background = _areaBackground; + gui.window.focused.background = _areaBackground; + gui.window.active.background = _areaBackground; + // box (also used by layout groups & areas) + gui.box.normal.background = _areaBackground; + gui.box.hover.background = _areaBackground; + gui.box.focused.background = _areaBackground; + gui.box.active.background = _areaBackground; + return gui; + } + + private static void LoadGUIAssets() + { + /*AsyncOperationHandle fontHandle = Addressables.LoadAssetAsync("Assets/Plugins/TextMesh Pro/Fonts/LiberationSans.ttf"); + fontHandle.Completed += handle => + { + _font = handle.Result; + _defaultCompletion++; + };*/ + _font = Resources.Load("Assets/Plugins/TextMesh Pro/Fonts/LiberationSans.ttf"); + // TODO generate larger textures with borders on them so it actually looks more like Techblox + _blueBackground = new Texture2D(1, 1); + _blueBackground.SetPixel(0, 0, new Color(0.004f, 0.522f, 0.847f) /* Gamecraft Blue, unused */); + _blueBackground.Apply(); + _grayBackground = new Texture2D(1, 1); + _grayBackground.SetPixel(0, 0, new Color(0.11f, 0.11f, 0.11f, 1f) /* Very dark gray */); + _grayBackground.Apply(); + _whiteBackground = new Texture2D(1, 1); + _whiteBackground.SetPixel(0, 0, new Color(0.961f, 0.961f, 0.961f) /* Very light gray */); + _whiteBackground.Apply(); + _textInputBackground = new Texture2D(1, 1); + _textInputBackground.SetPixel(0, 0, new Color(0.961f, 0.961f, 0.961f) /* Very light gray */); + _textInputBackground.Apply(); + _areaBackground = new Texture2D(1, 1); + _areaBackground.SetPixel(0, 0, new Color(0.051f, 0.051f, 0.051f, 1f) /* Very very dark gray */); + _areaBackground.Apply(); + _defaultCompletion++; + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Interface/IMGUI/Group.cs b/TechbloxModdingAPI/Interface/IMGUI/Group.cs new file mode 100644 index 0000000..b1dcccc --- /dev/null +++ b/TechbloxModdingAPI/Interface/IMGUI/Group.cs @@ -0,0 +1,157 @@ +using System; +using TechbloxModdingAPI.Utility; +using Svelto.DataStructures; +using UnityEngine; + +namespace TechbloxModdingAPI.Interface.IMGUI +{ + /// + /// A group of elements. + /// This wraps Unity's GUILayout Area and GUI Group system. + /// + public class Group : UIElement + { + private bool automaticLayout; + + private FasterList elements = new FasterList(); + + /// + /// The rectangular area in the window that the UI group can use + /// + public Rect Box { get; set; } + + public void OnGUI() + { + /*if (Constants.Default == null) return; + if (Constants.Default.box == null) return;*/ + GUIStyle guiStyle = Constants.Default.box; + UIElement[] elems = elements.ToArrayFast(out int count); + if (automaticLayout) + { + GUILayout.BeginArea(Box, guiStyle); + for (uint i = 0; i < count; i++) + { + /*try + { + if (elems[i].Enabled) + elems[i].OnGUI(); + } + catch (ArgumentException) + { + // ignore these, since this is (hopefully) just Unity being dumb + } + catch (Exception e) + { + Logging.MetaDebugLog($"Element '{elems[i].Name}' threw exception:\n{e.ToString()}"); + }*/ + if (elems[i].Enabled) + elems[i].OnGUI(); + } + GUILayout.EndArea(); + } + else + { + GUI.BeginGroup(Box, guiStyle); + for (uint i = 0; i < count; i++) + { + if (elems[i].Enabled) + elems[i].OnGUI(); + } + GUI.EndGroup(); + } + } + + /// + /// The group's unique name. + /// + public string Name { get; } + + /// + /// Whether to display the group and everything in it. + /// + public bool Enabled { set; get; } = true; + + /// + /// The amount of elements in the group. + /// + public int Length + { + get => elements.count; + } + + /// + /// Initializes a new instance of the class. + /// + /// The rectangular area to use in the window. + /// Name of the group. + /// Whether to use automatic UI layout. + public Group(Rect box, string name = null, bool automaticLayout = false) + { + Box = box; + if (name == null) + { + this.Name = typeof(Group).FullName + "::" + box.ToString().Replace(" ", ""); + } + else + { + this.Name = name; + } + this.automaticLayout = automaticLayout; + IMGUIManager.AddElement(this); + } + + /// + /// Add an element to the group. + /// + /// The element to add. + /// Index of the new element. + public int AddElement(UIElement element) + { + IMGUIManager.RemoveElement(element); // groups manage internal elements themselves + elements.Add(element); + return elements.count - 1; + } + + /// + /// Remove an element from the group. + /// + /// The element to remove. + /// Whether removal was successful. + public bool RemoveElement(UIElement element) + { + int index = IndexOf(element); + return RemoveAt(index); + } + + /// + /// Remove the element in a specific location. + /// + /// Index of the element. + /// Whether removal was successful. + public bool RemoveAt(int index) + { + if (index < 0 || index >= elements.count) return false; + IMGUIManager.AddElement(elements[index]); // re-add to global manager + elements.RemoveAt((uint) index); + return true; + } + + /// + /// Get the index of an element. + /// + /// The element to search for. + /// The element's index, or -1 if not found. + public int IndexOf(UIElement element) + { + UIElement[] elems = elements.ToArrayFast(out int count); + for (int i = 0; i < count; i++) + { + if (elems[i].Name == element.Name) + { + return i; + } + } + return -1; + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Interface/IMGUI/IMGUIManager.cs b/TechbloxModdingAPI/Interface/IMGUI/IMGUIManager.cs new file mode 100644 index 0000000..8b3c865 --- /dev/null +++ b/TechbloxModdingAPI/Interface/IMGUI/IMGUIManager.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using TechbloxModdingAPI.App; +using TechbloxModdingAPI.Utility; +using Rewired.Internal; +using Svelto.DataStructures; +using Svelto.Tasks; +using Svelto.Tasks.ExtraLean; +using Svelto.Tasks.ExtraLean.Unity; +using UnityEngine; + +namespace TechbloxModdingAPI.Interface.IMGUI +{ + /// + /// Keeps track of UIElement instances. + /// This also handles displaying and processing them during the MonoBehaviour.OnGUI phase of screen updates. + /// Most of this functionality is handled implicitly by the included UIElement implementations, + /// but this is left as a public API so it can be expanded. + /// + public static class IMGUIManager + { + internal static OnGuiRunner ImguiScheduler = new OnGuiRunner("TechbloxModdingAPI_IMGUIScheduler"); + + private static Dictionary _activeElements = new Dictionary(); + + /// + /// Add an UIElement instance to be managed by IMGUIManager. + /// + /// The UIElement instance. + public static void AddElement(UIElement e) + { + if (!ExistsElement(e)) + { + _activeElements[e.Name] = e; + } + } + + /// + /// Determine whether the UIElement instance is already tracked by IMGUIManager. + /// + /// The UIElement's unique name. + /// Whether the UIElement instance is already tracked by IMGUIManager (true) or not (false). + public static bool ExistsElement(string name) + { + return _activeElements.ContainsKey(name); + } + + /// + /// Determine whether the UIElement instance is already tracked by IMGUIManager. + /// + /// The UIElement instance. + /// Whether the UIElement instance is already tracked by IMGUIManager (true) or not (false). + public static bool ExistsElement(UIElement element) + { + return ExistsElement(element.Name); + } + + /// + /// Remove an UIElement instance from IMGUIManager. + /// The UIElement will become invisible and stop generating events. + /// + /// The UIElement's unique name. + /// Whether the UIElement instance existed in IMGUIManager (true) or not (false). + public static bool RemoveElement(string name) + { + if (ExistsElement(name)) + { + return _activeElements.Remove(name); + } + + return false; + } + + /// + /// Remove an UIElement instance from IMGUIManager. + /// The UIElement will become invisible and stop generating events. + /// + /// The UIElement instance. + /// Whether the UIElement instance existed in IMGUIManager (true) or not (false). + public static bool RemoveElement(UIElement element) + { + return RemoveElement(element.Name); + } + + private static void OnGUI() + { + foreach (var element in _activeElements.Values) + { + if (element.Enabled) + element.OnGUI(); + /*try + { + if (elements[i].Enabled) + elements[i].OnGUI(); + } + catch (ArgumentException) + { + // ignore these, since this is (hopefully) just Unity being dumb + } + catch (Exception e) + { + Logging.MetaDebugLog($"Element '{elements[i].Name}' threw exception:\n{e.ToString()}"); + }*/ + + } + } + + private static IEnumerator OnGUIAsync() + { + yield return (new Svelto.Tasks.Enumerators.WaitForSecondsEnumerator(5)).Continue(); // wait for some startup + while (true) + { + yield return Yield.It; + GUI.skin = Constants.Default; + OnGUI(); + } + } + + internal static void Init() + { + OnGUIAsync().RunOn(ImguiScheduler); + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Interface/IMGUI/Image.cs b/TechbloxModdingAPI/Interface/IMGUI/Image.cs new file mode 100644 index 0000000..2bea1f4 --- /dev/null +++ b/TechbloxModdingAPI/Interface/IMGUI/Image.cs @@ -0,0 +1,86 @@ +using UnityEngine; + +namespace TechbloxModdingAPI.Interface.IMGUI +{ + /// + /// An image. + /// This wraps Unity's IMGUI Label. + /// + public class Image : UIElement + { + private bool automaticLayout = false; + + /// + /// The image texture to display + /// + public Texture Texture { get; set; } + + /// + /// The rectangular area in the window that the image can use + /// + public Rect Box { get; set; } = Rect.zero; + + public void OnGUI() + { + //if (Texture == null) return; + if (automaticLayout) + { + GUILayout.Label(Texture, Constants.Default.label); + } + else + { + GUI.Label(Box, Texture, Constants.Default.label); + } + } + + /// + /// The image element's unique name. + /// + public string Name { get; } + + /// + /// Whether to display the image and everything in it. + /// + public bool Enabled { set; get; } = true; + + /// + /// Initializes a new instance of the class with automatic layout. + /// + /// Image to display. + /// The element's name. + public Image(Texture texture = null, string name = null) + { + automaticLayout = true; + Texture = texture; + if (name == null) + { + if (texture == null) + { + this.Name = typeof(Image).FullName + "::" + texture; + } + else + { + this.Name = typeof(Image).FullName + "::" + texture.name + "(" + texture.width + "x" + texture.height + ")"; + } + + } + else + { + this.Name = name; + } + IMGUIManager.AddElement(this); + } + + /// + /// Initializes a new instance of the class. + /// + /// Rectangular area for the image. + /// Image to display. + /// The element's name. + public Image(Rect box, Texture texture = null, string name = null) : this(texture, name) + { + this.Box = box; + automaticLayout = false; + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Interface/IMGUI/Label.cs b/TechbloxModdingAPI/Interface/IMGUI/Label.cs new file mode 100644 index 0000000..960a6ca --- /dev/null +++ b/TechbloxModdingAPI/Interface/IMGUI/Label.cs @@ -0,0 +1,77 @@ +using UnityEngine; + +namespace TechbloxModdingAPI.Interface.IMGUI +{ + /// + /// A simple text label. + /// This wraps Unity IMGUI's Label. + /// + public class Label : UIElement + { + private bool automaticLayout = false; + + /// + /// String to display on the label. + /// + public string Text { get; set; } + + /// + /// The rectangular area that the label can use. + /// + public Rect Box { get; set; } = Rect.zero; + + public void OnGUI() + { + if (automaticLayout) + { + GUILayout.Label(Text, Constants.Default.label); + } + else + { + GUI.Label(Box, Text, Constants.Default.label); + } + } + + /// + /// The label's unique name. + /// + public string Name { get; } + + /// + /// Whether to display the label. + /// + public bool Enabled { set; get; } = true; + + /// + /// Initializes a new instance of the class with automatic layout. + /// + /// Initial string to display on the label. + /// The element's name. + public Label(string initialText = null, string name = null) + { + automaticLayout = true; + Text = initialText; + if (name == null) + { + this.Name = typeof(Label).FullName + "::" + initialText; + } + else + { + this.Name = name; + } + IMGUIManager.AddElement(this); + } + + /// + /// Initializes a new instance of the . + /// + /// Rectangular area for the label. + /// Initial string to display on the label. + /// The element's name. + public Label(Rect box, string initialText = null, string name = null) : this(initialText, name) + { + this.Box = box; + automaticLayout = false; + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Interface/IMGUI/Text.cs b/TechbloxModdingAPI/Interface/IMGUI/Text.cs new file mode 100644 index 0000000..bdf6738 --- /dev/null +++ b/TechbloxModdingAPI/Interface/IMGUI/Text.cs @@ -0,0 +1,109 @@ +using System; +using UnityEngine; + +namespace TechbloxModdingAPI.Interface.IMGUI +{ + /// + /// A text input field. + /// This wraps Unity's IMGUI TextField and TextArea. + /// + public class Text : UIElement + { + private bool automaticLayout; + + /// + /// Whether the text input field is multiline (true -> TextArea) or not (false -> TextField). + /// + public bool Multiline { get; set; } + + private string text; + + /// + /// The rectangular area that the text field can use. + /// + public Rect Box { get; set; } = Rect.zero; + + /// + /// An event that fires whenever the text input is edited. + /// + public event EventHandler OnEdit; + + public void OnGUI() + { + string editedText = null; + if (automaticLayout) + { + if (Multiline) + { + editedText = GUILayout.TextArea(text, Constants.Default.textArea); + } + else + { + editedText = GUILayout.TextField(text, Constants.Default.textField); + } + } + else + { + if (Multiline) + { + editedText = GUI.TextArea(Box, text, Constants.Default.textArea); + } + else + { + editedText = GUI.TextField(Box, text, Constants.Default.textField); + } + } + + if (editedText != null && editedText != text) + { + OnEdit?.Invoke(this, editedText); + text = editedText; + } + } + + /// + /// The text field's unique name. + /// + public string Name { get; } + + /// + /// Whether to display the text field. + /// + public bool Enabled { set; get; } = true; + + /// + /// Initialize the text input field with automatic layout. + /// + /// Initial text in the input field. + /// The text field's name. + /// Allow multiple lines? + public Text(string initialText = null, string name = null, bool multiline = false) + { + this.Multiline = multiline; + automaticLayout = true; + text = initialText ?? ""; + if (name == null) + { + this.Name = typeof(Text).FullName + "::" + text; + } + else + { + this.Name = name; + } + IMGUIManager.AddElement(this); + } + + /// + /// Initialize the text input field. + /// + /// Rectangular area for the text field. + /// Initial text in the input field. + /// The text field's name. + /// Allow multiple lines? + public Text(Rect box, string initialText = null, string name = null, bool multiline = false) : this(initialText, name, multiline) + { + this.Box = box; + automaticLayout = false; + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Interface/IMGUI/UIElement.cs b/TechbloxModdingAPI/Interface/IMGUI/UIElement.cs new file mode 100644 index 0000000..dd66af6 --- /dev/null +++ b/TechbloxModdingAPI/Interface/IMGUI/UIElement.cs @@ -0,0 +1,28 @@ +using System; + +namespace TechbloxModdingAPI.Interface.IMGUI +{ + /// + /// GUI Element like a text field, button or picture. + /// This interface is used to wrap many elements from Unity's IMGUI system. + /// + public interface UIElement + { + /// + /// GUI operations to perform in the OnGUI scope. + /// This is basically equivalent to a MonoBehaviour's OnGUI method. + /// + void OnGUI(); + + /// + /// The element's name. + /// This should be unique for every instance of the class. + /// + string Name { get; } + + /// + /// Whether to display the UI element or not. + /// + bool Enabled { get; } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs b/TechbloxModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs similarity index 67% rename from GamecraftModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs rename to TechbloxModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs index a0ad5c1..79c6d5a 100644 --- a/GamecraftModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs +++ b/TechbloxModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs @@ -4,20 +4,20 @@ using System.Reflection; using Svelto.ECS; using HarmonyLib; -using GamecraftModdingAPI.Blocks; +using TechbloxModdingAPI.Blocks; -namespace GamecraftModdingAPI.Inventory +namespace TechbloxModdingAPI.Inventory { [HarmonyPatch] - public class HotbarSlotSelectionHandlerEnginePatch + class HotbarSlotSelectionHandlerEnginePatch { private static int selectedBlockInt = 0; public static BlockIDs EquippedPartID { get => (BlockIDs)selectedBlockInt; } - private static MethodInfo PatchedMethod { get; } = AccessTools.Method("Gamecraft.GUI.Hotbar.Blocks.SyncHotbarSlotSelectedToEquippedPartEngine:ActivateSlotForCube", parameters: new Type[] { typeof(uint), typeof(int), typeof(ExclusiveGroupStruct) }); + private static MethodInfo PatchedMethod { get; } = AccessTools.Method("Gamecraft.GUI.Hotbar.Blocks.SyncHotbarSlotSelectedToEquippedPartEngine:ActivateSlotForCube", parameters: new Type[] { typeof(uint), typeof(int) }); - public static void Prefix(uint playerID, int selectedDBPartID, ExclusiveGroupStruct groupID) + public static void Prefix(uint playerID, int selectedDBPartID) { selectedBlockInt = selectedDBPartID; } diff --git a/GamecraftModdingAPI/Main.cs b/TechbloxModdingAPI/Main.cs similarity index 53% rename from GamecraftModdingAPI/Main.cs rename to TechbloxModdingAPI/Main.cs index 584ae90..610aa8e 100644 --- a/GamecraftModdingAPI/Main.cs +++ b/TechbloxModdingAPI/Main.cs @@ -1,20 +1,22 @@ using System; using System.Reflection; -using GamecraftModdingAPI.Blocks; using HarmonyLib; using RobocraftX; +using RobocraftX.Schedulers; using RobocraftX.Services; using Svelto.Context; +using Svelto.Tasks.ExtraLean; +using TechbloxModdingAPI.App; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Events; +using TechbloxModdingAPI.Tasks; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; -using GamecraftModdingAPI.Events; -using GamecraftModdingAPI.Tasks; - -namespace GamecraftModdingAPI +namespace TechbloxModdingAPI { /// - /// The main class of the GamecraftModdingAPI. + /// The main class of the TechbloxModdingAPI. /// Use this to initialize the API before calling it. /// public static class Main @@ -28,8 +30,8 @@ namespace GamecraftModdingAPI private static int referenceCount = 0; /// - /// Initializes the GamecraftModdingAPI. - /// Call this as soon as possible after Gamecraft starts up. + /// Initializes the TechbloxModdingAPI. + /// Call this as soon as possible after Techblox starts up. /// Ideally, this should be called from your main Plugin class's OnApplicationStart() method. /// public static void Init() @@ -38,10 +40,10 @@ namespace GamecraftModdingAPI if (referenceCount > 1) { return; } if (IsInitialized) { - Logging.LogWarning("GamecraftModdingAPI.Main.Init() called but API is already initialized!"); + Logging.LogWarning("TechbloxModdingAPI.Main.Init() called but API is already initialized!"); return; } - Logging.MetaDebugLog($"Patching Gamecraft"); + Logging.MetaDebugLog($"Patching Techblox"); var currentAssembly = Assembly.GetExecutingAssembly(); harmony = new Harmony(currentAssembly.GetName().Name); try @@ -51,7 +53,7 @@ namespace GamecraftModdingAPI catch (Exception e) { //Can't use ErrorBuilder or Logging.LogException (which eventually uses ErrorBuilder) yet Logging.Log(e.ToString()); - Logging.LogWarning("Failed to patch Gamecraft. Attempting to patch to display error..."); + Logging.LogWarning("Failed to patch Techblox. Attempting to patch to display error..."); harmony.Patch(AccessTools.Method(typeof(FullGameCompositionRoot), "OnContextInitialized") .MakeGenericMethod(typeof(UnityContext)), new HarmonyMethod(((Action) OnPatchError).Method)); //Can't use lambdas here :( @@ -60,24 +62,9 @@ namespace GamecraftModdingAPI // init utility Logging.MetaDebugLog($"Initializing Utility"); -#pragma warning disable 0612,0618 Utility.GameState.Init(); - Utility.VersionTracking.Init(); - // 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)); - EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.Game, "GamecraftModdingAPIGameActivatedEventEmitter", false)); - EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameReloaded, "GamecraftModdingAPIGameReloadedEventEmitter", false)); - EventManager.AddEventEmitter(new SimpleEventEmitterEngine(EventType.GameSwitchedTo, "GamecraftModdingAPIGameSwitchedToEventEmitter", false)); - EventManager.AddEventEmitter(GameHostTransitionDeterministicGroupEnginePatch.buildEngine); - EventManager.AddEventEmitter(GameHostTransitionDeterministicGroupEnginePatch.simEngine); -#pragma warning restore 0612,0618 // init block implementors Logging.MetaDebugLog($"Initializing Blocks"); - // init inventory - Inventory.Hotbar.Init(); // init input Input.FakeInput.Init(); // init object-oriented classes @@ -85,16 +72,18 @@ namespace GamecraftModdingAPI Block.Init(); BlockGroup.Init(); Wire.Init(); - GameClient.Init(); - AsyncUtils.Init(); - GamecraftModdingAPI.App.Client.Init(); - GamecraftModdingAPI.App.Game.Init(); + Logging.MetaDebugLog($"Initializing Client"); + Client.Init(); + Game.Init(); + // init UI + Interface.IMGUI.Constants.Init(); + Interface.IMGUI.IMGUIManager.Init(); Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized"); } /// - /// Shuts down & cleans up the GamecraftModdingAPI. - /// Call this as late as possible before Gamecraft quits. + /// Shuts down & cleans up the TechbloxModdingAPI. + /// Call this as late as possible before Techblox quits. /// Ideally, this should be called from your main Plugin class's OnApplicationQuit() method. /// public static void Shutdown() @@ -104,7 +93,7 @@ namespace GamecraftModdingAPI { if (!IsInitialized) { - Logging.LogWarning("GamecraftModdingAPI.Main.Shutdown() called but API is not initialized!"); + Logging.LogWarning("TechbloxModdingAPI.Main.Shutdown() called but API is not initialized!"); return; } Scheduler.Dispose(); @@ -117,8 +106,8 @@ namespace GamecraftModdingAPI private static void OnPatchError() { - ErrorBuilder.DisplayMustQuitError("Failed to patch Gamecraft!\n" + - "Make sure you're using the latest version of GamecraftModdingAPI or disable mods if the API isn't released yet."); + ErrorBuilder.DisplayMustQuitError("Failed to patch Techblox!\n" + + "Make sure you're using the latest version of TechbloxModdingAPI or disable mods if the API isn't released yet."); } } } diff --git a/GamecraftModdingAPI/Persistence/DeserializeFromDiskEntitiesEnginePatch.cs b/TechbloxModdingAPI/Persistence/DeserializeFromDiskEntitiesEnginePatch.cs similarity index 94% rename from GamecraftModdingAPI/Persistence/DeserializeFromDiskEntitiesEnginePatch.cs rename to TechbloxModdingAPI/Persistence/DeserializeFromDiskEntitiesEnginePatch.cs index 0ff675f..6219ed1 100644 --- a/GamecraftModdingAPI/Persistence/DeserializeFromDiskEntitiesEnginePatch.cs +++ b/TechbloxModdingAPI/Persistence/DeserializeFromDiskEntitiesEnginePatch.cs @@ -8,16 +8,16 @@ using Svelto.ECS; using Svelto.ECS.Serialization; using HarmonyLib; -using GamecraftModdingAPI.Utility; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Persistence +namespace TechbloxModdingAPI.Persistence { - [HarmonyPatch] + //[HarmonyPatch] - TODO class DeserializeFromDiskEntitiesEnginePatch { internal static EntitiesDB entitiesDB = null; - private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0GamecraftModdingAPI\0\0\0"); + private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0TechbloxModdingAPI\0\0\0"); public static void Prefix(ref ISerializationData ____serializationData, ref FasterList ____bytesStream, ref IEntitySerialization ____entitySerializer, bool ____spawnBlocksOnly) { @@ -26,7 +26,7 @@ namespace GamecraftModdingAPI.Persistence SerializerManager.RegisterSerializers(SaveAndLoadCompositionRootPatch.currentEnginesRoot); uint originalPos = ____serializationData.dataPos; Logging.MetaDebugLog($"dataPos: {originalPos}"); - BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out uint count), ____serializationData.dataPos); + BinaryBufferReader bbr = new BinaryBufferReader(____bytesStream.ToArrayFast(out int count), ____serializationData.dataPos); byte[] frameBuffer = new byte[frameStart.Length]; Logging.MetaDebugLog($"serial data count: {____serializationData.data.count} capacity: {____serializationData.data.capacity}"); int i = 0; diff --git a/GamecraftModdingAPI/Persistence/IEntitySerializer.cs b/TechbloxModdingAPI/Persistence/IEntitySerializer.cs similarity index 90% rename from GamecraftModdingAPI/Persistence/IEntitySerializer.cs rename to TechbloxModdingAPI/Persistence/IEntitySerializer.cs index 5c4fb85..0d6e599 100644 --- a/GamecraftModdingAPI/Persistence/IEntitySerializer.cs +++ b/TechbloxModdingAPI/Persistence/IEntitySerializer.cs @@ -3,12 +3,12 @@ using Svelto.ECS; using Svelto.ECS.Serialization; -using GamecraftModdingAPI.Utility; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Persistence +namespace TechbloxModdingAPI.Persistence { /// - /// Entity serializer and deserializer interface for storing and retrieving data in a Gamecraft save file (GameSave.GC). + /// Entity serializer and deserializer interface for storing and retrieving data in a Techblox save file (GameSave.GC). /// public interface IEntitySerializer : IDeserializationFactory, IQueryingEntitiesEngine { diff --git a/GamecraftModdingAPI/Persistence/SaveAndLoadCompositionRootPatch.cs b/TechbloxModdingAPI/Persistence/SaveAndLoadCompositionRootPatch.cs similarity index 90% rename from GamecraftModdingAPI/Persistence/SaveAndLoadCompositionRootPatch.cs rename to TechbloxModdingAPI/Persistence/SaveAndLoadCompositionRootPatch.cs index 58f5708..b3ae6de 100644 --- a/GamecraftModdingAPI/Persistence/SaveAndLoadCompositionRootPatch.cs +++ b/TechbloxModdingAPI/Persistence/SaveAndLoadCompositionRootPatch.cs @@ -5,7 +5,7 @@ using Svelto.ECS; using HarmonyLib; -namespace GamecraftModdingAPI.Persistence +namespace TechbloxModdingAPI.Persistence { [HarmonyPatch(typeof(SaveAndLoadCompositionRoot), "Compose")] class SaveAndLoadCompositionRootPatch diff --git a/GamecraftModdingAPI/Persistence/SaveGameEnginePatch.cs b/TechbloxModdingAPI/Persistence/SaveGameEnginePatch.cs similarity index 90% rename from GamecraftModdingAPI/Persistence/SaveGameEnginePatch.cs rename to TechbloxModdingAPI/Persistence/SaveGameEnginePatch.cs index a603fa2..4386d3a 100644 --- a/GamecraftModdingAPI/Persistence/SaveGameEnginePatch.cs +++ b/TechbloxModdingAPI/Persistence/SaveGameEnginePatch.cs @@ -7,16 +7,15 @@ using RobocraftX.SaveAndLoad; using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.Serialization; - -using GamecraftModdingAPI.Utility; using HarmonyLib; +using TechbloxModdingAPI.Utility; -namespace GamecraftModdingAPI.Persistence +namespace TechbloxModdingAPI.Persistence { - [HarmonyPatch] + //[HarmonyPatch] - TODO class SaveGameEnginePatch { - private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0GamecraftModdingAPI\0\0\0"); + private static readonly byte[] frameStart = Encoding.UTF8.GetBytes("\0\0\0TechbloxModdingAPI\0\0\0"); public static void Postfix(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer) { @@ -27,10 +26,10 @@ namespace GamecraftModdingAPI.Persistence return; } serializationData.data.ExpandBy((uint)frameStart.Length); - BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint buffLen), serializationData.dataPos); + BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int buffLen), serializationData.dataPos); uint originalPos = serializationData.dataPos; Logging.MetaDebugLog($"dataPos: {originalPos}"); - // Add frame start so it's easier to find GamecraftModdingAPI-serialized components + // Add frame start so it's easier to find TechbloxModdingAPI-serialized components for (int i = 0; i < frameStart.Length; i++) { bbw.Write(frameStart[i]); diff --git a/GamecraftModdingAPI/Persistence/SerializerManager.cs b/TechbloxModdingAPI/Persistence/SerializerManager.cs similarity index 91% rename from GamecraftModdingAPI/Persistence/SerializerManager.cs rename to TechbloxModdingAPI/Persistence/SerializerManager.cs index 161662b..fe11320 100644 --- a/GamecraftModdingAPI/Persistence/SerializerManager.cs +++ b/TechbloxModdingAPI/Persistence/SerializerManager.cs @@ -4,15 +4,14 @@ using System.Linq; using Svelto.ECS; using Svelto.ECS.Serialization; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Persistence +namespace TechbloxModdingAPI.Persistence { /// /// Keeps track of serializers. /// This is used to add and retrieve serializers. - /// Added IEntitySerializations are used in serializing and deserializing Gamecraft save files (GameSave.GC). + /// Added IEntitySerializations are used in serializing and deserializing Techblox save files (GameSave.GC). /// public static class SerializerManager { @@ -59,7 +58,7 @@ namespace GamecraftModdingAPI.Persistence return _serializers.Count; } - public static void RegisterSerializers(EnginesRoot enginesRoot) + internal static void RegisterSerializers(EnginesRoot enginesRoot) { _lastEnginesRoot = enginesRoot; IEntitySerialization ies = enginesRoot.GenerateEntitySerializer(); diff --git a/GamecraftModdingAPI/Persistence/SimpleEntitySerializer.cs b/TechbloxModdingAPI/Persistence/SimpleEntitySerializer.cs similarity index 80% rename from GamecraftModdingAPI/Persistence/SimpleEntitySerializer.cs rename to TechbloxModdingAPI/Persistence/SimpleEntitySerializer.cs index 3e136ae..10e7ce1 100644 --- a/GamecraftModdingAPI/Persistence/SimpleEntitySerializer.cs +++ b/TechbloxModdingAPI/Persistence/SimpleEntitySerializer.cs @@ -5,7 +5,7 @@ using Svelto.ECS.Serialization; using RobocraftX.Common; -namespace GamecraftModdingAPI.Persistence +namespace TechbloxModdingAPI.Persistence { /// /// Simple entity serializer sufficient for simple entity components. @@ -23,16 +23,16 @@ namespace GamecraftModdingAPI.Persistence public EntitiesDB entitiesDB { set; protected get; } - public EntityComponentInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory, bool enginesRootIsDeserializationOnly) + public EntityInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory, bool enginesRootIsDeserializationOnly) { - EntityComponentInitializer esi = factory.BuildEntity(egid); + EntityInitializer esi = factory.BuildEntity(egid); entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref esi, serializationType); return esi; } public bool Deserialize(ref ISerializationData serializationData, IEntitySerialization entitySerializer) { - BinaryBufferReader bbr = new BinaryBufferReader(serializationData.data.ToArrayFast(out uint count), serializationData.dataPos); + BinaryBufferReader bbr = new BinaryBufferReader(serializationData.data.ToArrayFast(out int count), serializationData.dataPos); uint entityCount = bbr.ReadUint(); serializationData.dataPos = bbr.Position; for (uint i = 0; i < entityCount; i++) @@ -47,7 +47,7 @@ namespace GamecraftModdingAPI.Persistence public bool Serialize(ref ISerializationData serializationData, EntitiesDB entitiesDB, IEntitySerialization entitySerializer) { serializationData.data.ExpandBy(4u); - BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out uint count), serializationData.dataPos); + BinaryBufferWriter bbw = new BinaryBufferWriter(serializationData.data.ToArrayFast(out int count), serializationData.dataPos); EGID[] toSerialize = getEntitiesToSerialize(entitiesDB); bbw.Write((uint)toSerialize.Length); serializationData.dataPos = bbw.Position; diff --git a/TechbloxModdingAPI/Player.Events.cs b/TechbloxModdingAPI/Player.Events.cs new file mode 100644 index 0000000..1a07c36 --- /dev/null +++ b/TechbloxModdingAPI/Player.Events.cs @@ -0,0 +1,30 @@ +using System; +using Svelto.ECS; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI +{ + public partial class Player + { + internal WrappedHandler seatEntered; + public event EventHandler SeatEntered + { + add => seatEntered += value; + remove => seatEntered -= value; + } + + internal WrappedHandler seatExited; + public event EventHandler SeatExited + { + add => seatExited += value; + remove => seatExited -= value; + } + } + + public struct PlayerSeatEventArgs + { + public EGID SeatId; + public Seat Seat => (Seat)Block.New(SeatId); + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Player.cs b/TechbloxModdingAPI/Player.cs new file mode 100644 index 0000000..2ce88f1 --- /dev/null +++ b/TechbloxModdingAPI/Player.cs @@ -0,0 +1,538 @@ +using System; +using Gamecraft.Wires; +using RobocraftX.Character; +using RobocraftX.Character.Movement; +using Unity.Mathematics; +using RobocraftX.Common; +using RobocraftX.Common.Players; +using RobocraftX.GUI.Wires; +using RobocraftX.Physics; +using Svelto.ECS; +using Techblox.BuildingDrone; +using Techblox.Camera; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Players; +using TechbloxModdingAPI.Utility; +using UnityEngine; + +namespace TechbloxModdingAPI +{ + /// + /// An in-game player character. Any Leo you see is a player. + /// + public partial class Player : EcsObjectBase, IEquatable, IEquatable + { + // static functionality + private static PlayerEngine playerEngine = new PlayerEngine(); + private static PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine(); + private static Player localPlayer; + + /// + /// Checks if the specified player exists. + /// + /// Whether the player exists. + /// Player type. + public static bool Exists(PlayerType player) + { + switch (player) + { + case PlayerType.Remote: + return playerEngine.GetRemotePlayer() != uint.MaxValue; + case PlayerType.Local: + return playerEngine.GetLocalPlayer() != uint.MaxValue; + } + return false; + } + + /// + /// Checks if the specified player exists. + /// + /// Whether the player exists. + /// The player's unique identifier. + public static bool Exists(uint player) + { + return playerEngine.ExistsById(player); + } + + /// + /// The amount of Players in the current game. + /// + /// The count. + public static uint Count() + { + return (uint) playerEngine.GetAllPlayerCount(); + } + + /// + /// Returns the current player belonging to this client. + /// + public static Player LocalPlayer + { + get + { + if (localPlayer == null || localPlayer.Id != playerEngine.GetLocalPlayer()) + localPlayer = new Player(PlayerType.Local); + return localPlayer; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The player's unique identifier. + public Player(uint id) : base(new EGID(id, CharacterExclusiveGroups.OnFootGroup)) + { + this.Id = id; + if (!Exists(id)) + { + throw new PlayerNotFoundException($"No player with id {id} exists"); + } + this.Type = playerEngine.GetLocalPlayer() == id ? PlayerType.Local : PlayerType.Remote; + } + + /// + /// Initializes a new instance of the class. + /// + /// The player type. Chooses the first available player matching the criteria. + public Player(PlayerType player) : base(ecs => + { + uint id; + switch (player) + { + case PlayerType.Local: + id = playerEngine.GetLocalPlayer(); + break; + case PlayerType.Remote: + id = playerEngine.GetRemotePlayer(); + break; + default: + id = uint.MaxValue; + break; + } + + if (id == uint.MaxValue) + { + throw new PlayerNotFoundException($"No player of {player} type exists"); + } + + return new EGID(id, CharacterExclusiveGroups.OnFootGroup); + }) + { + this.Type = player; + } + + // object fields & properties + + /// + /// The player's type. + /// The player type is always relative to the current client, not the game host. + /// + /// The enumerated player type. + public PlayerType Type { get; } + + /// + /// The player's unique identifier. + /// + /// The identifier. + public new uint Id { get; } + + /// + /// The player's current position. + /// + /// The position. + public float3 Position + { + get => playerEngine.GetCharacterStruct(Id).Get().position; + set => playerEngine.SetLocation(Id, value, false); + } + + /// + /// The player's current rotation. + /// + /// The rotation. + public float3 Rotation + { + get => ((Quaternion) (GameState.IsBuildMode() + ? playerEngine.GetCameraStruct(Id).Get().rotation + : playerEngine.GetCharacterStruct(Id).Get().rotation)).eulerAngles; + set => _ = GameState.IsBuildMode() + ? playerEngine.GetCameraStruct(Id).Get().rotation = quaternion.Euler(value) + : playerEngine.GetCharacterStruct(Id).Get().rotation = quaternion.Euler(value); + } + + /// + /// The player's current velocity. + /// + /// The velocity. + public float3 Velocity + { + get => playerEngine.GetCharacterStruct(Id).Get().velocity; + set => playerEngine.GetCharacterStruct(Id).Get().velocity = value; + } + + /// + /// The player's current angular velocity. + /// + /// The angular velocity. + public float3 AngularVelocity + { + get => playerEngine.GetCharacterStruct(Id).Get().angularVelocity; + set => playerEngine.GetCharacterStruct(Id).Get().angularVelocity = value; + } + + /// + /// The player's mass. + /// + /// The mass. + public float Mass => + 1f / playerEngine.GetCharacterStruct(Id).Get().physicsMass.InverseMass; + + private float _ping = -1f; + + /// + /// The player's latest network ping time. + /// + /// The ping (s). + public float Ping + { + get + { + var opt = playerEngine.GetPlayerStruct(Id, Type); + if (opt) + { + _ping = opt.Get().lastPingTimeSinceLevelLoad ?? _ping; + } + return _ping; + } + } + + /// + /// The player's initial health when entering Simulation (aka Time Running) mode. + /// + /// The initial health. + public float InitialHealth + { + get + { + var opt = playerEngine.GetCharacterStruct(Id); + return opt ? opt.Get().initialHealth : -1f; + } + + set => playerEngine.GetCharacterStruct(Id).Get().initialHealth = value; + } + + /// + /// The player's current health in Simulation (aka Time Running) mode. + /// + /// The current health. + public float CurrentHealth + { + get + { + var opt = playerEngine.GetCharacterStruct(Id); + return opt ? opt.Get().currentHealth : -1f; + } + + set => playerEngine.GetCharacterStruct(Id).Get().currentHealth = value; + } + + /// + /// Whether this is damageable. + /// + /// true if damageable; otherwise, false. + public bool Damageable + { + get + { + var opt = playerEngine.GetCharacterStruct(Id); + return opt.Get().canTakeDamageStat; + } + + set + { + ref var healthStruct = ref playerEngine.GetCharacterStruct(Id).Get(); + healthStruct.canTakeDamage = value; + healthStruct.canTakeDamageStat = value; + } + } + + /// + /// The player's lives when initially entering Simulation (aka Time Running) mode. + /// + /// The initial lives. + public uint InitialLives + { + get + { + var opt = playerEngine.GetCharacterStruct(Id); + return opt ? opt.Get().initialLives : uint.MaxValue; + } + + set => playerEngine.GetCharacterStruct(Id).Get().initialLives = value; + } + + /// + /// The player's current lives in Simulation (aka Time Running) mode. + /// + /// The current lives. + public uint CurrentLives + { + get + { + var opt = playerEngine.GetCharacterStruct(Id); + return opt ? opt.Get().currentLives : uint.MaxValue; + } + + set => playerEngine.GetCharacterStruct(Id).Get().currentLives = value; + } + + /*/// + /// Whether the Game Over screen is displayed for the player. + /// + /// true if game over; otherwise, false. + public bool GameOver + { + get => playerEngine.GetGameOverScreen(Id); + }*/ + + /// + /// Whether the player is dead. + /// If true, hopefully it was quick. + /// + /// true if dead; otherwise, false. + public bool Dead + { + get => playerEngine.IsDead(Id); + } + + /// + /// The player's selected block ID in their hand. + /// + /// The selected block. + public BlockIDs SelectedBlock + { + get + { + var optstruct = playerEngine.GetCharacterStruct(Id); + return optstruct ? (BlockIDs) optstruct.Get().selectedDBPartID : BlockIDs.Invalid; + } + } + + /// + /// The player's selected block color in their hand. + /// + /// The selected block's color. + public BlockColor SelectedColor + { + get + { + var optstruct = playerEngine.GetCharacterStruct(Id); + return optstruct ? new BlockColor(optstruct.Get().indexInPalette) : BlockColors.Default; + } + } + + /// + /// The player's selected block colour in their hand. + /// + /// The selected block's colour. + public BlockColor SelectedColour + { + get + { + var optstruct = playerEngine.GetCharacterStruct(Id); + return optstruct ? new BlockColor(optstruct.Get().indexInPalette) : BlockColors.Default; + } + } + + /// + /// The player's selected blueprint in their hand. Set to null to clear. Dispose after usage. + /// + public Blueprint SelectedBlueprint + { + get + { + var lbiso = playerEngine.GetPlayerStruct(Id, Type); + return lbiso ? new Blueprint(lbiso.Get().selectedBlueprintId) : null; + } + set => BlockGroup._engine.SelectBlueprint(value?.Id ?? uint.MaxValue); + } + + /// + /// The player's mode in time stopped mode, determining what they place. + /// + public PlayerBuildingMode BuildingMode => (PlayerBuildingMode)Math.Log((double)playerEngine + .GetCharacterStruct(Id).Get().timeStoppedContext, 2); // It's a bit field in game now + + /// + /// Whether the player is sprinting. + /// + public bool Sprinting + { + get => GameState.IsBuildMode() + ? playerEngine.GetCharacterStruct(Id).Get().sprinting + : playerEngine.GetCharacterStruct(Id).Get().isSprinting; + set => _ = GameState.IsBuildMode() + ? playerEngine.GetCharacterStruct(Id).Get().sprinting = value + : playerEngine.GetCharacterStruct(Id).Get().isSprinting = value; + } + + /// + /// Movement speed setting. Build mode (camera) and simulation mode settings are separate. + /// + public float SpeedSetting + { + get => GameState.IsBuildMode() + ? playerEngine.GetCharacterStruct(Id).Get().speed + : playerEngine.GetCharacterStruct(Id).Get().moveSpeed; + set => _ = GameState.IsBuildMode() + ? playerEngine.GetCharacterStruct(Id).Get().speed = value + : playerEngine.GetCharacterStruct(Id).Get().moveSpeed = value; + } + + /// + /// The multiplier setting to use when sprinting. Build mode (camera) and simulation mode settings are separate. + /// + public float SpeedSprintMultiplierSetting + { + get => GameState.IsBuildMode() + ? playerEngine.GetCharacterStruct(Id).Get().speedSprintMultiplier + : playerEngine.GetCharacterStruct(Id).Get().sprintSpeedMultiplier; + set => _ = GameState.IsBuildMode() + ? playerEngine.GetCharacterStruct(Id).Get().speedSprintMultiplier = value + : playerEngine.GetCharacterStruct(Id).Get().sprintSpeedMultiplier = value; + } + + /// + /// The acceleration setting of the player. Build mode (camera) and simulation mode settings are separate. + /// + public float AccelerationSetting + { + get => GameState.IsBuildMode() + ? playerEngine.GetCharacterStruct(Id).Get().acceleration + : playerEngine.GetCharacterStruct(Id).Get().acceleration; + set => _ = GameState.IsBuildMode() + ? playerEngine.GetCharacterStruct(Id).Get().acceleration = value + : playerEngine.GetCharacterStruct(Id).Get().acceleration = value; + } + + // object methods + + /// + /// Teleport the player to the specified coordinates. + /// + /// The x coordinate. + /// The y coordinate. + /// The z coordinate. + /// If set to true teleport relative to the player's current position. + /// If set to true exit any seat the player is in. + public void Teleport(float x, float y, float z, bool relative = true, bool exitSeat = true) + { + float3 location = new float3(x, y, z); + if (relative) + { + location += Position; + } + playerEngine.SetLocation(Id, location, exitSeat: exitSeat); + } + + public void EnterSeat(Seat seat) + { + playerEngine.EnterSeat(Id, seat.Id); + } + + public void ExitSeat() + { + playerEngine.ExitSeat(Id); + } + + /// + /// Returns the block the player is currently looking at in build mode. + /// + /// The maximum distance from the player (default is the player's building reach) + /// The block or null if not found + public Block GetBlockLookedAt(float maxDistance = -1f) + { + var egid = playerEngine.GetThingLookedAt(Id, maxDistance); + return egid != default && egid.groupID != CommonExclusiveGroups.SIMULATION_BODIES_GROUP + && egid.groupID != WiresGUIExclusiveGroups.WireGroup + ? Block.New(egid) + : null; + } + + /// + /// Returns the rigid body the player is currently looking at during simulation. + /// + /// The maximum distance from the player (default is the player's building reach) + /// The body or null if not found + public SimBody GetSimBodyLookedAt(float maxDistance = -1f) + { + var egid = playerEngine.GetThingLookedAt(Id, maxDistance); + return egid != default && egid.groupID == CommonExclusiveGroups.SIMULATION_BODIES_GROUP + ? EcsObjectBase.GetInstance(egid, e => new SimBody(e)) + : null; + } + + /// + /// Returns the wire the player is currently looking at in build mode. + /// + /// The maximum distance from the player (default is the player's building reach) + /// The wire or null if not found + public Wire GetWireLookedAt(float maxDistance = -1f) + { + var egid = playerEngine.GetThingLookedAt(Id, maxDistance); + return egid != default && egid.groupID == WiresGUIExclusiveGroups.WireGroup + ? EcsObjectBase.GetInstance(new EGID(egid.entityID, NamedExclusiveGroup.Group), + e => new Wire(e)) + : null; + } + + /// + /// Returns the blocks that are in the player's current selection. + /// + /// An array of blocks or an empty array + public Block[] GetSelectedBlocks() + { + return playerEngine.GetSelectedBlocks(Id); + } + + public bool Equals(Player other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Id == other.Id; + } + + public bool Equals(EGID other) + { + return Id == other.entityID && other.groupID == (Type == PlayerType.Local + ? PlayersExclusiveGroups.LocalPlayers + : PlayersExclusiveGroups.RemotePlayers); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Player) obj); + } + + public override int GetHashCode() + { + return (int) Id; + } + + public override string ToString() + { + return $"{nameof(Type)}: {Type}, {nameof(Id)}: {Id}, {nameof(Position)}: {Position}, {nameof(Rotation)}: {Rotation}, {nameof(Mass)}: {Mass}"; + } + + // internal methods + + internal static void Init() + { + Utility.GameEngineManager.AddGameEngine(playerEngine); + Utility.GameEngineManager.AddGameEngine(playerEventsEngine); + } + } +} diff --git a/TechbloxModdingAPI/Players/PlayerBuildingMode.cs b/TechbloxModdingAPI/Players/PlayerBuildingMode.cs new file mode 100644 index 0000000..7bc25a8 --- /dev/null +++ b/TechbloxModdingAPI/Players/PlayerBuildingMode.cs @@ -0,0 +1,12 @@ +namespace TechbloxModdingAPI.Players +{ + public enum PlayerBuildingMode + { + BlockMode, + ColourMode, + ConfigMode, + BlueprintMode, + MaterialMode, + LandscapeMode + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Players/PlayerEngine.cs b/TechbloxModdingAPI/Players/PlayerEngine.cs new file mode 100644 index 0000000..0dc2298 --- /dev/null +++ b/TechbloxModdingAPI/Players/PlayerEngine.cs @@ -0,0 +1,231 @@ +using System; +using System.Runtime.CompilerServices; + +using RobocraftX.Character; +using RobocraftX.Character.Movement; +using RobocraftX.Common.Players; +using RobocraftX.Common.Input; +using RobocraftX.CR.MachineEditing.BoxSelect; +using RobocraftX.Physics; +using RobocraftX.Blocks.Ghost; +using Gamecraft.GUI.HUDFeedbackBlocks; +using RobocraftX.Blocks; +using RobocraftX.PilotSeat; +using Svelto.ECS; +using Techblox.Camera; +using Unity.Mathematics; +using Svelto.ECS.DataStructures; +using Svelto.ECS.EntityStructs; +using Techblox.BuildingDrone; + +using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Players +{ + internal class PlayerEngine : IFunEngine + { + public string Name { get; } = "TechbloxModdingAPIPlayerGameEngine"; + + public EntitiesDB entitiesDB { set; private get; } + + public bool isRemovable => false; + + public IEntityFunctions Functions { get; set; } + + private bool isReady = false; + + public void Dispose() + { + isReady = false; + } + + public void Ready() + { + isReady = true; + } + + public uint GetLocalPlayer() + { + if (!isReady) return uint.MaxValue; + var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.LocalPlayers).ToBuffer(); + if (localPlayers.count > 0) + { + return localPlayers.buffer[0].ID.entityID; + } + return uint.MaxValue; + } + + public uint GetRemotePlayer() + { + if (!isReady) return uint.MaxValue; + var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.RemotePlayers).ToBuffer(); + if (localPlayers.count > 0) + { + return localPlayers.buffer[0].ID.entityID; + } + return uint.MaxValue; + } + + public long GetAllPlayerCount() + { + if (entitiesDB == null) return 0; + long count = 0; + foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers) + { + count += entitiesDB.Count(eg); + } + return count; + } + + public long GetLocalPlayerCount() + { + if (entitiesDB == null) return 0; + return entitiesDB.Count(PlayersExclusiveGroups.LocalPlayers); + } + + public long GetRemotePlayerCount() + { + if (entitiesDB == null) return 0; + return entitiesDB.Count(PlayersExclusiveGroups.RemotePlayers); + } + + public bool ExistsById(uint playerId) + { + if (entitiesDB == null) return false; + return entitiesDB.Exists(playerId, PlayersExclusiveGroups.LocalPlayers) + || entitiesDB.Exists(playerId, PlayersExclusiveGroups.RemotePlayers); + } + + public bool SetLocation(uint playerId, float3 location, bool exitSeat = true) + { + if (entitiesDB == null) return false; + var rbesOpt = GetCharacterStruct(playerId, out var group); + if (!rbesOpt) + return false; + if (group == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat) + { + ExitSeat(playerId); + } + rbesOpt.Get().position = location; + return true; + } + + public bool GetGameOverScreen(uint playerId) + { + if (entitiesDB == null) return false; + ref HudActivatedBlocksEntityStruct habes = ref entitiesDB.QueryEntity(HUDFeedbackBlocksGUIExclusiveGroups.GameOverHudEgid); + NativeDynamicArrayCast nativeDynamicArrayCast = new NativeDynamicArrayCast(habes.activatedBlocksOrdered); + return nativeDynamicArrayCast.count > 0; + } + + public bool IsDead(uint playerId) + { + if (entitiesDB == null) return true; + return entitiesDB.Exists(playerId, CharacterExclusiveGroups.DeadCharacters); + } + + // reusable methods + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public OptionalRef GetCharacterStruct(uint playerId) where T : unmanaged, IEntityComponent => + GetCharacterStruct(playerId, out _); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public OptionalRef GetCharacterStruct(uint playerId, out ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent + { + group = default; + if (GameState.IsBuildMode()) + return entitiesDB.QueryEntityOptional(new EGID(playerId, LocalBuildingDrone.BuildGroup)); + + var characterGroups = CharacterExclusiveGroups.AllCharacters; + for (int i = 0; i < characterGroups.count; i++) + { + EGID egid = new EGID(playerId, characterGroups[i]); + var opt = entitiesDB.QueryEntityOptional(egid); + if (opt.Exists) + { + group = characterGroups[i]; + return opt; + } + } + + return new OptionalRef(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public OptionalRef GetPlayerStruct(uint playerId, PlayerType type) where T : unmanaged, IEntityComponent + { + var playerGroup = type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers; + EGID egid = new EGID(playerId, playerGroup); + return entitiesDB.QueryEntityOptional(egid); + } + + public OptionalRef GetCameraStruct(uint playerId) where T : unmanaged, IEntityComponent + { + return entitiesDB.QueryEntityOptional(new EGID(playerId, CameraExclusiveGroups.PhysicCameraGroup)); + } + + public EGID GetThingLookedAt(uint playerId, float maxDistance = -1f) + { + var opt = GetCameraStruct(playerId); + if (!opt) return default; + PhysicCameraRayCastEntityStruct rayCast = opt; + float distance = maxDistance < 0 + ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast, + GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize) + : maxDistance; + if (rayCast.hit && rayCast.distance <= distance) + return rayCast.hitEgid; //May be EGID.Empty (default) + + return default; + } + + public unsafe Block[] GetSelectedBlocks(uint playerid) + { + if (!entitiesDB.Exists(playerid, + BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)) + return Array.Empty(); + var state = entitiesDB.QueryEntity(playerid, + BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup); + var blocks = entitiesDB.QueryEntity(playerid, + BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup); + if (!state.active) return Array.Empty(); + var pointer = (EGID*) blocks.selectedBlocks.ToPointer(); + var ret = new Block[blocks.count]; + for (int j = 0; j < blocks.count; j++) + { + var egid = pointer[j]; + ret[j] = Block.New(egid); + } + + return ret; + } + + public void EnterSeat(uint playerId, EGID seatId) + { + PilotSeatGroupUtils.SwapTagTo(Functions, seatId); + var opt = GetCharacterStruct(playerId, out var group); + if (!opt) return; + ref CharacterPilotSeatEntityStruct charSeat = ref opt.Get(); + var charId = new EGID(playerId, group); + charSeat.pilotSeatEntity = entitiesDB.GetEntityReference(seatId); + charSeat.entryPositionOffset = + entitiesDB.QueryEntity(charId).position - + entitiesDB.QueryEntity(seatId).position; + ref var seat = ref entitiesDB.QueryEntity(seatId); + seat.occupyingCharacter = entitiesDB.GetEntityReference(charId); + charSeat.followCam = entitiesDB.QueryEntity(seatId).followCam; + Functions.SwapEntityGroup(charId, CharacterExclusiveGroups.InPilotSeatGroup); + } + + public void ExitSeat(uint playerId) + { + EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup); + var opt = entitiesDB.QueryEntityOptional(egid); + if (!opt) return; + opt.Get().instantExit = true; + entitiesDB.PublishEntityChange(egid); + } + } +} diff --git a/TechbloxModdingAPI/Players/PlayerEventsEngine.cs b/TechbloxModdingAPI/Players/PlayerEventsEngine.cs new file mode 100644 index 0000000..2c80f24 --- /dev/null +++ b/TechbloxModdingAPI/Players/PlayerEventsEngine.cs @@ -0,0 +1,33 @@ +using RobocraftX.Character; +using RobocraftX.Character.Movement; +using Svelto.ECS; +using TechbloxModdingAPI.Engines; + +namespace TechbloxModdingAPI.Players +{ + public class PlayerEventsEngine : IApiEngine, IReactOnSwap + { + public void Ready() + { + } + + public EntitiesDB entitiesDB { get; set; } + public void Dispose() + { + } + + public string Name => "TechbloxModdingAPIPlayerEventsEngine"; + public bool isRemovable => false; + + public void MovedTo(ref CharacterPilotSeatEntityStruct entityComponent, ExclusiveGroupStruct previousGroup, EGID egid) + { + var seatId = entityComponent.pilotSeatEntity.ToEGID(entitiesDB); + var player = EcsObjectBase.GetInstance(new EGID(egid.entityID, CharacterExclusiveGroups.OnFootGroup), + e => new Player(e.entityID)); + if (previousGroup == CharacterExclusiveGroups.InPilotSeatGroup) + player.seatExited.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId}); + else if (egid.groupID == CharacterExclusiveGroups.InPilotSeatGroup) + player.seatEntered.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId }); + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Players/PlayerExceptions.cs b/TechbloxModdingAPI/Players/PlayerExceptions.cs similarity index 80% rename from GamecraftModdingAPI/Players/PlayerExceptions.cs rename to TechbloxModdingAPI/Players/PlayerExceptions.cs index 94312b9..20dbea6 100644 --- a/GamecraftModdingAPI/Players/PlayerExceptions.cs +++ b/TechbloxModdingAPI/Players/PlayerExceptions.cs @@ -1,7 +1,7 @@ using System; -namespace GamecraftModdingAPI.Players +namespace TechbloxModdingAPI.Players { - public class PlayerException : GamecraftModdingAPIException + public class PlayerException : TechbloxModdingAPIException { public PlayerException() { diff --git a/GamecraftModdingAPI/Players/PlayerTests.cs b/TechbloxModdingAPI/Players/PlayerTests.cs similarity index 57% rename from GamecraftModdingAPI/Players/PlayerTests.cs rename to TechbloxModdingAPI/Players/PlayerTests.cs index 1af7992..c974518 100644 --- a/GamecraftModdingAPI/Players/PlayerTests.cs +++ b/TechbloxModdingAPI/Players/PlayerTests.cs @@ -1,11 +1,14 @@ -using System; +using System.Collections.Generic; +using Svelto.Tasks; +using Svelto.Tasks.Enumerators; using Unity.Mathematics; -using GamecraftModdingAPI; -using GamecraftModdingAPI.Tests; +using TechbloxModdingAPI.App; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Tests; -namespace GamecraftModdingAPI.Players +namespace TechbloxModdingAPI.Players { #if TEST /// @@ -30,6 +33,34 @@ namespace GamecraftModdingAPI.Players if (!Assert.Errorless(() => { p.Position = float3.zero + 1; }, "Player.Position = origin+1 errored: ", "Player moved to origin+1.")) return; Assert.CloseTo(p.Position, float3.zero + 1, "Player is not close to origin+1 despite being teleported there.", "Player.Position is at origin+1."); } + + [APITestCase(TestType.Game)] + public static void SeatEventTestBuild() + { + Player.LocalPlayer.SeatEntered += Assert.CallsBack("SeatEntered"); + Player.LocalPlayer.SeatExited += Assert.CallsBack("SeatExited"); + Block.PlaceNew(BlockIDs.DriverSeat, -1f); + } + + [APITestCase(TestType.SimulationMode)] + public static IEnumerator SeatEventTestSim() + { + var seats = Game.CurrentGame().GetBlocksInGame(BlockIDs.DriverSeat); + if (seats.Length == 0) + { + Assert.Fail("No driver seat found!"); + yield break; + } + + if (seats[0] is Seat seat) + Assert.Errorless(() => Player.LocalPlayer.EnterSeat(seat), "Failed to enter seat.", + "Entered seat successfully."); + else + Assert.Fail("Found a seat that is not a seat!"); + yield return new WaitForSecondsEnumerator(1).Continue(); + Assert.Errorless(() => Player.LocalPlayer.ExitSeat(), "Failed to exit seat.", + "Exited seat successfully."); + } [APITestCase(TestType.Menu)] public static void InvalidStateTest() diff --git a/GamecraftModdingAPI/Players/PlayerType.cs b/TechbloxModdingAPI/Players/PlayerType.cs similarity index 70% rename from GamecraftModdingAPI/Players/PlayerType.cs rename to TechbloxModdingAPI/Players/PlayerType.cs index 0b4966c..3e2214f 100644 --- a/GamecraftModdingAPI/Players/PlayerType.cs +++ b/TechbloxModdingAPI/Players/PlayerType.cs @@ -1,5 +1,5 @@ using System; -namespace GamecraftModdingAPI.Players +namespace TechbloxModdingAPI.Players { public enum PlayerType { diff --git a/GamecraftModdingAPI/SimBody.cs b/TechbloxModdingAPI/SimBody.cs similarity index 85% rename from GamecraftModdingAPI/SimBody.cs rename to TechbloxModdingAPI/SimBody.cs index 42812d9..bb05005 100644 --- a/GamecraftModdingAPI/SimBody.cs +++ b/TechbloxModdingAPI/SimBody.cs @@ -7,27 +7,27 @@ using Gamecraft.Damage; using RobocraftX.Common; using RobocraftX.Physics; -namespace GamecraftModdingAPI +namespace TechbloxModdingAPI { /// /// A rigid body (like a chunk of connected blocks) during simulation. /// - public class SimBody : IEquatable, IEquatable + public class SimBody : EcsObjectBase, IEquatable, IEquatable { - public EGID Id { get; } - /// /// The cluster this chunk belongs to, or null if no cluster destruction manager present or the chunk doesn't exist. /// Get the SimBody from a Block if possible for good performance here. /// - public Cluster Cluster => cluster ?? (cluster = clusterId == uint.MaxValue ? Block.BlockEngine.GetCluster(Id.entityID) : new Cluster(clusterId)); + public Cluster Cluster => cluster ??= clusterId == uint.MaxValue // Return cluster or if it's null then set it + ? Block.BlockEngine.GetCluster(Id.entityID) // If we don't have a clusterId set then get it from the game + : GetInstance(new EGID(clusterId, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP), + egid => new Cluster(egid)); // Otherwise get the cluster from the ID private Cluster cluster; private readonly uint clusterId = uint.MaxValue; - public SimBody(EGID id) + public SimBody(EGID id) : base(id) { - Id = id; } public SimBody(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_BODIES_GROUP)) @@ -92,26 +92,26 @@ namespace GamecraftModdingAPI public float InitialHealth { - get => Block.BlockEngine.GetBlockInfo(Id).initialHealth; - set => Block.BlockEngine.GetBlockInfo(Id).initialHealth = value; + get => Block.BlockEngine.GetBlockInfo(this).initialHealth; + set => Block.BlockEngine.GetBlockInfo(this).initialHealth = value; } public float CurrentHealth { - get => Block.BlockEngine.GetBlockInfo(Id).currentHealth; - set => Block.BlockEngine.GetBlockInfo(Id).currentHealth = value; + get => Block.BlockEngine.GetBlockInfo(this).currentHealth; + set => Block.BlockEngine.GetBlockInfo(this).currentHealth = value; } public float HealthMultiplier { - get => Block.BlockEngine.GetBlockInfo(Id).healthMultiplier; - set => Block.BlockEngine.GetBlockInfo(Id).healthMultiplier = value; + get => Block.BlockEngine.GetBlockInfo(this).healthMultiplier; + set => Block.BlockEngine.GetBlockInfo(this).healthMultiplier = value; } /// /// Whether the body can be moved or static. /// - public bool Static => Block.BlockEngine.GetBlockInfo(Id).isStatic; //Setting it doesn't have any effect + public bool Static => Block.BlockEngine.GetBlockInfo(this).isStatic; //Setting it doesn't have any effect /// /// The rigid bodies connected to this one via functional joints (broken ones don't count). @@ -132,7 +132,7 @@ namespace GamecraftModdingAPI private ref RigidBodyEntityStruct GetStruct() { - return ref Block.BlockEngine.GetBlockInfo(Id); + return ref Block.BlockEngine.GetBlockInfo(this); } public override string ToString() diff --git a/GamecraftModdingAPI/Tasks/ISchedulable.cs b/TechbloxModdingAPI/Tasks/ISchedulable.cs similarity index 92% rename from GamecraftModdingAPI/Tasks/ISchedulable.cs rename to TechbloxModdingAPI/Tasks/ISchedulable.cs index 0b04a3e..679e999 100644 --- a/GamecraftModdingAPI/Tasks/ISchedulable.cs +++ b/TechbloxModdingAPI/Tasks/ISchedulable.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Svelto.Tasks; -namespace GamecraftModdingAPI.Tasks +namespace TechbloxModdingAPI.Tasks { /// /// Interface for asynchronous tasks diff --git a/GamecraftModdingAPI/Tasks/Once.cs b/TechbloxModdingAPI/Tasks/Once.cs similarity index 96% rename from GamecraftModdingAPI/Tasks/Once.cs rename to TechbloxModdingAPI/Tasks/Once.cs index 52651e6..807dae7 100644 --- a/GamecraftModdingAPI/Tasks/Once.cs +++ b/TechbloxModdingAPI/Tasks/Once.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Svelto.Tasks; using Svelto.Tasks.Enumerators; -namespace GamecraftModdingAPI.Tasks +namespace TechbloxModdingAPI.Tasks { /// /// An asynchronous task to be performed once. diff --git a/GamecraftModdingAPI/Tasks/Repeatable.cs b/TechbloxModdingAPI/Tasks/Repeatable.cs similarity index 98% rename from GamecraftModdingAPI/Tasks/Repeatable.cs rename to TechbloxModdingAPI/Tasks/Repeatable.cs index 0b9982e..0cafe48 100644 --- a/GamecraftModdingAPI/Tasks/Repeatable.cs +++ b/TechbloxModdingAPI/Tasks/Repeatable.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Svelto.Tasks; using Svelto.Tasks.Enumerators; -namespace GamecraftModdingAPI.Tasks +namespace TechbloxModdingAPI.Tasks { /// /// An asynchronous repeating task. diff --git a/GamecraftModdingAPI/Tasks/Scheduler.cs b/TechbloxModdingAPI/Tasks/Scheduler.cs similarity index 92% rename from GamecraftModdingAPI/Tasks/Scheduler.cs rename to TechbloxModdingAPI/Tasks/Scheduler.cs index 803bd2f..764e9b7 100644 --- a/GamecraftModdingAPI/Tasks/Scheduler.cs +++ b/TechbloxModdingAPI/Tasks/Scheduler.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Svelto.Tasks.Lean; using Svelto.Tasks.ExtraLean; -namespace GamecraftModdingAPI.Tasks +namespace TechbloxModdingAPI.Tasks { /// /// Asynchronous task scheduling for ISchedulables. @@ -32,9 +32,9 @@ namespace GamecraftModdingAPI.Tasks } } - public static readonly Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner extraLeanRunner = new Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner("GamecraftModdingAPIExtraLean"); + public static readonly Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner extraLeanRunner = new Svelto.Tasks.ExtraLean.Unity.UpdateMonoRunner("TechbloxModdingAPIExtraLean"); - public static readonly Svelto.Tasks.Lean.Unity.UpdateMonoRunner leanRunner = new Svelto.Tasks.Lean.Unity.UpdateMonoRunner("GamecraftModdingAPILean"); + public static readonly Svelto.Tasks.Lean.Unity.UpdateMonoRunner leanRunner = new Svelto.Tasks.Lean.Unity.UpdateMonoRunner("TechbloxModdingAPILean"); /// /// Schedule a task to run asynchronously. @@ -42,7 +42,7 @@ namespace GamecraftModdingAPI.Tasks /// /// The task to run /// Schedule toRun on an extra lean runner? - /// Schedule toRun on Gamecraft's built-in UI task runner? + /// Schedule toRun on Techblox's built-in UI task runner? public static void Schedule(ISchedulable toRun, bool extraLean = false, bool ui = false) { if (extraLean) diff --git a/TechbloxModdingAPI/TechbloxModdingAPI.csproj b/TechbloxModdingAPI/TechbloxModdingAPI.csproj new file mode 100644 index 0000000..95927f8 --- /dev/null +++ b/TechbloxModdingAPI/TechbloxModdingAPI.csproj @@ -0,0 +1,1241 @@ + + + net472 + true + 2.0.0 + Exmods + GNU General Public Licence 3+ + https://git.exmods.org/modtainers/GamecraftModdingAPI + en-CA + true + 8 + + + + + + + DEBUG;TEST;TRACE + + + + + + + + + + + + + + ..\ref\TechbloxPreview_Data\Managed\IllusionInjector.dll + ..\..\ref\TechbloxPreview_Data\Managed\IllusionInjector.dll + + + ..\ref\TechbloxPreview_Data\Managed\IllusionPlugin.dll + ..\..\ref\TechbloxPreview_Data\Managed\IllusionPlugin.dll + + + ..\ref\TechbloxPreview_Data\Managed\Analytics.dll + ..\..\ref\TechbloxPreview_Data\Managed\Analytics.dll + + + ..\ref\TechbloxPreview_Data\Managed\Assembly-CSharp-firstpass.dll + ..\..\ref\TechbloxPreview_Data\Managed\Assembly-CSharp-firstpass.dll + + + ..\ref\TechbloxPreview_Data\Managed\Assembly-CSharp.dll + ..\..\ref\TechbloxPreview_Data\Managed\Assembly-CSharp.dll + + + ..\ref\TechbloxPreview_Data\Managed\BevelEffect.dll + ..\..\ref\TechbloxPreview_Data\Managed\BevelEffect.dll + + + ..\ref\TechbloxPreview_Data\Managed\Blocks.HUDFeedbackBlocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Blocks.HUDFeedbackBlocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Boxophobic.TheVehetationEngine.Runtime.dll + ..\..\ref\TechbloxPreview_Data\Managed\Boxophobic.TheVehetationEngine.Runtime.dll + + + ..\ref\TechbloxPreview_Data\Managed\Boxophobic.Utils.Scripts.dll + ..\..\ref\TechbloxPreview_Data\Managed\Boxophobic.Utils.Scripts.dll + + + ..\ref\TechbloxPreview_Data\Managed\DataLoader.dll + ..\..\ref\TechbloxPreview_Data\Managed\DataLoader.dll + + + ..\ref\TechbloxPreview_Data\Managed\DDNA.dll + ..\..\ref\TechbloxPreview_Data\Managed\DDNA.dll + + + ..\ref\TechbloxPreview_Data\Managed\EasyButtons.dll + ..\..\ref\TechbloxPreview_Data\Managed\EasyButtons.dll + + + ..\ref\TechbloxPreview_Data\Managed\EOSSDK.dll + ..\..\ref\TechbloxPreview_Data\Managed\EOSSDK.dll + + + ..\ref\TechbloxPreview_Data\Managed\FMODUnity.dll + ..\..\ref\TechbloxPreview_Data\Managed\FMODUnity.dll + + + ..\ref\TechbloxPreview_Data\Managed\FMODUnityResonance.dll + ..\..\ref\TechbloxPreview_Data\Managed\FMODUnityResonance.dll + + + ..\ref\TechbloxPreview_Data\Managed\FullGame.dll + ..\..\ref\TechbloxPreview_Data\Managed\FullGame.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.AudioBlocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.AudioBlocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.BlockEntityFactory.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.BlockEntityFactory.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.BlockGroups.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.BlockGroups.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.DamagingSurfaceBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.DamagingSurfaceBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.DestructionBlocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.DestructionBlocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.GenericPhysicsBlocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.LogicBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.LogicBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\GameCraft.Blocks.ProjectileBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\GameCraft.Blocks.ProjectileBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.TextBlock.CompositionRoot.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.TextBlock.CompositionRoot.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.TimerBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Blocks.TimerBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.BlocksEntityDescriptors.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.BlocksEntityDescriptors.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.CharacterVulnerability.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.CharacterVulnerability.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.ColourPalette.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.ColourPalette.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Damage.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Damage.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Effects.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Effects.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.ExplosionFragments.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.ExplosionFragments.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GraphicsSettings.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GraphicsSettings.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.BlueprintInventory.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.BlueprintInventory.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.BlueprintInventoryMock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.BlueprintInventoryMock.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Blueprints.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Blueprints.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.BlueprintSets.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.BlueprintSets.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.GameOptionsScreen.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.GameOptionsScreen.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Hotbar.Blocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Hotbar.Blocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Hotbar.BlueprintsHotbar.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Hotbar.BlueprintsHotbar.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Hotbar.Colours.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Hotbar.Colours.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.ModeBar.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.ModeBar.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.OptionsScreen.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.OptionsScreen.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TabsBar.Blocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TabsBar.Blocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TabsBar.Blueprints.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TabsBar.Blueprints.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TabsBar.Colours.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TabsBar.Colours.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TabsBar.Common.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TabsBar.Common.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TimeModeClock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.TimeModeClock.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Tweaks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Tweaks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Wires.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Wires.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Wires.Mockup.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.Wires.Mockup.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.InventoryTimeRunning.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.InventoryTimeRunning.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.JointBlocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.JointBlocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Music.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Music.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.NetStrings.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.NetStrings.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.PerformanceWarnings.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.PerformanceWarnings.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.PickupBlck.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.PickupBlck.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.PickupsCommon.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.PickupsCommon.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.PopupMessage.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.PopupMessage.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Projectiles.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Projectiles.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Serialization.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Serialization.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Tweaks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Tweaks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Tweaks.Mockup.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Tweaks.Mockup.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.VisualEffects.Decals.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.VisualEffects.Decals.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.VisualEffects.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.VisualEffects.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Wires.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Wires.dll + + + ..\ref\TechbloxPreview_Data\Managed\Gamecraft.Wires.Mockup.dll + ..\..\ref\TechbloxPreview_Data\Managed\Gamecraft.Wires.Mockup.dll + + + ..\ref\TechbloxPreview_Data\Managed\GameState.dll + ..\..\ref\TechbloxPreview_Data\Managed\GameState.dll + + + ..\ref\TechbloxPreview_Data\Managed\GhostShark.Outline.dll + ..\..\ref\TechbloxPreview_Data\Managed\GhostShark.Outline.dll + + + ..\ref\TechbloxPreview_Data\Managed\GPUInstancer.CrowdAnimations.dll + ..\..\ref\TechbloxPreview_Data\Managed\GPUInstancer.CrowdAnimations.dll + + + ..\ref\TechbloxPreview_Data\Managed\GPUInstancer.dll + ..\..\ref\TechbloxPreview_Data\Managed\GPUInstancer.dll + + + ..\ref\TechbloxPreview_Data\Managed\Havok.Physics.dll + ..\..\ref\TechbloxPreview_Data\Managed\Havok.Physics.dll + + + ..\ref\TechbloxPreview_Data\Managed\Havok.Physics.Hybrid.dll + ..\..\ref\TechbloxPreview_Data\Managed\Havok.Physics.Hybrid.dll + + + ..\ref\TechbloxPreview_Data\Managed\JWT.dll + ..\..\ref\TechbloxPreview_Data\Managed\JWT.dll + + + ..\ref\TechbloxPreview_Data\Managed\LZ4.dll + ..\..\ref\TechbloxPreview_Data\Managed\LZ4.dll + + + ..\ref\TechbloxPreview_Data\Managed\mscorlib.dll + ..\..\ref\TechbloxPreview_Data\Managed\mscorlib.dll + + + ..\ref\TechbloxPreview_Data\Managed\MultiplayerNetworking.dll + ..\..\ref\TechbloxPreview_Data\Managed\MultiplayerNetworking.dll + + + ..\ref\TechbloxPreview_Data\Managed\MultiplayerTest.dll + ..\..\ref\TechbloxPreview_Data\Managed\MultiplayerTest.dll + + + ..\ref\TechbloxPreview_Data\Managed\netstandard.dll + ..\..\ref\TechbloxPreview_Data\Managed\netstandard.dll + + + ..\ref\TechbloxPreview_Data\Managed\Newtonsoft.Json.dll + ..\..\ref\TechbloxPreview_Data\Managed\Newtonsoft.Json.dll + + + ..\ref\TechbloxPreview_Data\Managed\RCX.ScreenshotTaker.dll + ..\..\ref\TechbloxPreview_Data\Managed\RCX.ScreenshotTaker.dll + + + ..\ref\TechbloxPreview_Data\Managed\Rewired_Core.dll + ..\..\ref\TechbloxPreview_Data\Managed\Rewired_Core.dll + + + ..\ref\TechbloxPreview_Data\Managed\Rewired_Windows.dll + ..\..\ref\TechbloxPreview_Data\Managed\Rewired_Windows.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftECS.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftECS.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.AccountPreferences.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.AccountPreferences.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Blocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Blocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Blocks.Ghost.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Blocks.Ghost.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Blocks.Triggers.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Blocks.Triggers.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Building.BoxSelect.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Building.BoxSelect.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Building.Jobs.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Building.Jobs.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Character.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Character.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Common.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Common.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.ControlsScreen.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.ControlsScreen.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Crosshair.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Crosshair.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.FrontEnd.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.FrontEnd.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.BlockLabel.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.BlockLabel.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.DebugDisplay.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.DebugDisplay.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.Hotbar.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.Hotbar.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.Inventory.BlocksInventory.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.Inventory.BlocksInventory.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.Inventory.ColourInventory.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.Inventory.ColourInventory.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.Inventory.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.Inventory.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.PauseMenu.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.PauseMenu.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.ScaleGhost.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.ScaleGhost.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.TabsBar.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.GUI.TabsBar.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Input.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Input.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.MachineEditor.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.MachineEditor.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.MainGame.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.MainGame.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.MainGameMock.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.MainGameMock.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.MainSimulation.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.MainSimulation.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.MockCharacter.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.MockCharacter.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Multiplayer.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Multiplayer.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Multiplayer.GUI.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Multiplayer.GUI.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Multiplayer.Serializers.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Multiplayer.Serializers.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.MultiplayerInput.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.MultiplayerInput.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.ObjectIdBlocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.ObjectIdBlocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Party.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Party.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Physics.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Physics.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.PilotSeat.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.PilotSeat.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Player.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Player.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Rendering.Mock.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Rendering.Mock.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.SaveAndLoad.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.SaveAndLoad.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.SaveGameDialog.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.SaveGameDialog.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.Services.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.Services.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.SignalHandling.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.SignalHandling.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.SpawnPoints.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.SpawnPoints.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX.StateSync.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX.StateSync.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocraftX_TextBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocraftX_TextBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\RobocratX.SimulationMockCompositionRoot.dll + ..\..\ref\TechbloxPreview_Data\Managed\RobocratX.SimulationMockCompositionRoot.dll + + + ..\ref\TechbloxPreview_Data\Managed\SpecializedDescriptors.dll + ..\..\ref\TechbloxPreview_Data\Managed\SpecializedDescriptors.dll + + + ..\ref\TechbloxPreview_Data\Managed\StringFormatter.dll + ..\..\ref\TechbloxPreview_Data\Managed\StringFormatter.dll + + + ..\ref\TechbloxPreview_Data\Managed\Svelto.Common.dll + ..\..\ref\TechbloxPreview_Data\Managed\Svelto.Common.dll + + + ..\ref\TechbloxPreview_Data\Managed\Svelto.ECS.dll + ..\..\ref\TechbloxPreview_Data\Managed\Svelto.ECS.dll + + + ..\ref\TechbloxPreview_Data\Managed\Svelto.ECS.GUI.dll + ..\..\ref\TechbloxPreview_Data\Managed\Svelto.ECS.GUI.dll + + + ..\ref\TechbloxPreview_Data\Managed\Svelto.Services.dll + ..\..\ref\TechbloxPreview_Data\Managed\Svelto.Services.dll + + + ..\ref\TechbloxPreview_Data\Managed\Svelto.Tasks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Svelto.Tasks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.AtmosphereBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.AtmosphereBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.AutoForward.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.AutoForward.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Backend.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Backend.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Blocks.LightBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Blocks.LightBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Building.Rules.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Building.Rules.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.BuildingDrone.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.BuildingDrone.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Camera.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Camera.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.ContextSensitiveTextHint.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.ContextSensitiveTextHint.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.EngineBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.EngineBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Environment.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Environment.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.EnvironmentBlocks.BuildingEnvironment.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.EnvironmentBlocks.BuildingEnvironment.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.EnvironmentBlocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.EnvironmentBlocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.EnvironmentBlocks.SimulationWorldEnvironment.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.EnvironmentBlocks.SimulationWorldEnvironment.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GameSelection.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GameSelection.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Building.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Building.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.BuildRules.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.BuildRules.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.BuildRules.MockUps.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.BuildRules.MockUps.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.GamePortal.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.GamePortal.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.GamePortal.MockUps.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.GamePortal.MockUps.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Hotbar.Landscapes.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Hotbar.Landscapes.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Hotbar.Materials.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Hotbar.Materials.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Inventory.Common.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Inventory.Common.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Inventory.Landscapes.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Inventory.Landscapes.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Inventory.Materials.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Inventory.Materials.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Login.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Login.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Mocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Mocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Mocks.DynamicListBuild.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Mocks.DynamicListBuild.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.MyGamesScreen.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.MyGamesScreen.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Notifications.MockUps.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.Notifications.MockUps.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.TabsBar.Landscapes.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.TabsBar.Landscapes.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.TabsBar.Materials.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.GUI.TabsBar.Materials.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.InputCapture.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.InputCapture.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Pointer.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Pointer.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.ProceduralReflectionProbes.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.ProceduralReflectionProbes.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.Common.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.Common.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.DOTS.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.DOTS.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.GPUI.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.GPUI.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.Unity.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Rendering.Unity.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.SaveGamesConversion.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.SaveGamesConversion.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Services.Eos.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Services.Eos.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Services.GameDetails.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Services.GameDetails.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.Services.Storage.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.Services.Storage.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.SwitchAnimation.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.SwitchAnimation.dll + + + ..\ref\TechbloxPreview_Data\Managed\Techblox.WheelRigBlock.dll + ..\..\ref\TechbloxPreview_Data\Managed\Techblox.WheelRigBlock.dll + + + ..\ref\TechbloxPreview_Data\Managed\UniTask.Addressables.dll + ..\..\ref\TechbloxPreview_Data\Managed\UniTask.Addressables.dll + + + ..\ref\TechbloxPreview_Data\Managed\UniTask.dll + ..\..\ref\TechbloxPreview_Data\Managed\UniTask.dll + + + ..\ref\TechbloxPreview_Data\Managed\UniTask.DOTween.dll + ..\..\ref\TechbloxPreview_Data\Managed\UniTask.DOTween.dll + + + ..\ref\TechbloxPreview_Data\Managed\UniTask.Linq.dll + ..\..\ref\TechbloxPreview_Data\Managed\UniTask.Linq.dll + + + ..\ref\TechbloxPreview_Data\Managed\UniTask.TextMeshPro.dll + ..\..\ref\TechbloxPreview_Data\Managed\UniTask.TextMeshPro.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Addressables.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Addressables.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Cecil.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Cecil.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Cecil.Mdb.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Cecil.Mdb.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Cecil.Pdb.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Cecil.Pdb.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Cecil.Rocks.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Cecil.Rocks.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Burst.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Burst.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Unsafe.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Burst.Unsafe.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Collections.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Collections.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Collections.LowLevel.ILSupport.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Collections.LowLevel.ILSupport.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Deformations.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Deformations.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Entities.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Entities.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Entities.Hybrid.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Entities.Hybrid.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.InternalAPIEngineBridge.012.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.InternalAPIEngineBridge.012.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Jobs.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Jobs.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Mathematics.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Mathematics.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Mathematics.Extensions.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Mathematics.Extensions.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Mathematics.Extensions.Hybrid.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Mathematics.Extensions.Hybrid.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.MemoryProfiler.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.MemoryProfiler.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Physics.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Physics.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Physics.Hybrid.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Physics.Hybrid.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Platforms.Common.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Platforms.Common.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Properties.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Properties.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Properties.Reflection.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Properties.Reflection.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Properties.UI.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Properties.UI.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Recorder.Base.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Recorder.Base.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Recorder.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Recorder.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Rendering.Hybrid.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Rendering.Hybrid.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.Core.Runtime.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.Core.ShaderLibrary.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.HighDefinition.Config.Runtime.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.HighDefinition.Config.Runtime.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.HighDefinition.Runtime.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.HighDefinition.Runtime.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.ResourceManager.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.ResourceManager.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Scenes.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Scenes.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.ScriptableBuildPipeline.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.ScriptableBuildPipeline.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Serialization.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Serialization.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.TextMeshPro.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.TextMeshPro.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Timeline.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Timeline.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Transforms.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Transforms.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.Transforms.Hybrid.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.Transforms.Hybrid.dll + + + ..\ref\TechbloxPreview_Data\Managed\Unity.VisualEffectGraph.Runtime.dll + ..\..\ref\TechbloxPreview_Data\Managed\Unity.VisualEffectGraph.Runtime.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.AccessibilityModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.AccessibilityModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.AIModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.AIModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.AndroidJNIModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.AndroidJNIModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.AnimationModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.AnimationModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.ARModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.ARModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.AssetBundleModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.AssetBundleModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.AudioModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.AudioModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.ClothModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.ClothModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.ClusterInputModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.ClusterInputModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.ClusterRendererModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.ClusterRendererModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.CoreModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.CoreModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.CrashReportingModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.CrashReportingModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.DirectorModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.DirectorModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.DSPGraphModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.DSPGraphModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.GameCenterModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.GameCenterModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.GIModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.GIModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.GridModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.GridModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.HotReloadModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.HotReloadModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.ImageConversionModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.ImageConversionModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.IMGUIModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.IMGUIModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.InputLegacyModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.InputLegacyModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.InputModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.InputModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.JSONSerializeModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.JSONSerializeModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.LocalizationModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.LocalizationModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.ParticleSystemModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.ParticleSystemModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.PerformanceReportingModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.PerformanceReportingModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.Physics2DModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.Physics2DModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.PhysicsModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.PhysicsModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.ProfilerModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.ProfilerModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.ScreenCaptureModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.ScreenCaptureModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.SharedInternalsModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.SharedInternalsModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.SpriteMaskModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.SpriteMaskModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.SpriteShapeModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.SpriteShapeModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.StreamingModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.StreamingModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.SubstanceModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.SubstanceModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.SubsystemsModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.SubsystemsModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.TerrainModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.TerrainModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.TerrainPhysicsModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.TerrainPhysicsModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.TextCoreModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.TextCoreModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.TextRenderingModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.TextRenderingModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.TilemapModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.TilemapModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.TLSModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.TLSModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UI.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UI.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UIElementsModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UIElementsModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UIElementsNativeModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UIElementsNativeModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UIModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UIModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UmbraModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UmbraModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UNETModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UNETModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityAnalyticsModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityAnalyticsModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityConnectModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityConnectModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityCurlModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityCurlModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityTestProtocolModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityTestProtocolModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.VehiclesModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.VehiclesModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.VFXModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.VFXModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.VideoModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.VideoModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.VirtualTexturingModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.VirtualTexturingModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.VRModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.VRModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.WindModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.WindModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\UnityEngine.XRModule.dll + ..\..\ref\TechbloxPreview_Data\Managed\UnityEngine.XRModule.dll + + + ..\ref\TechbloxPreview_Data\Managed\VisualProfiler.dll + ..\..\ref\TechbloxPreview_Data\Managed\VisualProfiler.dll + + + ..\ref\TechbloxPreview_Data\Managed\Whinarn.UnityMeshSimplifier.Runtime.dll + ..\..\ref\TechbloxPreview_Data\Managed\Whinarn.UnityMeshSimplifier.Runtime.dll + + + + + + + + \ No newline at end of file diff --git a/TechbloxModdingAPI/TechbloxModdingAPIException.cs b/TechbloxModdingAPI/TechbloxModdingAPIException.cs new file mode 100644 index 0000000..3a1b73f --- /dev/null +++ b/TechbloxModdingAPI/TechbloxModdingAPIException.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.Serialization; + +namespace TechbloxModdingAPI +{ + public class TechbloxModdingAPIException : Exception + { + public TechbloxModdingAPIException() + { + } + + public TechbloxModdingAPIException(string message) : base(message) + { + } + + public TechbloxModdingAPIException(string message, Exception innerException) : base(message, innerException) + { + } + + protected TechbloxModdingAPIException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/GamecraftModdingAPI/Tests/APITestAttributes.cs b/TechbloxModdingAPI/Tests/APITestAttributes.cs similarity index 97% rename from GamecraftModdingAPI/Tests/APITestAttributes.cs rename to TechbloxModdingAPI/Tests/APITestAttributes.cs index cf9da49..0ebe81d 100644 --- a/GamecraftModdingAPI/Tests/APITestAttributes.cs +++ b/TechbloxModdingAPI/Tests/APITestAttributes.cs @@ -1,5 +1,5 @@ using System; -namespace GamecraftModdingAPI.Tests +namespace TechbloxModdingAPI.Tests { /// /// Test type. diff --git a/GamecraftModdingAPI/Tests/Assert.cs b/TechbloxModdingAPI/Tests/Assert.cs similarity index 92% rename from GamecraftModdingAPI/Tests/Assert.cs rename to TechbloxModdingAPI/Tests/Assert.cs index 78f0597..f71b132 100644 --- a/GamecraftModdingAPI/Tests/Assert.cs +++ b/TechbloxModdingAPI/Tests/Assert.cs @@ -5,7 +5,7 @@ using System.Runtime.CompilerServices; using Unity.Mathematics; -namespace GamecraftModdingAPI.Tests +namespace TechbloxModdingAPI.Tests { /// /// API test system assertion utilities. @@ -119,6 +119,25 @@ namespace GamecraftModdingAPI.Tests return true; } + public static bool Errorful(Action tryThis, string err = null, string success = null) where T : Exception + { + if (err == null) err = $"{tryThis} was expected to error but completed without errors."; + if (success == null) success = $"{tryThis} completed with an expected error."; + try + { + tryThis(); + } + catch (T e) + { + TestRoot.TestsPassed = true; + Log(PASS + success + " " + e.GetType().Name + ": " + e.Message); + return true; + } + Log(FAIL + err); + TestRoot.TestsPassed = false; + return false; + } + public static bool CloseTo(float a, float b, string err = null, string success = null, float delta = float.Epsilon) { if (err == null) err = $"{a} is not within {delta} of {b}."; diff --git a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs new file mode 100644 index 0000000..75d0cc4 --- /dev/null +++ b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Text; +using TechbloxModdingAPI.App; +using HarmonyLib; +using IllusionInjector; +// test +using RobocraftX.FrontEnd; +using Unity.Mathematics; +using UnityEngine; +using RobocraftX.Common.Input; +using Svelto.Tasks; +using Svelto.Tasks.Lean; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Commands; +using TechbloxModdingAPI.Input; +using TechbloxModdingAPI.Players; +using TechbloxModdingAPI.Tasks; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI.Tests +{ +#if DEBUG + // unused by design + /// + /// Modding API implemented as a standalone IPA Plugin. + /// Ideally, TechbloxModdingAPI should be loaded by another mod; not itself + /// + class TechbloxModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin + { + + private static Harmony harmony { get; set; } + + public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name; + + public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + + public string HarmonyID { get; } = "org.git.exmods.modtainers.techbloxmoddingapi"; + + public override void OnApplicationQuit() + { + Main.Shutdown(); + } + + public override void OnApplicationStart() + { + FileLog.Reset(); + Harmony.DEBUG = true; + Main.Init(); + Logging.MetaDebugLog($"Version group id {ApiExclusiveGroups.versionGroup}"); + // disable background music + Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers())); + //AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :( + + //Utility.VersionTracking.Enable();//(very) unstable + + // debug/test handlers + Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!"); + Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler"); + Game.Enter += (s, a) => + { + Player.LocalPlayer.SeatEntered += (sender, args) => + Console.WriteLine($"Player {Player.LocalPlayer} entered seat {args.Seat}"); + Player.LocalPlayer.SeatExited += (sender, args) => + Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}"); + }; + + // debug/test commands + if (Dependency.Hell("ExtraCommands")) + { + CommandBuilder.Builder() + .Name("Exit") + .Description("Close Techblox immediately, without any prompts") + .Action(() => { UnityEngine.Application.Quit(); }) + .Build(); + + CommandBuilder.Builder() + .Name("SetFOV") + .Description("Set the player camera's field of view") + .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; }) + .Build(); + + CommandBuilder.Builder() + .Name("MoveLastBlock") + .Description("Move the most-recently-placed block, and any connected blocks by the given offset") + .Action((float x, float y, float z) => + { + if (GameState.IsBuildMode()) + foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes()) + block.Position += new Unity.Mathematics.float3(x, y, z); + else + Logging.CommandLogError("Blocks can only be moved in Build mode!"); + }).Build(); + + CommandBuilder.Builder() + .Name("PlaceAluminium") + .Description("Place a block of aluminium at the given coordinates") + .Action((float x, float y, float z) => + { + var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z), force: true); + Logging.CommandLog("Block placed with type: " + block.Type); + }) + .Build(); + + CommandBuilder.Builder() + .Name("PlaceAluminiumLots") + .Description("Place a lot of blocks of aluminium at the given coordinates") + .Action((float x, float y, float z) => + { + Logging.CommandLog("Starting..."); + var sw = Stopwatch.StartNew(); + for (int i = 0; i < 100; i++) + for (int j = 0; j < 100; j++) + Block.PlaceNew(BlockIDs.Cube, new float3(x + i, y, z + j)); + sw.Stop(); + Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms"); + }) + .Build(); + + Block b = null; + CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up") + .Action(() => + { + if (b == null) + { + b = new Player(PlayerType.Local).GetBlockLookedAt(); + Logging.CommandLog("Block saved: " + b); + } + else + Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0))); + }).Build(); + + CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.") + .Action(() => { throw new Exception("Error Command always throws an error"); }) + .Build(); + + CommandBuilder.Builder("ColorBlock", + "Change color of the block looked at if there's any.") + .Action(str => + { + if (!Enum.TryParse(str, out BlockColors color)) + { + Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values."); + var s = str.Split(' '); + new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]), + float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3])); + return; + } + + new Player(PlayerType.Local).GetBlockLookedAt().Color = color; + Logging.CommandLog("Colored block to " + color); + + }).Build(); + + CommandBuilder.Builder("MoveBlockByID", "Gets a block based on its object identifier and teleports it up.") + .Action(ch => + { + foreach (var body in SimBody.GetFromObjectID(ch)) + { + Logging.CommandLog("SimBody: " + body); + body.Position += new float3(0, 10, 0); + foreach (var bodyConnectedBody in body.GetConnectedBodies()) + { + Logging.CommandLog("Moving " + bodyConnectedBody); + bodyConnectedBody.Position += new float3(0, 10, 0); + } + } + }).Build(); + + CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.") + .Action((float val, float max) => + { + var body = new Player(PlayerType.Local).GetSimBodyLookedAt(); + if (body == null) return; + body.CurrentHealth = val; + body.InitialHealth = max; + Logging.CommandLog("Health set to: " + val); + }).Build(); + + CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group") + .Action((float x, float y, float z) => + { + var pos = new float3(x, y, z); + var group = BlockGroup.Create(new Block(BlockIDs.Cube, pos) {Color = BlockColors.Aqua}); + new Block(BlockIDs.Cube, pos += new float3(1, 0, 0)) + {Color = BlockColors.Blue, BlockGroup = group}; + new Block(BlockIDs.Cube, pos += new float3(1, 0, 0)) + {Color = BlockColors.Green, BlockGroup = group}; + new Block(BlockIDs.Cube, pos + new float3(1, 0, 0)) + {Color = BlockColors.Lime, BlockGroup = group}; + }).Build(); + + CommandBuilder.Builder("placeCustomBlock", "Places a custom block, needs a custom catalog and assets.") + .Action((float x, float y, float z) => + { + Logging.CommandLog("Block placed: " + + Block.PlaceNew((BlockIDs) 500, new float3(0, 0, 0))); + }).Build(); + + CommandBuilder.Builder("toggleTimeMode", "Enters or exits simulation.") + .Action((float x, float y, float z) => + { + Game.CurrentGame().ToggleTimeMode(); + }).Build(); + + CommandBuilder.Builder("testColorBlock", "Tests coloring a block to default color") + .Action(() => Player.LocalPlayer.GetBlockLookedAt().Color = BlockColors.Default).Build(); + CommandBuilder.Builder("testMaterialBlock", "Tests materialing a block to default material") + .Action(() => Player.LocalPlayer.GetBlockLookedAt().Material = BlockMaterial.Default).Build(); + CommandBuilder.Builder("testGameName", "Tests changing the game name") + .Action(() => Game.CurrentGame().Name = "Test").Build(); + CommandBuilder.Builder("makeBlockStatic", "Makes a block you look at static") + .Action(() => Player.LocalPlayer.GetBlockLookedAt().Static = true).Build(); + + Game.AddPersistentDebugInfo("InstalledMods", InstalledMods); + /*Block.Placed += (sender, args) => + Logging.MetaDebugLog("Placed block " + args.Block); + Block.Removed += (sender, args) => + Logging.MetaDebugLog("Removed block " + args.Block);*/ + } + + // dependency test + if (Dependency.Hell("TechbloxScripting", new Version("0.0.1.0"))) + { + Logging.LogWarning("You're in TechbloxScripting dependency hell"); + } + else + { + Logging.Log("Compatible TechbloxScripting detected"); + } + // Interface test + /*Group uiGroup = new Group(new Rect(20, 20, 200, 500), "TechbloxModdingAPI_UITestGroup", true); + var button = new Button("TEST"); + button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");}; + var button2 = new Button("TEST2"); + button2.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");}; + Text uiText = new Text("", multiline: true); + uiText.OnEdit += (t, txt) => { Logging.MetaDebugLog($"Text in {((Text)t).Name} is now '{txt}'"); }; + Label uiLabel = new Label("Label!"); + Image uiImg = new Image(name:"Behold this texture!"); + uiImg.Enabled = false; + uiGroup.AddElement(button); + uiGroup.AddElement(button2); + uiGroup.AddElement(uiText); + uiGroup.AddElement(uiLabel); + uiGroup.AddElement(uiImg);*/ + + /*Addressables.LoadAssetAsync("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg") + .Completed += + handle => + { + uiImg.Texture = handle.Result; + uiImg.Enabled = true; + Logging.MetaDebugLog($"Got blue bg asset {handle.Result}"); + };*/ + + /*((FasterList)AccessTools.Property(typeof(GuiInputMap), "GuiInputsButtonDown").GetValue(null)) + .Add(new GuiInputMap.GuiInputMapElement(RewiredConsts.Action.ToggleCommandLine, GuiIn))*/ + + /*Game.Enter += (sender, e) => + { + ushort lastKey = ushort.MaxValue; + foreach (var kv in FullGameFields._dataDb.GetValues() + .OrderBy(kv=>ushort.Parse(kv.Key))) + { + var data = (CubeListData) kv.Value; + ushort currentKey = ushort.Parse(kv.Key); + var toReplace = new Dictionary + { + {"Scalable", ""}, {"Qtr", "Quarter"}, {"RNeg", "Rounded Negative"}, + {"Neg", "Negative"}, {"Tetra", "Tetrahedron"}, + {"RWedge", "Rounded Wedge"}, {"RTetra", "Rounded Tetrahedron"} + }; + string name = LocalizationService.Localize(data.CubeNameKey).Split(' ').Select(str => + str.Length > 0 ? char.ToUpper(str[0]) + str.Substring(1) : str).Aggregate((a, b) => a + b) + .Replace("-", ""); + foreach (var rkv in toReplace) + { + name = Regex.Replace(name, rkv.Key + "([A-Z]|$)", rkv.Value + "$1"); + } + Console.WriteLine($"{name}{(currentKey != lastKey + 1 ? $" = {currentKey}" : "")},"); + lastKey = currentKey; + } + };*/ + /*Game.Enter += (sender, e) => + { + ushort lastKey = ushort.MaxValue; + Console.WriteLine("Materials:\n" + FullGameFields._dataDb.GetValues() + .OrderBy(kv => ushort.Parse(kv.Key)) + .Select(kv => + { + ushort currentKey = ushort.Parse(kv.Key); + string result = $"{((MaterialPropertiesData)kv.Value).Name}{(currentKey != lastKey + 1 ? $" = {kv.Key}" : "")},"; + lastKey = currentKey; + return result; + }) + .Aggregate((a, b) => a + "\n" + b)); + };*/ + + CommandBuilder.Builder("takeScreenshot", "Enables the screenshot taker") + .Action(() => + { + Game.CurrentGame().EnableScreenshotTaker(); + }).Build(); + + CommandBuilder.Builder("testPositionDefault", "Tests the Block.Position property's default value.") + .Action(() => + { + IEnumerator Loop() + { + for (int i = 0; i < 2; i++) + { + Console.WriteLine("A"); + var block = Block.PlaceNew(BlockIDs.Cube, 1); + Console.WriteLine("B"); + while (!block.Exists) + yield return Yield.It; + Console.WriteLine("C"); + block.Remove(); + Console.WriteLine("D"); + while (block.Exists) + yield return Yield.It; + Console.WriteLine("E - Pos: " + block.Position); + block.Position = 4; + Console.WriteLine("F - Pos: " + block.Position); + } + } + + Loop().RunOn(Scheduler.leanRunner); + }).Build(); + + CommandBuilder.Builder("importAssetBundle") + .Action(() => + { + Logging.CommandLog("Importing asset bundle..."); + var ab = AssetBundle.LoadFromFile( + @"filepath"); + Logging.CommandLog("Imported asset bundle: " + ab); + var assets = ab.LoadAllAssets(); + Logging.CommandLog("Loaded " + assets.Length + " assets"); + foreach (var asset in assets) + { + Logging.CommandLog(asset); + } + }).Build(); +#if TEST + TestRoot.RunTests(); +#endif + } + + private string modsString; + private string InstalledMods() + { + if (modsString != null) return modsString; + StringBuilder sb = new StringBuilder("Installed mods:"); + foreach (var plugin in PluginManager.Plugins) + sb.Append("\n" + plugin.Name + " - " + plugin.Version); + return modsString = sb.ToString(); + } + + public override void OnUpdate() + { + if (UnityEngine.Input.GetKeyDown(KeyCode.End)) + { + Console.WriteLine("Pressed button to toggle console"); + FakeInput.CustomInput(new LocalCosmeticInputEntityComponent {commandLineToggleInput = true}); + } + } + + [HarmonyPatch] + public class MinimumSpecsPatch + { + public static bool Prefix(ref bool __result) + { + __result = true; + return false; + } + + public static MethodInfo TargetMethod() + { + return ((Func) MinimumSpecsCheck.CheckRequirementsMet).Method; + } + } + } +#endif +} diff --git a/GamecraftModdingAPI/Tests/TestRoot.cs b/TechbloxModdingAPI/Tests/TestRoot.cs similarity index 69% rename from GamecraftModdingAPI/Tests/TestRoot.cs rename to TechbloxModdingAPI/Tests/TestRoot.cs index 22f3035..7177818 100644 --- a/GamecraftModdingAPI/Tests/TestRoot.cs +++ b/TechbloxModdingAPI/Tests/TestRoot.cs @@ -8,12 +8,11 @@ using Svelto.Tasks; using Svelto.Tasks.Lean; using Svelto.Tasks.Enumerators; using UnityEngine; +using TechbloxModdingAPI.App; +using TechbloxModdingAPI.Tasks; +using TechbloxModdingAPI.Utility; -using GamecraftModdingAPI.App; -using GamecraftModdingAPI.Tasks; -using GamecraftModdingAPI.Utility; - -namespace GamecraftModdingAPI.Tests +namespace TechbloxModdingAPI.Tests { /// /// API test system root class. @@ -22,7 +21,7 @@ namespace GamecraftModdingAPI.Tests { public static bool AutoShutdown = true; - public const string ReportFile = "GamecraftModdingAPI_tests.log"; + public const string ReportFile = "TechbloxModdingAPI_tests.log"; private static bool _testsPassed = false; @@ -67,7 +66,7 @@ namespace GamecraftModdingAPI.Tests // flow control Game.Enter += (sender, args) => { GameTests().RunOn(RobocraftX.Schedulers.Lean.EveryFrameStepRunner_TimeRunningAndStopped); }; Game.Exit += (s, a) => state = "ReturningFromGame"; - Client.EnterMenu += (sender, args) => + Client.EnterMenu += (sender, args) => { if (state == "EnteringMenu") { @@ -150,71 +149,59 @@ namespace GamecraftModdingAPI.Tests Game currentGame = Game.CurrentGame(); // in-game tests yield return new WaitForSecondsEnumerator(5).Continue(); // wait for game to finish loading - foreach (Type t in testTypes) - { - foreach (MethodBase m in t.GetMethods()) - { - APITestCaseAttribute a = m.GetCustomAttribute(); - if (a != null && a.TestType == TestType.Game) - { - try - { - m.Invoke(null, new object[0]); - } - catch (Exception e) - { - Assert.Fail($"Game test '{m}' raised an exception: {e.ToString()}"); - } - yield return Yield.It; - } - } - } - currentGame.ToggleTimeMode(); - yield return new WaitForSecondsEnumerator(5).Continue(); - // simulation tests - foreach (Type t in testTypes) - { - foreach (MethodBase m in t.GetMethods()) - { - APITestCaseAttribute a = m.GetCustomAttribute(); - if (a != null && a.TestType == TestType.SimulationMode) - { - try - { - m.Invoke(null, new object[0]); - } - catch (Exception e) - { - Assert.Fail($"Simulation test '{m}' raised an exception: {e.ToString()}"); - } - yield return Yield.It; - } - } - } - currentGame.ToggleTimeMode(); - yield return new WaitForSecondsEnumerator(5).Continue(); - // build tests - foreach (Type t in testTypes) - { - foreach (MethodBase m in t.GetMethods()) - { - APITestCaseAttribute a = m.GetCustomAttribute(); - if (a != null && a.TestType == TestType.EditMode) - { + var testTypesToRun = new[] + { + TestType.Game, + TestType.SimulationMode, + TestType.EditMode + }; + for (var index = 0; index < testTypesToRun.Length; index++) + { + foreach (Type t in testTypes) + { + foreach (MethodBase m in t.GetMethods()) + { + APITestCaseAttribute a = m.GetCustomAttribute(); + if (a == null || a.TestType != testTypesToRun[index]) continue; + + object ret = null; try - { - m.Invoke(null, new object[0]); - } - catch (Exception e) - { - Assert.Fail($"Build test '{m}' raised an exception: {e.ToString()}"); - } + { + ret = m.Invoke(null, new object[0]); + } + catch (Exception e) + { + Assert.Fail($"{a.TestType} test '{m}' raised an exception: {e}"); + } + + if (ret is IEnumerator enumerator) + { //Support enumerator methods with added exception handling + bool cont; + do + { //Can't use yield return in a try block... + try + { //And with Continue() exceptions aren't caught + cont = enumerator.MoveNext(); + } + catch (Exception e) + { + Assert.Fail($"{a.TestType} test '{m}' raised an exception: {e}"); + cont = false; + } + + yield return Yield.It; + } while (cont); + } + yield return Yield.It; - } - } - } + } + } + + if (index + 1 < testTypesToRun.Length) //Don't toggle on the last test + currentGame.ToggleTimeMode(); + yield return new WaitForSecondsEnumerator(5).Continue(); + } // exit game - yield return new WaitForSecondsEnumerator(5).Continue(); yield return ReturnToMenu().Continue(); } @@ -276,7 +263,7 @@ namespace GamecraftModdingAPI.Tests /// /// Runs the tests. /// - /// Assembly to search for tests. When set to null, this uses the GamecraftModdingAPI assembly. + /// Assembly to search for tests. When set to null, this uses the TechbloxModdingAPI assembly. public static void RunTests(Assembly asm = null) { if (asm == null) asm = Assembly.GetExecutingAssembly(); @@ -284,8 +271,8 @@ namespace GamecraftModdingAPI.Tests Logging.MetaLog("Starting test run"); // log metadata Assert.Log($"Unity {Application.unityVersion}"); - Assert.Log($"Gamecraft {Application.version}"); - Assert.Log($"GamecraftModdingAPI {Assembly.GetExecutingAssembly().GetName().Version}"); + Assert.Log($"Techblox {Application.version}"); + Assert.Log($"TechbloxModdingAPI {Assembly.GetExecutingAssembly().GetName().Version}"); Assert.Log($"Testing {asm.GetName().Name} {asm.GetName().Version}"); Assert.Log($"START: --- {DateTime.Now.ToString()} --- ({testTypes.Count} tests classes detected)"); StartUp(); diff --git a/GamecraftModdingAPI/Tests/TestTest.cs b/TechbloxModdingAPI/Tests/TestTest.cs similarity index 96% rename from GamecraftModdingAPI/Tests/TestTest.cs rename to TechbloxModdingAPI/Tests/TestTest.cs index 02eeda0..5e0ed46 100644 --- a/GamecraftModdingAPI/Tests/TestTest.cs +++ b/TechbloxModdingAPI/Tests/TestTest.cs @@ -4,7 +4,7 @@ using System.Reflection; using HarmonyLib; -namespace GamecraftModdingAPI.Tests +namespace TechbloxModdingAPI.Tests { #if TEST /// diff --git a/TechbloxModdingAPI/Tests/TestValueAttribute.cs b/TechbloxModdingAPI/Tests/TestValueAttribute.cs new file mode 100644 index 0000000..48794c9 --- /dev/null +++ b/TechbloxModdingAPI/Tests/TestValueAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace TechbloxModdingAPI.Tests +{ + [AttributeUsage(AttributeTargets.Property)] + public class TestValueAttribute : Attribute + { + public object PossibleValue { get; } + + /// + /// + /// When set, the property test accepts the specified value in addition to the test input.
+ /// Useful if setting the property isn't always possible. + /// + ///
+ public TestValueAttribute(object possibleValue) => PossibleValue = possibleValue; + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/ApiExclusiveGroups.cs b/TechbloxModdingAPI/Utility/ApiExclusiveGroups.cs similarity index 58% rename from GamecraftModdingAPI/Utility/ApiExclusiveGroups.cs rename to TechbloxModdingAPI/Utility/ApiExclusiveGroups.cs index 19211e6..a451673 100644 --- a/GamecraftModdingAPI/Utility/ApiExclusiveGroups.cs +++ b/TechbloxModdingAPI/Utility/ApiExclusiveGroups.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Svelto.ECS; -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { public static class ApiExclusiveGroups { @@ -18,7 +18,7 @@ namespace GamecraftModdingAPI.Utility { if (_eventsExclusiveGroup == null) { - _eventsExclusiveGroup = new ExclusiveGroup("GamecraftModdingAPI EventGroup"); + _eventsExclusiveGroup = new ExclusiveGroup("TechbloxModdingAPI EventGroup"); } return _eventsExclusiveGroup; } @@ -34,10 +34,22 @@ namespace GamecraftModdingAPI.Utility { if (_versionGroup == null) { - _versionGroup = new ExclusiveGroup("GamecraftModdingAPI VersionGroup"); + _versionGroup = new ExclusiveGroup("TechbloxModdingAPI VersionGroup"); } return _versionGroup; } - } + } + + private static ExclusiveGroup _customBlockGroup; + + public static ExclusiveGroup customBlockGroup + { + get + { + if (_customBlockGroup == null) + _customBlockGroup = new ExclusiveGroup("TechbloxModdingAPI CustomBlockGroup"); + return _customBlockGroup; + } + } } } diff --git a/GamecraftModdingAPI/Utility/Audio.cs b/TechbloxModdingAPI/Utility/Audio.cs similarity index 67% rename from GamecraftModdingAPI/Utility/Audio.cs rename to TechbloxModdingAPI/Utility/Audio.cs index 9d227a8..71b7f66 100644 --- a/GamecraftModdingAPI/Utility/Audio.cs +++ b/TechbloxModdingAPI/Utility/Audio.cs @@ -6,7 +6,7 @@ using FMODUnity; using RobocraftX.Common.Audio; using Unity.Mathematics; -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { public class Audio { @@ -43,40 +43,24 @@ namespace GamecraftModdingAPI.Utility { get { - sound.getParameterValue(key, out float val, out float finalVal); + sound.getParameterByName(key, out float val, out float finalVal); return val; } - set => sound.setParameterValue(key, value); + set => sound.setParameterByName(key, value); } - public float this[int index] + public float this[PARAMETER_ID index] { get { - sound.getParameterValueByIndex(index, out float val, out float finalVal); + sound.getParameterByID(index, out float val, out float finalVal); return val; } - set => sound.setParameterValueByIndex(index, value); + set => sound.setParameterByID(index, value); } - public string[] Parameters - { - get - { - sound.getParameterCount(out int count); - string[] parameters = new string[count]; - for (int i = 0; i < count; i++) - { - sound.getParameterByIndex(i, out ParameterInstance param); - param.getDescription(out PARAMETER_DESCRIPTION desc); - parameters[i] = desc.name; - } - return parameters; - } - } - public float3 Position { get diff --git a/GamecraftModdingAPI/Utility/AudioTools.cs b/TechbloxModdingAPI/Utility/AudioTools.cs similarity index 98% rename from GamecraftModdingAPI/Utility/AudioTools.cs rename to TechbloxModdingAPI/Utility/AudioTools.cs index 4d4af0d..6f78feb 100644 --- a/GamecraftModdingAPI/Utility/AudioTools.cs +++ b/TechbloxModdingAPI/Utility/AudioTools.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using FMODUnity; using FMOD.Studio; -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { /// /// Common operations on audio objects diff --git a/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs b/TechbloxModdingAPI/Utility/DebugInterfaceEngine.cs similarity index 90% rename from GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs rename to TechbloxModdingAPI/Utility/DebugInterfaceEngine.cs index 0e4d023..7f9d97c 100644 --- a/GamecraftModdingAPI/Utility/DebugInterfaceEngine.cs +++ b/TechbloxModdingAPI/Utility/DebugInterfaceEngine.cs @@ -5,15 +5,15 @@ using System.Reflection; using System.Reflection.Emit; using System.Text; using System.Text.Formatting; -using GamecraftModdingAPI.Blocks; -using GamecraftModdingAPI.Engines; -using GamecraftModdingAPI.Players; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Players; using HarmonyLib; using RobocraftX.GUI.Debug; using Svelto.ECS; using Svelto.ECS.Experimental; +using TechbloxModdingAPI.Engines; -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { public class DebugInterfaceEngine : IApiEngine { @@ -31,8 +31,8 @@ namespace GamecraftModdingAPI.Utility public void SetInfo(string id, Func contentGetter) => _extraInfo[id] = contentGetter; public bool RemoveInfo(string id) => _extraInfo.Remove(id); - public string Name { get; } = "GamecraftModdingAPIDebugInterfaceGameEngine"; - public bool isRemovable { get; } = true; + public string Name => "TechbloxModdingAPIDebugInterfaceGameEngine"; + public bool isRemovable => true; [HarmonyPatch] private class Patch diff --git a/GamecraftModdingAPI/Utility/Dependency.cs b/TechbloxModdingAPI/Utility/Dependency.cs similarity index 98% rename from GamecraftModdingAPI/Utility/Dependency.cs rename to TechbloxModdingAPI/Utility/Dependency.cs index c7dd64d..3f33277 100644 --- a/GamecraftModdingAPI/Utility/Dependency.cs +++ b/TechbloxModdingAPI/Utility/Dependency.cs @@ -3,7 +3,7 @@ using IllusionInjector; using IllusionPlugin; -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { /// /// Simple plugin interaction operations diff --git a/GamecraftModdingAPI/Utility/FullGameFields.cs b/TechbloxModdingAPI/Utility/FullGameFields.cs similarity index 91% rename from GamecraftModdingAPI/Utility/FullGameFields.cs rename to TechbloxModdingAPI/Utility/FullGameFields.cs index 432103d..0a4dd1d 100644 --- a/GamecraftModdingAPI/Utility/FullGameFields.cs +++ b/TechbloxModdingAPI/Utility/FullGameFields.cs @@ -14,12 +14,12 @@ using RobocraftX.Rendering; using Svelto.Context; using Svelto.DataStructures; using Svelto.ECS; -using Svelto.ECS.Schedulers.Unity; +using Svelto.ECS.Schedulers; using UnityEngine; using Unity.Entities; using Unity.Physics.Systems; -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { /// /// Public access to the private variables in RobocraftX.FullGameCompositionRoot @@ -72,14 +72,6 @@ namespace GamecraftModdingAPI.Utility } } - public static SimpleEntitiesSubmissionScheduler _mainGameSubmissionScheduler - { - get - { - return (SimpleEntitiesSubmissionScheduler)fgcr?.Field("_sub").Field("_mainGameSubmissionScheduler").GetValue(); - } - } - public static BuildPhysicsWorld _physicsWorldSystem { get diff --git a/GamecraftModdingAPI/Utility/GameEngineManager.cs b/TechbloxModdingAPI/Utility/GameEngineManager.cs similarity index 59% rename from GamecraftModdingAPI/Utility/GameEngineManager.cs rename to TechbloxModdingAPI/Utility/GameEngineManager.cs index cc4973f..16bf4e2 100644 --- a/GamecraftModdingAPI/Utility/GameEngineManager.cs +++ b/TechbloxModdingAPI/Utility/GameEngineManager.cs @@ -4,11 +4,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using RobocraftX.StateSync; using Svelto.ECS; +using TechbloxModdingAPI.Engines; -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { /// /// Keeps track of custom game-modifying engines @@ -26,10 +26,10 @@ namespace GamecraftModdingAPI.Utility { Logging.MetaDebugLog($"Registering Game IApiEngine {engine.Name}"); _lastEngineRoot.AddEngine(engine); - if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType())) - { - ((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory(); - } + if (engine is IFactoryEngine factoryEngine) + factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory(); + if (engine is IFunEngine funEngine) + funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions(); } } @@ -61,18 +61,23 @@ namespace GamecraftModdingAPI.Utility } } - public static void RegisterEngines(EnginesRoot enginesRoot) + public static void RegisterEngines(StateSyncRegistrationHelper helper) { + var enginesRoot = helper.enginesRoot; _lastEngineRoot = enginesRoot; - IEntityFactory factory = enginesRoot.GenerateEntityFactory(); + IEntityFactory factory = enginesRoot.GenerateEntityFactory(); + IEntityFunctions functions = enginesRoot.GenerateEntityFunctions(); foreach (var key in _gameEngines.Keys) { Logging.MetaDebugLog($"Registering Game IApiEngine {_gameEngines[key].Name}"); - enginesRoot.AddEngine(_gameEngines[key]); - if (typeof(IFactoryEngine).IsAssignableFrom(_gameEngines[key].GetType())) - { - ((IFactoryEngine)_gameEngines[key]).Factory = factory; - } + if (_gameEngines[key] is IDeterministicEngine detEngine) + helper.AddDeterministicEngine(detEngine); + else + enginesRoot.AddEngine(_gameEngines[key]); + if (_gameEngines[key] is IFactoryEngine factEngine) + factEngine.Factory = factory; + if (_gameEngines[key] is IFunEngine funEngine) + funEngine.Functions = functions; } } } diff --git a/GamecraftModdingAPI/Utility/GameState.cs b/TechbloxModdingAPI/Utility/GameState.cs similarity index 85% rename from GamecraftModdingAPI/Utility/GameState.cs rename to TechbloxModdingAPI/Utility/GameState.cs index ed096c4..79a72fe 100644 --- a/GamecraftModdingAPI/Utility/GameState.cs +++ b/TechbloxModdingAPI/Utility/GameState.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { /// - /// Utility to get the state of the current Gamecraft game + /// Utility to get the state of the current Techblox game /// public static class GameState { @@ -34,7 +34,7 @@ namespace GamecraftModdingAPI.Utility /// /// Is a game loaded? /// - /// Whether Gamecraft has a game open (false = Main Menu) + /// Whether Techblox has a game open (false = Main Menu) public static bool IsInGame() { return gameEngine.IsInGame; diff --git a/GamecraftModdingAPI/Utility/GameStateEngine.cs b/TechbloxModdingAPI/Utility/GameStateEngine.cs similarity index 84% rename from GamecraftModdingAPI/Utility/GameStateEngine.cs rename to TechbloxModdingAPI/Utility/GameStateEngine.cs index be20ba1..936a8ef 100644 --- a/GamecraftModdingAPI/Utility/GameStateEngine.cs +++ b/TechbloxModdingAPI/Utility/GameStateEngine.cs @@ -6,14 +6,13 @@ using System.Text; using System.Threading.Tasks; using RobocraftX.SimulationModeState; +using TechbloxModdingAPI.Engines; -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { class GameStateEngine : IApiEngine { - public string Name { get; } = "GamecraftModdingAPIGameStateGameEngine"; + public string Name { get; } = "TechbloxModdingAPIGameStateGameEngine"; public EntitiesDB entitiesDB { set; private get; } diff --git a/GamecraftModdingAPI/Utility/Logging.cs b/TechbloxModdingAPI/Utility/Logging.cs similarity index 77% rename from GamecraftModdingAPI/Utility/Logging.cs rename to TechbloxModdingAPI/Utility/Logging.cs index 80ef323..0147eff 100644 --- a/GamecraftModdingAPI/Utility/Logging.cs +++ b/TechbloxModdingAPI/Utility/Logging.cs @@ -6,11 +6,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { /// - /// Utility class to access Gamecraft's built-in logging capabilities. - /// The log is saved to %APPDATA%\..\LocalLow\FreeJam\Gamecraft\Player.Log + /// Utility class to access Techblox's built-in logging capabilities. + /// The log is saved to %APPDATA%\..\LocalLow\FreeJam\Techblox\Player.Log /// public static class Logging { @@ -21,7 +21,7 @@ namespace GamecraftModdingAPI.Utility } /// - /// Write a regular message to Gamecraft's log + /// Write a regular message to Techblox's log /// /// The object to log [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -37,7 +37,7 @@ namespace GamecraftModdingAPI.Utility } /// - /// Write a debug message to Gamecraft's log + /// Write a debug message to Techblox's log /// /// The object to log [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -53,7 +53,7 @@ namespace GamecraftModdingAPI.Utility } /// - /// Write a debug message and object to Gamecraft's log + /// Write a debug message and object to Techblox's log /// The reason this method exists in Svelto.Console is beyond my understanding /// /// The type of the extra debug object @@ -72,7 +72,7 @@ namespace GamecraftModdingAPI.Utility } /// - /// Write an error message to Gamecraft's log + /// Write an error message to Techblox's log /// /// The object to log /// The extra data to pass to the ILogger @@ -83,7 +83,7 @@ namespace GamecraftModdingAPI.Utility } /// - /// Write an exception to Gamecraft's log and to the screen and exit game + /// Write an exception to Techblox's log and to the screen and exit game /// /// The exception to log /// The extra data to pass to the ILogger. @@ -95,7 +95,7 @@ namespace GamecraftModdingAPI.Utility } /// - /// Write an exception message to Gamecraft's log and to the screen and exit game + /// Write an exception message to Techblox's log and to the screen and exit game /// /// The object to log /// The exception to log @@ -114,7 +114,7 @@ namespace GamecraftModdingAPI.Utility } /// - /// Write a warning message to Gamecraft's log + /// Write a warning message to Techblox's log /// /// The object to log [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -123,28 +123,10 @@ namespace GamecraftModdingAPI.Utility Svelto.Console.LogWarning(obj.ToString()); } - [Obsolete("SystemLog was removed from Svelto.Common")] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SystemLog(string msg) - { - Svelto.Console.Log(msg); - } - - /// - /// Write a message to stdout (ie the terminal, like Command Prompt or PowerShell) - /// - /// The object to log - [Obsolete("SystemLog was removed from Svelto.Common")] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SystemLog(object obj) - { - Svelto.Console.Log(obj.ToString()); - } - // descriptive logging /// - /// Write a descriptive message to Gamecraft's log only when the API is a Debug build + /// Write a descriptive message to Techblox's log only when the API is a Debug build /// /// The object to log [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -156,7 +138,7 @@ namespace GamecraftModdingAPI.Utility } /// - /// Write a descriptive message to Gamecraft's log including the current time and the calling method's name + /// Write a descriptive message to Techblox's log including the current time and the calling method's name /// /// The object to log [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -169,7 +151,7 @@ namespace GamecraftModdingAPI.Utility // CLI logging /// - /// Write a message to Gamecraft's command line + /// Write a message to Techblox's command line /// /// The object to log [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -181,11 +163,11 @@ namespace GamecraftModdingAPI.Utility [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CommandLog(string msg) { - uREPL.Log.Output(msg); + Log(msg); } /// - /// Write an error message to Gamecraft's command line + /// Write an error message to Techblox's command line /// /// The object to log [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -197,11 +179,11 @@ namespace GamecraftModdingAPI.Utility [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CommandLogError(string msg) { - uREPL.Log.Error(msg); + LogError(msg); } /// - /// Write a warning message to Gamecraft's command line + /// Write a warning message to Techblox's command line /// /// The object to log [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -213,7 +195,7 @@ namespace GamecraftModdingAPI.Utility [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CommandLogWarning(string msg) { - uREPL.Log.Warn(msg); + LogWarning(msg); } } diff --git a/TechbloxModdingAPI/Utility/ManagedApiExtensions.cs b/TechbloxModdingAPI/Utility/ManagedApiExtensions.cs new file mode 100644 index 0000000..6829b2d --- /dev/null +++ b/TechbloxModdingAPI/Utility/ManagedApiExtensions.cs @@ -0,0 +1,58 @@ +using Svelto.ECS; +using Svelto.ECS.Hybrid; + +namespace TechbloxModdingAPI.Utility +{ + public static class ManagedApiExtensions + { + /// + /// Attempts to query an entity and returns an optional that contains the result if succeeded. + /// This overload does not take initializer data into account. + /// + /// The entities DB + /// The EGID to query + /// The component type to query + /// An optional that contains the result on success or is empty if not found + public static OptionalRef QueryEntityOptional(this EntitiesDB entitiesDB, EGID egid) + where T : struct, IEntityViewComponent + { + return entitiesDB.TryQueryEntitiesAndIndex(egid, out uint index, out var array) + ? new OptionalRef(array, index) + : new OptionalRef(); + } + + /// + /// Attempts to query an entity and returns the result in an optional reference. + /// + /// The entities DB to query from + /// The ECS object to query + /// The group of the entity if the object can have multiple + /// The component to query + /// A reference to the component or a dummy value + public static OptionalRef QueryEntityOptional(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) + where T : struct, IEntityViewComponent + { + EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); + var opt = QueryEntityOptional(entitiesDB, id); + return opt ? opt : new OptionalRef(obj, false); + } + + /// + /// Attempts to query an entity and returns the result or a dummy value that can be modified. + /// + /// The entities DB to query from + /// The ECS object to query + /// The group of the entity if the object can have multiple + /// The component to query + /// A reference to the component or a dummy value + public static ref T QueryEntityOrDefault(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) + where T : struct, IEntityViewComponent + { + EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); + var opt = QueryEntityOptional(entitiesDB, id); + if (opt) return ref opt.Get(); + if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrCreate(); + return ref opt.Get(); //Default value + } + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Utility/MenuEngineManager.cs b/TechbloxModdingAPI/Utility/MenuEngineManager.cs similarity index 75% rename from GamecraftModdingAPI/Utility/MenuEngineManager.cs rename to TechbloxModdingAPI/Utility/MenuEngineManager.cs index 9be203d..4358ca8 100644 --- a/GamecraftModdingAPI/Utility/MenuEngineManager.cs +++ b/TechbloxModdingAPI/Utility/MenuEngineManager.cs @@ -5,10 +5,9 @@ using System.Text; using System.Threading.Tasks; using Svelto.ECS; +using TechbloxModdingAPI.Engines; -using GamecraftModdingAPI.Engines; - -namespace GamecraftModdingAPI.Utility +namespace TechbloxModdingAPI.Utility { /// /// Keeps track of custom menu-modifying engines @@ -27,10 +26,10 @@ namespace GamecraftModdingAPI.Utility { Logging.MetaDebugLog($"Registering Menu IApiEngine {engine.Name}"); _lastEngineRoot.AddEngine(engine); - if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType())) - { - ((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory(); - } + if (engine is IFactoryEngine factoryEngine) + factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory(); + if (engine is IFunEngine funEngine) + funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions(); } } @@ -66,14 +65,13 @@ namespace GamecraftModdingAPI.Utility { _lastEngineRoot = enginesRoot; IEntityFactory factory = enginesRoot.GenerateEntityFactory(); + IEntityFunctions functions = enginesRoot.GenerateEntityFunctions(); foreach (var key in _menuEngines.Keys) { Logging.MetaDebugLog($"Registering Menu IApiEngine {_menuEngines[key].Name}"); enginesRoot.AddEngine(_menuEngines[key]); - if (typeof(IFactoryEngine).IsAssignableFrom(_menuEngines[key].GetType())) - { - ((IFactoryEngine)_menuEngines[key]).Factory = factory; - } + if (_menuEngines[key] is IFactoryEngine factEngine) factEngine.Factory = factory; + if(_menuEngines[key] is IFunEngine funEngine) funEngine.Functions = functions; } } } diff --git a/TechbloxModdingAPI/Utility/NativeApiExtensions.cs b/TechbloxModdingAPI/Utility/NativeApiExtensions.cs new file mode 100644 index 0000000..ff5ca45 --- /dev/null +++ b/TechbloxModdingAPI/Utility/NativeApiExtensions.cs @@ -0,0 +1,62 @@ +using Svelto.ECS; + +namespace TechbloxModdingAPI.Utility +{ + public static class NativeApiExtensions + { + /// + /// Attempts to query an entity and returns an optional that contains the result if succeeded. + /// This overload does not take initializer data into account. + /// + /// The entities DB + /// The EGID to query + /// The component type to query + /// An optional that contains the result on success or is empty if not found + public static OptionalRef QueryEntityOptional(this EntitiesDB entitiesDB, EGID egid) + where T : unmanaged, IEntityComponent + { + return entitiesDB.TryQueryEntitiesAndIndex(egid, out uint index, out var array) + ? new OptionalRef(array, index) + : new OptionalRef(); + } + + /// + /// Attempts to query an entity and returns the result in an optional reference. + /// + /// The entities DB to query from + /// The ECS object to query + /// The group of the entity if the object can have multiple + /// The component to query + /// A reference to the component or a dummy value + public static OptionalRef QueryEntityOptional(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) + where T : unmanaged, IEntityComponent + { + EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); + var opt = QueryEntityOptional(entitiesDB, id); + return opt ? opt : new OptionalRef(obj, true); + } + + /// + /// Attempts to query an entity and returns the result or a dummy value that can be modified. + /// + /// The entities DB to query from + /// The ECS object to query + /// The group of the entity if the object can have multiple + /// The component to query + /// A reference to the component or a dummy value + public static ref T QueryEntityOrDefault(this EntitiesDB entitiesDB, EcsObjectBase obj, ExclusiveGroupStruct group = default) + where T : unmanaged, IEntityComponent + { + EGID id = group == ExclusiveGroupStruct.Invalid ? obj.Id : new EGID(obj.Id.entityID, group); + var opt = QueryEntityOptional(entitiesDB, id); + if (opt) return ref opt.Get(); + if (obj.InitData.Valid) return ref obj.InitData.Initializer(id).GetOrCreate(); + /*if (!obj.InitData.Valid) return ref opt.Get(); //Default value + var init = obj.InitData.Initializer(id); + // Do not create the component if missing, as that can trigger Add() listeners that, in some cases, may be + // invalid if (ab)using the classes in an unusual way - TODO: Check entity descriptor or something + if (init.Has()) return ref init.Get();*/ + return ref opt.Get(); //Default value + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Utility/OptionalRef.cs b/TechbloxModdingAPI/Utility/OptionalRef.cs new file mode 100644 index 0000000..c83e0da --- /dev/null +++ b/TechbloxModdingAPI/Utility/OptionalRef.cs @@ -0,0 +1,94 @@ +using System; +using Svelto.DataStructures; +using Svelto.ECS; + +namespace TechbloxModdingAPI.Utility +{ + public ref struct OptionalRef where T : struct, IEntityComponent + { + private readonly State state; + private readonly uint index; + private NB array; + private MB managedArray; + private readonly EntityInitializer initializer; + //The possible fields are: (index && (array || managedArray)) || initializer + + public OptionalRef(NB array, uint index) + { + state = State.Native; + this.array = array; + this.index = index; + initializer = default; + } + + public OptionalRef(MB array, uint index) + { + state = State.Managed; + managedArray = array; + this.index = index; + initializer = default; + this.array = default; + } + + /// + /// Wraps the initializer data, if present. + /// + /// The object with the initializer + /// Whether the struct is unmanaged + public OptionalRef(EcsObjectBase obj, bool unmanaged) + { + if (obj.InitData.Valid) + { + initializer = obj.InitData.Initializer(obj.Id); + state = (unmanaged ? State.Native : State.Managed) | State.Initializer; + } + else + { + initializer = default; + state = State.Empty; + } + array = default; + index = default; + } + + /// + /// Returns the value or a default value if empty. Supports objects that are being initialized. + /// + /// The value or the default value + public ref T Get() + { + CompRefCache.Default = default; //The default value can be changed by mods + if (state == State.Empty) return ref CompRefCache.Default; + if ((state & State.Initializer) != State.Empty) return ref initializer.GetOrCreate(); + if ((state & State.Native) != State.Empty) return ref array[index]; + return ref managedArray[index]; + } + + public bool Exists => state != State.Empty; + public T? Nullable() => this ? Get() : default; + + public static implicit operator T(OptionalRef opt) => opt.Get(); + + public static implicit operator bool(OptionalRef opt) => opt.state != State.Empty; + + /// + /// Creates an instance of a struct T that can be referenced. + /// + private struct CompRefCache + { + public static T Default; + } + + /// + /// A byte that holds state in its bits. + /// + [Flags] + private enum State : byte + { + Empty, + Native, + Managed, + Initializer = 4 + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Utility/WeakDictionary.cs b/TechbloxModdingAPI/Utility/WeakDictionary.cs new file mode 100644 index 0000000..287b044 --- /dev/null +++ b/TechbloxModdingAPI/Utility/WeakDictionary.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace TechbloxModdingAPI.Utility +{ + public class WeakDictionary : IDictionary where TValue : class + { + private Dictionary> _dictionary = new Dictionary>(); + + public IEnumerator> GetEnumerator() + { + using var enumerator = _dictionary.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (enumerator.Current.Value.TryGetTarget(out var value)) + yield return new KeyValuePair(enumerator.Current.Key, value); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + _dictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return TryGetValue(item.Key, out var value) && item.Value == value; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new System.NotImplementedException(); + } + + public bool Remove(KeyValuePair item) + { + return Contains(item) && Remove(item.Key); + } + + public int Count => _dictionary.Count; + public bool IsReadOnly => false; + + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + public void Add(TKey key, TValue value) + { + _dictionary.Add(key, new WeakReference(value)); + } + + public bool Remove(TKey key) + { + return _dictionary.Remove(key); + } + + public bool TryGetValue(TKey key, out TValue value) + { + value = null; + return _dictionary.TryGetValue(key, out var reference) && reference.TryGetTarget(out value); + } + + public TValue this[TKey key] + { + get => TryGetValue(key, out var value) + ? value + : throw new KeyNotFoundException($"Key {key} not found in WeakDictionary."); + set => _dictionary[key] = new WeakReference(value); + } + + public ICollection Keys => _dictionary.Keys; + public ICollection Values => new ValueCollection(this); + + public class ValueCollection : ICollection, IReadOnlyCollection + { + private WeakDictionary _dictionary; + internal ValueCollection(WeakDictionary dictionary) + { + _dictionary = dictionary; + } + + public IEnumerator GetEnumerator() + { + using var enumerator = _dictionary.GetEnumerator(); + while (enumerator.MoveNext()) + { + yield return enumerator.Current.Value; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(TValue item) + { + throw new NotSupportedException("The value collection is read only."); + } + + public void Clear() + { + throw new NotSupportedException("The value collection is read only."); + } + + public bool Contains(TValue item) + { + return _dictionary.Any(kv => kv.Value == item); + } + + public void CopyTo(TValue[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool Remove(TValue item) + { + throw new NotSupportedException("The value collection is read only."); + } + + public int Count => _dictionary.Count; + public bool IsReadOnly => true; + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Utility/WrappedHandler.cs b/TechbloxModdingAPI/Utility/WrappedHandler.cs new file mode 100644 index 0000000..57ff3ba --- /dev/null +++ b/TechbloxModdingAPI/Utility/WrappedHandler.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using TechbloxModdingAPI.Events; + +namespace TechbloxModdingAPI.Utility +{ + /// + /// Wraps the event handler in a try-catch block to avoid propagating exceptions. + /// + /// The event arguments type + public struct WrappedHandler + { + private EventHandler eventHandler; + + /// + /// Store wrappers so we can unregister them properly + /// + private static Dictionary, EventHandler> wrappers = + new Dictionary, EventHandler>(); + + public static WrappedHandler operator +(WrappedHandler original, EventHandler added) + { + EventHandler wrapped = (sender, e) => + { + try + { + added(sender, e); + } + catch (Exception e1) + { + EventRuntimeException wrappedException = + new EventRuntimeException($"EventHandler with arg type {typeof(T).Name} threw an exception", + e1); + Logging.LogWarning(wrappedException.ToString()); + } + }; + wrappers.Add(added, wrapped); + return new WrappedHandler { eventHandler = original.eventHandler + wrapped }; + } + + public static WrappedHandler operator -(WrappedHandler original, EventHandler removed) + { + if (!wrappers.TryGetValue(removed, out var wrapped)) return original; + wrappers.Remove(removed); + return new WrappedHandler { eventHandler = original.eventHandler - wrapped }; + } + + public void Invoke(object sender, T args) + { + eventHandler?.Invoke(sender, args); + } + } +} \ No newline at end of file diff --git a/doxygen.conf b/doxygen.conf index bf447b4..2747deb 100644 --- a/doxygen.conf +++ b/doxygen.conf @@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "GamecraftModdingAPI" +PROJECT_NAME = "TechbloxModdingAPI" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "v1.5.0" +PROJECT_NUMBER = "v2.0.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "The unofficial Gamecraft modding API" +PROJECT_BRIEF = "The unofficial Techblox modding API" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -829,7 +829,7 @@ WARN_LOGFILE = dox.log # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT =./GamecraftModdingAPI/ +INPUT =./TechbloxModdingAPI/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -917,7 +917,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = ./GamecraftModdingAPI/bin ./GamecraftModdingAPI/obj +EXCLUDE = ./TechbloxModdingAPI/bin ./TechbloxModdingAPI/obj # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded