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
This commit is contained in:
Norbi Peti 2021-10-11 01:26:35 +02:00
parent 4bd636b8ed
commit 6204b226d1
18 changed files with 284 additions and 128 deletions

View file

@ -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<TaskContract> 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<BlockTagEntityStruct>();
List<EGID> blockEGIDs = new List<EGID>();
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<BlockTagEntityStruct>();
List<EGID> blockEGIDs = new List<EGID>();
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<DBEntityStruct>(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<DBEntityStruct>(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<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
if (local.enabled)

View file

@ -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)));
}
/// <summary>
/// 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
/// <param name="player">The player who placed the block</param>
/// <param name="force">Place even if not in build mode</param>
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;
/// <summary>

View file

@ -19,17 +19,16 @@ namespace TechbloxModdingAPI
public class BlockGroup : EcsObjectBase, ICollection<Block>, IDisposable
{
internal static BlueprintEngine _engine = new BlueprintEngine();
public override EGID Id { get; }
private readonly Block sourceBlock;
private readonly List<Block> 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<Block>(GetBlocks());
Block.Removed += OnBlockRemoved;

View file

@ -76,10 +76,11 @@ namespace TechbloxModdingAPI.Blocks
/// <param name="startPort">Starting port number, or guess if omitted.</param>
/// <param name="endPort">Ending port number, or guess if omitted.</param>
/// <exception cref="WireInvalidException">Guessing failed or wire does not exist.</exception>
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;
})
{
}
/// <summary>
@ -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.
/// </summary>
/// <param name="wireEgid">The wire ID.</param>
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
{
}
/// <summary>
/// The wire's in-game id.
/// </summary>
public override EGID Id
{
get => wireEGID;
}
/// <summary>
/// The wire's signal value, as a float.
/// </summary>

View file

@ -10,11 +10,8 @@ namespace TechbloxModdingAPI
/// </summary>
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))

View file

@ -10,7 +10,7 @@ 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<Type, WeakDictionary<EGID, EcsObjectBase>> _instances =
new Dictionary<Type, WeakDictionary<EGID, EcsObjectBase>>();
@ -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<EGID, EcsObjectBase>();
_instances.Add(GetType(), dict);
}
if (!dict.ContainsKey(id)) // Multiple instances may be created
dict.Add(id, this);
Id = id;
}
protected EcsObjectBase(Func<EcsObjectBase, EGID> 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

View file

@ -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()

View file

@ -0,0 +1,9 @@
using Svelto.ECS;
namespace TechbloxModdingAPI.Engines
{
public interface IFunEngine : IApiEngine
{
public IEntityFunctions Functions { set; }
}
}

View file

@ -0,0 +1,30 @@
using System;
using Svelto.ECS;
using TechbloxModdingAPI.Blocks;
using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI
{
public partial class Player
{
internal WrappedHandler<PlayerSeatEventArgs> seatEntered;
public event EventHandler<PlayerSeatEventArgs> SeatEntered
{
add => seatEntered += value;
remove => seatEntered -= value;
}
internal WrappedHandler<PlayerSeatEventArgs> seatExited;
public event EventHandler<PlayerSeatEventArgs> SeatExited
{
add => seatExited += value;
remove => seatExited -= value;
}
}
public struct PlayerSeatEventArgs
{
public EGID SeatId;
public Seat Seat => (Seat)Block.New(SeatId);
}
}

View file

@ -20,10 +20,11 @@ namespace TechbloxModdingAPI
/// <summary>
/// An in-game player character. Any Leo you see is a player.
/// </summary>
public class Player : IEquatable<Player>, IEquatable<EGID>
{ //TODO: Inherit EcsObjectBase and make Id an EGID, useful for caching
public partial class Player : EcsObjectBase, IEquatable<Player>, IEquatable<EGID>
{
// static functionality
private static PlayerEngine playerEngine = new PlayerEngine();
private static PlayerEventsEngine playerEventsEngine = new PlayerEventsEngine();
private static Player localPlayer;
/// <summary>
@ -79,7 +80,7 @@ namespace TechbloxModdingAPI
/// Initializes a new instance of the <see cref="T:TechbloxModdingAPI.Player"/> class.
/// </summary>
/// <param name="id">The player's unique identifier.</param>
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 <see cref="T:TechbloxModdingAPI.Player"/> class.
/// </summary>
/// <param name="player">The player type. Chooses the first available player matching the criteria.</param>
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.
/// </summary>
/// <value>The identifier.</value>
public uint Id { get; }
public new uint Id { get; }
/// <summary>
/// The player's current position.
@ -425,6 +435,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);
}
/// <summary>
/// Returns the block the player is currently looking at in build mode.
/// </summary>
@ -507,6 +527,7 @@ namespace TechbloxModdingAPI
internal static void Init()
{
Utility.GameEngineManager.AddGameEngine(playerEngine);
Utility.GameEngineManager.AddGameEngine(playerEventsEngine);
}
}
}

View file

@ -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<CharacterPilotSeatEntityStruct>(egid).instantExit = true;
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
ExitSeat(playerId);
}
rbesOpt.Get().position = location;
return true;
@ -183,12 +185,12 @@ namespace TechbloxModdingAPI.Players
{
if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
return new Block[0];
return Array.Empty<Block>();
var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
if (!state.active) return new Block[0];
if (!state.active) return Array.Empty<Block>();
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<OCCUPIED_TAG>(Functions, seatId);
var opt = GetCharacterStruct<CharacterPilotSeatEntityStruct>(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<PositionEntityStruct>(charId).position -
entitiesDB.QueryEntity<PositionEntityStruct>(seatId).position;
ref var seat = ref entitiesDB.QueryEntity<PilotSeatEntityStruct>(seatId);
seat.occupyingCharacter = entitiesDB.GetEntityReference(charId);
charSeat.followCam = entitiesDB.QueryEntity<SeatFollowCamComponent>(seatId).followCam;
Functions.SwapEntityGroup<CharacterEntityDescriptor>(charId, CharacterExclusiveGroups.InPilotSeatGroup);
}
public void ExitSeat(uint playerId)
{
EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup);
var opt = entitiesDB.QueryEntityOptional<CharacterPilotSeatEntityStruct>(egid);
if (!opt) return;
opt.Get().instantExit = true;
entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
}
}
}

View file

@ -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<CharacterPilotSeatEntityStruct>
{
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 });
}
}
}

View file

@ -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
@ -31,6 +34,34 @@ namespace TechbloxModdingAPI.Players
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<PlayerSeatEventArgs>("SeatEntered");
Player.LocalPlayer.SeatExited += Assert.CallsBack<PlayerSeatEventArgs>("SeatExited");
Block.PlaceNew(BlockIDs.DriverSeat, -1f);
}
[APITestCase(TestType.SimulationMode)]
public static IEnumerator<TaskContract> 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()
{

View file

@ -14,8 +14,6 @@ namespace TechbloxModdingAPI
/// </summary>
public class SimBody : EcsObjectBase, IEquatable<SimBody>, IEquatable<EGID>
{
public override EGID Id { get; }
/// <summary>
/// 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))

View file

@ -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"))

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}