aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/fparkan-game/src/main.rs3
-rw-r--r--crates/fparkan-assets/src/lib.rs1
-rw-r--r--crates/fparkan-prototype/src/lib.rs30
-rw-r--r--crates/fparkan-runtime/src/lib.rs13
-rw-r--r--docs/tomes/03-resources.md7
-rw-r--r--docs/tomes/06-behavior.md6
6 files changed, 60 insertions, 0 deletions
diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs
index 90effa7..7f059af 100644
--- a/apps/fparkan-game/src/main.rs
+++ b/apps/fparkan-game/src/main.rs
@@ -1810,6 +1810,7 @@ mod tests {
scale: [2.0, 3.0, 4.0],
visual_ids: Vec::new(),
properties: Vec::new(),
+ unit_components: Vec::new(),
};
assert_eq!(
mission_position_scale_transform(&draft),
@@ -1850,6 +1851,7 @@ mod tests {
scale: [1.0; 3],
visual_ids: Vec::new(),
properties: Vec::new(),
+ unit_components: Vec::new(),
}];
let commands = render_snapshot_commands_with_assets(&snapshot, None, Some(&drafts))?;
@@ -1887,6 +1889,7 @@ mod tests {
scale: [1.0; 3],
visual_ids: Vec::new(),
properties: Vec::new(),
+ unit_components: Vec::new(),
}];
let commands = render_snapshot_commands_with_assets(&snapshot, None, Some(&drafts))?;
diff --git a/crates/fparkan-assets/src/lib.rs b/crates/fparkan-assets/src/lib.rs
index 5bb3edf..99de160 100644
--- a/crates/fparkan-assets/src/lib.rs
+++ b/crates/fparkan-assets/src/lib.rs
@@ -3528,6 +3528,7 @@ mod tests {
roots: vec![prototype.key.clone()],
prototype_requests: vec![prototype.key.clone()],
root_prototype_request_spans: std::iter::once(0..1).collect(),
+ root_unit_components: vec![Vec::new()],
visual_dependencies_expanded: false,
nodes: vec![root_node, prototype_node, mesh_node],
edges: vec![
diff --git a/crates/fparkan-prototype/src/lib.rs b/crates/fparkan-prototype/src/lib.rs
index 6570a95..4010e6f 100644
--- a/crates/fparkan-prototype/src/lib.rs
+++ b/crates/fparkan-prototype/src/lib.rs
@@ -128,6 +128,13 @@ pub struct PrototypeGraph {
pub prototype_requests: Vec<PrototypeKey>,
/// Mission object-local spans of effective prototype requests.
pub root_prototype_request_spans: Vec<std::ops::Range<usize>>,
+ /// Ordered raw unit DAT component records for each mission root.
+ ///
+ /// This vector is aligned with [`Self::roots`]. Direct roots and legacy
+ /// unit bindings have an empty record list. Records are preserved without
+ /// assigning runtime semantics to their `kind`, links, descriptions, or
+ /// opaque tails.
+ pub root_unit_components: Vec<Vec<UnitComponentRecord>>,
/// Whether visual dependency expansion has already been applied.
pub visual_dependencies_expanded: bool,
/// Materialized prototype dependency graph nodes.
@@ -637,6 +644,7 @@ fn resolve_direct_prototype(
struct ResolvedPrototypeRequests {
expected_count: usize,
prototypes: Vec<EffectivePrototype>,
+ unit_components: Vec<UnitComponentRecord>,
}
fn resolve_prototype_requests(
@@ -652,6 +660,7 @@ fn resolve_prototype_requests(
Ok(ResolvedPrototypeRequests {
expected_count: 1,
prototypes: prototype.into_iter().collect(),
+ unit_components: Vec::new(),
})
}
@@ -686,6 +695,7 @@ fn resolve_unit_dat_prototype_requests(
return Ok(ResolvedPrototypeRequests {
expected_count: unit.records.len(),
prototypes,
+ unit_components: unit.records,
});
}
@@ -697,6 +707,7 @@ fn resolve_unit_dat_prototype_requests(
Ok(ResolvedPrototypeRequests {
expected_count: 1,
prototypes: prototype.into_iter().collect(),
+ unit_components: Vec::new(),
})
}
@@ -741,6 +752,9 @@ pub fn build_prototype_graph(
graph.roots.push(key);
let start = graph.prototype_requests.len();
let expansion = resolve_prototype_requests(repository, vfs, root)?;
+ graph
+ .root_unit_components
+ .push(expansion.unit_components.clone());
let root_provenance = provenance_for_root(root_index, root);
for prototype in expansion.prototypes {
let prototype_node = PrototypeGraphNode::prototype(
@@ -841,6 +855,9 @@ pub fn build_prototype_graph_report(
match resolve_prototype_requests(repository, vfs, root) {
Ok(expansion) => {
let expected = expansion.expected_count;
+ graph
+ .root_unit_components
+ .push(expansion.unit_components.clone());
if edge == PrototypeGraphEdge::MissionToUnitDat {
report.unit_component_count += expected;
}
@@ -929,6 +946,9 @@ pub fn build_prototype_graph_report(
}),
}),
}
+ if graph.root_unit_components.len() <= root_index {
+ graph.root_unit_components.push(Vec::new());
+ }
let end = graph.prototype_requests.len();
graph.root_prototype_request_spans.push(start..end);
}
@@ -1770,6 +1790,16 @@ mod tests {
assert_eq!(graph.prototype_requests.len(), 2);
assert_eq!(graph.prototype_requests[0].0 .0, b"component_a");
assert_eq!(graph.prototype_requests[1].0 .0, b"component_b");
+ assert_eq!(graph.root_unit_components.len(), 1);
+ assert_eq!(graph.root_unit_components[0].len(), 2);
+ assert_eq!(
+ cstr_bytes(&graph.root_unit_components[0][0].resource_raw),
+ b"component_a"
+ );
+ assert_eq!(
+ cstr_bytes(&graph.root_unit_components[0][1].resource_raw),
+ b"component_b"
+ );
assert_eq!(resolved.len(), 2);
assert_eq!(report.unit_reference_count, 1);
assert_eq!(report.unit_component_count, 2);
diff --git a/crates/fparkan-runtime/src/lib.rs b/crates/fparkan-runtime/src/lib.rs
index 740ad34..696bb33 100644
--- a/crates/fparkan-runtime/src/lib.rs
+++ b/crates/fparkan-runtime/src/lib.rs
@@ -31,6 +31,7 @@ use fparkan_assets::{
use fparkan_path::{normalize_relative, NormalizedPath, PathError, PathPolicy};
use fparkan_prototype::{
build_prototype_graph_report, PrototypeGraph, PrototypeGraphFailure, PrototypeGraphReport,
+ UnitComponentRecord,
};
use fparkan_resource::{resource_name, CachedResourceRepository, ResourceRepository};
use fparkan_terrain::SurfaceQuery;
@@ -229,6 +230,11 @@ pub struct MissionObjectDraft {
pub visual_ids: Vec<AssetId<PreparedVisual>>,
/// Ordered mission properties.
pub properties: Vec<MissionObjectProperty>,
+ /// Ordered raw Unit DAT components that selected this mission root.
+ ///
+ /// The records preserve source provenance for future controller loading;
+ /// their fields intentionally have no inferred Control/physics semantics.
+ pub unit_components: Vec<UnitComponentRecord>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
@@ -912,6 +918,11 @@ fn load_mission_with_options_and_progress(
name_raw: property.name_raw.clone(),
})
.collect(),
+ unit_components: prototype_graph
+ .root_unit_components
+ .get(index)
+ .cloned()
+ .unwrap_or_default(),
})
.collect();
let mut new_runtime_world = new_world(WorldConfig);
@@ -1608,6 +1619,7 @@ mod tests {
.expect("load mission");
let drafts = loaded_mission_object_drafts(&engine).expect("object drafts");
let assets = loaded_mission_assets(&engine).expect("mission assets");
+ let graph = loaded_prototype_graph(&engine).expect("prototype graph");
assert_eq!(drafts.len(), loaded.object_count);
assert!(drafts.iter().any(|draft| !draft.visual_ids.is_empty()));
@@ -1617,6 +1629,7 @@ mod tests {
u32::try_from(index).ok().map(OriginalObjectId)
);
assert_eq!(draft.visual_ids, assets.visuals_for_object(index));
+ assert_eq!(draft.unit_components, graph.root_unit_components[index]);
assert!(draft.position.iter().all(|component| component.is_finite()));
assert!(draft
.orientation_raw
diff --git a/docs/tomes/03-resources.md b/docs/tomes/03-resources.md
index 7bce1a1..94c22c2 100644
--- a/docs/tomes/03-resources.md
+++ b/docs/tomes/03-resources.md
@@ -468,6 +468,13 @@ string длиной 32 байта. Требование обязательног
Части 2. Все соответствуют формуле размера, `kind == 1` и
`archive_name == "objects.rlb"`.
+При построении mission prototype graph FParkan сохраняет для каждого root
+упорядоченный список этих raw records. Список передаётся в
+`MissionObjectDraft` без преобразования `kind`, `parent_or_link`, description
+или tails в предполагаемые роли. Так runtime уже располагает исходными данными
+составного unit для будущих Control/physics/AniMesh consumers, но не выдаёт
+структурное сходство за доказанную семантику.
+
## Вспомогательные форматы
MSH, материал и текстура отвечают за видимую форму. Полноценный прототип
diff --git a/docs/tomes/06-behavior.md b/docs/tomes/06-behavior.md
index a201c4b..5764bb0 100644
--- a/docs/tomes/06-behavior.md
+++ b/docs/tomes/06-behavior.md
@@ -276,6 +276,12 @@ component как ordered raw-string/resource provenance, но не для при
этим строкам смысловых имён до трассировки private update methods. Extractor:
`tools/ghidra/ExportAniMeshControlCaller.java`.
+Runtime сохраняет ordered raw Unit DAT records рядом с каждым mission object
+draft. Это создаёт проверяемую границу передачи данных от loader-а к будущему
+Control consumer-у: никакой компонент пока не получает имя `Control` только
+по `kind`, `parent_or_link` или description; semantic binding появится лишь
+после trace private update/load methods.
+
### Control system и physical model
`LoadControlSystem` загружает настройки controller-а: ограничения скорости,