allow texture streaming(png, jpeg for now) and use texture url instead of passing textures in base64

This commit is contained in:
Rusty Striker 2025-06-20 16:47:55 +03:00
parent 1d63e3d983
commit d29a2a8590
Signed by: RustyStriker
GPG key ID: 9DBDBC7C48FC3C31
5 changed files with 47 additions and 17 deletions

15
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -145,7 +145,7 @@ tavern.onspawntoken = (t) => {
token.style.left = `${t.x * GRID_SIZE}px`;
token.token_id = t.token_id;
token.innerHTML = `
<img src='data:image/jpg;base64,${t.img}' ondragstart='return false;'>
<img src='${t.img}' ondragstart='return false;'>
`
token.onmousedown = (e) => {
token.classList.remove('token-transition');

View file

@ -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::<Vec<_>>();
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 } => {

View file

@ -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<String>) -> 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))
}