Files
TheTree_mock/docs/plans/original-completion-2026-10-02/checkpoints/native-input-copy-r332-r337/028-source-before-allocation-guard.cpp
T

889 lines
56 KiB
C++

// Isolated, uncompiled bridge prototype. No production registration or integration yet.
#include "tree_core.hpp"
#include <cmath>
#include <array>
#include <new>
#include <godot_cpp/templates/sort_array.hpp>
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/variant/vector3i.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/godot.hpp>
#include <godot_cpp/variant/array.hpp>
#include <godot_cpp/variant/callable.hpp>
#include <godot_cpp/variant/dictionary.hpp>
#include <godot_cpp/variant/packed_int64_array.hpp>
#include <godot_cpp/variant/typed_array.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
using the_tree_native::I;
using the_tree_native::Edge;
class TheTreeNumericCore : public RefCounted {
GDCLASS(TheTreeNumericCore, RefCounted)
protected:
static void _bind_methods() {
ClassDB::bind_method(D_METHOD("performance_record_sample", "owner", "metric_id", "duration_us"), &TheTreeNumericCore::performance_record_sample);
ClassDB::bind_method(D_METHOD("guest_candidate_batch", "owner", "guest_id", "facilities", "context"), &TheTreeNumericCore::guest_candidate_batch);
ClassDB::bind_method(D_METHOD("guest_tree_edge_rows", "path", "current", "layers", "access"), &TheTreeNumericCore::guest_tree_edge_rows);
ClassDB::bind_method(D_METHOD("search", "cells", "indices", "start_index", "goals", "edge_provider"), &TheTreeNumericCore::search);
ClassDB::bind_method(D_METHOD("search_cached", "cells", "indices", "start_index", "goals", "edge_provider"), &TheTreeNumericCore::search_cached);
ClassDB::bind_method(D_METHOD("clear_edge_cache"), &TheTreeNumericCore::clear_edge_cache);
ClassDB::bind_method(D_METHOD("nearest_guest_access", "path", "start", "goals", "fallback_target", "layers", "access", "ticks"), &TheTreeNumericCore::nearest_guest_access);
ClassDB::bind_method(D_METHOD("project_guest_candidates", "runtime", "guest_id", "open_state", "degraded_state", "movement_scale", "stair_credit", "orthogonal_cost", "ticks"), &TheTreeNumericCore::project_guest_candidates);
}
private:
// Cache belongs to this instance and one caller-owned graph workspace.
// No reuse after cells, indices, filters or edge values change: the caller
// must clear it or release this core. The original search never uses it.
std::map<I, std::vector<Edge>> cached_edges;
// Per-public-call immutable names. No static/global lifetime, cached
// object values, script dispatch result or profile; recursive calls own
// separate scopes. Destruction precedes leaving the engine call.
struct ProjectionNames {
const StringName k_ACCESS_LAYER_DENIED{"ACCESS_LAYER_DENIED",false};
const StringName k_CONNECTOR{"CONNECTOR",false};
const StringName k_GOAL_NOT_PASSABLE{"GOAL_NOT_PASSABLE",false};
const StringName k_GOAL_TEMPORARILY_BLOCKED{"GOAL_TEMPORARILY_BLOCKED",false};
const StringName k_GOAL_UNREACHABLE{"GOAL_UNREACHABLE",false};
const StringName k_OK{"OK",false};
const StringName k_START_NOT_PASSABLE{"START_NOT_PASSABLE",false};
const StringName k_START_TEMPORARILY_BLOCKED{"START_TEMPORARILY_BLOCKED",false};
const StringName k__build_guest_query_tree_compact{"_build_guest_query_tree_compact",false};
const StringName k__cache_key{"_cache_key",false};
const StringName k__cell_traffic{"_cell_traffic",false};
const StringName k__copy_path{"_copy_path",false};
const StringName k__diagnose_no_route_reason{"_diagnose_no_route_reason",false};
const StringName k__find_facility_access_path{"_find_facility_access_path",false};
const StringName k__find_path_internal{"_find_path_internal",false};
const StringName k__guest_access_batch_can_select{"_guest_access_batch_can_select",false};
const StringName k__guest_batch_has_permitted_layer_crossing{"_guest_batch_has_permitted_layer_crossing",false};
const StringName k__guest_has_checked_in{"_guest_has_checked_in",false};
const StringName k__guest_step_connector_memo{"_guest_step_connector_memo",false};
const StringName k__guest_tree_arithmetic_wrapped{"_guest_tree_arithmetic_wrapped",false};
const StringName k__guest_tree_reconstruct_path{"_guest_tree_reconstruct_path",false};
const StringName k__need_relief_for_facility_tags{"_need_relief_for_facility_tags",false};
const StringName k__passable_cells{"_passable_cells",false};
const StringName k__path_cache{"_path_cache",false};
const StringName k__query_batch{"_query_batch",false};
const StringName k__route_failure{"_route_failure",false};
const StringName k__service_category_for_tags{"_service_category_for_tags",false};
const StringName k__sorted_name_keys{"_sorted_name_keys",false};
const StringName k_arithmetic_wrapped{"arithmetic_wrapped",false};
const StringName k_available_quantity{"available_quantity",false};
const StringName k_base_quality{"base_quality",false};
const StringName k_base_service_minutes{"base_service_minutes",false};
const StringName k_begin_guest_path_query_batch{"begin_guest_path_query_batch",false};
const StringName k_cache_misses{"cache_misses",false};
const StringName k_calculate_guest_batch_path_cost{"calculate_guest_batch_path_cost",false};
const StringName k_candidates{"candidates",false};
const StringName k_capacity{"capacity",false};
const StringName k_compact{"compact",false};
const StringName k_cost{"cost",false};
const StringName k_cost_values{"cost_values",false};
const StringName k_costs{"costs",false};
const StringName k_current_cell{"current_cell",false};
const StringName k_definition_id{"definition_id",false};
const StringName k_definition_registry{"definition_registry",false};
const StringName k_elevators{"elevators",false};
const StringName k_end_guest_path_query_batch{"end_guest_path_query_batch",false};
const StringName k_entrance{"entrance",false};
const StringName k_estimated_total_gold{"estimated_total_gold",false};
const StringName k_expected_quality{"expected_quality",false};
const StringName k_expected_wait{"expected_wait",false};
const StringName k_facility_id{"facility_id",false};
const StringName k_facility_service{"facility_service",false};
const StringName k_facility_tags{"facility_tags",false};
const StringName k_get_connector{"get_connector",false};
const StringName k_get_facility{"get_facility",false};
const StringName k_get_facility_business_price{"get_facility_business_price",false};
const StringName k_get_guest{"get_guest",false};
const StringName k_get_instances{"get_instances",false};
const StringName k_get_step_connector{"get_step_connector",false};
const StringName k_graph_revision{"graph_revision",false};
const StringName k_group_fit{"group_fit",false};
const StringName k_guest_service{"guest_service",false};
const StringName k_illegal_level{"illegal_level",false};
const StringName k_indices{"indices",false};
const StringName k_inputs_available{"inputs_available",false};
const StringName k_instance_id{"instance_id",false};
const StringName k_inventory_inputs{"inventory_inputs",false};
const StringName k_inventory_service{"inventory_service",false};
const StringName k_is_open{"is_open",false};
const StringName k_last_error{"last_error",false};
const StringName k_last_no_route_reason{"last_no_route_reason",false};
const StringName k_last_path_cost{"last_path_cost",false};
const StringName k_local_inventory_id{"local_inventory_id",false};
const StringName k_movement_type{"movement_type",false};
const StringName k_need_relief{"need_relief",false};
const StringName k_occupied_cells{"occupied_cells",false};
const StringName k_origin{"origin",false};
const StringName k_owns_connector{"owns_connector",false};
const StringName k_path{"path",false};
const StringName k_path_handle{"path_handle",false};
const StringName k_path_query_measured{"path_query_measured",false};
const StringName k_path_service{"path_service",false};
const StringName k_queue_entries{"queue_entries",false};
const StringName k_reachable{"reachable",false};
const StringName k_ready{"ready",false};
const StringName k_resource_path{"resource_path",false};
const StringName k_risk_level{"risk_level",false};
const StringName k_service_category{"service_category",false};
const StringName k_settled{"settled",false};
const StringName k_staff_only{"staff_only",false};
const StringName k_start{"start",false};
const StringName k_state{"state",false};
const StringName k_supported{"supported",false};
const StringName k_tags{"tags",false};
const StringName k_tagproj_hunger{"hunger",false};
const StringName k_tagproj_sleep{"sleep",false};
const StringName k_tagproj_hygiene{"hygiene",false};
const StringName k_tagproj_medical{"medical",false};
const StringName k_tagproj_leisure{"leisure",false};
const StringName k_tagproj_intimacy{"intimacy",false};
const StringName k_tagproj_dining{"dining",false};
const StringName k_tagproj_lodging{"lodging",false};
const StringName k_tagproj_social{"social",false};
const StringName k_tagproj_bath{"bath",false};
const StringName k_tagproj_wellness{"wellness",false};
const StringName k_tagproj_adult_only{"adult_only",false};
const StringName k_tagproj_restricted_service{"restricted_service",false};
const StringName k_tagproj_food{"food",false};
const StringName k_tagproj_service{"service",false};
const StringName k_target_cell{"target_cell",false};
const StringName k_targets{"targets",false};
const StringName k_temporary_block{"temporary_block",false};
const StringName k_travel_minutes{"travel_minutes",false};
const StringName k_tree{"tree",false};
const StringName k_usable{"usable",false};
};
public:
// Whole builtin integer record operation. No retained sample values or time
// suppression. Unsupported data returns before any caller-owned mutation.
Dictionary performance_record_sample(Object *owner, const StringName &metric, int64_t duration) {
const StringName supported("supported",false), report_key("report",false);
Dictionary unsupported; unsupported[supported]=false;
Object *script=owner ? owner->get_script() : nullptr;
if (!script || String(script->get(StringName("resource_path",false))) != "res://scripts/core/performance_budget.gd" || metric==StringName() || duration<0) return unsupported;
const StringName samples_key("_samples",false), ordered_key("_ordered_samples",false), bytes_key("_ordered_sample_bytes",false), integer_key("_integer_window_bytes",false), totals_key("_totals",false), counts_key("_sample_counts",false), budgets_key("_budgets",false);
const Variant sv=owner->get(samples_key), ov=owner->get(ordered_key), bv=owner->get(bytes_key), iv=owner->get(integer_key), tv=owner->get(totals_key), cv=owner->get(counts_key), uv=owner->get(budgets_key), capacityv=owner->get(StringName("sample_capacity",false));
if (sv.get_type()!=Variant::DICTIONARY || ov.get_type()!=Variant::DICTIONARY || bv.get_type()!=Variant::DICTIONARY || iv.get_type()!=Variant::DICTIONARY || tv.get_type()!=Variant::DICTIONARY || cv.get_type()!=Variant::DICTIONARY || uv.get_type()!=Variant::DICTIONARY || capacityv.get_type()!=Variant::INT) return unsupported;
Dictionary samples=sv, orders=ov, rawbytes=bv, integers=iv, totals=tv, counts=cv, budgets=uv;
if (samples.is_read_only() || orders.is_read_only() || rawbytes.is_read_only() || integers.is_read_only() || totals.is_read_only() || counts.is_read_only()) return unsupported;
const std::array<Dictionary,7> maps{samples,orders,rawbytes,integers,totals,counts,budgets};
for (size_t i=0;i<maps.size();++i) for (size_t j=i+1;j<maps.size();++j)
if (UtilityFunctions::is_same(maps[i],maps[j])) return unsupported;
const bool existing=samples.has(metric);
Variant rawv=samples.get(metric,Array());
if (rawv.get_type()!=Variant::ARRAY) return unsupported;
Array values=rawv;
if (values.is_read_only()) return unsupported;
for (int64_t i=0;i<values.size();++i) if (values[i].get_type()!=Variant::INT) return unsupported;
Variant totalv=totals.get(metric,0), countv=counts.get(metric,0), budgetv=budgets.get(metric,Dictionary());
if (totalv.get_type()!=Variant::INT || countv.get_type()!=Variant::INT || budgetv.get_type()!=Variant::DICTIONARY) return unsupported;
Dictionary budget=budgetv;
for (const Dictionary &map: maps) if (UtilityFunctions::is_same(budget,map)) return unsupported;
const StringName aggregation_key("aggregation",false), limit_key("limit_us",false);
Variant aggregationv=budget.get(aggregation_key,1), limitv=budget.get(limit_key,0);
if (aggregationv.get_type()!=Variant::INT || limitv.get_type()!=Variant::INT) return unsupported;
// One exact byte snapshot serves both builtin validation helpers inside
// this call; there are no external calls between their original checks.
PackedByteArray before=UtilityFunctions::var_to_bytes(values);
TypedArray<int64_t> ordered;
Variant savedbytes=rawbytes.get(metric,PackedByteArray());
bool reuse=existing && orders.has(metric) && savedbytes.get_type()==Variant::PACKED_BYTE_ARRAY && PackedByteArray(savedbytes)==before;
if (reuse) {
Variant orderv=orders[metric];
if (orderv.get_type()!=Variant::ARRAY) return unsupported;
Array prior=orderv;
if (prior.is_read_only() || !prior.is_typed() || prior.get_typed_builtin()!=Variant::INT || prior.size()!=values.size()) return unsupported;
ordered=prior;
} else {
for (int64_t i=0;i<values.size();++i) ordered.append(values[i]);
ordered.sort();
}
// Guards completed: mutations preserve raw and reused ordered aliases.
if (!existing) samples[metric]=values;
integers[metric]=before;
if (!reuse) { orders[metric]=ordered; rawbytes[metric]=before; }
values.append(duration);
ordered.insert(ordered.bsearch(duration),duration);
if (values.size()>int64_t(capacityv)) {
Variant removed=values.pop_front();
ordered.remove_at(ordered.bsearch(removed));
}
PackedByteArray after=UtilityFunctions::var_to_bytes(values);
rawbytes[metric]=after; integers[metric]=after;
const int64_t lifetime_total=the_tree_native::signed_add(int64_t(totalv),duration);
const int64_t lifetime_count=the_tree_native::signed_add(int64_t(countv),1);
totals[metric]=lifetime_total; counts[metric]=lifetime_count;
const int64_t average=lifetime_count>0 ? lifetime_total/lifetime_count : 0;
const int64_t maximum=ordered.is_empty() ? 0 : std::max<int64_t>(0,int64_t(ordered[ordered.size()-1]));
int64_t p95=0;
if (!ordered.is_empty()) {
const int64_t index=std::max<int64_t>(the_tree_native::signed_add(the_tree_native::signed_add(signed_product(95,ordered.size()),99)/100,-1),0);
p95=ordered[std::min<int64_t>(index,ordered.size()-1)];
}
const int64_t aggregation=aggregationv, limit=limitv;
// Original default match is P95, including unknown aggregation values.
const int64_t measured=aggregation==0 ? average : (aggregation==2 ? maximum : p95);
Dictionary report;
report[StringName("metric_id",false)]=metric;
report[StringName("window_sample_count",false)]=values.size();
report[StringName("lifetime_sample_count",false)]=lifetime_count;
report[StringName("average_us",false)]=average;
report[StringName("p95_us",false)]=p95;
report[StringName("maximum_us",false)]=maximum;
report[aggregation_key]=aggregation;
report[StringName("measured_us",false)]=measured;
report[limit_key]=limit;
report[StringName("budget_exceeded",false)]=limit>0 && measured>limit;
Dictionary result; result[supported]=true; result[report_key]=report; return result;
}
#include "guest_candidate_batch.inc"
void clear_edge_cache() { cached_edges.clear(); }
Dictionary search(const Array &cells, const Dictionary &indices, I start_index,
const Dictionary &goals, const Callable &edge_provider) {
return search_impl(cells, indices, start_index, goals, edge_provider, false);
}
Dictionary search_cached(const Array &cells, const Dictionary &indices, I start_index,
const Dictionary &goals, const Callable &edge_provider) {
return search_impl(cells, indices, start_index, goals, edge_provider, true);
}
// One ordered projection, with no new persistent ownership/cache. Only
// builtin pure getters and the already guarded sole budget observer qualify.
Dictionary project_guest_candidates(Object *runtime, const StringName &guest_id,
int64_t open_state, int64_t degraded_state, int64_t movement_scale,
int64_t stair_credit, int64_t orthogonal_cost, const Callable &ticks) {
const ProjectionNames n;
Dictionary unsupported;
unsupported[n.k_supported] = false;
if (!builtin(n,runtime, "res://autoload/simulation_runtime.gd") || !ticks.is_valid()) return unsupported;
Object *path = runtime->get(n.k_path_service);
Object *elevators = runtime->get(n.k_elevators);
Object *facilities = runtime->get(n.k_facility_service);
Object *definitions = runtime->get(n.k_definition_registry);
Object *inventory = runtime->get(n.k_inventory_service);
Object *guests = runtime->get(n.k_guest_service);
if (!builtin(n,path,"res://scripts/world/path_service.gd") ||
!builtin(n,elevators,"res://scripts/transport/elevator_service.gd") ||
!builtin(n,facilities,"res://scripts/facility/facility_service.gd") ||
!builtin(n,definitions,"res://scripts/core/definition_registry.gd") ||
!builtin(n,inventory,"res://scripts/inventory/inventory_service.gd") ||
!builtin(n,guests,"res://scripts/guest/guest_service.gd") ||
!static_cast<bool>(runtime->call(n.k__guest_access_batch_can_select))) return unsupported;
Object *guest = guests->call(n.k_get_guest,guest_id);
Array candidates;
if (!guest) {
Dictionary result; result[n.k_supported] = true;
result[n.k_candidates] = candidates; return result;
}
if (!builtin(n,guest,"res://scripts/guest/guest_runtime.gd")) return unsupported;
const Array initial = facilities->call(n.k_get_instances);
// Preflight is read-only: default registry/get_instances have no public
// error/counter effects. Custom resources retain the original whole loop.
for (int64_t i=0;i<initial.size();++i) {
Object *facility = initial[i];
if (!builtin(n,facility,"res://scripts/facility/facility_instance.gd")) return unsupported;
Object *definition = definitions->call(n.k_get_facility,facility->get(n.k_definition_id));
if (definition && !builtin(n,definition,"res://scripts/data/facility_definition.gd")) return unsupported;
}
Array goals;
for (int64_t i=0;i<initial.size();++i) goals.append_array(facility_access_cells(n,path,initial[i]));
const Vector3i start = guest->get(n.k_current_cell);
path->call(n.k_begin_guest_path_query_batch,start,goals);
const Array ordered = facilities->call(n.k_get_instances);
TypedArray<int64_t> layers; layers.push_back(0); layers.push_back(1);
TypedArray<StringName> access_ids;
for (int64_t i=0;i<ordered.size();++i) {
Object *facility = ordered[i];
Object *definition = definitions->call(n.k_get_facility,facility->get(n.k_definition_id));
if (!definition) continue;
const Array tags = definition->get(n.k_tags);
if (tags.has(n.k_staff_only)) continue;
if (tags.has(n.k_entrance) && static_cast<bool>(runtime->call(n.k__guest_has_checked_in,guest))) continue;
bool has_inputs = true;
const Dictionary inputs = definition->get(n.k_inventory_inputs);
const Array items = sorted_builtin_input_names(n,runtime,inputs);
for (int64_t j=0;j<items.size();++j) {
const int64_t quantity = inventory->call(n.k_available_quantity,facility->get(n.k_local_inventory_id),items[j]);
if (quantity < static_cast<int64_t>(inputs[items[j]])) {has_inputs=false;break;}
}
if (!has_inputs) {
Dictionary row;row[n.k_facility_id]=facility->get(n.k_instance_id);
row[n.k_inputs_available]=false;candidates.push_back(row);continue;
}
const Array destinations = facility_access_cells(n,path,facility);
Dictionary route = nearest_guest_access_impl(n,path,start,destinations,facility->get(n.k_origin),layers,access_ids,ticks);
if (static_cast<bool>(route.get(n.k_supported,false))) route.erase(n.k_supported);
else route = Dictionary(runtime->call(n.k__find_facility_access_path,start,facility));
const Array walk = route.get(n.k_path,Array());
const bool plain_tag_relief = builtin(n,runtime,"res://autoload/simulation_runtime.gd") &&
tags.is_typed() && tags.get_typed_builtin()==Variant::STRING_NAME;
const Dictionary relief = plain_tag_relief ? builtin_tag_relief(n,tags) :
Dictionary(runtime->call(n.k__need_relief_for_facility_tags,tags));
Dictionary row;
row[n.k_facility_id]=facility->get(n.k_instance_id);
row[n.k_facility_tags]=tags.duplicate();
const int64_t state = facility->get(n.k_state);
row[n.k_is_open]=(state==open_state || state==degraded_state);
row[n.k_reachable]=route.get(n.k_reachable,false);
row[n.k_travel_minutes]=guest_path_minutes(n,path,elevators,walk,movement_scale,stair_credit,orthogonal_cost);
const Array queue = facility->get(n.k_queue_entries);
const int64_t capacity = facility->get(n.k_capacity);
row[n.k_expected_wait]=signed_product(queue.size(),facility->get(n.k_base_service_minutes))/std::max<int64_t>(capacity,1);
row[n.k_estimated_total_gold]=runtime->call(n.k_get_facility_business_price,facility);
const int64_t quality = facility->get(n.k_base_quality);
row[n.k_expected_quality]=std::max<int64_t>(0,std::min<int64_t>(quality,100));
row[n.k_need_relief]=relief;
row[n.k_risk_level]=int64_t(0);row[n.k_illegal_level]=int64_t(0);
row[n.k_group_fit]=signed_product(std::min<int64_t>(capacity,4),5);
// Recheck at the original call position: preceding price/observer
// callbacks must retain a replaced/custom runtime's original call.
const bool plain_tag_category = builtin(n,runtime,"res://autoload/simulation_runtime.gd") &&
tags.is_typed() && tags.get_typed_builtin()==Variant::STRING_NAME;
row[n.k_service_category]=plain_tag_category ? Variant(builtin_tag_category(n,tags)) :
runtime->call(n.k__service_category_for_tags,tags);
Dictionary handle;handle[n.k_target_cell]=route.get(n.k_target_cell,guest->get(n.k_current_cell));
handle[n.k_graph_revision]=path->get(n.k_graph_revision);
row[n.k_path_handle]=handle;candidates.push_back(row);
}
path->call(n.k_end_guest_path_query_batch);
Dictionary result;result[n.k_supported]=true;result[n.k_candidates]=candidates;return result;
}
private:
// Direct translation of the two pure builtin tag methods. Fresh result,
// original insertion/branch order, no retained definition/tag memo.
static Dictionary builtin_tag_relief(const ProjectionNames &n,const Array &tags) {
Dictionary relief;
relief[n.k_tagproj_hunger]=int64_t(0);relief[n.k_tagproj_sleep]=int64_t(0);
relief[n.k_tagproj_hygiene]=int64_t(0);relief[n.k_tagproj_medical]=int64_t(0);
relief[n.k_tagproj_leisure]=int64_t(0);relief[n.k_tagproj_intimacy]=int64_t(0);
if(tags.has(n.k_tagproj_dining)) {relief[n.k_tagproj_hunger]=int64_t(80);relief[n.k_tagproj_leisure]=int64_t(10);}
if(tags.has(n.k_tagproj_lodging)) {relief[n.k_tagproj_sleep]=int64_t(90);relief[n.k_tagproj_hygiene]=std::max<int64_t>(int64_t(relief[n.k_tagproj_hygiene]),10);}
if(tags.has(n.k_tagproj_social)||tags.has(n.k_tagproj_leisure)) relief[n.k_tagproj_leisure]=std::max<int64_t>(int64_t(relief[n.k_tagproj_leisure]),40);
if(tags.has(n.k_tagproj_hygiene)||tags.has(n.k_tagproj_bath)) relief[n.k_tagproj_hygiene]=std::max<int64_t>(int64_t(relief[n.k_tagproj_hygiene]),80);
if(tags.has(n.k_tagproj_medical)) relief[n.k_tagproj_medical]=int64_t(90);
else if(tags.has(n.k_tagproj_wellness)) relief[n.k_tagproj_medical]=std::max<int64_t>(int64_t(relief[n.k_tagproj_medical]),20);
if(tags.has(n.k_tagproj_adult_only)||tags.has(n.k_tagproj_restricted_service)) relief[n.k_tagproj_intimacy]=int64_t(80);
return relief;
}
static StringName builtin_tag_category(const ProjectionNames &n,const Array &tags) {
if(tags.has(n.k_tagproj_dining))return n.k_tagproj_food;
if(tags.has(n.k_tagproj_lodging))return n.k_tagproj_lodging;
if(tags.has(n.k_tagproj_social))return n.k_tagproj_leisure;
return n.k_tagproj_service;
}
static bool builtin(const ProjectionNames &n,Object *object,const char *resource) {
if (!object) return false;Object *script=object->get_script();
return script && String(script->get(n.k_resource_path))==resource;
}
// Exact builtin cache-key spelling. No normalized/reordered profile and no
// persistent cache; String lengths count Unicode codepoints as GDScript does.
static String cache_key_suffix(const ProjectionNames &n,Object *path, const TypedArray<int64_t> &layers,
const TypedArray<StringName> &access) {
String layer_text, access_text;
for (int64_t i=0; i<layers.size(); ++i) {
if (i) layer_text += ",";
layer_text += String::num_int64(static_cast<int64_t>(layers[i]));
}
for (int64_t i=0; i<access.size(); ++i) {
if (i) access_text += ",";
const String value = String(static_cast<StringName>(access[i]));
access_text += String::num_int64(value.length()) + ":" + value;
}
return String("|L:") + layer_text + "|A:" + access_text + "|R:" +
String::num_int64(static_cast<int64_t>(path->get(n.k_graph_revision)));
}
static String cache_key_with_suffix(const Vector3i &start, const Vector3i &goal,
const String &suffix) {
return String::num_int64(start.x) + "," + String::num_int64(start.y) + "," +
String::num_int64(start.z) + ">" + String::num_int64(goal.x) + "," +
String::num_int64(goal.y) + "," + String::num_int64(goal.z) + suffix;
}
static int64_t signed_product(int64_t a,int64_t b) {
const uint64_t product=static_cast<uint64_t>(a)*static_cast<uint64_t>(b);
if (product<=static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) return static_cast<int64_t>(product);
return std::numeric_limits<int64_t>::min()+static_cast<int64_t>(product-(uint64_t(1)<<63));
}
static Array facility_access_cells(const ProjectionNames &n,Object *path,Object *facility) {
const Dictionary passable=path->get(n.k__passable_cells);
Array occupied=Array(facility->get(n.k_occupied_cells)).duplicate();
if (occupied.is_empty()) occupied.push_back(facility->get(n.k_origin));
Dictionary unique;
for (int64_t i=0;i<occupied.size();++i) {
const Vector3i cell=occupied[i];
for (int32_t dx : {-1,1}) {
// Match GDScript's Vector3i conversion after int64 addition.
const Vector3i candidate(int64_t(cell.x)+dx,cell.y,cell.z);
if (passable.has(candidate)) unique[candidate]=true;
}
}
const Vector3i origin=facility->get(n.k_origin);if(passable.has(origin)) unique[origin]=true;
const Array keys=unique.keys();std::vector<Vector3i> sorted;sorted.reserve(keys.size());
for (int64_t i=0;i<keys.size();++i) sorted.push_back(keys[i]);
std::sort(sorted.begin(),sorted.end(),[](const Vector3i &a,const Vector3i &b){
if(a.z!=b.z)return a.z<b.z;if(a.y!=b.y)return a.y<b.y;return a.x<b.x;
});
Array result;for(const Vector3i &cell:sorted)result.push_back(cell);return result;
}
static int64_t guest_path_minutes(const ProjectionNames &n,Object *path,Object *elevators,const Array &route,
int64_t movement_scale,int64_t stair_credit,int64_t orthogonal_cost) {
Dictionary memo=path->get(n.k__guest_step_connector_memo);
// Route-local only. Reuse an ownership result only after an actual
// BOOL return and a typed cached step; no script callback occurs on a
// repeated typed memo hit. A miss/conversion/custom script keeps calls.
const bool builtin_pair = builtin(n,path,"res://scripts/world/path_service.gd") &&
builtin(n,elevators,"res://scripts/transport/elevator_service.gd");
bool ownership_valid = false;
bool ownership_value = false;
StringName ownership_connector;
for(int64_t i=1;i<route.size();++i) {
const Vector3i from=route[i-1],to=route[i];
const Dictionary destinations=memo.get(from,Dictionary());
StringName connector;
const bool memo_hit = destinations.has(to);
const bool typed_memo_hit = memo_hit && destinations[to].get_type() == Variant::STRING_NAME;
if(memo_hit)connector=destinations[to];
else {TypedArray<StringName> access;connector=path->call(n.k_get_step_connector,from,to,access);}
bool special;
if (builtin_pair && typed_memo_hit && ownership_valid && connector == ownership_connector) {
special = ownership_value;
} else {
const Variant owned = elevators->call(n.k_owns_connector,connector);
special = static_cast<bool>(owned);
ownership_valid = builtin_pair && typed_memo_hit && owned.get_type() == Variant::BOOL;
ownership_value = special;
ownership_connector = connector;
}
if(!special && String(connector).begins_with("infra.connector:") && from.x==to.x && from.z==to.z &&
std::abs(int64_t(from.y)-int64_t(to.y))==1) {
const Dictionary detail=path->call(n.k_get_connector,connector);
special=detail.get(n.k_movement_type,StringName())==Variant(n.k_CONNECTOR) &&
static_cast<int64_t>(detail.get(n.k_cost,int64_t(0)))==150 && stair_credit>movement_scale;
}
if(special) {
const int64_t cost=path->call(n.k_calculate_guest_batch_path_cost,route[0],route[route.size()-1],route);
return static_cast<int64_t>(std::ceil(static_cast<double>(cost)/static_cast<double>(orthogonal_cost)));
}
}
return std::max<int64_t>(route.size()-1,0);
}
public:
// Stateless translation of the builtin nearest access query. The caller
// supplies the existing typed default profile and the real Time callable.
// Public cache publication, failure diagnostics and fallback stay ordered.
Dictionary nearest_guest_access(Object *path, const Vector3i &start,
const Array &goals, const Vector3i &fallback_target,
const TypedArray<int64_t> &layers, const TypedArray<StringName> &access,
const Callable &ticks) {
const ProjectionNames n;
return nearest_guest_access_impl(n,path,start,goals,fallback_target,layers,access,ticks);
}
private:
Dictionary nearest_guest_access_impl(const ProjectionNames &n,Object *path, const Vector3i &start,
const Array &goals, const Vector3i &fallback_target,
const TypedArray<int64_t> &layers, const TypedArray<StringName> &access,
const Callable &ticks) {
Dictionary unsupported;
unsupported[n.k_supported] = false;
if (!path || !ticks.is_valid() || goals.is_empty()) return unsupported;
Object *script = path->get_script();
if (!script || String(script->get(n.k_resource_path)) != "res://scripts/world/path_service.gd") return unsupported;
Dictionary batch = path->get(n.k__query_batch);
if (batch.is_empty() || start != static_cast<Vector3i>(batch[n.k_start])) return unsupported;
const Dictionary passable = path->get(n.k__passable_cells);
if (!passable.has(start) || blocked(n,path, start) || !layers.has(start.z)) return unsupported;
const Dictionary targets = batch[n.k_targets];
Dictionary cache = path->get(n.k__path_cache);
// Exact builtin validation loop has no callback before ticks.call().
// Build the unchanged profile/revision suffix lazily at the first key.
String key_suffix;
bool key_suffix_ready = false;
for (int64_t j = 0; j < goals.size(); ++j) {
if (goals[j].get_type() != Variant::VECTOR3I) return unsupported;
const Vector3i cell = goals[j];
if (!targets.has(cell)) {
const bool searchable = passable.has(cell) && layers.has(cell.z) && !blocked(n,path, cell);
if (searchable && (start.z == cell.z || static_cast<bool>(path->call(n.k__guest_batch_has_permitted_layer_crossing, start, cell, layers, access)))) return unsupported;
}
if (!key_suffix_ready) {
key_suffix = cache_key_suffix(n,path, layers, access);
key_suffix_ready = true;
}
const String key = cache_key_with_suffix(start, cell, key_suffix);
if (cache.has(key)) return unsupported;
}
const int64_t started = ticks.call();
if (!static_cast<bool>(batch[n.k_ready])) {
batch[n.k_tree] = path->call(n.k__build_guest_query_tree_compact, start, targets, layers, access);
batch[n.k_arithmetic_wrapped] = path->get(n.k__guest_tree_arithmetic_wrapped);
batch[n.k_ready] = true;
}
const Dictionary tree = batch[n.k_tree];
if (!static_cast<bool>(tree.get(n.k_usable, false)) || static_cast<bool>(batch.get(n.k_arithmetic_wrapped, false))) return unsupported;
Vector3i best_cell = fallback_target;
bool found = false;
int64_t best_cost = 0;
const Dictionary settled = tree[n.k_settled];
bool cost_view_ready=false,compact_cost_view=false;
Dictionary cost_indices,cost_dictionary;
PackedInt64Array cost_values;
for (int64_t j = 0; j < goals.size(); ++j) {
const Vector3i cell = goals[j];
if (!targets.has(cell) || !settled.has(cell)) continue;
// No callback occurs between these original numeric cost reads.
// Initialize lazily at the first eligible goal, and retain nothing
// past this call or reuse after the subsequent route callbacks.
if (!cost_view_ready) {
compact_cost_view=tree.get(n.k_compact,false);
if(compact_cost_view) {cost_indices=tree[n.k_indices];cost_values=tree[n.k_cost_values];}
else cost_dictionary=tree[n.k_costs];
cost_view_ready=true;
}
int64_t cost=0;
if(compact_cost_view) {
if(!cost_indices.has(cell)) return unsupported;
const int64_t index=cost_indices[cell];
if(index<0 || index>=cost_values.size()) return unsupported;
cost=cost_values[index];
} else {
if(!cost_dictionary.has(cell)) return unsupported;
cost=cost_dictionary[cell];
}
if (!found || cost < best_cost) {
found = true;
best_cost = cost;
best_cell = cell;
}
}
TypedArray<Vector3i> best_path;
if (found) {
best_path = resolve_ready_guest_point(n,path, start, best_cell, tree, layers, access);
if (best_path.is_empty()) return unsupported;
}
if (!goals.is_empty() && (!found || goals[goals.size() - 1] != Variant(best_cell))) {
resolve_ready_guest_point(n,path, start, goals[goals.size() - 1], tree, layers, access);
}
const int64_t elapsed = the_tree_native::signed_add(static_cast<int64_t>(ticks.call()), the_tree_native::signed_add(~started, 1));
path->emit_signal(n.k_path_query_measured, std::max<int64_t>(elapsed, 0));
Dictionary result;
result[n.k_supported] = true;
result[n.k_reachable] = found;
result[n.k_path] = best_path;
result[n.k_target_cell] = best_cell;
return result;
}
private:
// Only StringName keys qualify. Conversion-sensitive and custom runtime
// cases retain the original script sorter. No owner values are retained.
static Array sorted_builtin_input_names(const ProjectionNames &n,Object *runtime,const Dictionary &inputs) {
auto original=[&]() -> Array {return runtime->call(n.k__sorted_name_keys,inputs);};
if(!builtin(n,runtime,"res://autoload/simulation_runtime.gd")) return original();
const Array keys=inputs.keys();
std::vector<std::pair<String,StringName>> rows;
rows.reserve(keys.size());
for(int64_t i=0;i<keys.size();++i) {
if(keys[i].get_type()!=Variant::STRING_NAME) return original();
const StringName name=keys[i];rows.emplace_back(String(name),name);
}
// String ordering is the same engine operator used by the script.
std::sort(rows.begin(),rows.end(),[](const auto &a,const auto &b){return a.first<b.first;});
TypedArray<StringName> result;
for(const auto &row:rows) result.push_back(row.second);
return result;
}
// Fresh writable typed output, matching the builtin append loop. Check
// the script at this original call position, after any route callbacks.
static Variant copy_builtin_guest_route(const ProjectionNames &n,Object *path,const TypedArray<Vector3i> &route) {
if(!builtin(n,path,"res://scripts/world/path_service.gd")) return path->call(n.k__copy_path,route);
TypedArray<Vector3i> copied;
for(int64_t i=0;i<route.size();++i) copied.push_back(route[i]);
return copied;
}
static bool blocked(const ProjectionNames &n,Object *path, const Vector3i &cell) {
const Dictionary traffic_map = path->get(n.k__cell_traffic);
const Dictionary traffic = traffic_map.get(cell, Dictionary());
return !traffic.is_empty() && static_cast<bool>(traffic[n.k_temporary_block]);
}
static bool tree_has_cost(const ProjectionNames &n,const Dictionary &tree, const Vector3i &cell) {
if (!static_cast<bool>(tree.get(n.k_compact, false))) {
const Dictionary values = tree[n.k_costs];
return values.has(cell);
}
const Dictionary indices = tree[n.k_indices];
if (!indices.has(cell)) return false;
const int64_t index = indices[cell];
const PackedInt64Array values = tree[n.k_cost_values];
return index >= 0 && index < values.size();
}
static int64_t tree_cost(const ProjectionNames &n,const Dictionary &tree, const Vector3i &cell) {
if (!static_cast<bool>(tree.get(n.k_compact, false))) {
const Dictionary values = tree[n.k_costs];
return static_cast<int64_t>(values[cell]);
}
const Dictionary indices = tree[n.k_indices];
const PackedInt64Array values = tree[n.k_cost_values];
return values[static_cast<int64_t>(indices[cell])];
}
static TypedArray<Vector3i> resolve_ready_guest_point(const ProjectionNames &n,Object *path,
const Vector3i &start, const Vector3i &goal, const Dictionary &tree,
const TypedArray<int64_t> &layers, const TypedArray<StringName> &access) {
const Dictionary passable = path->get(n.k__passable_cells);
if (!passable.has(start)) return path->call(n.k__route_failure, n.k_START_NOT_PASSABLE);
if (!passable.has(goal)) return path->call(n.k__route_failure, n.k_GOAL_NOT_PASSABLE);
if (!layers.has(start.z) || !layers.has(goal.z)) return path->call(n.k__route_failure, n.k_ACCESS_LAYER_DENIED);
if (blocked(n,path, start)) return path->call(n.k__route_failure, n.k_START_TEMPORARILY_BLOCKED);
if (blocked(n,path, goal)) return path->call(n.k__route_failure, n.k_GOAL_TEMPORARILY_BLOCKED);
// ticks/tree callbacks may have changed the script or revision. Recheck
// at this original call position and never reuse the pre-tick suffix.
const String key = builtin(n,path,"res://scripts/world/path_service.gd") ?
cache_key_with_suffix(start, goal, cache_key_suffix(n,path, layers, access)) :
String(path->call(n.k__cache_key, start, goal, layers, access));
Dictionary cache = path->get(n.k__path_cache);
const Dictionary batch = path->get(n.k__query_batch);
const Dictionary targets = batch[n.k_targets];
if (cache.has(key) || !targets.has(goal)) return path->call(n.k__find_path_internal, start, goal);
path->set(n.k_cache_misses, the_tree_native::signed_add(static_cast<int64_t>(path->get(n.k_cache_misses)), 1));
if (start.z != goal.z && !static_cast<bool>(path->call(n.k__guest_batch_has_permitted_layer_crossing, start, goal, layers, access))) return path->call(n.k__route_failure, n.k_GOAL_UNREACHABLE);
TypedArray<Vector3i> route;
const Dictionary settled = tree[n.k_settled];
if (start == goal) route.push_back(start);
else if (settled.has(goal)) route = Array(path->call(n.k__guest_tree_reconstruct_path, tree, goal));
if (route.is_empty()) return path->call(n.k__route_failure, path->call(n.k__diagnose_no_route_reason, start, goal, layers, access));
cache[key] = copy_builtin_guest_route(n,path,route);
path->set(n.k_last_no_route_reason, n.k_OK);
path->set(n.k_last_path_cost, tree_cost(n,tree, goal));
path->set(n.k_last_error, String(""));
return route;
}
public:
private:
// One existing builtin provider operation, with no new value cache. Only
// connector-free WALK rows qualify; unsupported cases call the old provider.
// All work stays private until the original final shared[current] assignment.
struct WalkEdgeNames {
const StringName resource_path{"resource_path",false}, constants_method{"get_script_constant_map",false};
const StringName front{"FRONT",false}, back{"BACK",false}, walk{"MOVEMENT_WALK",false};
const StringName base{"ORTHOGONAL_MOVE_COST",false}, congestion{"CONGESTION_COST_PER_EXCESS",false};
const StringName workspace{"_guest_tree_workspace",false}, edges{"edges",false}, indices{"indices",false};
const StringName adjacency{"_adjacency",false}, traffic{"_cell_traffic",false}, vertical{"_vertical_walk_requires_connector",false};
const StringName to{"to",false}, connector{"connector_id",false}, access{"access_id",false}, movement{"movement_type",false};
const StringName base_cost{"base_cost",false}, cost{"cost",false}, temporary{"temporary_block",false};
const StringName dynamic{"dynamic_cost",false}, density{"current_density",false}, capacity{"base_capacity",false};
const StringName provider{"_guest_tree_shared_edges",false};
};
struct WalkEdgeContext {
Object *path=nullptr;
int64_t front=0, back=0, base=0, congestion=0;
StringName walk;
};
static bool walk_edge_context(const WalkEdgeNames &n,Object *path,const Array &layers,
const Array &access,WalkEdgeContext &context) {
if (!path || layers.size()!=2 || !access.is_empty() ||
layers[0].get_type()!=Variant::INT || layers[1].get_type()!=Variant::INT) return false;
Object *script=path->get_script();
if (!script || String(script->get(n.resource_path))!="res://scripts/world/path_service.gd" ||
!script->has_method(n.constants_method)) return false;
const Variant value=script->call(n.constants_method);
if (value.get_type()!=Variant::DICTIONARY) return false;
const Dictionary constants=value;
for (const StringName &key : {n.front,n.back,n.base,n.congestion})
if (constants.get(key,Variant()).get_type()!=Variant::INT) return false;
if (constants.get(n.walk,Variant()).get_type()!=Variant::STRING_NAME ||
int64_t(layers[0])!=int64_t(constants[n.front]) ||
int64_t(layers[1])!=int64_t(constants[n.back])) return false;
context.path=path;context.front=constants[n.front];context.back=constants[n.back];
context.base=constants[n.base];context.congestion=constants[n.congestion];context.walk=constants[n.walk];
return true;
}
static bool builtin_walk_edge_rows(const WalkEdgeNames &n,const WalkEdgeContext &context,
const Vector3i &current,Array &rows) {
if (!context.path) return false;
const Variant workspace_value=context.path->get(n.workspace);
if (workspace_value.get_type()!=Variant::DICTIONARY) return false;
const Dictionary workspace=workspace_value;
const Variant shared_value=workspace.get(n.edges,Variant());
if (shared_value.get_type()!=Variant::DICTIONARY) return false;
Dictionary shared=shared_value;
// Original cached-row branch precedes readonly and all other state reads.
if (shared.has(current)) {
const Variant cached=shared[current];
if (cached.get_type()!=Variant::ARRAY) return false;
rows=cached;return true;
}
if (shared.is_read_only()) return false;
const Variant indices_value=workspace.get(n.indices,Variant()), adjacency_value=context.path->get(n.adjacency);
const Variant traffic_value=context.path->get(n.traffic), vertical_value=context.path->get(n.vertical);
if (indices_value.get_type()!=Variant::DICTIONARY || adjacency_value.get_type()!=Variant::DICTIONARY ||
traffic_value.get_type()!=Variant::DICTIONARY || vertical_value.get_type()!=Variant::BOOL) return false;
const Dictionary indices=indices_value, adjacency=adjacency_value, traffic=traffic_value;
const Variant raw_value=adjacency.get(current,Array());
if (raw_value.get_type()!=Variant::ARRAY) return false;
const Array raw=raw_value;Array result;
for (int64_t i=0;i<raw.size();++i) {
if (raw[i].get_type()!=Variant::DICTIONARY) return false;
const Dictionary edge=raw[i];
if (edge.get(n.to,Variant()).get_type()!=Variant::VECTOR3I) return false;
const Vector3i neighbor=edge[n.to];
if (neighbor.z!=context.front && neighbor.z!=context.back) continue;
const Variant row_value=traffic.get(neighbor,Dictionary());
if (row_value.get_type()!=Variant::DICTIONARY) return false;
const Dictionary row=row_value;
if (!row.is_empty()) {
if (row.get(n.temporary,Variant()).get_type()!=Variant::BOOL) return false;
if (bool(row[n.temporary])) continue;
}
// Preserve old method for every connector/access/other movement row.
if (edge.get(n.connector,Variant()).get_type()!=Variant::STRING_NAME ||
edge.get(n.access,Variant()).get_type()!=Variant::STRING_NAME ||
edge.get(n.movement,Variant()).get_type()!=Variant::STRING_NAME) return false;
const StringName connector=edge[n.connector], access=edge[n.access], movement=edge[n.movement];
if (!connector.is_empty() || !access.is_empty() || movement!=context.walk) return false;
if (bool(vertical_value) && current.y!=neighbor.y) continue;
const Variant base=edge.get(n.base_cost,edge.get(n.cost,context.base));
if (base.get_type()!=Variant::INT) return false;
int64_t dynamic=0;
if (!row.is_empty()) {
for (const StringName &key : {n.dynamic,n.density,n.capacity})
if (row.get(key,Variant()).get_type()!=Variant::INT) return false;
const int64_t excess=the_tree_native::signed_add(int64_t(row[n.density]),
the_tree_native::signed_add(~int64_t(row[n.capacity]),1));
dynamic=the_tree_native::signed_add(int64_t(row[n.dynamic]),
signed_product(excess>0?excess:0,context.congestion));
}
const int64_t cost=the_tree_native::signed_add(int64_t(base),dynamic);
Array pair;pair.push_back(neighbor);pair.push_back(cost);result.push_back(pair);
// Same short circuit and first invalid prefix as the original method.
if (cost<=0 || !indices.has(neighbor)) break;
}
shared[current]=result;rows=result;return true;
}
public:
Dictionary guest_tree_edge_rows(Object *path,const Vector3i &current,const Array &layers,const Array &access) {
const WalkEdgeNames names;WalkEdgeContext context;Array rows;
Dictionary result;const StringName supported{"supported",false}, row_key{"rows",false};
result[supported]=false;
if (!walk_edge_context(names,path,layers,access,context) ||
!builtin_walk_edge_rows(names,context,current,rows)) return result;
result[supported]=true;result[row_key]=rows;return result;
}
private:
Dictionary search_impl(const Array &cells, const Dictionary &indices, I start_index,
const Dictionary &goals, const Callable &edge_provider, bool use_edge_cache) {
Dictionary output;
output[StringName("status")] = String("unusable");
if (start_index < 0 || start_index >= cells.size() || !edge_provider.is_valid()) return output;
try {
std::vector<I> goal_indices;
const Array goal_keys = goals.keys();
for (I j = 0; j < goal_keys.size(); ++j)
goal_indices.push_back(static_cast<I>(indices.get(goal_keys[j], I(-1))));
Array arguments;
arguments.resize(1);
const WalkEdgeNames walk_names;WalkEdgeContext walk_context;
if (edge_provider.get_method()==walk_names.provider && edge_provider.get_argument_count()==1 &&
edge_provider.get_bound_arguments_count()==2 && edge_provider.get_unbound_arguments_count()==0) {
const Array bound=edge_provider.get_bound_arguments();
if (bound.size()==2 && bound[0].get_type()==Variant::ARRAY && bound[1].get_type()==Variant::ARRAY)
walk_edge_context(walk_names,edge_provider.get_object(),bound[0],bound[1],walk_context);
}
// Call the original bound Godot edge method only when the search expands a node.
// Preserve its lazy cache population, filter ordering and first invalid row.
auto original_edges = [&](I current) {
arguments[0] = cells[current];
Array fused;bool fused_supported=false;
if (cells[current].get_type()==Variant::VECTOR3I) {
const Vector3i cell=cells[current];
fused_supported=builtin_walk_edge_rows(walk_names,walk_context,cell,fused);
}
const Variant value = fused_supported ? Variant(fused) : edge_provider.callv(arguments);
if (value.get_type() != Variant::ARRAY) return std::vector<Edge>{{-1, 0}};
const Array rows = value;
std::vector<Edge> result;
result.reserve(rows.size());
for (I j = 0; j < rows.size(); ++j) {
if (rows[j].get_type() != Variant::ARRAY) { result.push_back({-1, 0}); break; }
const Array row = rows[j];
if (row.size() != 2 || row[1].get_type() != Variant::INT) {
result.push_back({-1, 0}); break;
}
const I cost = static_cast<I>(row[1]);
// Nonpositive cost is rejected before requesting the neighbor index.
if (cost <= 0) { result.push_back({-1, cost}); break; }
const I neighbor = static_cast<I>(indices.get(row[0], I(-1)));
result.push_back({neighbor, cost});
if (neighbor < 0) break;
}
return result;
};
// Borrow cached rows only during the pure numeric edge loop.
// No engine callback runs while that borrowed vector is traversed.
std::vector<Edge> converted;
auto edges = [&](I current) -> const std::vector<Edge>& {
if (use_edge_cache) {
const auto found = cached_edges.find(current);
if (found != cached_edges.end()) return found->second;
}
converted = original_edges(current);
// Preserve the returned conversion even if a reentrant provider
// populated this cache key first. Never substitute its row here.
if (use_edge_cache) cached_edges.emplace(current, converted);
return converted;
};
const auto result = the_tree_native::search(cells.size(), start_index, goal_indices, edges);
output[StringName("status")] = String(result.status.c_str());
if (result.status != "usable") return output;
PackedInt64Array costs, parents;
if (costs.resize(result.costs.size()) != OK || parents.resize(result.parents.size()) != OK) {
output[StringName("status")] = String("unusable");
return output;
}
// Newly resized, private packed arrays own their writable storage.
// A provider may shrink caller cells. Preserve the old written
// prefix and resize-created zero tail, not the whole native vector.
const I written_count=cells.size();
if(written_count<=static_cast<I>(result.costs.size())) {
std::copy_n(result.costs.begin(),written_count,costs.ptrw());
std::copy_n(result.parents.begin(),written_count,parents.ptrw());
} else {
// Keep the original unsupported-growth path unchanged.
for (I j=0;j<cells.size();++j) {costs.set(j,result.costs[j]);parents.set(j,result.parents[j]);}
}
TypedArray<int64_t> discovered, parent_discovered;
for (I j : result.discovered) discovered.push_back(j);
for (I j : result.parent_discovered) parent_discovered.push_back(j);
Dictionary settled;
for (I j : result.settled) settled[cells[j]] = true;
output[StringName("cost_values")] = costs;
output[StringName("parent_indices")] = parents;
output[StringName("discovered")] = discovered;
output[StringName("parent_discovered")] = parent_discovered;
output[StringName("settled")] = settled;
return output;
} catch (const std::exception &) {
output = Dictionary();
output[StringName("status")] = String("unusable");
return output;
}
}
};
void initialize_tree_numeric_core(ModuleInitializationLevel level) {
if (level == MODULE_INITIALIZATION_LEVEL_SCENE) GDREGISTER_CLASS(TheTreeNumericCore);
}
void uninitialize_tree_numeric_core(ModuleInitializationLevel) {}
extern "C" {
GDExtensionBool GDE_EXPORT tree_numeric_core_init(GDExtensionInterfaceGetProcAddress get_proc,
GDExtensionClassLibraryPtr library, GDExtensionInitialization *initialization) {
GDExtensionBinding::InitObject init(get_proc, library, initialization);
init.register_initializer(initialize_tree_numeric_core);
init.register_terminator(uninitialize_tree_numeric_core);
init.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init.init();
}
}