A stable modding interface between Techblox and mods https://mod.exmods.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
9.8KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using RobocraftX.Character;
  4. using RobocraftX.Character.Movement;
  5. using RobocraftX.Common.Players;
  6. using RobocraftX.Common.Input;
  7. using RobocraftX.CR.MachineEditing.BoxSelect;
  8. using RobocraftX.Physics;
  9. using RobocraftX.Blocks.Ghost;
  10. using Gamecraft.GUI.HUDFeedbackBlocks;
  11. using RobocraftX.Multiplayer;
  12. using RobocraftX.SimulationModeState;
  13. using Svelto.ECS;
  14. using Techblox.Camera;
  15. using Unity.Mathematics;
  16. using Svelto.ECS.DataStructures;
  17. using Techblox.BuildingDrone;
  18. using Techblox.Character;
  19. using TechbloxModdingAPI.Engines;
  20. using TechbloxModdingAPI.Input;
  21. using TechbloxModdingAPI.Utility;
  22. namespace TechbloxModdingAPI.Players
  23. {
  24. internal class PlayerEngine : IFunEngine
  25. {
  26. public string Name { get; } = "TechbloxModdingAPIPlayerGameEngine";
  27. public EntitiesDB entitiesDB { set; private get; }
  28. public bool isRemovable => false;
  29. public IEntityFunctions Functions { get; set; }
  30. private bool isReady = false;
  31. public void Dispose()
  32. {
  33. isReady = false;
  34. }
  35. public void Ready()
  36. {
  37. isReady = true;
  38. }
  39. public uint GetLocalPlayer()
  40. {
  41. if (!isReady) return uint.MaxValue;
  42. var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers).ToBuffer();
  43. if (localPlayers.count > 0)
  44. {
  45. return localPlayers.buffer[0].ID.entityID;
  46. }
  47. return uint.MaxValue;
  48. }
  49. public uint GetRemotePlayer()
  50. {
  51. if (!isReady) return uint.MaxValue;
  52. var localPlayers = entitiesDB.QueryEntities<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers).ToBuffer();
  53. if (localPlayers.count > 0)
  54. {
  55. return localPlayers.buffer[0].ID.entityID;
  56. }
  57. return uint.MaxValue;
  58. }
  59. public long GetAllPlayerCount()
  60. {
  61. if (entitiesDB == null) return 0;
  62. long count = 0;
  63. foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers)
  64. {
  65. count += entitiesDB.Count<PlayerIDStruct>(eg);
  66. }
  67. return count;
  68. }
  69. public long GetLocalPlayerCount()
  70. {
  71. if (entitiesDB == null) return 0;
  72. return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.LocalPlayers);
  73. }
  74. public long GetRemotePlayerCount()
  75. {
  76. if (entitiesDB == null) return 0;
  77. return entitiesDB.Count<PlayerIDStruct>(PlayersExclusiveGroups.RemotePlayers);
  78. }
  79. public bool ExistsById(uint playerId)
  80. {
  81. if (entitiesDB == null) return false;
  82. return entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.LocalPlayers)
  83. || entitiesDB.Exists<PlayerIDStruct>(playerId, PlayersExclusiveGroups.RemotePlayers);
  84. }
  85. public bool SetLocation(uint playerId, float3 location, bool exitSeat = true)
  86. {
  87. if (entitiesDB == null) return false;
  88. var rbesOpt = GetCharacterStruct<RigidBodyEntityStruct>(playerId, out var group);
  89. if (!rbesOpt)
  90. return false;
  91. if (group == CharacterExclusiveGroups.InPilotSeatGroup && exitSeat)
  92. {
  93. ExitSeat(playerId);
  94. }
  95. rbesOpt.Get().position = location;
  96. return true;
  97. }
  98. public bool GetGameOverScreen(uint playerId)
  99. {
  100. if (entitiesDB == null) return false;
  101. ref HudActivatedBlocksEntityStruct habes = ref entitiesDB.QueryEntity<HudActivatedBlocksEntityStruct>(HUDFeedbackBlocksGUIExclusiveGroups.GameOverHudEgid);
  102. NativeDynamicArrayCast<EGID> nativeDynamicArrayCast = new NativeDynamicArrayCast<EGID>(habes.activatedBlocksOrdered);
  103. return nativeDynamicArrayCast.count > 0;
  104. }
  105. public bool IsDead(uint playerId)
  106. {
  107. if (entitiesDB == null) return true;
  108. return entitiesDB.Exists<RigidBodyEntityStruct>(playerId, CharacterExclusiveGroups.DeadCharacters);
  109. }
  110. // reusable methods
  111. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  112. public OptionalRef<T> GetCharacterStruct<T>(uint playerId) where T : unmanaged, IEntityComponent =>
  113. GetCharacterStruct<T>(playerId, out _);
  114. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  115. public OptionalRef<T> GetCharacterStruct<T>(uint playerId, out ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
  116. {
  117. group = default;
  118. if (GameState.IsBuildMode())
  119. return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, LocalBuildingDrone.BuildGroup));
  120. var characterGroups = CharacterExclusiveGroups.AllCharacters;
  121. for (int i = 0; i < characterGroups.count; i++)
  122. {
  123. EGID egid = new EGID(playerId, characterGroups[i]);
  124. var opt = entitiesDB.QueryEntityOptional<T>(egid);
  125. if (opt.Exists)
  126. {
  127. group = characterGroups[i];
  128. return opt;
  129. }
  130. }
  131. return new OptionalRef<T>();
  132. }
  133. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  134. public OptionalRef<T> GetPlayerStruct<T>(uint playerId, PlayerType type) where T : unmanaged, IEntityComponent
  135. {
  136. var playerGroup = type == PlayerType.Local ? PlayersExclusiveGroups.LocalPlayers : PlayersExclusiveGroups.RemotePlayers;
  137. EGID egid = new EGID(playerId, playerGroup);
  138. return entitiesDB.QueryEntityOptional<T>(egid);
  139. }
  140. public OptionalRef<T> GetCameraStruct<T>(uint playerId) where T : unmanaged, IEntityComponent
  141. {
  142. return entitiesDB.QueryEntityOptional<T>(new EGID(playerId, CameraExclusiveGroups.PhysicCameraGroup));
  143. }
  144. public EGID GetThingLookedAt(uint playerId, float maxDistance = -1f)
  145. {
  146. var opt = GetCameraStruct<PhysicCameraRayCastEntityStruct>(playerId);
  147. if (!opt) return default;
  148. PhysicCameraRayCastEntityStruct rayCast = opt;
  149. float distance = maxDistance < 0
  150. ? GhostBlockUtils.GetBuildInteractionDistance(entitiesDB, rayCast,
  151. GhostBlockUtils.GhostCastMethod.GhostCastProportionalToBlockSize)
  152. : maxDistance;
  153. if (rayCast.hit && rayCast.distance <= distance)
  154. return rayCast.hitEgid; //May be EGID.Empty (default)
  155. return default;
  156. }
  157. public unsafe Block[] GetSelectedBlocks(uint playerid)
  158. {
  159. if (!entitiesDB.Exists<BoxSelectStateEntityStruct>(playerid,
  160. BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup))
  161. return Array.Empty<Block>();
  162. var state = entitiesDB.QueryEntity<BoxSelectStateEntityStruct>(playerid,
  163. BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
  164. var blocks = entitiesDB.QueryEntity<SelectedBlocksStruct>(playerid,
  165. BoxSelectExclusiveGroups.BoxSelectVolumeExclusiveGroup);
  166. if (!state.active) return Array.Empty<Block>();
  167. var pointer = (EGID*) blocks.selectedBlocks.ToPointer();
  168. var ret = new Block[blocks.count];
  169. for (int j = 0; j < blocks.count; j++)
  170. {
  171. var egid = pointer[j];
  172. ret[j] = Block.New(egid);
  173. }
  174. return ret;
  175. }
  176. public void EnterSeat(uint playerId, EGID seatId)
  177. {
  178. if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
  179. return;
  180. /*PilotSeatGroupUtils.SwapTagTo<OCCUPIED_TAG>(Functions, seatId);
  181. var opt = GetCharacterStruct<CharacterPilotSeatEntityStruct>(playerId, out var group);
  182. if (!opt) return; - TODO: This is server code and mods run in client code atm. We can only send inputs even in singleplayer as it is.
  183. ref CharacterPilotSeatEntityStruct charSeat = ref opt.Get();
  184. var charId = new EGID(playerId, group);
  185. charSeat.pilotSeatEntity = entitiesDB.GetEntityReference(seatId);
  186. charSeat.entryPositionOffset =
  187. entitiesDB.QueryEntity<PositionEntityStruct>(charId).position -
  188. entitiesDB.QueryEntity<PositionEntityStruct>(seatId).position;
  189. ref var seat = ref entitiesDB.QueryEntity<PilotSeatEntityStruct>(seatId);
  190. seat.occupyingCharacter = entitiesDB.GetEntityReference(charId);
  191. charSeat.followCam = entitiesDB.QueryEntity<SeatFollowCamComponent>(seatId).followCam;
  192. Functions.SwapEntityGroup<CharacterEntityDescriptor>(charId, CharacterExclusiveGroups.InPilotSeatGroup);*/
  193. }
  194. public void ExitSeat(uint playerId)
  195. {
  196. if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
  197. return;
  198. EGID egid = new EGID(playerId, CharacterExclusiveGroups.InPilotSeatGroup);
  199. var opt = entitiesDB.QueryEntityOptional<CharacterPilotSeatEntityStruct>(egid);
  200. if (!opt) return;
  201. opt.Get().instantExit = true;
  202. entitiesDB.PublishEntityChange<CharacterPilotSeatEntityStruct>(egid);
  203. }
  204. public bool SpawnMachine(uint playerId)
  205. {
  206. if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
  207. return false;
  208. EGID egid = new EGID(playerId, CharacterExclusiveGroups.MachineSpawningGroup);
  209. if (!entitiesDB.Exists<CharacterTagEntityStruct>(egid))
  210. return false;
  211. if (entitiesDB.QueryEntity<CharacterMachineSpawningValidityComponent>(egid).isMachinePlacementInvalid)
  212. {
  213. Logging.MetaDebugLog("Machine placement invalid");
  214. return false;
  215. }
  216. //Functions.SwapEntityGroup<CharacterEntityDescriptor>(egid, CharacterExclusiveGroups.OnFootGroup);
  217. FakeInput.ActionInput(playerId, primary: true);
  218. return true;
  219. }
  220. public bool DespawnMachine(uint playerId)
  221. {
  222. if (!TimeRunningModeUtil.IsTimeRunningMode(entitiesDB))
  223. return false;
  224. GetCharacterStruct<CharacterTagEntityStruct>(playerId, out var group);
  225. if (group.isInvalid)
  226. return false;
  227. EGID egid = new EGID(playerId, group);
  228. if (!entitiesDB.Exists<CharacterTagEntityStruct>(egid))
  229. return false;
  230. Functions.SwapEntityGroup<CharacterEntityDescriptor>(egid, CharacterExclusiveGroups.MachineSpawningGroup);
  231. return true;
  232. }
  233. public uint GetPing()
  234. {
  235. return entitiesDB
  236. .QueryUniqueEntity<NetworkStatsEntityStruct>(MultiplayerExclusiveGroups.MultiplayerStateGroup)
  237. .networkStats.PingMs;
  238. }
  239. }
  240. }