From 6204b226d17a936906ca46eb2ecf4d22b81d56f7 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Mon, 11 Oct 2021 01:26:35 +0200 Subject: [PATCH] Seat events, and everything needed to get there - Added support for seat enter and exit events and a test for them - Added support for entering and exiting seat from code - Changed the Id property of ECS objects to non-abstract, requiring it in the constructor, so that the Player class can inherit EcsObjectBase - Added a weird constructor to EcsObjectBase that allows running code to determine the object Id - Added interface for engines that receive entity functions - Exposed the entity submission scheduler and removed it from FullGameFields because it moved from there - Made the Game.Enter event only fire after the first entity submission so the game is fully initialized and the local player exists - Added all seat groups to the dictionary --- TechbloxModdingAPI/App/GameGameEngine.cs | 63 +++++++++---------- TechbloxModdingAPI/Block.cs | 28 +++++---- TechbloxModdingAPI/BlockGroup.cs | 5 +- TechbloxModdingAPI/Blocks/Wire.cs | 25 ++++---- TechbloxModdingAPI/Cluster.cs | 5 +- TechbloxModdingAPI/EcsObjectBase.cs | 26 +++++--- TechbloxModdingAPI/Engines/EnginePatches.cs | 3 + TechbloxModdingAPI/Engines/IFunEngine.cs | 9 +++ TechbloxModdingAPI/Player.Events.cs | 30 +++++++++ TechbloxModdingAPI/Player.cs | 59 +++++++++++------ TechbloxModdingAPI/Players/PlayerEngine.cs | 44 ++++++++++--- .../Players/PlayerEventsEngine.cs | 33 ++++++++++ TechbloxModdingAPI/Players/PlayerTests.cs | 35 ++++++++++- TechbloxModdingAPI/SimBody.cs | 5 +- .../Tests/TechbloxModdingAPIPluginTest.cs | 8 +++ TechbloxModdingAPI/Utility/FullGameFields.cs | 8 --- .../Utility/GameEngineManager.cs | 11 ++-- .../Utility/MenuEngineManager.cs | 15 +++-- 18 files changed, 284 insertions(+), 128 deletions(-) create mode 100644 TechbloxModdingAPI/Engines/IFunEngine.cs create mode 100644 TechbloxModdingAPI/Player.Events.cs create mode 100644 TechbloxModdingAPI/Players/PlayerEventsEngine.cs diff --git a/TechbloxModdingAPI/App/GameGameEngine.cs b/TechbloxModdingAPI/App/GameGameEngine.cs index a773d61..9ec648e 100644 --- a/TechbloxModdingAPI/App/GameGameEngine.cs +++ b/TechbloxModdingAPI/App/GameGameEngine.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using RobocraftX.Common; using RobocraftX.Schedulers; @@ -11,6 +10,7 @@ using RobocraftX.Blocks; using RobocraftX.ScreenshotTaker; using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Engines; +using TechbloxModdingAPI.Tasks; using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.App @@ -35,6 +35,12 @@ namespace TechbloxModdingAPI.App public void Ready() { + EnteringGame().RunOn(Scheduler.leanRunner); + } + + private IEnumerator EnteringGame() + { + yield return new WaitForSubmissionEnumerator(GameLoadedEnginePatch.Scheduler).Continue(); EnterGame.Invoke(this, new GameEventArgs { GameName = GameMode.SaveGameDetails.Name, GamePath = GameMode.SaveGameDetails.Folder }); IsInGame = true; } @@ -96,40 +102,29 @@ namespace TechbloxModdingAPI.App TimeRunningModeUtil.ToggleTimeRunningState(entitiesDB); } - public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid) - { - var allBlocks = entitiesDB.QueryEntities(); - List blockEGIDs = new List(); - if (filter == BlockIDs.Invalid) - { - foreach (var (blocks, _) in allBlocks) - { - var buffer = blocks.ToBuffer().buffer; - for (int i = 0; i < buffer.capacity; i++) - blockEGIDs.Add(buffer[i].ID); - } + public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid) + { + var allBlocks = entitiesDB.QueryEntities(); + List blockEGIDs = new List(); + foreach (var (blocks, _) in allBlocks) + { + var (buffer, count) = blocks.ToBuffer(); + for (int i = 0; i < count; i++) + { + uint dbid; + if (filter == BlockIDs.Invalid) + dbid = (uint)filter; + else + dbid = entitiesDB.QueryEntity(buffer[i].ID).DBID; + if (dbid == (ulong)filter) + blockEGIDs.Add(buffer[i].ID); + } + } - return blockEGIDs.ToArray(); - } - else - { - foreach (var (blocks, _) in allBlocks) - { - var array = blocks.ToBuffer().buffer; - for (var index = 0; index < array.capacity; index++) - { - var block = array[index]; - uint dbid = entitiesDB.QueryEntity(block.ID).DBID; - if (dbid == (ulong) filter) - blockEGIDs.Add(block.ID); - } - } + return blockEGIDs.ToArray(); + } - return blockEGIDs.ToArray(); - } - } - - public void EnableScreenshotTaker() + public void EnableScreenshotTaker() { ref var local = ref entitiesDB.QueryEntity(ScreenshotTakerEgids.ScreenshotTaker); if (local.enabled) diff --git a/TechbloxModdingAPI/Block.cs b/TechbloxModdingAPI/Block.cs index 2cd995b..d94817f 100644 --- a/TechbloxModdingAPI/Block.cs +++ b/TechbloxModdingAPI/Block.cs @@ -99,12 +99,16 @@ namespace TechbloxModdingAPI {CommonExclusiveGroups.ENGINE_BLOCK_BUILD_GROUP, (id => new Engine(id), typeof(Engine))}, {CommonExclusiveGroups.LOGIC_BLOCK_GROUP, (id => new LogicGate(id), typeof(LogicGate))}, {CommonExclusiveGroups.PISTON_BLOCK_GROUP, (id => new Piston(id), typeof(Piston))}, - {SeatGroups.PASSENGER_BLOCK_BUILD_GROUP, (id => new Seat(id), typeof(Seat))}, - {SeatGroups.PILOTSEAT_BLOCK_BUILD_GROUP, (id => new Seat(id), typeof(Seat))}, {CommonExclusiveGroups.SERVO_BLOCK_GROUP, (id => new Servo(id), typeof(Servo))}, {CommonExclusiveGroups.WHEELRIG_BLOCK_BUILD_GROUP, (id => new WheelRig(id), typeof(WheelRig))} }; + static Block() + { + foreach (var group in SeatGroups.SEATS_BLOCK_GROUPS) // Adds driver and passenger seats, occupied and unoccupied + GroupToConstructor.Add(group, (id => new Seat(id), typeof(Seat))); + } + /// /// Returns a correctly typed instance of this block. The instances are shared for a specific block. /// If an instance is no longer referenced a new instance is returned. @@ -126,9 +130,8 @@ namespace TechbloxModdingAPI : GetInstance(egid, e => new Block(e)); } - public Block(EGID id) + public Block(EGID id) : base(id) { - Id = id; Type expectedType; if (GroupToConstructor.ContainsKey(id.groupID) && !GetType().IsAssignableFrom(expectedType = GroupToConstructor[id.groupID].Type)) @@ -156,17 +159,18 @@ namespace TechbloxModdingAPI /// The player who placed the block /// Place even if not in build mode public Block(BlockIDs type, float3 position, bool autoWire = false, Player player = null, bool force = false) + : base(block => + { + if (!PlacementEngine.IsInGame || !GameState.IsBuildMode() && !force) + throw new BlockException("Blocks can only be placed in build mode."); + var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire); + block.InitData = initializer; + Placed += ((Block)block).OnPlacedInit; + return initializer.EGID; + }) { - if (!PlacementEngine.IsInGame || !GameState.IsBuildMode() && !force) - throw new BlockException("Blocks can only be placed in build mode."); - var initializer = PlacementEngine.PlaceBlock(type, position, player, autoWire); - Id = initializer.EGID; - InitData = initializer; - Placed += OnPlacedInit; } - public override EGID Id { get; } - private EGID copiedFrom; /// diff --git a/TechbloxModdingAPI/BlockGroup.cs b/TechbloxModdingAPI/BlockGroup.cs index 39233e8..b8943b8 100644 --- a/TechbloxModdingAPI/BlockGroup.cs +++ b/TechbloxModdingAPI/BlockGroup.cs @@ -19,17 +19,16 @@ namespace TechbloxModdingAPI public class BlockGroup : EcsObjectBase, ICollection, IDisposable { internal static BlueprintEngine _engine = new BlueprintEngine(); - public override EGID Id { get; } private readonly Block sourceBlock; private readonly List blocks; private float3 position, rotation; internal bool PosAndRotCalculated; - internal BlockGroup(int id, Block block) + internal BlockGroup(int id, Block block) : base(new EGID((uint)id, + BlockGroupExclusiveGroups.BlockGroupEntityGroup)) { if (id == BlockGroupUtility.GROUP_UNASSIGNED) throw new BlockException("Cannot create a block group for blocks without a group!"); - Id = new EGID((uint) id, BlockGroupExclusiveGroups.BlockGroupEntityGroup); sourceBlock = block; blocks = new List(GetBlocks()); Block.Removed += OnBlockRemoved; diff --git a/TechbloxModdingAPI/Blocks/Wire.cs b/TechbloxModdingAPI/Blocks/Wire.cs index 0c66557..e59312d 100644 --- a/TechbloxModdingAPI/Blocks/Wire.cs +++ b/TechbloxModdingAPI/Blocks/Wire.cs @@ -76,10 +76,11 @@ namespace TechbloxModdingAPI.Blocks /// Starting port number, or guess if omitted. /// Ending port number, or guess if omitted. /// Guessing failed or wire does not exist. - public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue) + public Wire(Block start, Block end, byte startPort = Byte.MaxValue, byte endPort = Byte.MaxValue) : base(ecs => { - startBlockEGID = start.Id; - endBlockEGID = end.Id; + var th = (Wire)ecs; + th.startBlockEGID = start.Id; + th.endBlockEGID = end.Id; bool flipped = false; // find block ports EGID wire = signalEngine.MatchBlocksToWire(start.Id, end.Id, startPort, endPort); @@ -94,12 +95,16 @@ namespace TechbloxModdingAPI.Blocks if (wire != default) { - Construct(start.Id, end.Id, startPort, endPort, wire, flipped); + th.Construct(start.Id, end.Id, startPort, endPort, wire, flipped); } else { throw new WireInvalidException("Wire not found"); } + + return th.wireEGID; + }) + { } /// @@ -116,7 +121,7 @@ namespace TechbloxModdingAPI.Blocks { } - private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) + private Wire(EGID startBlock, EGID endBlock, byte startPort, byte endPort, EGID wire, bool inputToOutput) : base(wire) { Construct(startBlock, endBlock, startPort, endPort, wire, inputToOutput); } @@ -139,7 +144,7 @@ namespace TechbloxModdingAPI.Blocks /// Construct a wire object from an existing wire connection. /// /// The wire ID. - public Wire(EGID wireEgid) + public Wire(EGID wireEgid) : base(wireEgid) { WireEntityStruct wire = signalEngine.GetWire(wireEGID); Construct(wire.sourceBlockEGID, wire.destinationBlockEGID, wire.sourcePortUsage, wire.destinationPortUsage, @@ -151,14 +156,6 @@ namespace TechbloxModdingAPI.Blocks { } - /// - /// The wire's in-game id. - /// - public override EGID Id - { - get => wireEGID; - } - /// /// The wire's signal value, as a float. /// diff --git a/TechbloxModdingAPI/Cluster.cs b/TechbloxModdingAPI/Cluster.cs index 2544194..f88cec9 100644 --- a/TechbloxModdingAPI/Cluster.cs +++ b/TechbloxModdingAPI/Cluster.cs @@ -10,11 +10,8 @@ namespace TechbloxModdingAPI /// public class Cluster : EcsObjectBase { - public override EGID Id { get; } - - public Cluster(EGID id) + public Cluster(EGID id) : base(id) { - Id = id; } public Cluster(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_CLUSTERS_GROUP)) diff --git a/TechbloxModdingAPI/EcsObjectBase.cs b/TechbloxModdingAPI/EcsObjectBase.cs index f01561a..9698eaa 100644 --- a/TechbloxModdingAPI/EcsObjectBase.cs +++ b/TechbloxModdingAPI/EcsObjectBase.cs @@ -10,8 +10,8 @@ namespace TechbloxModdingAPI { public abstract class EcsObjectBase { - public abstract EGID Id { get; } //Abstract to support the 'place' Block constructor - + public EGID Id { get; } + private static readonly Dictionary> _instances = new Dictionary>(); @@ -39,7 +39,19 @@ namespace TechbloxModdingAPI return (T)instance; } - protected EcsObjectBase() + protected EcsObjectBase(EGID id) + { + if (!_instances.TryGetValue(GetType(), out var dict)) + { + dict = new WeakDictionary(); + _instances.Add(GetType(), dict); + } + if (!dict.ContainsKey(id)) // Multiple instances may be created + dict.Add(id, this); + Id = id; + } + + protected EcsObjectBase(Func initializer) { if (!_instances.TryGetValue(GetType(), out var dict)) { @@ -47,11 +59,9 @@ namespace TechbloxModdingAPI _instances.Add(GetType(), dict); } - // ReSharper disable VirtualMemberCallInConstructor - // The ID should not depend on the constructor - if (!dict.ContainsKey(Id)) // Multiple instances may be created - dict.Add(Id, this); - // ReSharper enable VirtualMemberCallInConstructor + var id = initializer(this); + if (!dict.ContainsKey(id)) // Multiple instances may be created + dict.Add(id, this); } #region ECS initializer stuff diff --git a/TechbloxModdingAPI/Engines/EnginePatches.cs b/TechbloxModdingAPI/Engines/EnginePatches.cs index f5774d9..8b6e6a5 100644 --- a/TechbloxModdingAPI/Engines/EnginePatches.cs +++ b/TechbloxModdingAPI/Engines/EnginePatches.cs @@ -5,6 +5,7 @@ using RobocraftX.CR.MainGame; using RobocraftX.FrontEnd; using RobocraftX.StateSync; using Svelto.ECS; +using Svelto.ECS.Schedulers; using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.Engines @@ -12,10 +13,12 @@ namespace TechbloxModdingAPI.Engines [HarmonyPatch] class GameLoadedEnginePatch { + public static EntitiesSubmissionScheduler Scheduler { get; private set; } public static void Postfix(StateSyncRegistrationHelper stateSyncReg) { // register all game engines, including deterministic GameEngineManager.RegisterEngines(stateSyncReg); + Scheduler = stateSyncReg.enginesRoot.scheduler; } public static MethodBase TargetMethod() diff --git a/TechbloxModdingAPI/Engines/IFunEngine.cs b/TechbloxModdingAPI/Engines/IFunEngine.cs new file mode 100644 index 0000000..d9ee7fe --- /dev/null +++ b/TechbloxModdingAPI/Engines/IFunEngine.cs @@ -0,0 +1,9 @@ +using Svelto.ECS; + +namespace TechbloxModdingAPI.Engines +{ + public interface IFunEngine : IApiEngine + { + public IEntityFunctions Functions { set; } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Player.Events.cs b/TechbloxModdingAPI/Player.Events.cs new file mode 100644 index 0000000..1a07c36 --- /dev/null +++ b/TechbloxModdingAPI/Player.Events.cs @@ -0,0 +1,30 @@ +using System; +using Svelto.ECS; +using TechbloxModdingAPI.Blocks; +using TechbloxModdingAPI.Utility; + +namespace TechbloxModdingAPI +{ + public partial class Player + { + internal WrappedHandler seatEntered; + public event EventHandler SeatEntered + { + add => seatEntered += value; + remove => seatEntered -= value; + } + + internal WrappedHandler seatExited; + public event EventHandler SeatExited + { + add => seatExited += value; + remove => seatExited -= value; + } + } + + public struct PlayerSeatEventArgs + { + public EGID SeatId; + public Seat Seat => (Seat)Block.New(SeatId); + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Player.cs b/TechbloxModdingAPI/Player.cs index 3dad9ea..4f5607a 100644 --- a/TechbloxModdingAPI/Player.cs +++ b/TechbloxModdingAPI/Player.cs @@ -20,10 +20,11 @@ namespace TechbloxModdingAPI /// /// An in-game player character. Any Leo you see is a player. /// - public class Player : IEquatable, IEquatable - { //TODO: Inherit EcsObjectBase and make Id an EGID, useful for caching + public partial class Player : EcsObjectBase, IEquatable, IEquatable + { // static functionality private static PlayerEngine playerEngine = new PlayerEngine(); + private static PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine(); private static Player localPlayer; /// @@ -79,7 +80,7 @@ namespace TechbloxModdingAPI /// Initializes a new instance of the class. /// /// The player's unique identifier. - public Player(uint id) + public Player(uint id) : base(new EGID(id, CharacterExclusiveGroups.OnFootGroup)) { this.Id = id; if (!Exists(id)) @@ -93,22 +94,31 @@ namespace TechbloxModdingAPI /// Initializes a new instance of the class. /// /// The player type. Chooses the first available player matching the criteria. - public Player(PlayerType player) + public Player(PlayerType player) : base(ecs => { - switch (player) - { - case PlayerType.Local: - this.Id = playerEngine.GetLocalPlayer(); - break; - case PlayerType.Remote: - this.Id = playerEngine.GetRemotePlayer(); - break; - } - if (this.Id == uint.MaxValue) - { - throw new PlayerNotFoundException($"No player of {player} type exists"); - } - this.Type = player; + uint id; + switch (player) + { + case PlayerType.Local: + id = playerEngine.GetLocalPlayer(); + break; + case PlayerType.Remote: + id = playerEngine.GetRemotePlayer(); + break; + default: + id = uint.MaxValue; + break; + } + + if (id == uint.MaxValue) + { + throw new PlayerNotFoundException($"No player of {player} type exists"); + } + + return new EGID(id, CharacterExclusiveGroups.OnFootGroup); + }) + { + this.Type = player; } // object fields & properties @@ -124,7 +134,7 @@ namespace TechbloxModdingAPI /// The player's unique identifier. /// /// The identifier. - public uint Id { get; } + public new uint Id { get; } /// /// The player's current position. @@ -424,6 +434,16 @@ namespace TechbloxModdingAPI } playerEngine.SetLocation(Id, location, exitSeat: exitSeat); } + + public void EnterSeat(Seat seat) + { + playerEngine.EnterSeat(Id, seat.Id); + } + + public void ExitSeat() + { + playerEngine.ExitSeat(Id); + } /// /// Returns the block the player is currently looking at in build mode. @@ -507,6 +527,7 @@ namespace TechbloxModdingAPI internal static void Init() { Utility.GameEngineManager.AddGameEngine(playerEngine); + Utility.GameEngineManager.AddGameEngine(playerEventsEngine); } } } diff --git a/TechbloxModdingAPI/Players/PlayerEngine.cs b/TechbloxModdingAPI/Players/PlayerEngine.cs index bb13308..0dc2298 100644 --- a/TechbloxModdingAPI/Players/PlayerEngine.cs +++ b/TechbloxModdingAPI/Players/PlayerEngine.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; using RobocraftX.Character; using RobocraftX.Character.Movement; @@ -8,10 +9,13 @@ using RobocraftX.CR.MachineEditing.BoxSelect; using RobocraftX.Physics; using RobocraftX.Blocks.Ghost; using Gamecraft.GUI.HUDFeedbackBlocks; +using RobocraftX.Blocks; +using RobocraftX.PilotSeat; using Svelto.ECS; using Techblox.Camera; using Unity.Mathematics; using Svelto.ECS.DataStructures; +using Svelto.ECS.EntityStructs; using Techblox.BuildingDrone; using TechbloxModdingAPI.Engines; @@ -19,7 +23,7 @@ using TechbloxModdingAPI.Utility; namespace TechbloxModdingAPI.Players { - internal class PlayerEngine : IApiEngine, IFactoryEngine + internal class PlayerEngine : IFunEngine { public string Name { get; } = "TechbloxModdingAPIPlayerGameEngine"; @@ -27,7 +31,7 @@ namespace TechbloxModdingAPI.Players public bool isRemovable => false; - public IEntityFactory Factory { set; private get; } + public IEntityFunctions Functions { get; set; } private bool isReady = false; @@ -101,9 +105,7 @@ namespace TechbloxModdingAPI.Players return false; if (group == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat) { - EGID egid = new EGID(playerId, group); - entitiesDB.QueryEntity(egid).instantExit = true; - entitiesDB.PublishEntityChange(egid); + ExitSeat(playerId); } rbesOpt.Get().position = location; return true; @@ -183,12 +185,12 @@ namespace TechbloxModdingAPI.Players { if (!entitiesDB.Exists(playerid, BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup)) - return new Block[0]; + return Array.Empty(); var state = entitiesDB.QueryEntity(playerid, BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup); var blocks = entitiesDB.QueryEntity(playerid, BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup); - if (!state.active) return new Block[0]; + if (!state.active) return Array.Empty(); var pointer = (EGID*) blocks.selectedBlocks.ToPointer(); var ret = new Block[blocks.count]; for (int j = 0; j < blocks.count; j++) @@ -199,5 +201,31 @@ namespace TechbloxModdingAPI.Players return ret; } + + public void EnterSeat(uint playerId, EGID seatId) + { + PilotSeatGroupUtils.SwapTagTo(Functions, seatId); + var opt = GetCharacterStruct(playerId, out var group); + if (!opt) return; + ref CharacterPilotSeatEntityStruct charSeat = ref opt.Get(); + var charId = new EGID(playerId, group); + charSeat.pilotSeatEntity = entitiesDB.GetEntityReference(seatId); + charSeat.entryPositionOffset = + entitiesDB.QueryEntity(charId).position - + entitiesDB.QueryEntity(seatId).position; + ref var seat = ref entitiesDB.QueryEntity(seatId); + seat.occupyingCharacter = entitiesDB.GetEntityReference(charId); + charSeat.followCam = entitiesDB.QueryEntity(seatId).followCam; + Functions.SwapEntityGroup(charId, CharacterExclusiveGroups.InPilotSeatGroup); + } + + public void ExitSeat(uint playerId) + { + EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup); + var opt = entitiesDB.QueryEntityOptional(egid); + if (!opt) return; + opt.Get().instantExit = true; + entitiesDB.PublishEntityChange(egid); + } } } diff --git a/TechbloxModdingAPI/Players/PlayerEventsEngine.cs b/TechbloxModdingAPI/Players/PlayerEventsEngine.cs new file mode 100644 index 0000000..2c80f24 --- /dev/null +++ b/TechbloxModdingAPI/Players/PlayerEventsEngine.cs @@ -0,0 +1,33 @@ +using RobocraftX.Character; +using RobocraftX.Character.Movement; +using Svelto.ECS; +using TechbloxModdingAPI.Engines; + +namespace TechbloxModdingAPI.Players +{ + public class PlayerEventsEngine : IApiEngine, IReactOnSwap + { + public void Ready() + { + } + + public EntitiesDB entitiesDB { get; set; } + public void Dispose() + { + } + + public string Name => "TechbloxModdingAPIPlayerEventsEngine"; + public bool isRemovable => false; + + public void MovedTo(ref CharacterPilotSeatEntityStruct entityComponent, ExclusiveGroupStruct previousGroup, EGID egid) + { + var seatId = entityComponent.pilotSeatEntity.ToEGID(entitiesDB); + var player = EcsObjectBase.GetInstance(new EGID(egid.entityID, CharacterExclusiveGroups.OnFootGroup), + e => new Player(e.entityID)); + if (previousGroup == CharacterExclusiveGroups.InPilotSeatGroup) + player.seatExited.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId}); + else if (egid.groupID == CharacterExclusiveGroups.InPilotSeatGroup) + player.seatEntered.Invoke(this, new PlayerSeatEventArgs { SeatId = seatId }); + } + } +} \ No newline at end of file diff --git a/TechbloxModdingAPI/Players/PlayerTests.cs b/TechbloxModdingAPI/Players/PlayerTests.cs index 8086d96..c974518 100644 --- a/TechbloxModdingAPI/Players/PlayerTests.cs +++ b/TechbloxModdingAPI/Players/PlayerTests.cs @@ -1,8 +1,11 @@ -using System; +using System.Collections.Generic; +using Svelto.Tasks; +using Svelto.Tasks.Enumerators; using Unity.Mathematics; -using TechbloxModdingAPI; +using TechbloxModdingAPI.App; +using TechbloxModdingAPI.Blocks; using TechbloxModdingAPI.Tests; namespace TechbloxModdingAPI.Players @@ -30,6 +33,34 @@ namespace TechbloxModdingAPI.Players if (!Assert.Errorless(() => { p.Position = float3.zero + 1; }, "Player.Position = origin+1 errored: ", "Player moved to origin+1.")) return; Assert.CloseTo(p.Position, float3.zero + 1, "Player is not close to origin+1 despite being teleported there.", "Player.Position is at origin+1."); } + + [APITestCase(TestType.Game)] + public static void SeatEventTestBuild() + { + Player.LocalPlayer.SeatEntered += Assert.CallsBack("SeatEntered"); + Player.LocalPlayer.SeatExited += Assert.CallsBack("SeatExited"); + Block.PlaceNew(BlockIDs.DriverSeat, -1f); + } + + [APITestCase(TestType.SimulationMode)] + public static IEnumerator SeatEventTestSim() + { + var seats = Game.CurrentGame().GetBlocksInGame(BlockIDs.DriverSeat); + if (seats.Length == 0) + { + Assert.Fail("No driver seat found!"); + yield break; + } + + if (seats[0] is Seat seat) + Assert.Errorless(() => Player.LocalPlayer.EnterSeat(seat), "Failed to enter seat.", + "Entered seat successfully."); + else + Assert.Fail("Found a seat that is not a seat!"); + yield return new WaitForSecondsEnumerator(1).Continue(); + Assert.Errorless(() => Player.LocalPlayer.ExitSeat(), "Failed to exit seat.", + "Exited seat successfully."); + } [APITestCase(TestType.Menu)] public static void InvalidStateTest() diff --git a/TechbloxModdingAPI/SimBody.cs b/TechbloxModdingAPI/SimBody.cs index 2f2f8b6..bb05005 100644 --- a/TechbloxModdingAPI/SimBody.cs +++ b/TechbloxModdingAPI/SimBody.cs @@ -14,8 +14,6 @@ namespace TechbloxModdingAPI /// public class SimBody : EcsObjectBase, IEquatable, IEquatable { - public override EGID Id { get; } - /// /// The cluster this chunk belongs to, or null if no cluster destruction manager present or the chunk doesn't exist. /// Get the SimBody from a Block if possible for good performance here. @@ -28,9 +26,8 @@ namespace TechbloxModdingAPI private Cluster cluster; private readonly uint clusterId = uint.MaxValue; - public SimBody(EGID id) + public SimBody(EGID id) : base(id) { - Id = id; } public SimBody(uint id) : this(new EGID(id, CommonExclusiveGroups.SIMULATION_BODIES_GROUP)) diff --git a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs index dd1d2b3..4c648eb 100644 --- a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs +++ b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs @@ -58,6 +58,14 @@ namespace TechbloxModdingAPI.Tests // debug/test handlers Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!"); + Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler"); + Game.Enter += (s, a) => + { + Player.LocalPlayer.SeatEntered += (sender, args) => + Console.WriteLine($"Player {Player.LocalPlayer} entered seat {args.Seat}"); + Player.LocalPlayer.SeatExited += (sender, args) => + Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}"); + }; // debug/test commands if (Dependency.Hell("ExtraCommands")) diff --git a/TechbloxModdingAPI/Utility/FullGameFields.cs b/TechbloxModdingAPI/Utility/FullGameFields.cs index e27f501..0a4dd1d 100644 --- a/TechbloxModdingAPI/Utility/FullGameFields.cs +++ b/TechbloxModdingAPI/Utility/FullGameFields.cs @@ -72,14 +72,6 @@ namespace TechbloxModdingAPI.Utility } } - public static SimpleEntitiesSubmissionScheduler _mainGameSubmissionScheduler - { - get - { - return (SimpleEntitiesSubmissionScheduler)fgcr?.Field("_sub").Field("_mainGameSubmissionScheduler").GetValue(); - } - } - public static BuildPhysicsWorld _physicsWorldSystem { get diff --git a/TechbloxModdingAPI/Utility/GameEngineManager.cs b/TechbloxModdingAPI/Utility/GameEngineManager.cs index fc51861..16bf4e2 100644 --- a/TechbloxModdingAPI/Utility/GameEngineManager.cs +++ b/TechbloxModdingAPI/Utility/GameEngineManager.cs @@ -26,10 +26,10 @@ namespace TechbloxModdingAPI.Utility { Logging.MetaDebugLog($"Registering Game IApiEngine {engine.Name}"); _lastEngineRoot.AddEngine(engine); - if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType())) - { - ((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory(); - } + if (engine is IFactoryEngine factoryEngine) + factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory(); + if (engine is IFunEngine funEngine) + funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions(); } } @@ -66,6 +66,7 @@ namespace TechbloxModdingAPI.Utility var enginesRoot = helper.enginesRoot; _lastEngineRoot = enginesRoot; IEntityFactory factory = enginesRoot.GenerateEntityFactory(); + IEntityFunctions functions = enginesRoot.GenerateEntityFunctions(); foreach (var key in _gameEngines.Keys) { Logging.MetaDebugLog($"Registering Game IApiEngine {_gameEngines[key].Name}"); @@ -75,6 +76,8 @@ namespace TechbloxModdingAPI.Utility enginesRoot.AddEngine(_gameEngines[key]); if (_gameEngines[key] is IFactoryEngine factEngine) factEngine.Factory = factory; + if (_gameEngines[key] is IFunEngine funEngine) + funEngine.Functions = functions; } } } diff --git a/TechbloxModdingAPI/Utility/MenuEngineManager.cs b/TechbloxModdingAPI/Utility/MenuEngineManager.cs index 3260563..4358ca8 100644 --- a/TechbloxModdingAPI/Utility/MenuEngineManager.cs +++ b/TechbloxModdingAPI/Utility/MenuEngineManager.cs @@ -26,10 +26,10 @@ namespace TechbloxModdingAPI.Utility { Logging.MetaDebugLog($"Registering Menu IApiEngine {engine.Name}"); _lastEngineRoot.AddEngine(engine); - if (typeof(IFactoryEngine).IsAssignableFrom(engine.GetType())) - { - ((IFactoryEngine)engine).Factory = _lastEngineRoot.GenerateEntityFactory(); - } + if (engine is IFactoryEngine factoryEngine) + factoryEngine.Factory = _lastEngineRoot.GenerateEntityFactory(); + if (engine is IFunEngine funEngine) + funEngine.Functions = _lastEngineRoot.GenerateEntityFunctions(); } } @@ -65,14 +65,13 @@ namespace TechbloxModdingAPI.Utility { _lastEngineRoot = enginesRoot; IEntityFactory factory = enginesRoot.GenerateEntityFactory(); + IEntityFunctions functions = enginesRoot.GenerateEntityFunctions(); foreach (var key in _menuEngines.Keys) { Logging.MetaDebugLog($"Registering Menu IApiEngine {_menuEngines[key].Name}"); enginesRoot.AddEngine(_menuEngines[key]); - if (_menuEngines[key] is IFactoryEngine factEngine) - { - factEngine.Factory = factory; - } + if (_menuEngines[key] is IFactoryEngine factEngine) factEngine.Factory = factory; + if(_menuEngines[key] is IFunEngine funEngine) funEngine.Functions = functions; } } }