Browse Source

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
tags/v2.1.0
NorbiPeti 2 years ago
parent
commit
6204b226d1
18 changed files with 286 additions and 130 deletions
  1. +31
    -36
      TechbloxModdingAPI/App/GameGameEngine.cs
  2. +16
    -12
      TechbloxModdingAPI/Block.cs
  3. +2
    -3
      TechbloxModdingAPI/BlockGroup.cs
  4. +11
    -14
      TechbloxModdingAPI/Blocks/Wire.cs
  5. +1
    -4
      TechbloxModdingAPI/Cluster.cs
  6. +18
    -8
      TechbloxModdingAPI/EcsObjectBase.cs
  7. +3
    -0
      TechbloxModdingAPI/Engines/EnginePatches.cs
  8. +9
    -0
      TechbloxModdingAPI/Engines/IFunEngine.cs
  9. +30
    -0
      TechbloxModdingAPI/Player.Events.cs
  10. +40
    -19
      TechbloxModdingAPI/Player.cs
  11. +36
    -8
      TechbloxModdingAPI/Players/PlayerEngine.cs
  12. +33
    -0
      TechbloxModdingAPI/Players/PlayerEventsEngine.cs
  13. +33
    -2
      TechbloxModdingAPI/Players/PlayerTests.cs
  14. +1
    -4
      TechbloxModdingAPI/SimBody.cs
  15. +8
    -0
      TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs
  16. +0
    -8
      TechbloxModdingAPI/Utility/FullGameFields.cs
  17. +7
    -4
      TechbloxModdingAPI/Utility/GameEngineManager.cs
  18. +7
    -8
      TechbloxModdingAPI/Utility/MenuEngineManager.cs

+ 31
- 36
TechbloxModdingAPI/App/GameGameEngine.cs 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);
}

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();
}
}

public void EnableScreenshotTaker()
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();
}

public void EnableScreenshotTaker()
{
ref var local = ref entitiesDB.QueryEntity<ScreenshotModeEntityStruct>(ScreenshotTakerEgids.ScreenshotTaker);
if (local.enabled)


+ 16
- 12
TechbloxModdingAPI/Block.cs 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>


+ 2
- 3
TechbloxModdingAPI/BlockGroup.cs 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;


+ 11
- 14
TechbloxModdingAPI/Blocks/Wire.cs 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>


+ 1
- 4
TechbloxModdingAPI/Cluster.cs 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))


+ 18
- 8
TechbloxModdingAPI/EcsObjectBase.cs View File

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


+ 3
- 0
TechbloxModdingAPI/Engines/EnginePatches.cs 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()


+ 9
- 0
TechbloxModdingAPI/Engines/IFunEngine.cs View File

@@ -0,0 +1,9 @@
using Svelto.ECS;

namespace TechbloxModdingAPI.Engines
{
public interface IFunEngine : IApiEngine
{
public IEntityFunctions Functions { set; }
}
}

+ 30
- 0
TechbloxModdingAPI/Player.Events.cs 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);
}
}

+ 40
- 19
TechbloxModdingAPI/Player.cs 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.
@@ -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);
}
/// <summary>
/// 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);
}
}
}

+ 36
- 8
TechbloxModdingAPI/Players/PlayerEngine.cs 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);
}
}
}

+ 33
- 0
TechbloxModdingAPI/Players/PlayerEventsEngine.cs 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 });
}
}
}

+ 33
- 2
TechbloxModdingAPI/Players/PlayerTests.cs 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
@@ -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<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()


+ 1
- 4
TechbloxModdingAPI/SimBody.cs 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))


+ 8
- 0
TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs 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"))


+ 0
- 8
TechbloxModdingAPI/Utility/FullGameFields.cs 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


+ 7
- 4
TechbloxModdingAPI/Utility/GameEngineManager.cs 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;
}
}
}


+ 7
- 8
TechbloxModdingAPI/Utility/MenuEngineManager.cs 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;
}
}
}


Loading…
Cancel
Save