diff --git a/Automation/gen_csproj.py b/Automation/gen_csproj.py old mode 100644 new mode 100755 diff --git a/GamecraftModdingAPI/App/AppEngine.cs b/GamecraftModdingAPI/App/AppEngine.cs index 4f20b7e..9cc454d 100644 --- a/GamecraftModdingAPI/App/AppEngine.cs +++ b/GamecraftModdingAPI/App/AppEngine.cs @@ -46,11 +46,12 @@ namespace GamecraftModdingAPI.App 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 {mgsevs[i].GameName}"); - games[i] = new Game(mgsevs[i].ID); + Utility.Logging.MetaDebugLog($"Found game named {mgsevsB[i].GameName}"); + games[i] = new Game(mgsevsB[i].ID); } return games; } diff --git a/GamecraftModdingAPI/App/CurrentGameMode.cs b/GamecraftModdingAPI/App/CurrentGameMode.cs new file mode 100644 index 0000000..84a6d78 --- /dev/null +++ b/GamecraftModdingAPI/App/CurrentGameMode.cs @@ -0,0 +1,23 @@ +namespace GamecraftModdingAPI.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/GamecraftModdingAPI/App/Game.cs index d6a4eff..3d73478 100644 --- a/GamecraftModdingAPI/App/Game.cs +++ b/GamecraftModdingAPI/App/Game.cs @@ -335,6 +335,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. diff --git a/GamecraftModdingAPI/App/GameGameEngine.cs b/GamecraftModdingAPI/App/GameGameEngine.cs index a616df5..85fb672 100644 --- a/GamecraftModdingAPI/App/GameGameEngine.cs +++ b/GamecraftModdingAPI/App/GameGameEngine.cs @@ -52,7 +52,7 @@ namespace GamecraftModdingAPI.App { if (async) { - ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING); + ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_TimeRunningAndStopped); } else { @@ -102,16 +102,27 @@ namespace GamecraftModdingAPI.App if (filter == BlockIDs.Invalid) { foreach (var (blocks, _) in allBlocks) - foreach (var block in blocks) - blockEGIDs.Add(block.ID); + { + 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) - foreach (var block in blocks) - if (block.DBID == (ulong) filter) - blockEGIDs.Add(block.ID); + { + 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 index 74bc42a..efcb73e 100644 --- a/GamecraftModdingAPI/App/GameMenuEngine.cs +++ b/GamecraftModdingAPI/App/GameMenuEngine.cs @@ -61,12 +61,13 @@ namespace GamecraftModdingAPI.App 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 (games[i].ID.entityID > max) + if (gamesB[i].ID.entityID > max) { - max = games[i].ID.entityID; + max = gamesB[i].ID.entityID; } } return max; @@ -118,11 +119,12 @@ namespace GamecraftModdingAPI.App { EntityCollection entities = entitiesDB.QueryEntities(MyGamesScreenExclusiveGroups.GameSlotGuiEntities); + var entitiesB = entities.ToBuffer().buffer; for (int i = 0; i < entities.count; i++) { - if (entities[i].ID.entityID == id.entityID) + if (entitiesB[i].ID.entityID == id.entityID) { - return ref entities[i]; + return ref entitiesB[i]; } } MyGamesSlotEntityViewStruct[] defRef = new MyGamesSlotEntityViewStruct[1]; diff --git a/GamecraftModdingAPI/Block.cs b/GamecraftModdingAPI/Block.cs index b901f81..524847d 100644 --- a/GamecraftModdingAPI/Block.cs +++ b/GamecraftModdingAPI/Block.cs @@ -3,12 +3,12 @@ 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 Unity.Entities; using Gamecraft.Blocks.GUI; using GamecraftModdingAPI.Blocks; @@ -29,6 +29,7 @@ namespace GamecraftModdingAPI 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(); @@ -36,16 +37,11 @@ namespace GamecraftModdingAPI /// 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. - /// - /// When placing multiple blocks, do not access properties immediately after creation as this - /// triggers a sync each time which can affect performance and may cause issues with the game. - /// You may either use AsyncUtils.WaitForSubmission() after placing all of the blocks - /// or simply access the block properties which will trigger the synchronization the first time a property is used. /// /// The block's type /// The block's color /// The block color's darkness (0-9) - 0 is default color - /// The block's position in the grid - default block size is 0.2 + /// 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 @@ -66,7 +62,7 @@ namespace GamecraftModdingAPI /// The block's type /// The block's color /// The block color's darkness (0-9) - 0 is default color - /// The block's position in the grid - default block size is 0.2 + /// 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 @@ -118,25 +114,35 @@ namespace GamecraftModdingAPI private static Dictionary> initializers = new Dictionary>(); - private static Dictionary typeToGroup = - new Dictionary + private static Dictionary typeToGroup = + new Dictionary { - {typeof(ConsoleBlock), new[] {CommonExclusiveGroups.BUILD_CONSOLE_BLOCK_GROUP}}, - {typeof(LogicGate), new [] {CommonExclusiveGroups.BUILD_LOGIC_BLOCK_GROUP}}, - {typeof(Motor), new[] {CommonExclusiveGroups.BUILD_MOTOR_BLOCK_GROUP}}, - {typeof(MusicBlock), new[] {CommonExclusiveGroups.BUILD_MUSIC_BLOCK_GROUP}}, - {typeof(Piston), new[] {CommonExclusiveGroups.BUILD_PISTON_BLOCK_GROUP}}, - {typeof(Servo), new[] {CommonExclusiveGroups.BUILD_SERVO_BLOCK_GROUP}}, + {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.BUILD_SPAWNPOINT_BLOCK_GROUP, - CommonExclusiveGroups.BUILD_BUILDINGSPAWN_BLOCK_GROUP + CommonExclusiveGroups.SPAWNPOINT_BLOCK_GROUP, + CommonExclusiveGroups.BUILDINGSPAWN_BLOCK_GROUP } }, - {typeof(TextBlock), new[] {CommonExclusiveGroups.BUILD_TEXT_BLOCK_GROUP}}, - {typeof(Timer), new[] {CommonExclusiveGroups.BUILD_TIMER_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}} }; /// @@ -184,7 +190,7 @@ namespace GamecraftModdingAPI type); ILGenerator il = dynamic.GetILGenerator(); - il.DeclareLocal(type); + //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 @@ -224,6 +230,7 @@ namespace GamecraftModdingAPI public EGID Id { get; } internal BlockEngine.BlockInitData InitData; + private EGID copiedFrom; /// /// The block's current position or zero if the block no longer exists. @@ -235,6 +242,9 @@ namespace GamecraftModdingAPI set { MovementEngine.MoveBlock(Id, InitData, value); + if (blockGroup != null) + blockGroup.PosAndRotCalculated = false; + BlockEngine.UpdateDisplayedBlock(Id); } } @@ -247,6 +257,9 @@ namespace GamecraftModdingAPI set { RotationEngine.RotateBlock(Id, InitData, value); + if (blockGroup != null) + blockGroup.PosAndRotCalculated = false; + BlockEngine.UpdateDisplayedBlock(Id); } } @@ -262,6 +275,7 @@ namespace GamecraftModdingAPI BlockEngine.SetBlockInfo(this, (ref ScalingEntityStruct st, float3 val) => st.scale = val, value); if (!Exists) return; //UpdateCollision needs the block to exist ScalingEngine.UpdateCollision(Id); + BlockEngine.UpdateDisplayedBlock(Id); } } @@ -307,9 +321,10 @@ namespace GamecraftModdingAPI BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, BlockColor val) => { color.indexInPalette = (byte) (val.Color + val.Darkness * 10); - color.overridePaletteColour = false; - color.needsUpdate = true; - BlockEngine.SetBlockColorFromPalette(ref color); + //color.overridePaletteColour = false; + //color.needsUpdate = true; + color.hasNetworkChange = true; + color.paletteColour = BlockEngine.ConvertBlockColor(color.indexInPalette); }, value); } } @@ -319,14 +334,15 @@ namespace GamecraftModdingAPI /// public float4 CustomColor { - get => BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.overriddenColour); + get => BlockEngine.GetBlockInfo(this, (ColourParameterEntityStruct st) => st.paletteColour); set { BlockEngine.SetBlockInfo(this, (ref ColourParameterEntityStruct color, float4 val) => { - color.overriddenColour = val; - color.overridePaletteColour = true; - color.needsUpdate = true; + color.paletteColour = val; + //color.overridePaletteColour = true; + //color.needsUpdate = true; + color.hasNetworkChange = true; }, value); } } @@ -347,6 +363,41 @@ namespace GamecraftModdingAPI } } + 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.
+ /// 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; + return blockGroup = BlockEngine.GetBlockInfo(this, + (BlockGroupEntityComponent bgec) => + 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); + 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. @@ -368,11 +419,34 @@ namespace GamecraftModdingAPI /// 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. + /// 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) => new SimBody(st.machineRigidBodyId, st.clusterId)); + (GridConnectionsEntityStruct st) => 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 T Copy() where T : Block + { + var block = PlaceNew(Type, Position, Rotation, Color.Color, Color.Darkness, UniformScale, Scale); + block.copiedFrom = Id; + if (Type == BlockIDs.ConsoleBlock + && (this is ConsoleBlock srcCB || (srcCB = Specialise()) != null) + && (block is ConsoleBlock dstCB || (dstCB = block.Specialise()) != null)) + { + //Console block properties are set by a separate engine in the game + dstCB.Arg1 = srcCB.Arg1; + dstCB.Arg2 = srcCB.Arg2; + dstCB.Arg3 = srcCB.Arg3; + dstCB.Command = srcCB.Command; + } + return block; } private void OnPlacedInit(object sender, BlockPlacedRemovedEventArgs e) @@ -380,6 +454,8 @@ namespace GamecraftModdingAPI 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 != EGID.Empty) + BlockCloneEngine.CopyBlockStats(copiedFrom, Id); } public override string ToString() @@ -422,6 +498,7 @@ namespace GamecraftModdingAPI GameEngineManager.AddGameEngine(BlockEventsEngine); GameEngineManager.AddGameEngine(ScalingEngine); GameEngineManager.AddGameEngine(SignalEngine); + GameEngineManager.AddGameEngine(BlockCloneEngine); Wire.signalEngine = SignalEngine; // requires same functionality, no need to duplicate the engine } @@ -439,7 +516,12 @@ namespace GamecraftModdingAPI //Lets improve that using delegates var block = New(Id.entityID, Id.groupID); - block.InitData = this.InitData; + if (this.InitData.Group != null) + { + block.InitData = this.InitData; + Placed += block.OnPlacedInit; //Reset InitData of new object + } + return block; } @@ -452,9 +534,5 @@ namespace GamecraftModdingAPI } } #endif - internal static void Setup(World physicsWorld) - { - ScalingEngine.Setup(physicsWorld.EntityManager); - } } } \ No newline at end of file diff --git a/GamecraftModdingAPI/BlockGroup.cs b/GamecraftModdingAPI/BlockGroup.cs new file mode 100644 index 0000000..c1add9a --- /dev/null +++ b/GamecraftModdingAPI/BlockGroup.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using Gamecraft.Blocks.BlockGroups; +using Unity.Mathematics; +using UnityEngine; + +using GamecraftModdingAPI.Blocks; +using GamecraftModdingAPI.Utility; + +namespace GamecraftModdingAPI +{ + /// + /// A group of blocks that can be selected together. The placed version of blueprints. Dispose after usage. + /// + public class BlockGroup : 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) + { + 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). Can only be used after initialization is complete. + /// + public float3 Position + { + get + { + if (!PosAndRotCalculated) + Refresh(); + return position; + } + set + { + var diff = value - position; + foreach (var block in blocks) + block.Position += diff; + if (!PosAndRotCalculated) //The condition can only be true if a block has been added/removed manually + Refresh(); //So the blocks array is up to date + else + position += diff; + } + } + + /// + /// The rotation of the block group. Can only be used after initialization is complete. + /// + public float3 Rotation + { + get + { + if (!PosAndRotCalculated) + Refresh(); + return rotation; + } + set + { + var diff = value - rotation; + var qdiff = Quaternion.Euler(diff); + foreach (var block in blocks) + { + block.Rotation += diff; + block.Position = qdiff * block.Position; + } + if (!PosAndRotCalculated) + Refresh(); + else + rotation += diff; + } + } + + /*/// + /// Removes all of the blocks in this group from the world. + /// + public void RemoveBlocks() + { + _engine.RemoveBlockGroup(Id); - TODO: Causes a hard crash + }*/ + + /// + /// 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 can be added to groups. + ///
+ /// The block to add + /// A new block group containing the given block + public static BlockGroup Create(Block block) + { + var bg = new BlockGroup(_engine.CreateBlockGroup(block.Position, Quaternion.Euler(block.Rotation)), block); + block.BlockGroup = bg; + return bg; + } + + /// + /// Collects each block that is a part of this group. Also sets the position and rotation. + /// + /// An array of blocks + private Block[] GetBlocks() + { + if (!sourceBlock.Exists) return new[] {sourceBlock}; //The block must exist to get the others + var ret = _engine.GetBlocksFromGroup(sourceBlock.Id, out var pos, out var rot); + position = pos; + rotation = ((Quaternion) rot).eulerAngles; + PosAndRotCalculated = true; + return ret; + } + + private void Refresh() + { + blocks.Clear(); + blocks.AddRange(GetBlocks()); + } + + internal static void Init() + { + GameEngineManager.AddGameEngine(_engine); + } + + public IEnumerator GetEnumerator() => blocks.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => blocks.GetEnumerator(); + + /// + /// Adds a block to the group. You can only add newly placed blocks + /// so that the game initializes the group membership properly. + /// + /// + /// + public void Add(Block item) + { + if (item == null) throw new NullReferenceException("Cannot add null to a block group"); + item.BlockGroup = this; //Calls AddInternal + } + + internal void AddInternal(Block item) + { + blocks.Add(item); + _engine.AddBlockToGroup(item.Id, Id); + } + + /// + /// Removes all blocks from this group. + /// You cannot remove blocks that have been initialized, only those that you placed recently. + /// + public void Clear() + { + while (blocks.Count > 0) + Remove(blocks[blocks.Count - 1]); + } + + public bool Contains(Block item) => blocks.Contains(item); + public void CopyTo(Block[] array, int arrayIndex) => blocks.CopyTo(array, arrayIndex); + + /// + /// Removes a block from this group. + /// You cannot remove blocks that have been initialized, only those that you placed recently. + /// + /// + /// + /// + public bool Remove(Block item) + { + if (item == null) throw new NullReferenceException("Cannot remove null from a block group"); + bool ret = item.BlockGroup == this; + if (ret) + item.BlockGroup = null; //Calls RemoveInternal + return ret; + } + + internal void RemoveInternal(Block item) => blocks.Remove(item); + + public int Count => blocks.Count; + 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/GamecraftModdingAPI/Blocks/BlockCloneEngine.cs b/GamecraftModdingAPI/Blocks/BlockCloneEngine.cs new file mode 100644 index 0000000..0acf44d --- /dev/null +++ b/GamecraftModdingAPI/Blocks/BlockCloneEngine.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Gamecraft.Wires; +using GamecraftModdingAPI.Engines; +using HarmonyLib; +using RobocraftX.Blocks; +using RobocraftX.Character; +using RobocraftX.Common; +using RobocraftX.Common.Players; +using Svelto.DataStructures; +using Svelto.ECS; + +namespace GamecraftModdingAPI.Blocks +{ + 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.placedBlockTweaksCopied = false; + 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.placedBlockTweaksCopied = 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; } = "GamecraftModdingAPIBlockCloneGameEngine"; + public bool isRemovable { get; } = false; + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Blocks/BlockColor.cs b/GamecraftModdingAPI/Blocks/BlockColor.cs index d7ada0d..bf22090 100644 --- a/GamecraftModdingAPI/Blocks/BlockColor.cs +++ b/GamecraftModdingAPI/Blocks/BlockColor.cs @@ -1,10 +1,17 @@ -namespace GamecraftModdingAPI.Blocks +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) @@ -14,6 +21,8 @@ } 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); } @@ -21,10 +30,14 @@ 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}"; diff --git a/GamecraftModdingAPI/Blocks/BlockEngine.cs b/GamecraftModdingAPI/Blocks/BlockEngine.cs index 2622d41..a365d54 100644 --- a/GamecraftModdingAPI/Blocks/BlockEngine.cs +++ b/GamecraftModdingAPI/Blocks/BlockEngine.cs @@ -2,15 +2,19 @@ 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.GUI.Hotbar.Colours; using RobocraftX.Physics; -using RobocraftX.Scene.Simulation; +using RobocraftX.Rendering; +using Svelto.ECS.EntityStructs; + using Svelto.DataStructures; using Svelto.ECS; using Svelto.ECS.Hybrid; +using Unity.Mathematics; using GamecraftModdingAPI.Engines; @@ -42,8 +46,14 @@ namespace GamecraftModdingAPI.Blocks FasterList cubes = new FasterList(10); var coll = entitiesDB.QueryEntities(); foreach (var (ecoll, _) in coll) - foreach (ref var conn in ecoll) - conn.isProcessed = false; + { + 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; }); @@ -54,12 +64,10 @@ namespace GamecraftModdingAPI.Blocks return ret; } - public void SetBlockColorFromPalette(ref ColourParameterEntityStruct color) - { - ref var paletteEntry = ref entitiesDB.QueryEntity(color.indexInPalette, - CommonExclusiveGroups.COLOUR_PALETTE_GROUP); - color.paletteColour = paletteEntry.Colour; - } + 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 { @@ -69,17 +77,17 @@ namespace GamecraftModdingAPI.Blocks return ref structHolder[0]; //Gets a default value automatically } - public ref T GetBlockInfoViewStruct(EGID blockID) where T : struct, INeedEGID, IEntityComponent + 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 - EntityCollection entities = entitiesDB.QueryEntities(blockID.groupID); + BT> entities = entitiesDB.QueryEntities(blockID.groupID).ToBuffer(); for (int i = 0; i < entities.count; i++) { - if (entities[i].ID == blockID) + if (entities.buffer[i].ID == blockID) { - return ref entities[i]; + return ref entities.buffer[i]; } } } @@ -143,6 +151,15 @@ namespace GamecraftModdingAPI.Blocks } } + public void UpdateDisplayedBlock(EGID id) + { + if (!BlockExists(id)) return; + var pos = entitiesDB.QueryEntity(id); + var rot = entitiesDB.QueryEntity(id); + var scale = entitiesDB.QueryEntity(id); + entitiesDB.QueryEntity(id).matrix = float4x4.TRS(pos.position, rot.rotation, scale.scale); + } + public bool BlockExists(EGID blockID) { return entitiesDB.Exists(blockID); @@ -161,43 +178,55 @@ namespace GamecraftModdingAPI.Blocks public SimBody[] GetSimBodiesFromID(byte id) { var ret = new FasterList(4); - if (!entitiesDB.HasAny(CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP)) - return new SimBody[0]; - var oids = entitiesDB.QueryEntities(CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP); - var connections = entitiesDB.QueryMappedEntities(CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP); - foreach (ref ObjectIdEntityStruct oid in oids) + var oide = entitiesDB.QueryEntities(); + EGIDMapper? connections = null; + foreach (var ((oids, count), _) in oide) { - 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 + 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: ; } - 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.BUILD_OBJID_BLOCK_GROUP)) - return new ObjectIdentifier[0]; - var oids = entitiesDB.QueryEntities(CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP); - foreach (ref ObjectIdEntityStruct oid in oids) - if (sim ? oid.simObjectId == id : oid.objectId == id) - ret.Add(new ObjectIdentifier(oid.ID)); + var oide = entitiesDB.QueryEntities(); + foreach (var ((oids, count), _) in oide) + { + for (int i = 0; i < count; i++) + { + ref ObjectIdEntityStruct oid = ref oids[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); + var joints = entitiesDB.QueryEntities(MachineSimulationGroups.JOINTS_GROUP).ToBuffer(); var list = new FasterList(4); - foreach (var joint in joints) + 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)); @@ -212,14 +241,16 @@ namespace GamecraftModdingAPI.Blocks var bodies = new HashSet(); foreach (var (coll, _) in groups) { - foreach (var conn in coll) + 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)).ToArray(); + return bodies.Select(id => new SimBody(id, cid)).ToArray(); } public EGID? FindBlockEGID(uint id) @@ -239,9 +270,12 @@ namespace GamecraftModdingAPI.Blocks var groups = entitiesDB.QueryEntities(); foreach (var (coll, _) in groups) { - foreach (var conn in coll) + var array = coll.ToBuffer().buffer; + for (var index = 0; index < array.capacity; index++) { - if (conn.machineRigidBodyId == sbid) + 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); } } @@ -249,6 +283,24 @@ namespace GamecraftModdingAPI.Blocks 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() { diff --git a/GamecraftModdingAPI/Blocks/BlockEngineInit.cs b/GamecraftModdingAPI/Blocks/BlockEngineInit.cs index 70f713a..1bf6c15 100644 --- a/GamecraftModdingAPI/Blocks/BlockEngineInit.cs +++ b/GamecraftModdingAPI/Blocks/BlockEngineInit.cs @@ -14,10 +14,10 @@ namespace GamecraftModdingAPI.Blocks ///
internal struct BlockInitData { - public FasterDictionary, ITypeSafeDictionary> Group; + public FasterDictionary Group; } - internal delegate FasterDictionary, ITypeSafeDictionary> GetInitGroup( + internal delegate FasterDictionary GetInitGroup( EntityComponentInitializer initializer); /// diff --git a/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs b/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs index 1e5ce21..d1c2639 100644 --- a/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs +++ b/GamecraftModdingAPI/Blocks/BlockEventsEngine.cs @@ -5,10 +5,11 @@ using Svelto.ECS; using GamecraftModdingAPI.Engines; using GamecraftModdingAPI.Utility; +using RobocraftX.Blocks; namespace GamecraftModdingAPI.Blocks { - public class BlockEventsEngine : IReactionaryEngine + public class BlockEventsEngine : IReactionaryEngine { public event EventHandler Placed; public event EventHandler Removed; @@ -27,20 +28,20 @@ namespace GamecraftModdingAPI.Blocks public bool isRemovable { get; } = false; private bool shouldAddRemove; - public void Add(ref DBEntityStruct entityComponent, EGID egid) + public void Add(ref BlockTagEntityStruct entityComponent, EGID egid) { if (!(shouldAddRemove = !shouldAddRemove)) return; ExceptionUtil.InvokeEvent(Placed, this, - new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID}); + new BlockPlacedRemovedEventArgs {ID = egid}); } - public void Remove(ref DBEntityStruct entityComponent, EGID egid) + public void Remove(ref BlockTagEntityStruct entityComponent, EGID egid) { if (!(shouldAddRemove = !shouldAddRemove)) return; ExceptionUtil.InvokeEvent(Removed, this, - new BlockPlacedRemovedEventArgs {ID = egid, Type = (BlockIDs) entityComponent.DBID}); + new BlockPlacedRemovedEventArgs {ID = egid}); } } diff --git a/GamecraftModdingAPI/Blocks/BlockIDs.cs b/GamecraftModdingAPI/Blocks/BlockIDs.cs index 33f9522..9591ae2 100644 --- a/GamecraftModdingAPI/Blocks/BlockIDs.cs +++ b/GamecraftModdingAPI/Blocks/BlockIDs.cs @@ -228,7 +228,7 @@ namespace GamecraftModdingAPI.Blocks ObjectiveHUD, GameStatsHUD, //231 GameOverBlock, - SFXBlockGameplay=240, + SFXBlockGameplay = 240, SFXBlock8Bit, SFXBlockInstrument, SFXBlockSciFi, @@ -248,7 +248,92 @@ namespace GamecraftModdingAPI.Blocks PlasmaCannonBlock, QuantumRiflePickup = 300, QuantumRifleAmmoPickup, - MagmaRockCube=777, + 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, + GridCube, + GridSlicedCube, + GridSlope, + GridCorner, + MagmaRockCube = 777, MagmaRockCubeSliced, MagmaRockSlope, MagmaRockCorner, @@ -267,7 +352,7 @@ namespace GamecraftModdingAPI.Blocks HexNetSlopeRounded, HexNetCornerRounded, //794 MagmaRockBulgedInner, - HexNetCylinder=797, + HexNetCylinder = 797, HexNetHemisphere, HexNetSphere, HexNetTubeCorner //800 diff --git a/GamecraftModdingAPI/Blocks/BlueprintEngine.cs b/GamecraftModdingAPI/Blocks/BlueprintEngine.cs new file mode 100644 index 0000000..ddfb051 --- /dev/null +++ b/GamecraftModdingAPI/Blocks/BlueprintEngine.cs @@ -0,0 +1,319 @@ +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.Common; +using RobocraftX.CR.MachineEditing.BoxSelect; +using RobocraftX.CR.MachineEditing.BoxSelect.ClipboardOperations; +using Svelto.DataStructures; +using Svelto.ECS; +using Svelto.ECS.DataStructures; +using Svelto.ECS.EntityStructs; +using Svelto.ECS.Serialization; +using Unity.Collections; +using Unity.Mathematics; +using UnityEngine; +using Allocator = Svelto.Common.Allocator; + +namespace GamecraftModdingAPI.Blocks +{ + public class BlueprintEngine : IFactoryEngine + { + private readonly MethodInfo getBlocksFromGroup = + AccessTools.Method("RobocraftX.CR.MachineEditing.PlaceBlockUtility:GetBlocksSharingBlockgroup"); + + 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; + private static IEntityFunctions entityFunctions; + private static ClipboardSerializationDataResourceManager clipboardManager; + 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(); + } + + public Block[] GetBlocksFromGroup(EGID blockID, out float3 pos, out quaternion rot) + { + var blockPos = default(float3); + var blockRot = default(quaternion); + var parameters = new object[] {blockID, selectedBlocksInGroup, entitiesDB, blockPos, blockRot}; + getBlocksFromGroup.Invoke(null, parameters); + pos = (float3) parameters[3]; + rot = (quaternion) parameters[4]; + int count = selectedBlocksInGroup.Count(); + var ret = new Block[count]; + for (uint i = 0; i < count; i++) + ret[i] = new Block(selectedBlocksInGroup.Get(i)); + selectedBlocksInGroup.FastClear(); + return ret; + } + + public void RemoveBlockGroup(int id) + { + BlockGroupUtility.RemoveAllBlocksInBlockGroup(id, entitiesDB, removedConnections, nativeRemove, + connectionFactory, default).Complete(); + } + + public int CreateBlockGroup(float3 position, quaternion rotation) + { + int nextFilterId = BlockGroupUtility.NextFilterId; + Factory.BuildEntity((uint) nextFilterId, + BlockGroupExclusiveGroups.BlockGroupEntityGroup).Init(new BlockGroupTransformEntityComponent + { + blockGroupGridRotation = rotation, + blockGroupGridPosition = position + }); + 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, resourceID, false, -1); + } + + public uint CreateBlueprint() + { + 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]; + using (var enumerator = selected.GetEnumerator()) + { + for (var i = 0; enumerator.MoveNext(); i++) + { + var block = enumerator.Current; + blockIDs[i] = block.Id; + } + } + + var serializationData = clipboardManager.GetSerializationData(blueprintID); + 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); + //ref ColliderAabb collider = ref EntityNativeDBExtensions.QueryEntity(entitiesDB, (uint) groupID, BlockGroupExclusiveGroups.BlockGroupEntityGroup); + //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, -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); + foreach (var block in blocks) + { + GhostChildUtility.BuildGhostChild(in playerID, block.Id, in pos, in rot, entitiesDB, + BuildGhostBlueprintFactory, false); + } + } + + public Block[] PlaceBlueprintBlocks(uint blueprintID, uint playerID, float3 pos, float3 rot) + { //RobocraftX.CR.MachineEditing.PlaceBlueprintUtility.PlaceBlocksFromSerialisedData + var serializationData = clipboardManager.GetSerializationData(blueprintID); + var blueprintData = serializationData.blueprintData; + blueprintData.dataPos = 0U; + uint selectionSize; + PositionEntityStruct selectionPosition; + RotationEntityStruct selectionRotation; + uint version; + BoxSelectSerializationUtilities.ReadClipboardHeader(blueprintData, out selectionSize, out selectionPosition, out selectionRotation, out version); + ((FasterList) LocalBlockMap.GetValue(null)).Clear(); + if (version <= 1U) + { + uint groupsCount; + BoxSelectSerializationUtilities.ReadBlockGroupData(blueprintData, out groupsCount); + for (int index = 0; (long) index < (long) groupsCount; ++index) + { + int nextFilterId = BlockGroupUtility.NextFilterId; + entitySerialization.DeserializeNewEntity(new EGID((uint) nextFilterId, BlockGroupExclusiveGroups.BlockGroupEntityGroup), blueprintData, 1); + } + } + int nextFilterId1 = BlockGroupUtility.NextFilterId; + 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}; + var rots = new RotationEntityStruct {rotation = frot}; + for (int index = 0; (long) index < (long) selectionSize; ++index) + BuildBlock.Invoke(null, + new object[] + { + playerID, grid, poss, rots, selectionPosition, selectionRotation, blueprintData, + entitySerialization, nextFilterId1 + }); + /* + uint playerId, in GridRotationStruct ghostParentGrid, + in PositionEntityStruct ghostParentPosition, in RotationEntityStruct ghostParentRotation, + in PositionEntityStruct selectionPosition, in RotationEntityStruct selectionRotation, + ISerializationData serializationData, EntitiesDB entitiesDb, + IEntitySerialization entitySerialization, int blockGroupId + */ + if (globalBlockMap == null) + globalBlockMap = FullGameFields._deserialisedBlockMap; + var placedBlocks = (FasterList) LocalBlockMap.GetValue(null); + globalBlockMap.Clear(); + globalBlockMap.AddRange(placedBlocks); + BuildWires.Invoke(null, + 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]); + 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); + } + + public void DisposeBlueprint(uint blueprintID) + { + clipboardManager.DecrementRefCount(blueprintID); + } + + public string Name { get; } = "GamecraftModdingAPIBlueprintGameEngine"; + public bool isRemovable { get; } = false; + + [HarmonyPatch] + private static class RemoveEnginePatch + { + public static void Prefix(IEntityFunctions entityFunctions, + MachineGraphConnectionEntityFactory machineGraphConnectionEntityFactory) + { + nativeRemove = entityFunctions.ToNativeRemove("GCAPI" + nameof(BlueprintEngine)); + connectionFactory = machineGraphConnectionEntityFactory; + BlueprintEngine.entityFunctions = entityFunctions; + } + + public static MethodBase TargetMethod() + { + return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.RemoveBlockEngine"))[0]; + } + } + + [HarmonyPatch] + private static class SelectEnginePatch + { + public static void Prefix(ClipboardSerializationDataResourceManager clipboardSerializationDataResourceManager, + IEntitySerialization entitySerialization, + IEntityFactory entityFactory) + { + clipboardManager = clipboardSerializationDataResourceManager; + BlueprintEngine.entitySerialization = entitySerialization; + BlueprintEngine.entityFactory = entityFactory; + } + + public static MethodBase TargetMethod() + { + return AccessTools.GetDeclaredConstructors(AccessTools.TypeByName("RobocraftX.CR.MachineEditing.SelectBlockEngine"))[0]; + } + } + + [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/GamecraftModdingAPI/Blocks/ConsoleBlock.cs b/GamecraftModdingAPI/Blocks/ConsoleBlock.cs index c8982e4..e696fca 100644 --- a/GamecraftModdingAPI/Blocks/ConsoleBlock.cs +++ b/GamecraftModdingAPI/Blocks/ConsoleBlock.cs @@ -16,7 +16,7 @@ namespace GamecraftModdingAPI.Blocks { } - public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_CONSOLE_BLOCK_GROUP)) + public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.CONSOLE_BLOCK_GROUP)) { } diff --git a/GamecraftModdingAPI/Blocks/DampedSpring.cs b/GamecraftModdingAPI/Blocks/DampedSpring.cs new file mode 100644 index 0000000..5a5a936 --- /dev/null +++ b/GamecraftModdingAPI/Blocks/DampedSpring.cs @@ -0,0 +1,48 @@ +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 index 124bc10..2ec4cef 100644 --- a/GamecraftModdingAPI/Blocks/LogicGate.cs +++ b/GamecraftModdingAPI/Blocks/LogicGate.cs @@ -9,7 +9,7 @@ namespace GamecraftModdingAPI.Blocks { } - public LogicGate(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_LOGIC_BLOCK_GROUP)) + public LogicGate(uint id) : base(new EGID(id, CommonExclusiveGroups.LOGIC_BLOCK_GROUP)) { } } diff --git a/GamecraftModdingAPI/Blocks/Motor.cs b/GamecraftModdingAPI/Blocks/Motor.cs index 3c38a52..0a69d27 100644 --- a/GamecraftModdingAPI/Blocks/Motor.cs +++ b/GamecraftModdingAPI/Blocks/Motor.cs @@ -15,7 +15,7 @@ namespace GamecraftModdingAPI.Blocks { } - public Motor(uint id): base(new EGID(id, CommonExclusiveGroups.BUILD_MOTOR_BLOCK_GROUP)) + public Motor(uint id): base(new EGID(id, CommonExclusiveGroups.MOTOR_BLOCK_GROUP)) { } diff --git a/GamecraftModdingAPI/Blocks/MovementEngine.cs b/GamecraftModdingAPI/Blocks/MovementEngine.cs index a4ac0fa..fb3aa55 100644 --- a/GamecraftModdingAPI/Blocks/MovementEngine.cs +++ b/GamecraftModdingAPI/Blocks/MovementEngine.cs @@ -41,9 +41,9 @@ namespace GamecraftModdingAPI.Blocks { 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}); + init.GetOrCreate().position = vector; + init.GetOrCreate().position = vector; + init.GetOrCreate().position = vector; return vector; } ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity(blockID); diff --git a/GamecraftModdingAPI/Blocks/MusicBlock.cs b/GamecraftModdingAPI/Blocks/MusicBlock.cs index 185913b..6b99b6f 100644 --- a/GamecraftModdingAPI/Blocks/MusicBlock.cs +++ b/GamecraftModdingAPI/Blocks/MusicBlock.cs @@ -20,7 +20,7 @@ namespace GamecraftModdingAPI.Blocks { } - public MusicBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_MUSIC_BLOCK_GROUP)) + public MusicBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.MUSIC_BLOCK_GROUP)) { } @@ -97,7 +97,7 @@ namespace GamecraftModdingAPI.Blocks { get { - Assert.Log("Block exists: " + Exists); + //Assert.Log("Block exists: " + Exists); return BlockEngine.GetBlockInfo(this, (MusicBlockDataEntityStruct msdes) => (ChannelType) msdes.channelType); } diff --git a/GamecraftModdingAPI/Blocks/ObjectIdentifier.cs b/GamecraftModdingAPI/Blocks/ObjectIdentifier.cs index 0dc835a..1233343 100644 --- a/GamecraftModdingAPI/Blocks/ObjectIdentifier.cs +++ b/GamecraftModdingAPI/Blocks/ObjectIdentifier.cs @@ -10,7 +10,7 @@ namespace GamecraftModdingAPI.Blocks { } - public ObjectIdentifier(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_OBJID_BLOCK_GROUP)) + public ObjectIdentifier(uint id) : base(new EGID(id, CommonExclusiveGroups.OBJID_BLOCK_GROUP)) { } diff --git a/GamecraftModdingAPI/Blocks/Piston.cs b/GamecraftModdingAPI/Blocks/Piston.cs index c3f2497..04b3aeb 100644 --- a/GamecraftModdingAPI/Blocks/Piston.cs +++ b/GamecraftModdingAPI/Blocks/Piston.cs @@ -15,7 +15,7 @@ namespace GamecraftModdingAPI.Blocks { } - public Piston(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_PISTON_BLOCK_GROUP)) + public Piston(uint id) : base(new EGID(id, CommonExclusiveGroups.PISTON_BLOCK_GROUP)) { } diff --git a/GamecraftModdingAPI/Blocks/PlacementEngine.cs b/GamecraftModdingAPI/Blocks/PlacementEngine.cs index e111598..b0d1365 100644 --- a/GamecraftModdingAPI/Blocks/PlacementEngine.cs +++ b/GamecraftModdingAPI/Blocks/PlacementEngine.cs @@ -16,6 +16,7 @@ using UnityEngine; using GamecraftModdingAPI.Utility; using GamecraftModdingAPI.Engines; using GamecraftModdingAPI.Players; +using RobocraftX.Rendering.GPUI; namespace GamecraftModdingAPI.Blocks { @@ -52,15 +53,15 @@ namespace GamecraftModdingAPI.Blocks private EntityComponentInitializer BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId) { if (_blockEntityFactory == null) - throw new Exception("The factory is null."); + throw new BlockException("The factory is null."); if (uscale < 1) - throw new Exception("Scale needs to be at least 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.DBIDMAP.ContainsKey(dbid)) - throw new Exception("Block with ID " + dbid + " not found!"); + 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); @@ -70,9 +71,7 @@ namespace GamecraftModdingAPI.Blocks DBEntityStruct dbEntity = new DBEntityStruct {DBID = dbid}; BlockPlacementScaleEntityStruct placementScale = new BlockPlacementScaleEntityStruct { - blockPlacementHeight = uscale, blockPlacementWidth = uscale, desiredScaleFactor = uscale, - snapGridScale = uscale, - unitSnapOffset = 0, isUsingUnitSize = true + blockPlacementHeight = uscale, blockPlacementWidth = uscale, desiredScaleFactor = uscale }; EquippedColourStruct colour = new EquippedColourStruct {indexInPalette = color}; @@ -83,7 +82,7 @@ namespace GamecraftModdingAPI.Blocks structInitializer.Init(new ColourParameterEntityStruct { indexInPalette = colour.indexInPalette, - needsUpdate = true + hasNetworkChange = true }); uint prefabId = PrefabsID.GetPrefabId(dbid, 0); structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId)); @@ -102,6 +101,13 @@ namespace GamecraftModdingAPI.Blocks loadedFromDisk = false, placedBy = playerId }); + + /*structInitializer.Init(new CollisionFilterOverride + { + belongsTo = 32U, + collidesWith = 239532U + });*/ + PrimaryRotationUtility.InitialisePrimaryDirection(rotation.rotation, ref structInitializer); EGID playerEGID = new EGID(playerId, CharacterExclusiveGroups.OnFootGroup); ref PickedBlockExtraDataStruct pickedBlock = ref entitiesDB.QueryEntity(playerEGID); diff --git a/GamecraftModdingAPI/Blocks/RemovalEngine.cs b/GamecraftModdingAPI/Blocks/RemovalEngine.cs index 629fd7e..c27c339 100644 --- a/GamecraftModdingAPI/Blocks/RemovalEngine.cs +++ b/GamecraftModdingAPI/Blocks/RemovalEngine.cs @@ -20,8 +20,11 @@ namespace GamecraftModdingAPI.Blocks if (!entitiesDB.Exists(target)) return false; var connections = entitiesDB.QueryEntity(target); + var groups = entitiesDB.FindGroups(); + var connStructMapper = + entitiesDB.QueryNativeMappedEntities(groups); for (int i = connections.connections.Count() - 1; i >= 0; i--) - _connectionFactory.RemoveConnection(connections, i, entitiesDB); + _connectionFactory.RemoveConnection(connections, i, connStructMapper); _entityFunctions.RemoveEntity(target); return true; } diff --git a/GamecraftModdingAPI/Blocks/RotationEngine.cs b/GamecraftModdingAPI/Blocks/RotationEngine.cs index ca97874..fbf8c98 100644 --- a/GamecraftModdingAPI/Blocks/RotationEngine.cs +++ b/GamecraftModdingAPI/Blocks/RotationEngine.cs @@ -41,9 +41,9 @@ namespace GamecraftModdingAPI.Blocks { 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}}); + init.GetOrCreate().rotation = Quaternion.Euler(vector); + init.GetOrCreate().rotation = Quaternion.Euler(vector); + init.GetOrCreate().rotation = Quaternion.Euler(vector); return vector; } ref RotationEntityStruct rotStruct = ref this.entitiesDB.QueryEntity(blockID); diff --git a/GamecraftModdingAPI/Blocks/ScalingEngine.cs b/GamecraftModdingAPI/Blocks/ScalingEngine.cs index a531865..fcbf900 100644 --- a/GamecraftModdingAPI/Blocks/ScalingEngine.cs +++ b/GamecraftModdingAPI/Blocks/ScalingEngine.cs @@ -27,10 +27,12 @@ namespace GamecraftModdingAPI.Blocks public string Name { get; } = "GamecraftModdingAPIScalingEngine"; public bool isRemovable { get; } = false; - private static EntityManager _entityManager; //Unity entity manager + private EntityManager _entityManager; //Unity entity manager public void UpdateCollision(EGID egid) { + if (_entityManager == default) + _entityManager = FullGameFields._physicsWorld.EntityManager; //Assuming the block exists var entity = entitiesDB.QueryEntity(egid).uecsEntity; var pes = new UECSPhysicsEntityCreationStruct(); @@ -38,11 +40,6 @@ namespace GamecraftModdingAPI.Blocks _entityManager.DestroyEntity(entity); } - internal void Setup(EntityManager entityManager) - { - _entityManager = entityManager; - } - [HarmonyPatch] public class PhysicsEnginePatch { diff --git a/GamecraftModdingAPI/Blocks/Servo.cs b/GamecraftModdingAPI/Blocks/Servo.cs index 1177fb6..606a48a 100644 --- a/GamecraftModdingAPI/Blocks/Servo.cs +++ b/GamecraftModdingAPI/Blocks/Servo.cs @@ -15,7 +15,7 @@ namespace GamecraftModdingAPI.Blocks { } - public Servo(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SERVO_BLOCK_GROUP)) + public Servo(uint id) : base(new EGID(id, CommonExclusiveGroups.SERVO_BLOCK_GROUP)) { } diff --git a/GamecraftModdingAPI/Blocks/SfxBlock.cs b/GamecraftModdingAPI/Blocks/SfxBlock.cs new file mode 100644 index 0000000..88d69d2 --- /dev/null +++ b/GamecraftModdingAPI/Blocks/SfxBlock.cs @@ -0,0 +1,209 @@ +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/SignalEngine.cs b/GamecraftModdingAPI/Blocks/SignalEngine.cs index 763a1a1..0980d79 100644 --- a/GamecraftModdingAPI/Blocks/SignalEngine.cs +++ b/GamecraftModdingAPI/Blocks/SignalEngine.cs @@ -115,7 +115,8 @@ namespace GamecraftModdingAPI.Blocks public bool SetSignal(uint signalID, float signal, bool input = true) { var array = GetSignalStruct(signalID, out uint index, input); - if (array.count > 0) array[index].valueAsFloat = signal; + var arrayB = array.ToBuffer(); + if (array.count > 0) arrayB.buffer[index].valueAsFloat = signal; return false; } @@ -128,9 +129,10 @@ namespace GamecraftModdingAPI.Blocks public float AddSignal(uint signalID, float signal, bool clamp = true, bool input = true) { var array = GetSignalStruct(signalID, out uint index, input); + var arrayB = array.ToBuffer(); if (array.count > 0) { - ref var channelData = ref array[index]; + ref var channelData = ref arrayB.buffer[index]; channelData.valueAsFloat += signal; if (clamp) { @@ -159,7 +161,8 @@ namespace GamecraftModdingAPI.Blocks public float GetSignal(uint signalID, bool input = true) { var array = GetSignalStruct(signalID, out uint index, input); - return array.count > 0 ? array[index].valueAsFloat : 0f; + var arrayB = array.ToBuffer(); + return array.count > 0 ? arrayB.buffer[index].valueAsFloat : 0f; } public uint[] GetSignalIDs(EGID blockID, bool input = true) @@ -244,13 +247,14 @@ namespace GamecraftModdingAPI.Blocks { 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++) { - if ((wires[i].destinationPortUsage == port.usage && wires[i].destinationBlockEGID == blockID) - || (wires[i].sourcePortUsage == port.usage && wires[i].sourceBlockEGID == blockID)) + if ((wiresB[i].destinationPortUsage == port.usage && wiresB[i].destinationBlockEGID == blockID) + || (wiresB[i].sourcePortUsage == port.usage && wiresB[i].sourceBlockEGID == blockID)) { exists = true; - return ref wires[i]; + return ref wiresB[i]; } } exists = false; @@ -286,6 +290,7 @@ namespace GamecraftModdingAPI.Blocks } EntityCollection wires = entitiesDB.QueryEntities(NamedExclusiveGroup.Group); + var wiresB = wires.ToBuffer().buffer; for (int endIndex = 0; endIndex < endPorts.Length; endIndex++) { PortEntityStruct endPES = entitiesDB.QueryEntity(endPorts[endIndex]); @@ -294,11 +299,11 @@ namespace GamecraftModdingAPI.Blocks PortEntityStruct startPES = entitiesDB.QueryEntity(startPorts[startIndex]); for (int w = 0; w < wires.count; w++) { - if ((wires[w].destinationPortUsage == endPES.usage && wires[w].destinationBlockEGID == endBlock) - && (wires[w].sourcePortUsage == startPES.usage && wires[w].sourceBlockEGID == startBlock)) + if ((wiresB[w].destinationPortUsage == endPES.usage && wiresB[w].destinationBlockEGID == endBlock) + && (wiresB[w].sourcePortUsage == startPES.usage && wiresB[w].sourceBlockEGID == startBlock)) { exists = true; - return ref wires[w]; + return ref wiresB[w]; } } } @@ -313,10 +318,11 @@ namespace GamecraftModdingAPI.Blocks { ref PortEntityStruct port = ref entitiesDB.QueryEntity(portID); var channels = entitiesDB.QueryEntities(NamedExclusiveGroup.Group); + var channelsB = channels.ToBuffer(); if (port.firstChannelIndexCachedInSim < channels.count) { exists = true; - return ref channels[port.firstChannelIndexCachedInSim]; + return ref channelsB.buffer[port.firstChannelIndexCachedInSim]; } exists = false; ChannelDataStruct[] defRef = new ChannelDataStruct[1]; @@ -327,8 +333,15 @@ namespace GamecraftModdingAPI.Blocks { var res = new FasterList(); foreach (var (coll, _) in entitiesDB.QueryEntities()) - foreach (ref BlockPortsStruct s in coll) - res.Add(s.ID); + { + var collB = coll.ToBuffer(); + for (int i = 0; i < coll.count; i++) + { + ref BlockPortsStruct s = ref collB.buffer[i]; + res.Add(s.ID); + } + } + return res.ToArray(); } @@ -358,15 +371,16 @@ namespace GamecraftModdingAPI.Blocks return result; } - private T[] Search(ExclusiveGroup group, Func isMatch) where T : struct, IEntityComponent + private T[] Search(ExclusiveGroup group, Func isMatch) where T : unmanaged, IEntityComponent { FasterList results = new FasterList(); EntityCollection components = entitiesDB.QueryEntities(group); + var componentsB = components.ToBuffer(); for (uint i = 0; i < components.count; i++) { - if (isMatch(components[i])) + if (isMatch(componentsB.buffer[i])) { - results.Add(components[i]); + results.Add(componentsB.buffer[i]); } } return results.ToArray(); diff --git a/GamecraftModdingAPI/Blocks/SpawnPoint.cs b/GamecraftModdingAPI/Blocks/SpawnPoint.cs index 7616acb..17bffd9 100644 --- a/GamecraftModdingAPI/Blocks/SpawnPoint.cs +++ b/GamecraftModdingAPI/Blocks/SpawnPoint.cs @@ -17,7 +17,7 @@ namespace GamecraftModdingAPI.Blocks { } - public SpawnPoint(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_SPAWNPOINT_BLOCK_GROUP)) + public SpawnPoint(uint id) : base(new EGID(id, CommonExclusiveGroups.SPAWNPOINT_BLOCK_GROUP)) { } diff --git a/GamecraftModdingAPI/Blocks/TextBlock.cs b/GamecraftModdingAPI/Blocks/TextBlock.cs index e4b4c73..d7d620a 100644 --- a/GamecraftModdingAPI/Blocks/TextBlock.cs +++ b/GamecraftModdingAPI/Blocks/TextBlock.cs @@ -16,7 +16,7 @@ namespace GamecraftModdingAPI.Blocks { } - public TextBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TEXT_BLOCK_GROUP)) + public TextBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.TEXT_BLOCK_GROUP)) { } @@ -31,6 +31,7 @@ namespace GamecraftModdingAPI.Blocks set { + if (value == null) value = ""; BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) => { tbds.textCurrent.Set(val); @@ -50,6 +51,7 @@ namespace GamecraftModdingAPI.Blocks set { + if (value == null) value = ""; BlockEngine.SetBlockInfo(this, (ref TextBlockDataStruct tbds, string val) => tbds.textBlockID.Set(val), value); BlockEngine.SetBlockInfo(this, diff --git a/GamecraftModdingAPI/Blocks/Timer.cs b/GamecraftModdingAPI/Blocks/Timer.cs index 1aeecd9..0bbd302 100644 --- a/GamecraftModdingAPI/Blocks/Timer.cs +++ b/GamecraftModdingAPI/Blocks/Timer.cs @@ -17,7 +17,7 @@ namespace GamecraftModdingAPI.Blocks { } - public Timer(uint id) : base(new EGID(id, CommonExclusiveGroups.BUILD_TIMER_BLOCK_GROUP)) + public Timer(uint id) : base(new EGID(id, CommonExclusiveGroups.TIMER_BLOCK_GROUP)) { } diff --git a/GamecraftModdingAPI/Blueprint.cs b/GamecraftModdingAPI/Blueprint.cs new file mode 100644 index 0000000..4d9dff0 --- /dev/null +++ b/GamecraftModdingAPI/Blueprint.cs @@ -0,0 +1,102 @@ +using System; +using Unity.Mathematics; +using UnityEngine; + +namespace GamecraftModdingAPI +{ + /// + /// Represents a blueprint in the inventory. When placed it becomes a block group. + /// + public class Blueprint : IDisposable + { + public uint Id { get; } + + internal Blueprint(uint id) + { + Id = id; + BlockGroup._engine.InitBlueprint(id); + Refresh(); + } + + /// + /// 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. + /// + /// A blueprint that doesn't have any blocks + public static Blueprint Create() + { + return new Blueprint(BlockGroup._engine.CreateBlueprint()); + } + + /// + /// Set the blocks that the blueprint contains. + /// Use the BlockGroup overload for automatically calculated position and rotation. + /// + /// The array of blocks to use + /// 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(); + } + + /// + /// Store the blocks from the given group in the blueprint with correct position and rotation for the blueprint. + /// + /// The block group to store + public void StoreBlocks(BlockGroup group) + { + BlockGroup._engine.ReplaceBlueprint(Player.LocalPlayer.Id, Id, group, group.Position, + Quaternion.Euler(group.Rotation)); + Refresh(); + } + + /// + /// Places the blocks the blueprint contains at the specified position and rotation. + /// + /// The position of the blueprint + /// The rotation of the blueprint + /// An array of the placed blocks + public Block[] PlaceBlocks(float3 position, float3 rotation) + { + 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/GamecraftModdingAPI/Cluster.cs index 074c26c..79b5822 100644 --- a/GamecraftModdingAPI/Cluster.cs +++ b/GamecraftModdingAPI/Cluster.cs @@ -6,6 +6,7 @@ namespace GamecraftModdingAPI { /// /// 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 { @@ -37,5 +38,37 @@ namespace GamecraftModdingAPI get => Block.BlockEngine.GetBlockInfo(Id).healthMultiplier; set => Block.BlockEngine.GetBlockInfo(Id).healthMultiplier = value; } + + /// + /// Returns the simulation-time rigid bodies for the chunks in this cluster. + /// + /// An array of sim-bodies + public SimBody[] GetSimBodies() + { + return Block.BlockEngine.GetClusterBodies(Id.entityID); + } + + public override string ToString() + { + return $"{nameof(Id)}: {Id}"; + } + + protected bool Equals(Cluster other) + { + return Id.Equals(other.Id); + } + + 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((Cluster) obj); + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } } } \ No newline at end of file diff --git a/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs b/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs index 4142fb3..c3a5fb5 100644 --- a/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs +++ b/GamecraftModdingAPI/Events/GameActivatedComposePatch.cs @@ -31,8 +31,6 @@ namespace GamecraftModdingAPI.Events GameEngineManager.RegisterEngines(enginesRoot); // initialize AsyncUtils AsyncUtils.Setup(enginesRoot); - // initialize Block - Block.Setup(physicsWorld); // A new EnginesRoot is always created when ActivateGame is called // so all event emitters and handlers must be re-registered. EventManager.RegisterEngines(enginesRoot); diff --git a/GamecraftModdingAPI/GamecraftModdingAPI.csproj b/GamecraftModdingAPI/GamecraftModdingAPI.csproj index ee28a68..c76ebd9 100644 --- a/GamecraftModdingAPI/GamecraftModdingAPI.csproj +++ b/GamecraftModdingAPI/GamecraftModdingAPI.csproj @@ -2,7 +2,7 @@ net472 true - 1.5.0 + 1.7.0 Exmods GNU General Public Licence 3+ https://git.exmods.org/modtainers/GamecraftModdingAPI @@ -17,21 +17,18 @@ DEBUG;TEST;TRACE - + + - - ..\ref\Gamecraft_Data\Managed\IllusionInjector.dll - ..\..\ref\Gamecraft_Data\Managed\IllusionInjector.dll - - - ..\ref\Gamecraft_Data\Managed\IllusionPlugin.dll - ..\..\ref\Gamecraft_Data\Managed\IllusionPlugin.dll + + ..\ref\Gamecraft_Data\Managed\Accessibility.dll + ..\..\ref\Gamecraft_Data\Managed\Accessibility.dll ..\ref\Gamecraft_Data\Managed\Analytics.dll @@ -77,6 +74,10 @@ ..\ref\Gamecraft_Data\Managed\DDNA.dll ..\..\ref\Gamecraft_Data\Managed\DDNA.dll + + ..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll + ..\..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll + ..\ref\Gamecraft_Data\Managed\FMOD.dll ..\..\ref\Gamecraft_Data\Managed\FMOD.dll @@ -89,14 +90,14 @@ ..\ref\Gamecraft_Data\Managed\Gamecraft.AudioBlocks.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.AudioBlocks.dll - - ..\ref\Gamecraft_Data\Managed\Gamecraft.BlockCompositionRoot.dll - ..\..\ref\Gamecraft_Data\Managed\Gamecraft.BlockCompositionRoot.dll - ..\ref\Gamecraft_Data\Managed\Gamecraft.BlockEntityFactory.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.BlockEntityFactory.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.BlockGroups.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.BlockGroups.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.ConsoleBlock.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Blocks.ConsoleBlock.dll @@ -145,6 +146,10 @@ ..\ref\Gamecraft_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.CharacterVulnerabilityGui.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.ColourPalette.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.ColourPalette.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.Damage.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.Damage.dll @@ -161,18 +166,74 @@ ..\ref\Gamecraft_Data\Managed\Gamecraft.GraphicsSettings.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GraphicsSettings.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.BlueprintInventory.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.BlueprintInventory.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.BlueprintInventoryMock.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.BlueprintInventoryMock.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Blueprints.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Blueprints.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.BlueprintSets.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.BlueprintSets.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.ConsoleBlock.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.ConsoleBlock.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.GameOptionsScreen.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.GameOptionsScreen.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.GraphicsScreen.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Hotbar.Blocks.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Hotbar.Blocks.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Hotbar.Colours.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Hotbar.Colours.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.HUDFeedbackBlocks.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.ModeBar.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.ModeBar.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.OptionsScreen.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.OptionsScreen.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TabsBar.Blocks.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TabsBar.Blocks.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TabsBar.Blueprints.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TabsBar.Blueprints.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TabsBar.Colours.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TabsBar.Colours.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TabsBar.Common.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TabsBar.Common.dll + + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TimeModeClock.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.TimeModeClock.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Tweaks.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.Tweaks.dll @@ -189,6 +250,10 @@ ..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUI.WorldSpaceGuis.dll + + ..\ref\Gamecraft_Data\Managed\Gamecraft.GUIs.Hotbar.BlueprintsHotbar.dll + ..\..\ref\Gamecraft_Data\Managed\Gamecraft.GUIs.Hotbar.BlueprintsHotbar.dll + ..\ref\Gamecraft_Data\Managed\Gamecraft.InventoryTimeRunning.dll ..\..\ref\Gamecraft_Data\Managed\Gamecraft.InventoryTimeRunning.dll @@ -249,6 +314,10 @@ ..\ref\Gamecraft_Data\Managed\GameState.dll ..\..\ref\Gamecraft_Data\Managed\GameState.dll + + ..\ref\Gamecraft_Data\Managed\GhostShark.Outline.dll + ..\..\ref\Gamecraft_Data\Managed\GhostShark.Outline.dll + ..\ref\Gamecraft_Data\Managed\GPUInstancer.dll ..\..\ref\Gamecraft_Data\Managed\GPUInstancer.dll @@ -261,10 +330,26 @@ ..\ref\Gamecraft_Data\Managed\Havok.Physics.Hybrid.dll ..\..\ref\Gamecraft_Data\Managed\Havok.Physics.Hybrid.dll + + ..\ref\Gamecraft_Data\Managed\IllusionInjector.dll + ..\..\ref\Gamecraft_Data\Managed\IllusionInjector.dll + + + ..\ref\Gamecraft_Data\Managed\IllusionPlugin.dll + ..\..\ref\Gamecraft_Data\Managed\IllusionPlugin.dll + + + ..\ref\Gamecraft_Data\Managed\JWT.dll + ..\..\ref\Gamecraft_Data\Managed\JWT.dll + ..\ref\Gamecraft_Data\Managed\LZ4.dll ..\..\ref\Gamecraft_Data\Managed\LZ4.dll + + ..\ref\Gamecraft_Data\Managed\mscorlib.dll + ..\..\ref\Gamecraft_Data\Managed\mscorlib.dll + ..\ref\Gamecraft_Data\Managed\MultiplayerNetworking.dll ..\..\ref\Gamecraft_Data\Managed\MultiplayerNetworking.dll @@ -273,10 +358,30 @@ ..\ref\Gamecraft_Data\Managed\MultiplayerTest.dll ..\..\ref\Gamecraft_Data\Managed\MultiplayerTest.dll + + ..\ref\Gamecraft_Data\Managed\netstandard.dll + ..\..\ref\Gamecraft_Data\Managed\netstandard.dll + + + ..\ref\Gamecraft_Data\Managed\Newtonsoft.Json.dll + ..\..\ref\Gamecraft_Data\Managed\Newtonsoft.Json.dll + + + ..\ref\Gamecraft_Data\Managed\Novell.Directory.Ldap.dll + ..\..\ref\Gamecraft_Data\Managed\Novell.Directory.Ldap.dll + ..\ref\Gamecraft_Data\Managed\RCX.ScreenshotTaker.dll ..\..\ref\Gamecraft_Data\Managed\RCX.ScreenshotTaker.dll + + ..\ref\Gamecraft_Data\Managed\Rewired_Core.dll + ..\..\ref\Gamecraft_Data\Managed\Rewired_Core.dll + + + ..\ref\Gamecraft_Data\Managed\Rewired_Windows.dll + ..\..\ref\Gamecraft_Data\Managed\Rewired_Windows.dll + ..\ref\Gamecraft_Data\Managed\RobocraftECS.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftECS.dll @@ -341,6 +446,22 @@ ..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.dll + + ..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.Hotbar.dll + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.Hotbar.dll + + + ..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.Inventory.BlocksInventory.dll + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.Inventory.BlocksInventory.dll + + + ..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.Inventory.ColourInventory.dll + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.Inventory.ColourInventory.dll + + + ..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.Inventory.dll + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.Inventory.dll + ..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.RemoveBlock.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.RemoveBlock.dll @@ -349,6 +470,10 @@ ..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.ScaleGhost.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.ScaleGhost.dll + + ..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.TabsBar.dll + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUI.TabsBar.dll + ..\ref\Gamecraft_Data\Managed\RobocraftX.GUIs.WorkshopPrefabs.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.GUIs.WorkshopPrefabs.dll @@ -377,6 +502,10 @@ ..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.dll + + ..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.GUI.dll + ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.GUI.dll + ..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX.Multiplayer.NetworkEntityStream.dll @@ -449,9 +578,9 @@ ..\ref\Gamecraft_Data\Managed\RobocraftX_TextBlock.dll ..\..\ref\Gamecraft_Data\Managed\RobocraftX_TextBlock.dll - - ..\ref\Gamecraft_Data\Managed\RobocratX.SimulationCompositionRoot.dll - ..\..\ref\Gamecraft_Data\Managed\RobocratX.SimulationCompositionRoot.dll + + ..\ref\Gamecraft_Data\Managed\RobocratX.SimulationMockCompositionRoot.dll + ..\..\ref\Gamecraft_Data\Managed\RobocratX.SimulationMockCompositionRoot.dll ..\ref\Gamecraft_Data\Managed\SpawningPointCompositionRoot.dll @@ -465,9 +594,9 @@ ..\ref\Gamecraft_Data\Managed\StringFormatter.dll ..\..\ref\Gamecraft_Data\Managed\StringFormatter.dll - - ..\ref\Gamecraft_Data\Managed\Svelto.Common_3.dll - ..\..\ref\Gamecraft_Data\Managed\Svelto.Common_3.dll + + ..\ref\Gamecraft_Data\Managed\Svelto.Common.dll + ..\..\ref\Gamecraft_Data\Managed\Svelto.Common.dll ..\ref\Gamecraft_Data\Managed\Svelto.ECS.dll @@ -489,34 +618,6 @@ ..\ref\Gamecraft_Data\Managed\Unity.Addressables.dll ..\..\ref\Gamecraft_Data\Managed\Unity.Addressables.dll - - ..\ref\Gamecraft_Data\Managed\Unity.Animation.Curves.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Animation.Curves.dll - - - ..\ref\Gamecraft_Data\Managed\Unity.Animation.Curves.Hybrid.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Animation.Curves.Hybrid.dll - - - ..\ref\Gamecraft_Data\Managed\Unity.Animation.DefaultGraphPipeline.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Animation.DefaultGraphPipeline.dll - - - ..\ref\Gamecraft_Data\Managed\Unity.Animation.DefaultGraphPipeline.Hybrid.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Animation.DefaultGraphPipeline.Hybrid.dll - - - ..\ref\Gamecraft_Data\Managed\Unity.Animation.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Animation.dll - - - ..\ref\Gamecraft_Data\Managed\Unity.Animation.Graph.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Animation.Graph.dll - - - ..\ref\Gamecraft_Data\Managed\Unity.Animation.Hybrid.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Animation.Hybrid.dll - ..\ref\Gamecraft_Data\Managed\Unity.Build.SlimPlayerRuntime.dll ..\..\ref\Gamecraft_Data\Managed\Unity.Build.SlimPlayerRuntime.dll @@ -525,13 +626,17 @@ ..\ref\Gamecraft_Data\Managed\Unity.Burst.dll ..\..\ref\Gamecraft_Data\Managed\Unity.Burst.dll + + ..\ref\Gamecraft_Data\Managed\Unity.Burst.Unsafe.dll + ..\..\ref\Gamecraft_Data\Managed\Unity.Burst.Unsafe.dll + ..\ref\Gamecraft_Data\Managed\Unity.Collections.dll ..\..\ref\Gamecraft_Data\Managed\Unity.Collections.dll - - ..\ref\Gamecraft_Data\Managed\Unity.DataFlowGraph.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.DataFlowGraph.dll + + ..\ref\Gamecraft_Data\Managed\Unity.Collections.LowLevel.ILSupport.dll + ..\..\ref\Gamecraft_Data\Managed\Unity.Collections.LowLevel.ILSupport.dll ..\ref\Gamecraft_Data\Managed\Unity.Deformations.dll @@ -545,6 +650,10 @@ ..\ref\Gamecraft_Data\Managed\Unity.Entities.Hybrid.dll ..\..\ref\Gamecraft_Data\Managed\Unity.Entities.Hybrid.dll + + ..\ref\Gamecraft_Data\Managed\Unity.InternalAPIEngineBridge.012.dll + ..\..\ref\Gamecraft_Data\Managed\Unity.InternalAPIEngineBridge.012.dll + ..\ref\Gamecraft_Data\Managed\Unity.Jobs.dll ..\..\ref\Gamecraft_Data\Managed\Unity.Jobs.dll @@ -577,10 +686,6 @@ ..\ref\Gamecraft_Data\Managed\Unity.Platforms.Common.dll ..\..\ref\Gamecraft_Data\Managed\Unity.Platforms.Common.dll - - ..\ref\Gamecraft_Data\Managed\Unity.Postprocessing.Runtime.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Postprocessing.Runtime.dll - ..\ref\Gamecraft_Data\Managed\Unity.Properties.dll ..\..\ref\Gamecraft_Data\Managed\Unity.Properties.dll @@ -621,9 +726,9 @@ ..\ref\Gamecraft_Data\Managed\Unity.ResourceManager.dll ..\..\ref\Gamecraft_Data\Managed\Unity.ResourceManager.dll - - ..\ref\Gamecraft_Data\Managed\Unity.Scenes.Hybrid.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Scenes.Hybrid.dll + + ..\ref\Gamecraft_Data\Managed\Unity.Scenes.dll + ..\..\ref\Gamecraft_Data\Managed\Unity.Scenes.dll ..\ref\Gamecraft_Data\Managed\Unity.ScriptableBuildPipeline.dll @@ -653,58 +758,6 @@ ..\ref\Gamecraft_Data\Managed\Unity.VisualEffectGraph.Runtime.dll ..\..\ref\Gamecraft_Data\Managed\Unity.VisualEffectGraph.Runtime.dll - - ..\ref\Gamecraft_Data\Managed\UnityEngine.UI.dll - ..\..\ref\Gamecraft_Data\Managed\UnityEngine.UI.dll - - - ..\ref\Gamecraft_Data\Managed\uREPL.dll - ..\..\ref\Gamecraft_Data\Managed\uREPL.dll - - - ..\ref\Gamecraft_Data\Managed\VisualProfiler.dll - ..\..\ref\Gamecraft_Data\Managed\VisualProfiler.dll - - - ..\ref\Gamecraft_Data\Managed\Accessibility.dll - ..\..\ref\Gamecraft_Data\Managed\Accessibility.dll - - - ..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll - ..\..\ref\Gamecraft_Data\Managed\Facepunch.Steamworks.Win64.dll - - - ..\ref\Gamecraft_Data\Managed\JWT.dll - ..\..\ref\Gamecraft_Data\Managed\JWT.dll - - - ..\ref\Gamecraft_Data\Managed\mscorlib.dll - ..\..\ref\Gamecraft_Data\Managed\mscorlib.dll - - - ..\ref\Gamecraft_Data\Managed\netstandard.dll - ..\..\ref\Gamecraft_Data\Managed\netstandard.dll - - - ..\ref\Gamecraft_Data\Managed\Newtonsoft.Json.dll - ..\..\ref\Gamecraft_Data\Managed\Newtonsoft.Json.dll - - - ..\ref\Gamecraft_Data\Managed\Novell.Directory.Ldap.dll - ..\..\ref\Gamecraft_Data\Managed\Novell.Directory.Ldap.dll - - - ..\ref\Gamecraft_Data\Managed\Rewired_Core.dll - ..\..\ref\Gamecraft_Data\Managed\Rewired_Core.dll - - - ..\ref\Gamecraft_Data\Managed\Rewired_Windows.dll - ..\..\ref\Gamecraft_Data\Managed\Rewired_Windows.dll - - - ..\ref\Gamecraft_Data\Managed\Unity.Burst.Unsafe.dll - ..\..\ref\Gamecraft_Data\Managed\Unity.Burst.Unsafe.dll - ..\ref\Gamecraft_Data\Managed\UnityEngine.AccessibilityModule.dll ..\..\ref\Gamecraft_Data\Managed\UnityEngine.AccessibilityModule.dll @@ -873,10 +926,18 @@ ..\ref\Gamecraft_Data\Managed\UnityEngine.TLSModule.dll ..\..\ref\Gamecraft_Data\Managed\UnityEngine.TLSModule.dll + + ..\ref\Gamecraft_Data\Managed\UnityEngine.UI.dll + ..\..\ref\Gamecraft_Data\Managed\UnityEngine.UI.dll + ..\ref\Gamecraft_Data\Managed\UnityEngine.UIElementsModule.dll ..\..\ref\Gamecraft_Data\Managed\UnityEngine.UIElementsModule.dll + + ..\ref\Gamecraft_Data\Managed\UnityEngine.UIElementsNativeModule.dll + ..\..\ref\Gamecraft_Data\Managed\UnityEngine.UIElementsNativeModule.dll + ..\ref\Gamecraft_Data\Managed\UnityEngine.UIModule.dll ..\..\ref\Gamecraft_Data\Managed\UnityEngine.UIModule.dll @@ -933,6 +994,10 @@ ..\ref\Gamecraft_Data\Managed\UnityEngine.VideoModule.dll ..\..\ref\Gamecraft_Data\Managed\UnityEngine.VideoModule.dll + + ..\ref\Gamecraft_Data\Managed\UnityEngine.VirtualTexturingModule.dll + ..\..\ref\Gamecraft_Data\Managed\UnityEngine.VirtualTexturingModule.dll + ..\ref\Gamecraft_Data\Managed\UnityEngine.VRModule.dll ..\..\ref\Gamecraft_Data\Managed\UnityEngine.VRModule.dll @@ -945,6 +1010,14 @@ ..\ref\Gamecraft_Data\Managed\UnityEngine.XRModule.dll ..\..\ref\Gamecraft_Data\Managed\UnityEngine.XRModule.dll + + ..\ref\Gamecraft_Data\Managed\uREPL.dll + ..\..\ref\Gamecraft_Data\Managed\uREPL.dll + + + ..\ref\Gamecraft_Data\Managed\VisualProfiler.dll + ..\..\ref\Gamecraft_Data\Managed\VisualProfiler.dll + \ No newline at end of file diff --git a/GamecraftModdingAPI/Input/FakeInput.cs b/GamecraftModdingAPI/Input/FakeInput.cs index df6c3ce..8cab6f5 100644 --- a/GamecraftModdingAPI/Input/FakeInput.cs +++ b/GamecraftModdingAPI/Input/FakeInput.cs @@ -76,7 +76,7 @@ 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; + //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; @@ -125,7 +125,7 @@ namespace GamecraftModdingAPI.Input if (tertiary) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.TertiaryAction; if (primaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.PrimaryActionHeld; if (secondaryHeld) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.SecondaryActionHeld; - if (toggleUnitGrid) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid; + //if (toggleUnitGrid) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleUnitGrid; if (ctrl) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.CtrlAction; if (toggleColourMode) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ToggleColourMode; if (scaleBlockUp) currentInput.actionMask |= RobocraftX.Common.Input.ActionInput.ScaleBlockUp; diff --git a/GamecraftModdingAPI/Inventory/HotbarEngine.cs b/GamecraftModdingAPI/Inventory/HotbarEngine.cs index d5eb2d8..34cdf12 100644 --- a/GamecraftModdingAPI/Inventory/HotbarEngine.cs +++ b/GamecraftModdingAPI/Inventory/HotbarEngine.cs @@ -36,13 +36,13 @@ namespace GamecraftModdingAPI.Inventory public bool SelectBlock(int block, uint playerID, bool cubeSelectedByPick = false) { - var inputs = entitiesDB.QueryEntities(InputExclusiveGroups.LocalPlayers); + var inputs = entitiesDB.QueryEntities(InputExclusiveGroups.LocalPlayers).ToBuffer(); if (inputs.count == 0) return false; for (int i = 0; i < inputs.count; i++) { - if (inputs[i].ID.entityID == playerID) { - inputs[i].cubeSelectedByPick = cubeSelectedByPick; - inputs[i].selectedCube = block; + if (inputs.buffer[i].ID.entityID == playerID) { + inputs.buffer[i].cubeSelectedByPick = cubeSelectedByPick; + inputs.buffer[i].selectedCube = block; return true; } } diff --git a/GamecraftModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs b/GamecraftModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs index dd8a375..a0ad5c1 100644 --- a/GamecraftModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs +++ b/GamecraftModdingAPI/Inventory/HotbarSlotSelectionHandlerEnginePatch.cs @@ -1,8 +1,6 @@ using System; using System.Reflection; -using RobocraftX.GUI; -using RobocraftX.GUI.Hotbar; using Svelto.ECS; using HarmonyLib; @@ -17,7 +15,7 @@ namespace GamecraftModdingAPI.Inventory public static BlockIDs EquippedPartID { get => (BlockIDs)selectedBlockInt; } - private static MethodInfo PatchedMethod { get; } = AccessTools.Method(AccessTools.TypeByName("RobocraftX.GUI.Hotbar.HotbarSlotSelectionHandlerEngine"), "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), typeof(ExclusiveGroupStruct) }); public static void Prefix(uint playerID, int selectedDBPartID, ExclusiveGroupStruct groupID) { diff --git a/GamecraftModdingAPI/Main.cs b/GamecraftModdingAPI/Main.cs index 4a8cf24..584ae90 100644 --- a/GamecraftModdingAPI/Main.cs +++ b/GamecraftModdingAPI/Main.cs @@ -1,17 +1,15 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Reflection; using GamecraftModdingAPI.Blocks; using HarmonyLib; +using RobocraftX; +using RobocraftX.Services; +using Svelto.Context; + using GamecraftModdingAPI.Utility; using GamecraftModdingAPI.Events; -using GamecraftModdingAPI.Players; using GamecraftModdingAPI.Tasks; -using uREPL; namespace GamecraftModdingAPI { @@ -46,7 +44,20 @@ namespace GamecraftModdingAPI Logging.MetaDebugLog($"Patching Gamecraft"); var currentAssembly = Assembly.GetExecutingAssembly(); harmony = new Harmony(currentAssembly.GetName().Name); - harmony.PatchAll(currentAssembly); + try + { + harmony.PatchAll(currentAssembly); + } + 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..."); + harmony.Patch(AccessTools.Method(typeof(FullGameCompositionRoot), "OnContextInitialized") + .MakeGenericMethod(typeof(UnityContext)), + new HarmonyMethod(((Action) OnPatchError).Method)); //Can't use lambdas here :( + return; + } + // init utility Logging.MetaDebugLog($"Initializing Utility"); #pragma warning disable 0612,0618 @@ -72,6 +83,7 @@ namespace GamecraftModdingAPI // init object-oriented classes Player.Init(); Block.Init(); + BlockGroup.Init(); Wire.Init(); GameClient.Init(); AsyncUtils.Init(); @@ -102,5 +114,11 @@ namespace GamecraftModdingAPI Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown"); } } + + 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."); + } } } diff --git a/GamecraftModdingAPI/Player.cs b/GamecraftModdingAPI/Player.cs index 40dd385..0bed6a2 100644 --- a/GamecraftModdingAPI/Player.cs +++ b/GamecraftModdingAPI/Player.cs @@ -1,5 +1,4 @@ using System; - using Unity.Mathematics; using RobocraftX.Common; using RobocraftX.Common.Players; @@ -17,6 +16,7 @@ namespace GamecraftModdingAPI { // static functionality private static PlayerEngine playerEngine = new PlayerEngine(); + private static Player localPlayer; /// /// Checks if the specified player exists. @@ -54,6 +54,19 @@ namespace GamecraftModdingAPI 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. /// @@ -329,7 +342,24 @@ namespace GamecraftModdingAPI } } - // object methods + /// + /// The player's selected blueprint in their hand. Set to null to clear. Dispose after usage. + /// + public Blueprint SelectedBlueprint + { + get => playerEngine.GetPlayerStruct(Id, out LocalBlueprintInputStruct lbis) + ? new Blueprint(lbis.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) playerEngine + .GetCharacterStruct(Id, out _).timeStoppedContext; + + // object methods /// /// Teleport the player to the specified coordinates. diff --git a/GamecraftModdingAPI/Players/PlayerBuildingMode.cs b/GamecraftModdingAPI/Players/PlayerBuildingMode.cs new file mode 100644 index 0000000..c7a5e37 --- /dev/null +++ b/GamecraftModdingAPI/Players/PlayerBuildingMode.cs @@ -0,0 +1,10 @@ +namespace GamecraftModdingAPI.Players +{ + public enum PlayerBuildingMode + { + BlockMode, + ColourMode, + ConfigMode, + BlueprintMode + } +} \ No newline at end of file diff --git a/GamecraftModdingAPI/Players/PlayerEngine.cs b/GamecraftModdingAPI/Players/PlayerEngine.cs index 6d461f2..e253eac 100644 --- a/GamecraftModdingAPI/Players/PlayerEngine.cs +++ b/GamecraftModdingAPI/Players/PlayerEngine.cs @@ -50,10 +50,10 @@ namespace GamecraftModdingAPI.Players public uint GetLocalPlayer() { if (!isReady) return uint.MaxValue; - var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.LocalPlayers); + var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.LocalPlayers).ToBuffer(); if (localPlayers.count > 0) { - return localPlayers[0].ID.entityID; + return localPlayers.buffer[0].ID.entityID; } return uint.MaxValue; } @@ -61,10 +61,10 @@ namespace GamecraftModdingAPI.Players public uint GetRemotePlayer() { if (!isReady) return uint.MaxValue; - var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.RemotePlayers); + var localPlayers = entitiesDB.QueryEntities(PlayersExclusiveGroups.RemotePlayers).ToBuffer(); if (localPlayers.count > 0) { - return localPlayers[0].ID.entityID; + return localPlayers.buffer[0].ID.entityID; } return uint.MaxValue; } diff --git a/GamecraftModdingAPI/SimBody.cs b/GamecraftModdingAPI/SimBody.cs index a3f6d55..42812d9 100644 --- a/GamecraftModdingAPI/SimBody.cs +++ b/GamecraftModdingAPI/SimBody.cs @@ -17,12 +17,13 @@ namespace GamecraftModdingAPI public EGID Id { get; } /// - /// The cluster this chunk belongs to, or null if the chunk doesn't exist. Get the SimBody from a Block if possible for good performance here. + /// 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)); private Cluster cluster; - private uint clusterId; + private readonly uint clusterId = uint.MaxValue; public SimBody(EGID id) { @@ -120,6 +121,15 @@ namespace GamecraftModdingAPI return Block.BlockEngine.GetConnectedSimBodies(Id.entityID); } + /// + /// The blocks that form this rigid body. + /// + /// + public Block[] GetBlocks() + { + return Block.BlockEngine.GetBodyBlocks(Id.entityID); + } + private ref RigidBodyEntityStruct GetStruct() { return ref Block.BlockEngine.GetBlockInfo(Id); diff --git a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs index 81b1f91..268fdb2 100644 --- a/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs +++ b/GamecraftModdingAPI/Tests/GamecraftModdingAPIPluginTest.cs @@ -1,18 +1,22 @@ 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 RobocraftX.Schedulers; using Svelto.Tasks.ExtraLean; using uREPL; @@ -22,6 +26,7 @@ using GamecraftModdingAPI.Events; using GamecraftModdingAPI.Utility; using GamecraftModdingAPI.Blocks; using GamecraftModdingAPI.Players; +using EventType = GamecraftModdingAPI.Events.EventType; namespace GamecraftModdingAPI.Tests { @@ -176,11 +181,6 @@ namespace GamecraftModdingAPI.Tests Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms"); }) .Build(); - //With Sync(): 1135ms - //Without Sync(): 134ms - //Async: 348 794ms, doesn't freeze game - //Without Sync() but wait for submission: 530ms - //With Sync() at the end: 380ms Block b = null; CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up") @@ -277,6 +277,20 @@ namespace GamecraftModdingAPI.Tests 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); diff --git a/GamecraftModdingAPI/Tests/TestRoot.cs b/GamecraftModdingAPI/Tests/TestRoot.cs index 6acb51c..22f3035 100644 --- a/GamecraftModdingAPI/Tests/TestRoot.cs +++ b/GamecraftModdingAPI/Tests/TestRoot.cs @@ -65,7 +65,7 @@ namespace GamecraftModdingAPI.Tests _testsCountPassed = 0; _testsCountFailed = 0; // flow control - Game.Enter += (sender, args) => { GameTests().RunOn(RobocraftX.Schedulers.Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING); }; + Game.Enter += (sender, args) => { GameTests().RunOn(RobocraftX.Schedulers.Lean.EveryFrameStepRunner_TimeRunningAndStopped); }; Game.Exit += (s, a) => state = "ReturningFromGame"; Client.EnterMenu += (sender, args) => { diff --git a/GamecraftModdingAPI/Utility/FullGameFields.cs b/GamecraftModdingAPI/Utility/FullGameFields.cs index 8fd8895..ccf80de 100644 --- a/GamecraftModdingAPI/Utility/FullGameFields.cs +++ b/GamecraftModdingAPI/Utility/FullGameFields.cs @@ -12,8 +12,9 @@ using RobocraftX.GUI; using RobocraftX.Multiplayer; 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; @@ -159,6 +160,14 @@ namespace GamecraftModdingAPI.Utility } } + public static FasterList _deserialisedBlockMap + { + get + { + return (FasterList) fgcr?.Field("_deserialisedBlockMap").GetValue(); + } + } + private static Traverse fgcr; public static void Init(FullGameCompositionRoot instance) diff --git a/doxygen.conf b/doxygen.conf index bf447b4..9958d11 100644 --- a/doxygen.conf +++ b/doxygen.conf @@ -38,7 +38,7 @@ PROJECT_NAME = "GamecraftModdingAPI" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "v1.5.0" +PROJECT_NUMBER = "v1.7.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