mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-18 12:05:21 +02:00
127 lines
5.6 KiB
C++
127 lines
5.6 KiB
C++
#include "park_editor_internal.h"
|
|
#include "Engine/Game/Build/addresses.h"
|
|
#include "Engine/Game/Build/20260929/buildkit_limits.h"
|
|
#include "Engine/Game/Build/20260929/engine.h"
|
|
#include "Extension/Objects/local_placements_runtime.h"
|
|
|
|
namespace dingosdk::profile_runtime::park_editor_detail {
|
|
bool surface_body(std::uintptr_t world, std::uint32_t index, std::uintptr_t data, editor::SurfaceBody &body) {
|
|
const auto &api = editor_state().picking_api;
|
|
if (!api.ready)
|
|
return false;
|
|
alignas(16) const std::array<std::uint64_t, 3> hit{world, index, data};
|
|
api.hit_body(hit.data(), body.data());
|
|
return body[0] == world && api.body_valid(body.data());
|
|
}
|
|
std::uint64_t surface_owner(std::uintptr_t world, std::uint32_t index, std::uintptr_t data) {
|
|
auto &e = editor_state();
|
|
const auto &api = e.picking_api;
|
|
alignas(16) editor::SurfaceBody body{};
|
|
alignas(16) std::array<std::uint64_t, 2> entity{}, root{};
|
|
if (!surface_body(world, index, data, body))
|
|
return 0;
|
|
api.body_entity(entity.data(), body.data());
|
|
if (!entity[0])
|
|
return 0;
|
|
auto &r = placements_runtime();
|
|
r.resolve(root.data(), entity[0], false);
|
|
if (!r.valid(root.data()))
|
|
return 0;
|
|
if (std::any_of(r.rows.begin(), r.rows.end(), [&](const auto &row) {
|
|
return row.spawned && row.entity == root[0];
|
|
}))
|
|
return root[0];
|
|
// Persisted Build Kit replicas do not populate the general entity mapping
|
|
// tables in a local session. Their native drop ID and replicated instance
|
|
// key match the authoritative object even though their ECS IDs differ.
|
|
if (const auto client = editor::buildkit_identity(api, root[0])) {
|
|
if (const auto it = e.native_drops.find(client->drop_id); it != e.native_drops.end()) {
|
|
alignas(16) std::array<std::uint64_t, 2> server{};
|
|
r.resolve(server.data(), it->second, false);
|
|
if (server[0] == it->second && r.valid(server.data())) {
|
|
const auto identity = editor::buildkit_identity(api, server[0]);
|
|
if (identity && *identity == *client) {
|
|
e.client_roots[server[0]] = root[0];
|
|
return server[0];
|
|
}
|
|
}
|
|
}
|
|
return 0; // A recycled drop ID must not bind a stale visible replica.
|
|
}
|
|
return editor::mapped_entity(api, root[0], 1);
|
|
}
|
|
std::uint64_t client_root(std::uint64_t server) {
|
|
auto &e = editor_state();
|
|
auto &r = placements_runtime();
|
|
if (const auto it = e.client_roots.find(server); it != e.client_roots.end()) {
|
|
alignas(16) std::array<std::uint64_t, 2> root{};
|
|
r.resolve(root.data(), it->second, false);
|
|
if (root[0] == it->second && r.valid(root.data())) {
|
|
const auto client = editor::buildkit_identity(e.picking_api, root[0]);
|
|
const auto authoritative = editor::buildkit_identity(e.picking_api, server);
|
|
if (client && authoritative && *client == *authoritative)
|
|
return root[0];
|
|
}
|
|
e.client_roots.erase(it);
|
|
}
|
|
return editor::mapped_entity(e.picking_api, server, 0);
|
|
}
|
|
void update_highlights() {
|
|
auto &e = editor_state();
|
|
auto &r = placements_runtime();
|
|
if (!e.highlight_api.ready || !e.picking_api.ready)
|
|
return;
|
|
std::set<std::uint64_t> desired;
|
|
if (GetTickCount64() < e.selection_time + 2000) {
|
|
std::vector<std::uint64_t> servers;
|
|
for (const auto &row : r.rows)
|
|
if (row.spawned &&
|
|
std::find(e.selection.begin(), e.selection.end(), row.saved_id) != e.selection.end())
|
|
servers.push_back(row.entity);
|
|
if (e.transaction && e.transaction->preview && e.transaction->preview->placing &&
|
|
!e.transaction->preview->cancelled && !e.transaction->steps.empty() &&
|
|
e.transaction->steps.front().entity)
|
|
servers.push_back(e.transaction->steps.front().entity);
|
|
for (const auto server : servers) {
|
|
const auto client = client_root(server);
|
|
if (client)
|
|
desired.insert(client);
|
|
}
|
|
}
|
|
// Resolve each entity afresh. Never retain a component pointer across ticks.
|
|
for (auto it = e.highlights.begin(); it != e.highlights.end();) {
|
|
if (desired.contains(it->first)) {
|
|
++it;
|
|
continue;
|
|
}
|
|
std::array<std::uint64_t, 2> root{};
|
|
r.resolve(root.data(), it->first, false);
|
|
if (root[0] == it->first && r.valid(root.data()))
|
|
editor::highlight_value(e.highlight_api, root.data(), it->second.original, it->second.applied);
|
|
it = e.highlights.erase(it);
|
|
}
|
|
if (desired.empty())
|
|
return;
|
|
std::uintptr_t manager{};
|
|
const auto base = local_runtime().base;
|
|
if (!memory::read(base + addr::engine::settings_manager, manager) || !manager)
|
|
return;
|
|
const auto settings = e.highlight_api.settings(
|
|
manager, reinterpret_cast<const void *>(base + addr::buildkit_limits::object_persistence_settings_type));
|
|
std::int32_t color{};
|
|
if (!settings || !memory::read(settings + 0x88, color))
|
|
return;
|
|
const editor::HighlightValue applied{color, 1}; // BuildKitColorSetOutline.
|
|
for (const auto client : desired) {
|
|
if (e.highlights.contains(client))
|
|
continue;
|
|
std::array<std::uint64_t, 2> root{};
|
|
r.resolve(root.data(), client, false);
|
|
if (root[0] != client || !r.valid(root.data()))
|
|
continue;
|
|
if (const auto original = editor::highlight_value(e.highlight_api, root.data(), applied))
|
|
e.highlights.emplace(client, EditorRuntime::HighlightLease{*original, applied});
|
|
}
|
|
}
|
|
} // namespace dingosdk::profile_runtime::park_editor_detail
|