Linked repository source
examples/logistics_post/declarations/logistics_post.odin
This read-only view comes from the file in the Gododin repository. It retains its design, example or evidence context.
Download the original file · Return to the guide
// Package declarations is the authored native class package for
// logistics_post (DX-8). One Node owns the pure-Odin depot-logistics post:
// the scene imports its authored map once, submits jobs with stable ids, and
// drives fixed ticks; the kernel owns assignment, routing, inventory, and the
// worker state, and the scene reads one bounded batch snapshot per frame for
// presentation. The job_completed signal is the only event — one emission per
// delivered job, never per agent per tick.
package declarations
import "core:mem"
import gd "gododin:bindings/typed/call"
import logistics "../subsystem"
// Logistics_Settings is the native settings Resource the Inspector edits and
// the saved scene retains: worker count and simulation seed. The scene's
// configure reads both; interactive tick pacing stays in the scene's UI.
@(gododin_class="Resource")
Logistics_Settings :: struct {
worker_count: i64 `godot:"property"`,
seed: i64 `godot:"property"`,
}
// Job_Event is the payload of the job_completed signal: the stable job id.
Job_Event :: struct {
job_id: i64,
}
@(gododin_class="Node2D")
Logistics_Post :: struct {
allocator: mem.Allocator,
post: ^logistics.Post,
width: i64,
height: i64,
last_error: i64,
ticks: i64,
deliveries: i64,
// The scene-owned settings Resource and the single coarse event.
settings: gd.Ref(Logistics_Settings) `godot:"property"`,
job_completed: gd.Signal(Job_Event),
}
logistics_post_init :: proc(self: ^Logistics_Post, allocator: mem.Allocator) -> bool {
// Field assignment only: the bridge has already written this slot's
// signal token, and a whole-struct zeroing would wipe it.
self.allocator = allocator
self.post = nil
self.last_error = 0
self.ticks = 0
self.deliveries = 0
return true
}
logistics_post_deinit :: proc(self: ^Logistics_Post) {
if self.post != nil {
_ = logistics.post_destroy(&self.post)
}
}
// configure imports the authored map (one bounded whole-grid snapshot the
// kernel copies) and spawns the workers. Reconfiguration destroys the
// previous post first; a failed import leaves the old one live.
//
// Returns 0, -1 call validation, -2 already configured + shutdown refused,
// -3 rejected configuration (bounds, kind bytes, worker count, or spawn room).
@(gododin)
logistics_post_configure :: proc(
self: ^Logistics_Post,
call: gd.Main_Call,
width, height: i64,
cells: []u8,
worker_count, seed: i64,
) -> i64 {
if gd.validate_main_call(call) != .None { return -1 }
created, created_ok := logistics.post_create(
i32(width), i32(height), cells, i32(worker_count), u32(seed & 0xffffffff), self.allocator)
if !created_ok {
self.last_error = -3
return -3
}
if self.post != nil {
if !logistics.post_destroy(&self.post) {
// Keep the failed retirement reachable and own the new post too.
self.last_error = -2
self.post = created
return -2
}
}
self.post = created
self.width = width
self.height = height
self.last_error = 0
self.ticks = 0
self.deliveries = 0
return 0
}
// submit_job registers one source→destination delivery and returns its
// stable id (replays identically for the same job order).
@(gododin)
logistics_post_submit_job :: proc(self: ^Logistics_Post, call: gd.Main_Call, source, dest: i64) -> i64 {
if gd.validate_main_call(call) != .None { return -1 }
if self.post == nil { self.last_error = -2; return -2 }
id := logistics.post_submit_job(self.post, i32(source), i32(dest))
if id < 0 { self.last_error = -3 }
return id
}
// tick advances one fixed simulation step and emits job_completed once per
// job delivered this tick. Ticks are explicit — the scene owns pacing.
@(gododin)
logistics_post_tick :: proc(self: ^Logistics_Post, call: gd.Main_Call) -> i64 {
if gd.validate_main_call(call) != .None { return -1 }
if self.post == nil { self.last_error = -2; return -2 }
delivered := logistics.post_tick(self.post)
self.ticks = i64(self.post.ticks)
self.deliveries = i64(self.post.deliveries)
delivered_ids: [logistics.MAX_WORKERS]i64
written := logistics.last_delivered_ids(self.post, delivered_ids[:])
for index in 0..<written {
payload := Job_Event{job_id = delivered_ids[index]}
if error := gd.emit(call, self.job_completed, payload); error != .None {
self.last_error = i64(error)
return i64(error)
}
}
return delivered
}
// worker_snapshot copies the bounded batch presentation snapshot: five f32s
// per worker (id, cell x, cell y, state, claimed job id). One call per frame
// for the whole scene — never per agent.
@(gododin)
logistics_post_worker_snapshot :: proc(self: ^Logistics_Post, out: []f32) -> i64 {
if self.post == nil { self.last_error = -2; return -2 }
count := logistics.worker_snapshot(self.post, out)
if count < 0 { self.last_error = -3 }
return count
}
@(gododin)
logistics_post_job_state :: proc(self: ^Logistics_Post, id: i64) -> i64 {
if self.post == nil { return -1 }
return logistics.job_state(self.post, id)
}
@(gododin)
logistics_post_get_job_count :: proc(self: ^Logistics_Post) -> i64 {
if self.post == nil { return 0 }
return self.post.job_count
}
@(gododin)
logistics_post_get_worker_count :: proc(self: ^Logistics_Post) -> i64 {
if self.post == nil { return 0 }
return i64(self.post.worker_count)
}
@(gododin)
logistics_post_get_ticks :: proc(self: ^Logistics_Post) -> i64 {
return self.ticks
}
@(gododin)
logistics_post_get_deliveries :: proc(self: ^Logistics_Post) -> i64 {
return self.deliveries
}
@(gododin)
logistics_post_get_digest :: proc(self: ^Logistics_Post) -> i64 {
if self.post == nil { return 0 }
return i64(logistics.state_digest(self.post))
}
@(gododin)
logistics_post_get_last_error :: proc(self: ^Logistics_Post) -> i64 {
return self.last_error
}