teleport_move.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /**
  2. * The teleport itself. The camera hook publishes a forward vector and reads the bound key once a
  3. * frame. The physics hook applies the move before the sync it runs ahead of. Physics owns the
  4. * position, so writing the object placement would move the camera alone.
  5. */
  6. #include <Windows.h>
  7. #include <array>
  8. #include <atomic>
  9. #include <cstddef>
  10. #include <cstdint>
  11. #include <cstdio>
  12. #include "../../../core/logging/log.h"
  13. #include "../../../core/ui/runtime/ui_visibility_runtime.h"
  14. #include "../../../state/account/account_state.h"
  15. #include "../../../state/runtime/runtime.h"
  16. #include "../../movement/movement_settings_store.h"
  17. #include "../noclip/runtime.h"
  18. #include "../polled_input/runtime.h"
  19. #include "internal.h"
  20. #include "runtime.h"
  21. namespace sunrise::client::hooks::teleport {
  22. namespace {
  23. /**
  24. * Frames a press stays pending. Orbit and loading screens tick the camera but never the player's
  25. * physics, so a request with no limit is used up later and reads as a queued teleport.
  26. */
  27. constexpr std::uint32_t kRequestLifetimeFrames = 3;
  28. /** Frames an ordinary physics tick gets to collect a request before the forced path takes it. */
  29. constexpr std::uint32_t kForceAfterFrames = 1;
  30. /**
  31. * Frames the injected press is held. It has to survive at least one scan and one integration
  32. * step, or the move it exists to publish is never read.
  33. */
  34. constexpr std::uint32_t kPressFrames = 2;
  35. /** Authored action driven to wake the body. Forward is the gentlest one that moves it. */
  36. constexpr std::uint16_t kForwardAction =
  37. static_cast<std::uint16_t>(state::account::settings::bindings::Action::moveForward);
  38. std::atomic_bool g_requested{false};
  39. std::atomic_bool g_forwardValid{false};
  40. std::atomic_bool g_keyDown{false};
  41. std::atomic_uint32_t g_requestAge{0};
  42. /** Set while the feature is usable, so the per-tick path costs one atomic read when it is not. */
  43. std::atomic_bool g_active{false};
  44. /**
  45. * The player's physics component, kept from the last tick that carried it. At rest the sync stops
  46. * being called for the player at all, so the pointer is the only way back to them.
  47. */
  48. std::atomic<std::byte*> g_playerComponent{nullptr};
  49. /** Frames left before the injected press is released. */
  50. std::atomic_uint32_t g_pressFrames{0};
  51. ControlledHandle g_controlledHandle{};
  52. CameraSingleton g_cameraSingleton{};
  53. /** Written by the camera hook and read by the physics hook. Both run on the same thread. */
  54. std::array<float, kVectorLanes> g_forward{};
  55. /**
  56. * Reads one value out of game memory without faulting on a torn pointer.
  57. * @param address Source address.
  58. * @param value Receives the value.
  59. * @return True when Windows copied the whole value.
  60. */
  61. template <typename T> [[nodiscard]] bool read_at(const std::byte* address, T& value) noexcept {
  62. if (address == nullptr) {
  63. return false;
  64. }
  65. SIZE_T read = 0;
  66. return ReadProcessMemory(GetCurrentProcess(), address, &value, sizeof value, &read) != FALSE
  67. && read == sizeof value;
  68. }
  69. /**
  70. * Writes one vector into game memory. The call applies page protection itself.
  71. * @param address Destination address.
  72. * @param value Three lanes to store.
  73. * @return True when Windows copied the whole vector.
  74. */
  75. [[nodiscard]] bool write_vector(std::byte* address,
  76. const std::array<float, kVectorLanes>& value) noexcept {
  77. if (address == nullptr) {
  78. return false;
  79. }
  80. SIZE_T written = 0;
  81. const SIZE_T size = sizeof(float) * kVectorLanes;
  82. return WriteProcessMemory(GetCurrentProcess(), address, value.data(), size, &written) != FALSE
  83. && written == size;
  84. }
  85. /**
  86. * Finds the rigid body a physics component drives.
  87. * @param component Physics component.
  88. * @return The body, or null when the chain breaks.
  89. */
  90. [[nodiscard]] std::byte* body_of(std::byte* component) noexcept {
  91. std::byte* array = nullptr;
  92. std::int32_t index = 0;
  93. if (!read_at(component + kPhysicsComponentBodyArray, array)
  94. || !read_at(component + kPhysicsComponentBodyIndex, index) || array == nullptr
  95. || index < 0) {
  96. return nullptr;
  97. }
  98. std::byte* body = nullptr;
  99. const std::size_t offset = kBodyEntryStride * static_cast<std::size_t>(index) + kBodyPointer;
  100. return read_at(array + offset, body) ? body : nullptr;
  101. }
  102. /**
  103. * Ages a pending request and drops it once nothing has taken it. A press is meant for the moment
  104. * it is made, so one that finds no player physics tick is dropped, not held for the next one.
  105. */
  106. void expire_request() noexcept {
  107. if (!g_requested.load(std::memory_order_acquire)) {
  108. return;
  109. }
  110. if (g_requestAge.fetch_add(1, std::memory_order_relaxed) + 1 >= kRequestLifetimeFrames) {
  111. g_requested.store(false, std::memory_order_release);
  112. }
  113. }
  114. /**
  115. * Runs the whole move for a component already proved to be the player's.
  116. * @param component Physics component driving the player.
  117. * @return True when the body was found and its position was written.
  118. */
  119. [[nodiscard]] bool perform_move(std::byte* component) noexcept;
  120. /** @param reason Key naming the step that stopped the move. */
  121. void report_skip(const char* reason) noexcept;
  122. /**
  123. * Starts the injected press that wakes the body.
  124. * Nothing reads the new body position until something integrates it, so the move is published by
  125. * driving the player's own forward action rather than by writing what it would have produced.
  126. */
  127. void begin_press() noexcept {
  128. const state::AccountState account = state::account_snapshot();
  129. const auto& binding = account.settings.keyBindings.values[kForwardAction];
  130. if (!binding.primary.has_value()) {
  131. return;
  132. }
  133. const std::uint32_t virtualKey = action_key(*binding.primary);
  134. if (virtualKey == 0) {
  135. report_skip("no_key");
  136. return;
  137. }
  138. hooks::polled_input::hold_key(virtualKey);
  139. g_pressFrames.store(kPressFrames, std::memory_order_release);
  140. }
  141. /** Releases the injected press once it has been scanned. */
  142. void end_press() noexcept {
  143. if (g_pressFrames.load(std::memory_order_acquire) == 0) {
  144. return;
  145. }
  146. if (g_pressFrames.fetch_sub(1, std::memory_order_acq_rel) <= 1) {
  147. hooks::polled_input::release_key();
  148. }
  149. }
  150. /**
  151. * Reports the gate values the sync tests before it publishes a transform.
  152. *
  153. * The move lands while moving and does nothing at rest for all three write targets, so what
  154. * changes at rest is upstream of the write. These four gates are what the sync reads first.
  155. *
  156. * @param component Physics component owning the player.
  157. * @param body Rigid body behind it.
  158. */
  159. void report_gates(const std::byte* component, const std::byte* body) noexcept {
  160. std::uint8_t suppressed = 0;
  161. std::int32_t bodyIndex = 0;
  162. std::uint32_t bodyFlags = 0;
  163. std::uint8_t motionType = 0;
  164. (void)read_at(component + kPhysicsComponentSuppress, suppressed);
  165. (void)read_at(component + kPhysicsComponentBodyIndex, bodyIndex);
  166. (void)read_at(body + kBodyFlags, bodyFlags);
  167. (void)read_at(body + kBodyMotionType, motionType);
  168. std::array<char, 160> line{};
  169. const int written = std::snprintf(line.data(),
  170. line.size(),
  171. "ev=teleport stage=gates suppress=%u index=%d "
  172. "flags=0x%08X active=%u motion=%u",
  173. static_cast<unsigned>(suppressed),
  174. static_cast<int>(bodyIndex),
  175. static_cast<unsigned>(bodyFlags),
  176. (bodyFlags & kBodyActiveBit) != 0 ? 1U : 0U,
  177. static_cast<unsigned>(motionType));
  178. if (written > 0) {
  179. core::log::write(core::log::Channel::client,
  180. core::log::Level::info,
  181. {line.data(), static_cast<std::size_t>(written)});
  182. }
  183. }
  184. /**
  185. * @param component Candidate physics component.
  186. * @return True when it drives the object the local player controls.
  187. */
  188. [[nodiscard]] bool owns_player(std::byte* component) noexcept {
  189. std::uint32_t controlled = kInvalidHandle;
  190. g_controlledHandle(&controlled);
  191. if (controlled == kInvalidHandle) {
  192. return false;
  193. }
  194. std::uint16_t owner = 0;
  195. return read_at(component + kPhysicsComponentObjectHandle, owner)
  196. && (controlled & kHandleIndexMask)
  197. == (static_cast<std::uint32_t>(owner) & kHandleIndexMask);
  198. }
  199. /** @param reason Key naming the step that stopped the move. */
  200. void report_skip(const char* reason) noexcept {
  201. std::array<char, 96> line{};
  202. const int written = std::snprintf(
  203. line.data(), line.size(), "ev=teleport stage=move result=skip reason=%s", reason);
  204. if (written > 0) {
  205. core::log::write(core::log::Channel::client,
  206. core::log::Level::warn,
  207. {line.data(), static_cast<std::size_t>(written)});
  208. }
  209. }
  210. /**
  211. * Writes one vertical velocity, leaving run momentum on the other two lanes.
  212. * @param body Rigid body to write.
  213. * @param value Vertical velocity to store.
  214. */
  215. void set_vertical_velocity(std::byte* body, float value) noexcept {
  216. std::array<float, kVectorLanes> velocity{};
  217. if (!read_at(body + kBodyVelocityX, velocity)) {
  218. return;
  219. }
  220. velocity[kVerticalLane] = value;
  221. (void)write_vector(body + kBodyVelocityX, velocity);
  222. }
  223. /**
  224. * Adds one world delta to a stored position.
  225. * @param address Vector to move.
  226. * @param delta World units per lane.
  227. * @param before Receives the value read.
  228. * @param after Receives the value written.
  229. * @return True when the new value was stored.
  230. */
  231. [[nodiscard]] bool offset_vector(std::byte* address,
  232. const std::array<float, kVectorLanes>& delta,
  233. std::array<float, kVectorLanes>& before,
  234. std::array<float, kVectorLanes>& after) noexcept {
  235. if (!read_at(address, before)) {
  236. return false;
  237. }
  238. for (std::size_t lane = 0; lane < kVectorLanes; ++lane) {
  239. after[lane] = before[lane] + delta[lane];
  240. }
  241. return write_vector(address, after);
  242. }
  243. /**
  244. * Adds the configured distance along the published forward vector.
  245. *
  246. * Only the rigid body is written. The physics component's own vector is composed against the body
  247. * orientation rather than added to it, so a world delta applied there corrupts the transform.
  248. *
  249. * @param body Rigid body being moved.
  250. * @param distance World units to travel.
  251. * @return True when the new position was stored.
  252. */
  253. [[nodiscard]] bool move_body(std::byte* body, float distance) noexcept {
  254. std::array<float, kVectorLanes> delta{};
  255. for (std::size_t lane = 0; lane < kVectorLanes; ++lane) {
  256. delta[lane] = g_forward[lane] * distance;
  257. }
  258. std::array<float, kVectorLanes> position{};
  259. std::array<float, kVectorLanes> moved{};
  260. if (!offset_vector(body + kBodyPositionX, delta, position, moved)) {
  261. report_skip("body");
  262. return false;
  263. }
  264. std::array<char, 160> line{};
  265. const int written = std::snprintf(line.data(),
  266. line.size(),
  267. "ev=teleport stage=move result=ok dist=%.1f "
  268. "from=%.1f,%.1f,%.1f to=%.1f,%.1f,%.1f",
  269. static_cast<double>(distance),
  270. static_cast<double>(position[0]),
  271. static_cast<double>(position[1]),
  272. static_cast<double>(position[2]),
  273. static_cast<double>(moved[0]),
  274. static_cast<double>(moved[1]),
  275. static_cast<double>(moved[2]));
  276. if (written > 0) {
  277. core::log::write(core::log::Channel::client,
  278. core::log::Level::info,
  279. {line.data(), static_cast<std::size_t>(written)});
  280. }
  281. return true;
  282. }
  283. /**
  284. * Runs the whole move for a component already proved to be the player's.
  285. * @param component Physics component driving the player.
  286. * @return True when the body was found and its position was written.
  287. */
  288. [[nodiscard]] bool perform_move(std::byte* component) noexcept {
  289. std::byte* const body = body_of(component);
  290. if (body == nullptr) {
  291. report_skip("no_body");
  292. return false;
  293. }
  294. report_gates(component, body);
  295. set_vertical_velocity(body, 0.0F);
  296. if (!move_body(body, client::movement::get().distance)) {
  297. return false;
  298. }
  299. begin_press();
  300. return true;
  301. }
  302. } // namespace
  303. /** Publishes the two functions the hooks call. */
  304. void publish_targets(ControlledHandle controlled, CameraSingleton singleton) noexcept {
  305. g_controlledHandle = controlled;
  306. g_cameraSingleton = singleton;
  307. }
  308. /** Drops those functions and every latched request. */
  309. void clear_targets() noexcept {
  310. g_controlledHandle = nullptr;
  311. g_cameraSingleton = nullptr;
  312. g_requested.store(false, std::memory_order_release);
  313. g_forwardValid.store(false, std::memory_order_release);
  314. g_keyDown.store(false, std::memory_order_relaxed);
  315. g_requestAge.store(0, std::memory_order_relaxed);
  316. g_active.store(false, std::memory_order_relaxed);
  317. g_playerComponent.store(nullptr, std::memory_order_relaxed);
  318. }
  319. /** Publishes the camera forward vector for the physics tick that follows. */
  320. void capture_forward(std::uint32_t playerIndex) noexcept {
  321. if (playerIndex == kInvalidHandle || g_cameraSingleton == nullptr) {
  322. return;
  323. }
  324. std::byte* const camera = g_cameraSingleton();
  325. if (camera == nullptr) {
  326. return;
  327. }
  328. std::array<float, kVectorLanes> forward{};
  329. if (!read_at(camera + kCameraBlockStride * playerIndex + kCameraForwardX, forward)) {
  330. return;
  331. }
  332. g_forward = forward;
  333. g_forwardValid.store(true, std::memory_order_release);
  334. }
  335. /** Latches one teleport request if the bound key went down this frame. */
  336. void poll_request() noexcept {
  337. end_press();
  338. expire_request();
  339. const client::movement::Settings settings = client::movement::get();
  340. const bool usable = settings.enabled && settings.virtualKey != client::movement::kNoKey;
  341. g_active.store(usable, std::memory_order_relaxed);
  342. if (!usable) {
  343. g_keyDown.store(false, std::memory_order_relaxed);
  344. return;
  345. }
  346. // An open interface owns the keyboard, so the key that binds the teleport must not fire it.
  347. if (core::ui::runtime::snapshot().visible) {
  348. g_keyDown.store(false, std::memory_order_relaxed);
  349. return;
  350. }
  351. const bool down = (GetAsyncKeyState(static_cast<int>(settings.virtualKey)) & 0x8000) != 0;
  352. if (down && !g_keyDown.exchange(down, std::memory_order_relaxed)) {
  353. g_requestAge.store(0, std::memory_order_relaxed);
  354. g_requested.store(true, std::memory_order_release);
  355. return;
  356. }
  357. g_keyDown.store(down, std::memory_order_relaxed);
  358. }
  359. /** Moves the local player if a request is pending and this component owns them. */
  360. void apply_pending(void* component) noexcept {
  361. if (!g_active.load(std::memory_order_relaxed) || component == nullptr
  362. || g_controlledHandle == nullptr) {
  363. return;
  364. }
  365. const bool requested = g_requested.load(std::memory_order_acquire);
  366. // The ownership test runs per component, so it is paid only while a request is open or until
  367. // the player's component is known. Once it is known, an ordinary tick costs two atomic reads.
  368. if (!requested && g_playerComponent.load(std::memory_order_relaxed) != nullptr) {
  369. return;
  370. }
  371. if (!owns_player(static_cast<std::byte*>(component))) {
  372. return;
  373. }
  374. std::byte* const physics = static_cast<std::byte*>(component);
  375. g_playerComponent.store(physics, std::memory_order_relaxed);
  376. if (!requested || !g_forwardValid.load(std::memory_order_acquire)) {
  377. return;
  378. }
  379. g_requested.store(false, std::memory_order_release);
  380. if (perform_move(physics)) {
  381. noclip::invalidate_target();
  382. }
  383. }
  384. /** Runs the move for a request no physics tick collected. */
  385. void force_pending() noexcept {
  386. if (!g_requested.load(std::memory_order_acquire)
  387. || !g_forwardValid.load(std::memory_order_acquire)
  388. || g_requestAge.load(std::memory_order_relaxed) < kForceAfterFrames) {
  389. return;
  390. }
  391. std::byte* const physics = g_playerComponent.load(std::memory_order_relaxed);
  392. // The cached pointer outlives a destination change, so it is proved again before use.
  393. if (physics == nullptr || g_controlledHandle == nullptr || !owns_player(physics)) {
  394. return;
  395. }
  396. g_requested.store(false, std::memory_order_release);
  397. if (!perform_move(physics)) {
  398. return;
  399. }
  400. noclip::invalidate_target();
  401. invoke_sync(physics);
  402. core::log::write(
  403. core::log::Channel::client, core::log::Level::info, "ev=teleport stage=force result=ok");
  404. }
  405. } // namespace sunrise::client::hooks::teleport