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.

282 lines
10KB

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