P1a: material import, sixteen faces, sample engine and quarry
This commit is contained in:
@@ -11,6 +11,7 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
zhila-core = { path = "../zhila-core" }
|
||||
zhila-engine = { path = "../zhila-engine" }
|
||||
zhila-material = { path = "../zhila-material" }
|
||||
zhila-protocol = { path = "../zhila-protocol" }
|
||||
tokio.workspace = true
|
||||
tokio-tungstenite.workspace = true
|
||||
|
||||
@@ -20,7 +20,7 @@ use tracing::{info, warn};
|
||||
use zhila_core::{CoreError, Engine, Session};
|
||||
use zhila_engine::{AudioEngine, NullEngine};
|
||||
use zhila_protocol::{
|
||||
Actor, ClientMessage, CommandResult, ErrorCode, PROTOCOL_VERSION, ServerMessage,
|
||||
Actor, ClientMessage, CommandResult, ErrorCode, PROTOCOL_VERSION, Seal, ServerMessage,
|
||||
};
|
||||
|
||||
const HELLO_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
@@ -216,7 +216,7 @@ async fn handle(stream: TcpStream, shared: Shared) -> anyhow::Result<()> {
|
||||
protocol,
|
||||
token,
|
||||
} => (client, protocol, token),
|
||||
ClientMessage::Command { .. } => {
|
||||
ClientMessage::Command { .. } | ClientMessage::ImportCore { .. } => {
|
||||
let _ = send_out(
|
||||
&mut sink,
|
||||
error_message(None, ErrorCode::Protocol, "expected hello before commands"),
|
||||
@@ -303,9 +303,60 @@ async fn handle(stream: TcpStream, shared: Shared) -> anyhow::Result<()> {
|
||||
}
|
||||
});
|
||||
|
||||
let mut pending_import: Option<(u64, String, Seal)> = None;
|
||||
while let Some(frame) = source.next().await {
|
||||
let message = match frame {
|
||||
Ok(Message::Text(text)) => text,
|
||||
Ok(Message::Binary(bytes)) => {
|
||||
let Some((id, name, seal)) = pending_import.take() else {
|
||||
let _ = direct_tx.send(error_message(
|
||||
None,
|
||||
ErrorCode::BadRequest,
|
||||
"a binary frame arrived without an import header",
|
||||
));
|
||||
continue;
|
||||
};
|
||||
let import = tokio::task::spawn_blocking(move || {
|
||||
zhila_material::import_bytes(&name, seal, bytes.to_vec())
|
||||
})
|
||||
.await;
|
||||
match import {
|
||||
Ok(Ok(core)) => {
|
||||
let summary = core.summary.clone();
|
||||
let state = {
|
||||
let mut session = shared.session.lock().await;
|
||||
session.import_core(core, Actor::Hand)
|
||||
};
|
||||
let _ = direct_tx.send(Outbound::Text(
|
||||
serde_json::to_string(&ServerMessage::Imported { id, core: summary })
|
||||
.expect("import serializes"),
|
||||
));
|
||||
let snapshot = ServerMessage::Snapshot {
|
||||
revision: state.revision,
|
||||
state,
|
||||
};
|
||||
let _ = shared
|
||||
.out
|
||||
.send(Outbound::Text(serde_json::to_string(&snapshot).unwrap()));
|
||||
}
|
||||
Ok(Err(error)) => {
|
||||
let _ = direct_tx.send(error_message(
|
||||
Some(id),
|
||||
ErrorCode::BadRequest,
|
||||
error.to_string(),
|
||||
));
|
||||
}
|
||||
Err(error) => {
|
||||
warn!("import task failed: {error}");
|
||||
let _ = direct_tx.send(error_message(
|
||||
Some(id),
|
||||
ErrorCode::Internal,
|
||||
"the import task failed",
|
||||
));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Ok(Message::Close(_)) => break,
|
||||
Ok(_) => continue,
|
||||
Err(error) => {
|
||||
@@ -330,6 +381,17 @@ async fn handle(stream: TcpStream, shared: Shared) -> anyhow::Result<()> {
|
||||
ClientMessage::Hello { .. } => {
|
||||
let _ = direct_tx.send(error_message(None, ErrorCode::Protocol, "already greeted"));
|
||||
}
|
||||
ClientMessage::ImportCore { id, name, seal } => {
|
||||
if pending_import.is_some() {
|
||||
let _ = direct_tx.send(error_message(
|
||||
Some(id),
|
||||
ErrorCode::BadRequest,
|
||||
"the previous import has not received its bytes yet",
|
||||
));
|
||||
} else {
|
||||
pending_import = Some((id, name, seal));
|
||||
}
|
||||
}
|
||||
ClientMessage::Command { id, command } => {
|
||||
let outcome = {
|
||||
let mut session = shared.session.lock().await;
|
||||
@@ -403,9 +465,13 @@ fn origin_allowed(origin: &str) -> bool {
|
||||
|
||||
fn error_code(error: &CoreError) -> ErrorCode {
|
||||
match error {
|
||||
CoreError::InvalidTempo | CoreError::InvalidVelocity | CoreError::InvalidGain => {
|
||||
ErrorCode::BadRequest
|
||||
}
|
||||
CoreError::InvalidTempo
|
||||
| CoreError::InvalidVelocity
|
||||
| CoreError::InvalidGain
|
||||
| CoreError::InvalidRow
|
||||
| CoreError::InvalidStep
|
||||
| CoreError::UnknownFace
|
||||
| CoreError::UnsupportedCommand => ErrorCode::BadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user