diff --git a/Cargo.lock b/Cargo.lock
index 7b153ba..457b7d8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -978,13 +978,13 @@ name = "open_tavern"
version = "0.1.0"
dependencies = [
"axum",
- "base64",
"futures-util",
"serde",
"serde_json",
"sqlx",
"tavern_macros",
"tokio",
+ "tokio-util",
]
[[package]]
@@ -1751,6 +1751,19 @@ dependencies = [
"tungstenite",
]
+[[package]]
+name = "tokio-util"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
[[package]]
name = "tower"
version = "0.4.13"
diff --git a/Cargo.toml b/Cargo.toml
index d6b1b0c..138d9fc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@ axum = { version = "0.8.4", features = ["ws"] }
serde = "1.*.*"
serde_json = "1.*.*"
tokio = { version = "1.*.*", features = ["full"] }
+tokio-util = { version = "0.7.15", features = ["io"] }
sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite"] }
tavern_macros = { version = "0.1.0", path = "tavern_macros" }
futures-util = "0.3.31"
-base64 = "0.22.1"
diff --git a/assets/web/app.js b/assets/web/app.js
index b583ac8..12c13a7 100644
--- a/assets/web/app.js
+++ b/assets/web/app.js
@@ -145,7 +145,7 @@ tavern.onspawntoken = (t) => {
token.style.left = `${t.x * GRID_SIZE}px`;
token.token_id = t.token_id;
token.innerHTML = `
-
+
`
token.onmousedown = (e) => {
token.classList.remove('token-transition');
diff --git a/src/lib.rs b/src/lib.rs
index c5ddc1a..42709c3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -144,15 +144,13 @@ impl GameServer {
api::Request::GetTokens { scene } => {
for token_id in self.game.available_tokens(scene) {
if let Some(ti) = self.game.token_info(0, token_id) {
- let bits = std::fs::read(&ti.img_source).expect("FAILED READING TOKEN IMAGE");
- let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
_ = broadcast.send((
Some(id.clone()),
api::Response::SpawnToken(SpawnToken {
token_id: token_id,
x: ti.x,
y: ti.y,
- img,
+ img: ti.img_source.clone(),
}),
));
}
@@ -160,7 +158,7 @@ impl GameServer {
}
api::Request::GetCurrentScene => {
let scene = self.game.current_scene();
- let mut scene_tokens = self
+ let scene_tokens = self
.game
.available_tokens(scene)
.iter()
@@ -173,10 +171,6 @@ impl GameServer {
img: info.img_source.clone(),
})
.collect::>();
- for spawn in scene_tokens.iter_mut() {
- let bits = std::fs::read(&spawn.img).expect("FAILED READING TOKEN IMAGE");
- spawn.img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
- }
_ = broadcast.send((
Some(id.clone()),
api::Response::ShowScene {
@@ -193,11 +187,9 @@ impl GameServer {
img_path,
} => {
let token_id = self.game.create_token(map_id, character, img_path.clone(), x, y);
- let bits = std::fs::read(&img_path).expect("FAILED READING TOKEN IMAGE");
- let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
_ = broadcast.send((
Some(id.clone()),
- api::Response::SpawnToken(SpawnToken { token_id, x, y, img }),
+ api::Response::SpawnToken(SpawnToken { token_id, x, y, img: img_path.clone() }),
));
}
api::Request::MoveToken { token_id, x, y } => {
diff --git a/src/main.rs b/src/main.rs
index 18d5794..6f26a18 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,5 @@
use axum::{
- Router,
- extract::ws::{self, Message},
- response, routing,
+ extract::ws::{self, Message}, http::StatusCode, response::{self, IntoResponse}, routing, Router
};
use futures_util::{
SinkExt, StreamExt,
@@ -9,6 +7,7 @@ use futures_util::{
};
use open_tavern::api::{Request, RequestError, Response};
use tokio::sync::{broadcast, mpsc};
+use tokio_util::io::ReaderStream;
#[tokio::main]
async fn main() {
@@ -20,6 +19,7 @@ async fn main() {
.route("/tavern.js", routing::get(socket))
.route("/app.js", routing::get(app_js))
.route("/style.css", routing::get(style))
+ .route("/assets/{*asset}", routing::get(get_asset))
.route(
"/ws",
routing::get(move |w| ws_handler(w, msend, bsend2.clone().subscribe())),
@@ -191,3 +191,28 @@ async fn style() -> impl response::IntoResponse {
std::fs::read_to_string("./assets/web/style.css").unwrap(),
)
}
+
+async fn get_asset(asset: axum::extract::Path) -> impl IntoResponse {
+ println!("Asset requested: {}", asset.0);
+ let supported_file_types = [
+ (".jpg", "image/jpeg"), (".jpeg", "image/jpeg"), (".png", "image/png")
+ ];
+ let mime = match supported_file_types.iter()
+ .filter(|t| asset.0.ends_with(t.0))
+ .map(|t| t.1)
+ .next() {
+ Some(t) => t,
+ None => return Err((StatusCode::UNSUPPORTED_MEDIA_TYPE, "Unsupported file type".to_string())),
+ };
+ let file = match tokio::fs::File::open(format!("assets/{}", asset.0)).await {
+ Ok(f) => f,
+ Err(err) => return Err((StatusCode::NOT_FOUND, format!("File not found: {}", err))),
+ };
+ let stream = ReaderStream::new(file);
+ let body = axum::body::Body::from_stream(stream);
+
+ let headers = [
+ (axum::http::header::CONTENT_TYPE, mime)
+ ];
+ Ok((headers, body))
+}
\ No newline at end of file