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.

272 lines
9.5KB

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