#include "client_source_spawn.h" #include "client_source_spawn_internal.h" #include "Engine/Game/Build/20260929/engine.h" #include "Engine/Game/UI/game_view.h" #include "free_flight.h" #include namespace dingosdk::client_source::detail { SourceCameraSnapshot source_camera_snapshot(const SourceTrial& trial, std::uintptr_t client) { // This smaller ownership/TLS snapshot deliberately does not require the // skater, physics, UI state or player binding to survive until restoration. SourceReader reader; const auto base = trial.base; source_require(source_object(client) && reader.pointer(client) == base + addr::engine::client_vtable, "Free camera client identity rejected."); SourceCameraSnapshot result; auto& id = result.identity; id.context = reader.pointer(client, 8); const auto type_offset = reader.value(base, addr::engine::context_type_offset); const auto client_offset = reader.value(base, spawn::context_client_offset); const auto manager_offset = reader.value(base, addr::engine::context_player_manager_offset); source_require(source_object(id.context) && type_offset >= 8 && type_offset <= 0x1000000 && client_offset >= 8 && client_offset <= 0x1000000 && manager_offset <= 0x1000000, "Free camera context layout rejected."); const auto array = static_cast(__readgsqword(0x58)); const auto index = reader.value(base, addr::engine::tls_index); source_require(source_object(array) && index <= 4095, "Free camera TLS layout rejected."); const auto block = reader.pointer(array, std::uintptr_t{index} * 8); source_require(source_object(block) && reader.value(block, 0xb19) && reader.pointer(block, 0x550) == id.context && reader.value(id.context, type_offset) == 0xbf0f9789 && reader.pointer(id.context, client_offset) == client, "Free camera native client scope mismatch."); source_require(!reader.value(base, spawn::camera_local_id), "Native camera local ID is not zero."); id.manager = reader.pointer(id.context, manager_offset); source_require(source_object(id.manager) && reader.pointer(id.manager) == base + addr::engine::local_player_manager_vtable, "Free camera client manager identity rejected."); id.controller = reader.pointer(id.manager, 0x710); source_require(source_object(id.controller) && reader.pointer(id.controller) == base + spawn::camera_controller_vtable && !reader.value(id.controller, 0x118), "Existing local camera controller unavailable."); result.mode = reader.value(id.controller, 8); id.priority = reader.value(id.controller, 0xd4); id.selector = reader.pointer(id.controller, 0xc0); id.camera = reader.pointer(id.controller, 0xc8); source_require(source_object(id.selector) && source_object(id.camera) && reader.pointer(id.camera) == base + spawn::free_camera_vtable, "Existing FreeCamera identity rejected."); source_require(!reader.value(id.selector) && !reader.value(id.selector, 4), "Existing camera selector local ID or kind changed."); const auto sentinel = id.selector + 8; // Native header/sentinel is inline, not its left-node pointer. (void)reader.pointer(id.selector, 8); const auto root = reader.pointer(id.selector, 0x18); const auto count = reader.value(id.selector, 0x28); id.node = reader.pointer(id.selector, 0x38); source_require(source_object(sentinel) && source_object(root) && count && count <= 32 && source_object(id.node) && id.node != sentinel && !reader.value(id.node, 0x20) && reader.pointer(id.node, 0x28) == id.camera, "Existing selector no longer selects the owned FreeCamera."); id.helpers = {reader.pointer(id.camera, 0x1d0), reader.pointer(id.camera, 0x1d8)}; for (std::size_t i = 0; i < id.helpers.size(); ++i) { const auto helper = id.helpers[i]; source_require(source_object(helper) && reader.pointer(helper) == base + spawn::free_camera_helper_vtables[i] && source_object(reader.pointer(helper, 0x40)) && reader.value(helper, 0x52) <= 1, "Existing free-camera input helper unavailable."); } const auto selected_helper = reader.pointer(id.camera, 0x1c8); source_require(selected_helper == id.helpers[0] || selected_helper == id.helpers[1], "FreeCamera selected helper changed."); result.input_active = reader.value(id.camera, 0x1c0); source_require(result.input_active <= 1, "FreeCamera activation flag rejected."); id.view_manager = reader.pointer(base, spawn::view_manager); source_require(source_object(id.view_manager) && reader.pointer(id.view_manager) == base + spawn::view_manager_vtable, "Camera view manager identity rejected."); source_require(reader.pointer(base, spawn::view_manager_registration) == id.view_manager && reader.pointer(base + spawn::view_manager_vtable) == base + spawn::view_manager_methods[0] && reader.pointer(base + spawn::view_manager_vtable, 8) == base + spawn::view_manager_methods[1] && reader.pointer(base + spawn::view_manager_vtable, 0x28) == base + spawn::view_manager_methods[2] && reader.pointer(base + spawn::view_manager_vtable, 0x98) == base + spawn::view_manager_methods[3], "Native camera registration service changed."); const auto begin = reader.pointer(id.view_manager, 8), end = reader.pointer(id.view_manager, 0x10); const auto view_count = reader.count(begin, end, 8); source_require(view_count <= 32, "Camera view collection exceeds bound."); for (std::size_t i = 0; i < view_count; ++i) { const auto view = reader.pointer(begin, i * 8); source_require(source_object(view) && reader.pointer(view) == base + spawn::camera_view_vtable, "Camera view type rejected."); if (!reader.value(view, 0x148) && !reader.value(view, 0x14c)) { source_require(!id.view, "Local camera view is ambiguous."); id.view = view; } } source_require(id.view != 0, "Local camera view unavailable."); result.view_enabled = reader.value(id.view, 0x45d); source_require(result.view_enabled <= 1, "Local camera view activation flag rejected."); result.active = reader.pointer(id.view, 0x2b0); source_require(source_object(result.active), "Local view has no selected camera."); std::array prefix{}; source_require(reader.pointer(base + spawn::camera_controller_vtable, 0xb8) == base + spawn::camera_mode_switch && reader.raw(base + spawn::camera_mode_switch, prefix.data(), prefix.size()) && prefix == spawn::camera_mode_prefix, "Native camera mode-switch identity changed."); reader.verify(); return result; } std::array debug_view_matrix(const SourceTrial& trial, const SourceCameraSnapshot& camera) { SourceReader reader; const auto vtable = reader.pointer(camera.active); source_require(vtable == trial.base + spawn::free_camera_vtable || vtable == trial.base + addr::engine::camera_vtable, "The current camera does not expose a supported view transform."); std::array prefix{}; source_require(reader.pointer(trial.base + spawn::free_camera_vtable, 0xd0) == trial.base + spawn::camera_transform_setter && reader.raw(trial.base + spawn::camera_transform_setter, prefix.data(), prefix.size()) && prefix == spawn::camera_transform_prefix, "Freecam transform setter changed."); std::array matrix{}; for (std::size_t row = 0; row < 4; ++row) { const auto values = reader.value>(camera.active, 0x50 + row * 16); std::copy(values.begin(), values.end(), matrix.begin() + row * 4); } source_require(valid_flight_transform(matrix), "Current camera pose is unavailable."); reader.verify(); return matrix; } } namespace dingosdk { using namespace client_source::detail; bool update_party_camera(std::uintptr_t base, std::uintptr_t client, bool ready, bool phase, const std::array* transform, std::string& detail, float fov) noexcept { struct Lease { bool owned{}, uncertain{}; SourceCameraIdentity identity; DWORD thread{}; float saved_fov{}; }; static Lease lease; SourceLastError error; auto& s = source_state(); if (!transform && !lease.owned) return true; if (!s.initialized.load(std::memory_order_acquire) || s.busy.test_and_set(std::memory_order_acquire)) return false; SourceBusyScope busy{s.busy}; try { auto& trial = s.trial; source_require(base == trial.base && (!lease.thread || lease.thread == GetCurrentThreadId()), "Spectate is waiting for the native client thread."); lease.thread = GetCurrentThreadId(); SourceReader reader; // A destroyed world owns its old camera. Forget that lease without // touching freed memory or changing a new world's selected camera. if (lease.owned && reader.pointer(client, 8) != lease.identity.context) lease = {}; if (!transform && !lease.owned) return true; source_require(phase, "Spectate is waiting for the native camera phase."); auto camera = source_camera_snapshot(trial, client); if (lease.owned && camera.identity != lease.identity) { lease = {}; source_require(!transform, "The spectate camera was replaced by the game."); return true; } if (!transform) { if (lease.saved_fov > 0) (void)first_person_write_fov(lease.identity.camera, lease.saved_fov); if (camera.mode == 1 && camera.active == lease.identity.camera) { trial.camera_mode(lease.identity.controller, 0); camera = source_camera_snapshot(trial, client); source_require(camera.mode == 0 && camera.active != lease.identity.camera, "Waiting for the original camera to return."); } lease = {}; detail = "Original camera restored."; return true; } source_require(ready && valid_flight_transform(*transform), "Spectate target is unavailable on this map."); source_require(!lease.uncertain, "Spectate camera activation was not verified; exit Spectate to restore it."); if (!lease.owned) { source_require(!trial.debug.camera_owned && !trial.debug.noclip && camera.mode == 0 && camera.view_enabled && !camera.input_active, "Exit Freecam or Noclip before spectating a party member."); lease.identity = camera.identity; lease.owned = lease.uncertain = true; lease.saved_fov = reader.value(camera.identity.camera, camera_fov_offset); if (!std::isfinite(lease.saved_fov) || lease.saved_fov <= 1 || lease.saved_fov >= 175) lease.saved_fov = 0; trial.camera_mode(camera.identity.controller, 1); camera = source_camera_snapshot(trial, client); source_require(camera.identity == lease.identity && camera.mode == 1 && camera.active == lease.identity.camera, "Native spectate camera activation could not be verified."); lease.uncertain = false; } source_require(camera.mode == 1 && camera.active == lease.identity.camera, "Another native camera has taken control of the view."); trial.camera_transform(lease.identity.camera, transform); if (fov > 1 && fov < 175) (void)first_person_write_fov(lease.identity.camera, fov); detail = "Spectating party member."; return true; } catch (const SourceGuard& issue) { detail = issue.message; } catch (...) { detail = "Native spectate camera is unavailable."; } return false; } bool read_local_camera_transform(std::uintptr_t base, std::uintptr_t client, std::array& transform) noexcept { SourceLastError error; auto& state = source_state(); if (!state.initialized.load(std::memory_order_acquire) || state.busy.test_and_set(std::memory_order_acquire)) return false; SourceBusyScope scope{state.busy}; try { if (state.trial.base != base) return false; transform = debug_view_matrix(state.trial, source_camera_snapshot(state.trial, client)); return true; } catch (...) { return false; } } bool publish_local_camera_view(std::uintptr_t base, std::uintptr_t client) noexcept { SourceLastError error; auto& state = source_state(); if (!state.initialized.load(std::memory_order_acquire) || state.busy.test_and_set(std::memory_order_acquire)) return false; SourceBusyScope scope{state.busy}; try { if (state.trial.base != base) return false; const auto camera = source_camera_snapshot(state.trial, client); const auto view = debug_view_matrix(state.trial, camera); SourceReader reader; // Camera's degree FOV at +ac, initialized by spawn::camera_fov_init. const auto fov = reader.value(camera.active, 0xac); reader.verify(); if (!std::isfinite(fov) || fov <= 1 || fov >= 175) return false; publish_game_view({view, fov, camera.active}); return true; } catch (...) { return false; } } }