Phase 0: scaffold three-service backend (api/worker/frontend)
api/ (ElysiaJS+Bun WS gateway skeleton), worker/ (Rust, Redis dirty-chunk stream consumer behind a swappable RenderBackend trait), frontend/ (ElysiaJS+Pug+Tailwind4+Alpine, one server-rendered page) — all three verified running locally. docker-compose wires them up with postgres, redis, minio (rendered tile/mesh object storage), and Caddy as reverse proxy.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
mod render;
|
||||
|
||||
use redis::AsyncCommands;
|
||||
use render::{CpuRenderBackend, RenderBackend};
|
||||
|
||||
const DIRTY_CHUNK_STREAM: &str = "mcmapper:dirty-chunks";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let redis_url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://redis:6379".into());
|
||||
let client = redis::Client::open(redis_url)?;
|
||||
let mut conn = client.get_multiplexed_async_connection().await?;
|
||||
|
||||
let backend: Box<dyn RenderBackend> = Box::new(CpuRenderBackend::new());
|
||||
println!("[worker] render backend: {}", backend.name());
|
||||
println!("[worker] consuming stream '{DIRTY_CHUNK_STREAM}'");
|
||||
|
||||
// Phase 0 skeleton: poll the dirty-chunk stream and log entries. Consumer-group setup,
|
||||
// actual tile rasterization and chunk meshing land in Phase 1+ (see render::RenderBackend).
|
||||
loop {
|
||||
let entries: redis::streams::StreamReadReply = conn
|
||||
.xread_options(
|
||||
&[DIRTY_CHUNK_STREAM],
|
||||
&["$"],
|
||||
&redis::streams::StreamReadOptions::default().block(5000),
|
||||
)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
for key in entries.keys {
|
||||
for id in key.ids {
|
||||
println!("[worker] dirty chunk event {}: {:?}", id.id, id.map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
use super::RenderBackend;
|
||||
|
||||
/// CPU rendering backend, parallelized with rayon. Thread count/task-batch granularity is
|
||||
/// config-tunable (`auto` via `std::thread::available_parallelism`, or a `server`/`consumer`
|
||||
/// profile) — wired up once there's an actual render workload to tune (Phase 1+).
|
||||
pub struct CpuRenderBackend;
|
||||
|
||||
impl CpuRenderBackend {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderBackend for CpuRenderBackend {
|
||||
fn name(&self) -> &'static str {
|
||||
"cpu"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
mod cpu;
|
||||
|
||||
pub use cpu::CpuRenderBackend;
|
||||
|
||||
/// Swappable rendering strategy (CPU via rayon / GPU via wgpu / hybrid) selected at startup
|
||||
/// via the `RENDER_BACKEND` env var. Only the CPU path exists so far; GPU/hybrid land in
|
||||
/// Phase 8, with automatic fallback to CPU if `gpu`/`hybrid` is requested but no compatible
|
||||
/// GPU is found.
|
||||
pub trait RenderBackend: Send + Sync {
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
// Tile rasterization and chunk meshing methods land alongside their Phase 1/2 callers —
|
||||
// no point defining signatures for data shapes that don't exist yet.
|
||||
}
|
||||
Reference in New Issue
Block a user