11 files changed, 0 insertions(+), 903 deletions(-)
D client/Cargo.toml
D client/src/chat.rs
D client/src/lib.rs
D client/src/macros.rs
D client/src/rendering/mod.rs
D client/src/rendering/renderer_canvascentric.rs
D client/src/rendering/renderer_playercentric.rs
D client/src/rendering/util.rs
D client/src/textures/loader_fast.rs
D client/src/textures/loader_slow.rs
D client/src/textures/mod.rs
D client/Cargo.toml => client/Cargo.toml +0 -62
@@ 1,62 0,0 @@
-[package]
-name = "starkingdoms-client"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-wasm-bindgen = "0.2"
-js-sys = "0.3"
-console_log = { version = "1", features = ["color"] }
-log = "0.4"
-futures = { version = "0.3", default-features = false }
-wasm-bindgen-futures = "0.4"
-url = "2.3"
-starkingdoms-protocol = { version = "0.1.0", path = "../protocol" }
-rmp-serde = "1.1"
-ws_stream_wasm = "0.7"
-serde = { version = "1", features = ["derive"] }
-lazy_static = "1.4"
-markdown = "1.0.0-alpha.7" # DO NOT DOWNGRADE
-async-recursion = "1"
-console_error_panic_hook = "0.1"
-async-trait = "0.1.68"
-image = { version = "0.24.6", optional = true }
-ron = { version = "0.8", optional = true }
-base64 = { version = "0.21.0", optional = true }
-
-[dependencies.web-sys]
-version = "0.3.4"
-features = [
- 'Document',
- 'Element',
- 'HtmlCanvasElement',
- 'Window',
- 'CanvasRenderingContext2d',
- 'HtmlImageElement',
- 'SvgImageElement',
- 'HtmlVideoElement',
- 'ImageBitmap',
- 'OffscreenCanvas',
- 'VideoFrame',
- 'CanvasWindingRule',
- 'Path2d',
- 'CanvasPattern',
- 'CanvasGradient',
- 'HitRegionOptions',
- 'ImageData',
- 'TextMetrics',
- 'DomMatrix',
- 'CssStyleDeclaration'
-]
-
-[features]
-textures-slow = []
-textures-fast = ['image', 'ron', 'base64']
-
-renderer-canvascentric = []
-renderer-playercentric = []>
\ No newline at end of file
D client/src/chat.rs => client/src/chat.rs +0 -25
@@ 1,25 0,0 @@
-use std::error::Error;
-use wasm_bindgen::prelude::*;
-use starkingdoms_protocol::MessageC2S;
-use crate::CLIENT;
-use futures::SinkExt;
-use starkingdoms_protocol::message_c2s::MessageC2SChat;
-
-#[wasm_bindgen]
-// TODO: Switch to async-aware mutexes
-#[allow(clippy::await_holding_lock)]
-pub async fn send_chat(message: &str) -> Result<(), JsError> {
- let client_data = &mut CLIENT.write()?.client_data;
-
- if let Some(data) = client_data {
- let msg = MessageC2S::Chat(MessageC2SChat {
- message: message.to_string(),
- special_fields: Default::default(),
- }).try_into().map_err(|e: Box<dyn Error> | JsError::new(&e.to_string()))?;
- send!(data.tx, msg).await?;
- } else {
- return Err(JsError::new("Client not yet connected to server"));
- }
-
- Ok(())
-}>
\ No newline at end of file
D client/src/lib.rs => client/src/lib.rs +0 -327
@@ 1,327 0,0 @@
-use std::error::Error;
-use std::panic;
-use std::str::FromStr;
-use futures::stream::{SplitSink, SplitStream};
-use futures::{StreamExt};
-use log::{error, info, Level, trace, warn};
-use wasm_bindgen::prelude::*;
-use ws_stream_wasm::{WsErr, WsMessage, WsMeta, WsStream};
-use futures::SinkExt;
-use lazy_static::lazy_static;
-use std::sync::Arc;
-use std::sync::RwLock;
-use async_recursion::async_recursion;
-use futures::FutureExt;
-use wasm_bindgen_futures::JsFuture;
-use web_sys::{Window};
-use starkingdoms_protocol::message_c2s::{MessageC2SGoodbye, MessageC2SHello, MessageC2SPing};
-use starkingdoms_protocol::{MessageC2S, MessageS2C, PROTOCOL_VERSION};
-use starkingdoms_protocol::goodbye_reason::GoodbyeReason;
-use starkingdoms_protocol::planet::Planet;
-use starkingdoms_protocol::player::Player;
-use starkingdoms_protocol::state::State;
-
-use crate::rendering::Renderer;
-use crate::rendering::renderer::WebRenderer;
-use crate::textures::loader::TextureLoader;
-use crate::textures::{TextureManager, TextureSize};
-
-
-#[macro_use]
-pub mod macros;
-pub mod chat;
-pub mod rendering;
-pub mod textures;
-
-#[wasm_bindgen]
-extern {
- pub fn alert(s: &str);
-}
-
-#[derive(Debug)]
-pub struct Client {
- pub client_data: Option<ClientData>,
- pub planets: Vec<Planet>,
- pub x: f64,
- pub y: f64,
- pub players: Vec<Player>
-}
-
-#[derive(Debug)]
-pub struct ClientData {
- pub state: State,
- pub tx: SplitSink<WsStream, WsMessage>,
- pub rx: SplitStream<WsStream>,
- pub pong_timeout: u64,
- pub textures: TextureLoader,
- pub renderer: WebRenderer,
- pub username: String
-}
-
-pub const PONG_MAX_TIMEOUT: u64 = 5;
-
-lazy_static! {
- pub static ref CLIENT: Arc<RwLock<Client>> = Arc::new(RwLock::new(Client {
- client_data: None,
- planets: vec![],
- x: 0f64,
- y: 0f64,
- players: vec![]
- }));
- //pub static ref BUTTONS: Arc<Buttons> = Arc::new(Buttons { up: false, left: false, right: false, down: false });
-}
-
-pub const MAX_CONNECTION_TRIES: i32 = 10;
-
-#[wasm_bindgen]
-pub async fn rust_init(gateway: &str, username: &str, texture_size: &str) -> Result<(), JsValue> {
- panic::set_hook(Box::new(console_error_panic_hook::hook));
-
- set_status("Starting logger...");
-
- console_log::init_with_level(Level::Debug).unwrap();
-
- info!("Logger setup successfully");
-
- info!("Loading sprites...");
- set_status("Loading sprites..");
-
- let textures = TextureLoader::load(TextureSize::from_str(texture_size).unwrap()).map_err(|e| e.to_string())?;
-
- match main(gateway, username, 1, textures, WebRenderer::get("canvas").await.unwrap()).await {
- Ok(c) => c,
- Err(e) => {
- error!("Error initializing gateway client: {}", e);
- return Err(JsValue::from_str(&e.to_string()));
- }
- };
-
- info!("StarKingdoms client set up successfully");
-
- Ok(())
-}
-
-#[async_recursion(?Send)]
-pub async fn main(gateway: &str, username: &str, backoff: i32, textures: TextureLoader, renderer: WebRenderer) -> Result<(), Box<dyn Error>> {
- if backoff != 1 {
- info!("Backing off connection: waiting {} seconds", backoff * backoff);
- wait_for(sleep(backoff * backoff * 1000)).await;
- }
-
- if backoff > MAX_CONNECTION_TRIES {
- set_status("Connection to server failed");
- return Err("Hit backoff limit during reconnection attempt".into());
- }
- if backoff == 1 {
- set_status("Connecting to server...");
- } else {
- set_status(&format!("Connection failed, retrying... (try {}/10)", backoff));
- }
-
- info!("FAST CONNECT: {}", gateway);
- let gateway_url = url::Url::parse(gateway)?;
- trace!("Gateway URL parsed");
- let (_ws, ws_stream) = match WsMeta::connect(gateway_url, None).await {
- Ok(r) => r,
- Err(e) => {
- return match e {
- WsErr::ConnectionFailed { .. } => {
- main(gateway, username, backoff + 1, textures, renderer).await
- },
- _ => {
- Err(e.into())
- }
- }
- }
- };
- trace!("Connected to gateway socket");
- let (tx, rx) = ws_stream.split();
-
- let mut client_data = ClientData {
- state: State::Handshake,
- tx,
- rx,
- pong_timeout: (js_sys::Date::now() as u64 / 1000) + 5,
- textures,
- renderer,
- username: username.to_string()
- };
-
- trace!("Split stream, handshaking with server");
-
- set_status("Handshaking with server...");
-
- let msg = MessageC2S::Hello(MessageC2SHello {
- version: PROTOCOL_VERSION,
- requested_username: username.to_string(),
- next_state: State::Play.into(),
- special_fields: Default::default(),
- }).try_into()?;
- send!(client_data.tx, msg).await?;
-
- trace!("Sent handshake start packet");
-
- if let Some(msg) = recv_now!(client_data.rx)? {
- let typed_msg: MessageS2C = msg;
-
- match typed_msg {
- MessageS2C::Hello(pkt) => {
- info!("FAST CONNECT - connected to server protocol {} given username {}, switching to state {:?}", pkt.version, pkt.given_username, pkt.next_state);
- client_data.state = pkt.next_state.unwrap();
- },
- MessageS2C::Goodbye(pkt) => {
- error!("server disconnected before finishing handshake: {:?}", pkt.reason);
- return Err(format!("disconnected by server: {:?}", pkt.reason).into());
- },
- _ => {
- warn!("received unexpected packet from server: {:?}", typed_msg);
- }
- }
- } else {
- error!("Server closed the connection")
- }
-
- CLIENT.write()?.client_data = Some(client_data);
-
- set_status(&format!("Connected! Username: {}", username));
-
- Ok(())
-}
-
-#[wasm_bindgen]
-pub async fn send_ping_pong() -> Result<(), JsError> {
- let mut client = CLIENT.write()?;
-
- if client.client_data.is_none() {
- return Err(JsError::new("Client not yet initialized"));
- }
-
- let client_data = client.client_data.as_mut().unwrap();
-
-
- send!(client_data.tx, MessageC2S::Ping(MessageC2SPing {special_fields: Default::default()}).try_into().map_err(|_| JsError::new("I/O Error"))?).await?;
-
- Ok(())
-}
-
-#[wasm_bindgen]
-pub async fn update_socket() -> Result<(), JsError> {
-
- let mut client = CLIENT.write()?;
-
- if client.client_data.is_none() {
- return Err(JsError::new("Client not yet initialized"));
- }
-
- let client_data = client.client_data.as_mut().unwrap();
-
- if client_data.pong_timeout < (js_sys::Date::now() as u64 / 1000) {
- error!("Connection timed out");
- let msg = MessageC2S::Goodbye(MessageC2SGoodbye {
- reason: GoodbyeReason::PingPongTimeout.into(),
- special_fields: Default::default(),
- }).try_into().map_err(|e: Box<dyn Error> | JsError::new(&e.to_string()))?;
- send!(client_data.tx, msg).await?;
- client.client_data = None;
- set_status("Connection timed out. Reload to reconnect");
- return Err(JsError::new("Connection timed out"));
- }
-
- let maybe_msg: Option<MessageS2C> = match recv!(client_data.rx) {
- Ok(r) => r,
- Err(e) => {
- return Err(JsError::new(e))
- }
- };
-
- if let Some(msg) = maybe_msg {
- match msg {
- MessageS2C::Goodbye(pkt) => {
- info!("server sent disconnect: {:?}", pkt.reason);
- client.client_data = None;
- return Err(JsError::new("disconnected by server"));
- }
- MessageS2C::Chat(pkt) => {
- info!("[CHAT] {}: {}", pkt.from, pkt.message);
-
- let window: Window = web_sys::window().expect("no global `window` exists");
- let document = window.document().expect("should have a document on window");
- let chatbox = document.get_element_by_id("chats").expect("chatbox does not exist");
-
- let new_elem = document.create_element("div").expect("could not create element");
-
- let msg_formatted = markdown::to_html(&format!("**[{}]** {}", pkt.from, pkt.message));
-
- new_elem.set_inner_html(&msg_formatted);
-
- chatbox.append_child(&new_elem).unwrap();
- },
- MessageS2C::Pong(_) => {
- client_data.pong_timeout = (js_sys::Date::now() as u64 / 1000) + PONG_MAX_TIMEOUT
- },
- MessageS2C::PlanetData(pkt) => {
- client.planets = pkt.planets;
- },
- MessageS2C::PlayersUpdate(pkt) => {
- let me = pkt.players.iter().find(|i| i.username == client_data.username);
- if let Some(me) = me {
- client.x = me.x as f64;
- client.y = me.y as f64;
- }
- client.players = pkt.players;
- }
- _ => {
- warn!("server sent unexpected packet {:?}, ignoring", msg);
- }
- }
- }
-
- Ok(())
-}
-
-#[wasm_bindgen]
-pub fn set_status(new_status: &str) {
- let window: Window = web_sys::window().expect("no global `window` exists");
- let document = window.document().expect("should have a document on window");
- let status = document.get_element_by_id("status").expect("statusbox does not exist");
- status.set_inner_html(new_status);
-}
-
-#[wasm_bindgen]
-pub fn sleep(ms: i32) -> js_sys::Promise {
- js_sys::Promise::new(&mut |resolve, _| {
- web_sys::window()
- .unwrap()
- .set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, ms)
- .unwrap();
- })
-}
-
-pub async fn wait_for(promise: js_sys::Promise) -> JsFuture {
- wasm_bindgen_futures::JsFuture::from(promise)
-}
-
-#[wasm_bindgen]
-pub fn version() -> u32 {
- PROTOCOL_VERSION
-}
-
-#[wasm_bindgen]
-pub fn get_texture(texture_id: &str) -> Option<String> {
- let client = CLIENT.read().unwrap();
- if let Some(client_data) = &client.client_data {
- client_data.textures.get_texture(texture_id)
- } else {
- None
- }
-}
-
-#[wasm_bindgen]
-pub async fn render_frame(delta: f64) -> Result<(), JsError> {
- let client = CLIENT.read().unwrap();
- if let Some(client_data) = &client.client_data {
- Ok(client_data.renderer.render_frame(delta).await.map_err(|e| JsError::new(&format!("{}", e)))?)
- } else {
- Err(JsError::new("Client not yet initialized"))
- }
-}
D client/src/macros.rs => client/src/macros.rs +0 -64
@@ 1,64 0,0 @@
-use ws_stream_wasm::WsMessage;
-
-#[macro_export]
-macro_rules! send {
- ($writer:expr,$pkt:expr) => {
- $writer.send($crate::macros::__generic_packet_to_message($pkt))
- };
-}
-
-#[macro_export]
-macro_rules! recv {
- ($reader:expr) => {
- {
- if let Some(future_result) = $reader.next().now_or_never() {
- if let Some(msg) = future_result {
- if let WsMessage::Binary(msg) = msg {
- match MessageS2C::try_from(msg.as_slice()) {
- Ok(d) => Ok(Some(d)),
- Err(e) => {
- log::error!("error deserializing message: {}", e);
- Ok(None)
- }
- }
- } else {
- Ok(None)
- }
- } else {
- log::error!("pipe closed");
- Err("Pipe closed")
- }
- } else {
- Ok(None)
- }
- }
- }
-}
-
-#[macro_export]
-macro_rules! recv_now {
- ($reader:expr) => {
- {
- if let Some(msg) = $reader.next().await {
- if let WsMessage::Binary(msg) = msg {
- match MessageS2C::try_from(msg.as_slice()) {
- Ok(d) => Ok(Some(d)),
- Err(e) => {
- log::error!("error deserializing message: {}", e);
- Ok(None)
- }
- }
- } else {
- Ok(None)
- }
- } else {
- log::error!("pipe closed");
- Err("Pipe closed")
- }
- }
- };
-}
-
-pub fn __generic_packet_to_message(pkt: Vec<u8>) -> WsMessage {
- WsMessage::from(pkt)
-}>
\ No newline at end of file
D client/src/rendering/mod.rs => client/src/rendering/mod.rs +0 -23
@@ 1,23 0,0 @@
-use std::error::Error;
-use async_trait::async_trait;
-
-#[cfg(all(feature = "renderer-playercentric", feature = "renderer-canvascentric"))]
-compile_error!("Mutually exclusive features renderer-playercentric and renderer-canvascentric selected");
-#[cfg(not(any(feature = "renderer-playercentric", feature = "renderer-canvascentric")))]
-compile_error!("Required feature renderer not selected");
-
-#[cfg(feature = "renderer-canvascentric")]
-#[path = "renderer_canvascentric.rs"]
-pub mod renderer;
-
-#[cfg(feature = "renderer-playercentric")]
-#[path = "renderer_playercentric.rs"]
-pub mod renderer;
-
-pub mod util;
-
-#[async_trait]
-pub trait Renderer {
- async fn get(canvas_element_id: &str) -> Result<Self, Box<dyn Error>> where Self: Sized;
- async fn render_frame(&self, time_delta_ms: f64) -> Result<(), Box<dyn Error>>;
-}>
\ No newline at end of file
D client/src/rendering/renderer_canvascentric.rs => client/src/rendering/renderer_canvascentric.rs +0 -110
@@ 1,110 0,0 @@
-use std::error::Error;
-use async_trait::async_trait;
-use log::debug;
-use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlImageElement};
-use crate::rendering::Renderer;
-use wasm_bindgen::{JsCast, JsValue};
-use crate::CLIENT;
-use crate::rendering::util::texid_to_html_image_unchecked;
-use crate::textures::TextureManager;
-
-#[derive(Debug)]
-pub struct WebRenderer {
- canvas_element_id: String
-}
-
-pub const USERNAME_TEXT_ALIGN: &str = "center";
-pub const USERNAME_FONT: &str = "30px Segoe UI";
-pub const USERNAME_COLOR: &str = "white";
-pub const USERNAME_OFFSET_X: f64 = 0f64;
-pub const USERNAME_OFFSET_Y: f64 = -35f64;
-
-pub const HEARTY_OFFSET_X: f64 = -25f64;
-pub const HEARTY_OFFSET_Y: f64 = -25f64;
-pub const HEARTY_WIDTH: f64 = 50f64;
-pub const HEARTY_HEIGHT: f64 = 50f64;
-
-#[async_trait]
-impl Renderer for WebRenderer {
- async fn get(canvas_element_id: &str) -> Result<Self, Box<dyn Error>> {
- Ok(Self {
- canvas_element_id: canvas_element_id.to_string()
- })
- }
-
- async fn render_frame(&self, _time_delta_ms: f64) -> Result<(), Box<dyn Error>> {
- // TODO - core is working on this, please no touchy without telling him
- // TODO - until this notice is removed
- // time_delta_ms is the delta, in ms, from when the last render_frame was called by the browser
- let window = web_sys::window().ok_or("window needs to exist")?;
- let document = window.document().ok_or("window.document needs to exist")?;
- let canvas_element = document.get_element_by_id(&self.canvas_element_id).ok_or("canvas element does not exist")?;
- let typed_canvas_element: HtmlCanvasElement = canvas_element.dyn_into::<web_sys::HtmlCanvasElement>().map_err(|_| ()).unwrap();
- let context = typed_canvas_element.get_context("2d").unwrap().unwrap().dyn_into::<CanvasRenderingContext2d>().unwrap();
- let client = CLIENT.read()?;
- if client.client_data.is_none() {
- return Err("client not yet initialized".into());
- }
- let client_data = client.client_data.as_ref().unwrap();
-
- context.set_transform(1f64, 0f64, 0f64, 1f64, 0f64, 0f64).map_err(|e: JsValue| e.as_string().unwrap())?;
- context.clear_rect(0f64, 0f64, typed_canvas_element.width() as f64, typed_canvas_element.height() as f64);
-
- let hearty = texid_to_html_image_unchecked("hearty");
-
- // draw players
- for player in &client.players {
- context.save(); // save current position
-
- // teleport to the player's location
- context.translate(-player.x, -player.y).map_err(|e| e.as_string().unwrap())?;
-
- debug!("[render] PL: ({}, {}) {}", player.x, player.y, player.username);
-
- // draw username
- context.set_text_align(USERNAME_TEXT_ALIGN);
- context.set_font(USERNAME_FONT);
- context.set_fill_style(&JsValue::from_str(USERNAME_COLOR)); // CssStyleColor
- context.fill_text(&player.username, USERNAME_OFFSET_X, USERNAME_OFFSET_Y).map_err(|e: JsValue| e.as_string().unwrap())?;
-
- // rotate the canvas so we can draw hearty
- context.rotate(player.rotation).map_err(|e| e.as_string().unwrap())?;
-
- // draw hearty
- context.draw_image_with_html_image_element_and_dw_and_dh(&hearty, HEARTY_OFFSET_X, HEARTY_OFFSET_Y, HEARTY_WIDTH, HEARTY_HEIGHT).map_err(|e| e.as_string().unwrap())?;
-
- context.restore(); // return to canvas base
- }
-
- // finally, translate to hearty
- context.translate(-client.x + ((typed_canvas_element.width() / 2) as f64), -client.y + ((typed_canvas_element.height() / 2) as f64)).map_err(|e| e.as_string().unwrap())?;
-
-/*
- context.begin_path();
-
- // Draw the outer circle.
- context
- .arc(75.0, 75.0, 50.0, 0.0, std::f64::consts::PI * 2.0)
- .unwrap();
-
- // Draw the mouth.
- context.move_to(110.0, 75.0);
- context.arc(75.0, 75.0, 35.0, 0.0, std::f64::consts::PI).unwrap();
-
- // Draw the left eye.
- context.move_to(65.0, 65.0);
- context
- .arc(60.0, 65.0, 5.0, 0.0, std::f64::consts::PI * 2.0)
- .unwrap();
-
- // Draw the right eye.
- context.move_to(95.0, 65.0);
- context
- .arc(90.0, 65.0, 5.0, 0.0, std::f64::consts::PI * 2.0)
- .unwrap();
-
- context.stroke();
-*/
- Ok(())
- }
-}
D client/src/rendering/renderer_playercentric.rs => client/src/rendering/renderer_playercentric.rs +0 -120
@@ 1,120 0,0 @@
-use std::error::Error;
-use async_trait::async_trait;
-use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlImageElement};
-use crate::rendering::Renderer;
-use wasm_bindgen::{JsCast, JsValue};
-use crate::CLIENT;
-
-// TODO: Remove all the f32s
-
-pub const STARFIELD_RENDER_SCALE: f64 = 1.0;
-
-#[derive(Debug)]
-pub struct WebRenderer {
- canvas_element_id: String
-}
-
-#[async_trait]
-impl Renderer for WebRenderer {
- async fn get(canvas_element_id: &str) -> Result<Self, Box<dyn Error>> {
- Ok(Self {
- canvas_element_id: canvas_element_id.to_string()
- })
- }
-
- async fn render_frame(&self, _time_delta_ms: f64) -> Result<(), Box<dyn Error>> {
- // TODO - terra is working on this, please no touchy without telling him
- // TODO - until this notice is removed
- // time_delta_ms is the delta, in ms, from when the last render_frame was called by the browser
- let window = web_sys::window().ok_or("window needs to exist")?;
- let document = window.document().ok_or("window.document needs to exist")?;
- let canvas_element = document.get_element_by_id(&self.canvas_element_id).ok_or("canvas element does not exist")?;
- let typed_canvas_element: HtmlCanvasElement = canvas_element.dyn_into::<web_sys::HtmlCanvasElement>().map_err(|_| ()).unwrap();
- let context = typed_canvas_element.get_context("2d").unwrap().unwrap().dyn_into::<CanvasRenderingContext2d>().unwrap();
- let client = CLIENT.read()?;
- if client.client_data.is_none() {
- return Err("client not yet initialized".into());
- }
- let _client_data = client.client_data.as_ref().unwrap();
-
- //let camera_translate_x = -client.x + (typed_canvas_element.width() / 2) as f64;
- let viewer_size_x = typed_canvas_element.width() as f64;
- //let camera_translate_y = -client.y + (typed_canvas_element.height() / 2) as f64;
- let viewer_size_y = typed_canvas_element.height() as f64;
-
- typed_canvas_element.style().set_property("background-position", &format!("{}px {}px", -client.x / STARFIELD_RENDER_SCALE, -client.y / STARFIELD_RENDER_SCALE)).map_err(|e| e.as_string().unwrap())?;
-
- context.set_transform(1f64, 0f64, 0f64, 1f64, 0f64, 0f64).map_err(|e: JsValue| e.as_string().unwrap())?;
- context.clear_rect(0f64, 0f64, viewer_size_x, viewer_size_y);
-
- // *dont* translate the camera. we're movign everything else around us. cameracentrism.
- // only translation will be to center our core module.
- //context.translate(camera_translate_x, camera_translate_y).map_err(|e: JsValue| e.as_string().unwrap())?;
- context.translate(viewer_size_x / 2.0, viewer_size_y / 2.0).map_err(|e: JsValue| e.as_string().unwrap())?;
-
- for planet in &client.planets {
- //context.save();
-
- //context.set_transform(1f64, 0f64, 0f64, 1f64, 0f64, 0f64).map_err(|e: JsValue| e.as_string().unwrap())?;
- //context.translate(-planet.x, -planet.y).map_err(|e: JsValue| e.as_string().unwrap())?;
-
- let texture_image = document.get_element_by_id(&format!("tex-{}", planet.planet_type.unwrap().as_texture_id())).unwrap().dyn_into::<HtmlImageElement>().unwrap();
- // pos:
- //debug!("P {} {}", planet.x - planet.radius - client.x, planet.y - planet.radius - client.y);
- context.draw_image_with_html_image_element_and_dw_and_dh(&texture_image, (planet.x - planet.radius - client.x as f32) as f64, (planet.y - planet.radius - client.y as f32) as f64, planet.radius as f64 * 2f64, planet.radius as f64 * 2f64).map_err(|e: JsValue| e.as_string().unwrap())?;
-
- //context.restore();
- }
-
- for player in &client.players {
- context.save();
-
- //context.translate(player.x, player.y).map_err(|e: JsValue| e.as_string().unwrap())?;
- //gaah fuck why noo i didnt want this. godforsaken canvas rotation
- context.translate(player.x as f64 - client.x, player.y as f64 - client.y).map_err(|e: JsValue| e.as_string().unwrap())?; // fwip
-
- context.set_text_align("center");
- context.set_font("30px Segoe UI");
- context.set_fill_style(&JsValue::from_str("white")); // CssStyleColor
- context.fill_text(&player.username, 0f64, -35f64).map_err(|e: JsValue| e.as_string().unwrap())?;
-
- context.rotate(player.rotation as f64).map_err(|e: JsValue| e.as_string().unwrap())?; // fwip
-
- let texture_image = document.get_element_by_id("tex-hearty").unwrap().dyn_into::<HtmlImageElement>().unwrap();
- //context.draw_image_with_html_image_element_and_dw_and_dh(&texture_image, player.x - 25f64 - client.x, player.y - 25f64 - client.y, 50f64, 50f64).map_err(|e: JsValue| e.as_string().unwrap())?;
- context.draw_image_with_html_image_element_and_dw_and_dh(&texture_image, -25f64, -25f64, 50f64, 50f64).map_err(|e: JsValue| e.as_string().unwrap())?; // sktch
-
- //context.rotate(-player.rotation).map_err(|e: JsValue| e.as_string().unwrap())?; // fwoop
-
- context.restore();
- }
-
-/*
- context.begin_path();
-
- // Draw the outer circle.
- context
- .arc(75.0, 75.0, 50.0, 0.0, std::f64::consts::PI * 2.0)
- .unwrap();
-
- // Draw the mouth.
- context.move_to(110.0, 75.0);
- context.arc(75.0, 75.0, 35.0, 0.0, std::f64::consts::PI).unwrap();
-
- // Draw the left eye.
- context.move_to(65.0, 65.0);
- context
- .arc(60.0, 65.0, 5.0, 0.0, std::f64::consts::PI * 2.0)
- .unwrap();
-
- // Draw the right eye.
- context.move_to(95.0, 65.0);
- context
- .arc(90.0, 65.0, 5.0, 0.0, std::f64::consts::PI * 2.0)
- .unwrap();
-
- context.stroke();
-*/
- Ok(())
- }
-}
D client/src/rendering/util.rs => client/src/rendering/util.rs +0 -8
@@ 1,8 0,0 @@
-use web_sys::HtmlImageElement;
-use wasm_bindgen::JsCast;
-
-pub fn texid_to_html_image_unchecked(tex: &str) -> HtmlImageElement {
- let window = web_sys::window().expect("window needs to exist");
- let document = window.document().expect("window.document needs to exist");
- document.get_element_by_id(&format!("tex-{}", tex)).unwrap().dyn_into::<HtmlImageElement>().unwrap()
-}>
\ No newline at end of file
D client/src/textures/loader_fast.rs => client/src/textures/loader_fast.rs +0 -96
@@ 1,96 0,0 @@
-use std::collections::HashMap;
-use std::error::Error;
-use std::io::Cursor;
-use base64::Engine;
-use image::ImageOutputFormat;
-use log::debug;
-use serde::{Deserialize, Serialize};
-use crate::textures::{TextureManager, TextureSize};
-
-pub const SPRITESHEET_IMAGE_FILE_FULL: &[u8] = include_bytes!("../../../assets/dist/spritesheet-full.png");
-pub const SPRITESHEET_DATA_FILE_FULL: &str = include_str!("../../../assets/dist/spritesheet-full.ron");
-
-pub const SPRITESHEET_IMAGE_FILE_375: &[u8] = include_bytes!("../../../assets/dist/spritesheet-375.png");
-pub const SPRITESHEET_DATA_FILE_375: &str = include_str!("../../../assets/dist/spritesheet-375.ron");
-
-pub const SPRITESHEET_IMAGE_FILE_125: &[u8] = include_bytes!("../../../assets/dist/spritesheet-125.png");
-pub const SPRITESHEET_DATA_FILE_125: &str = include_str!("../../../assets/dist/spritesheet-125.ron");
-
-#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
-pub struct SpritePosition {
- pub name: String,
- pub x: f32,
- pub y: f32,
- pub width: f32,
- pub height: f32,
- pub offsets: Option<[f32; 2]>,
-}
-
-#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
-pub struct SerializedSpriteSheet {
- pub texture_width: f32,
- pub texture_height: f32,
- pub sprites: Vec<SpritePosition>,
-}
-
-#[derive(Debug)]
-pub struct TextureLoader {
- pub sprites: HashMap<String, String>
-}
-impl TextureManager for TextureLoader {
- fn load(size: TextureSize) -> Result<Self, Box<dyn Error>> where Self: Sized {
- debug!("Loading textures - starting fast texture loader (size: {})", size.to_string());
- let start = js_sys::Date::now() as u64;
- // load the generated spritesheet data
- let spritesheet_data: SerializedSpriteSheet = ron::from_str(pick_data_file(size))?;
-
- // load the generated spritesheet image
- let spritesheet_image = image::load_from_memory(pick_image_file(size))?;
-
- if spritesheet_image.width() as f32 != spritesheet_data.texture_width {
- return Err("Image width mismatch between spritesheet and data file".into());
- }
- if spritesheet_image.height() as f32 != spritesheet_data.texture_height {
- return Err("Image height mismatch between spritesheet and data file".into());
- }
-
- let mut sprites = HashMap::new();
-
- for sprite in spritesheet_data.sprites {
- debug!("Loading texture {} ({}x{}, start at {}, {})", sprite.name, sprite.width, sprite.height, sprite.x, sprite.y);
- let sprite_img = spritesheet_image.crop_imm(sprite.x as u32, sprite.y as u32, sprite.width as u32, sprite.height as u32);
- let mut image_data: Vec<u8> = Vec::new();
- sprite_img.write_to(&mut Cursor::new(&mut image_data), ImageOutputFormat::Png)
- .unwrap();
- let res_base64 = base64::engine::general_purpose::STANDARD.encode(image_data);
- sprites.insert(sprite.name, format!("data:image/png;base64,{}", res_base64));
- }
-
- let end = js_sys::Date::now() as u64;
- debug!("Loaded {} sprites from spritesheet in {} ms", sprites.len(), end - start);
-
- Ok(Self {
- sprites,
- })
- }
-
- fn get_texture(&self, texture_id: &str) -> Option<String> {
- self.sprites.get(texture_id).map(|u| u.clone())
- }
-}
-
-fn pick_data_file(for_size: TextureSize) -> &'static str {
- match for_size {
- TextureSize::Full => SPRITESHEET_DATA_FILE_FULL,
- TextureSize::Scaled375 => SPRITESHEET_DATA_FILE_375,
- TextureSize::Scaled125 => SPRITESHEET_DATA_FILE_125
- }
-}
-
-fn pick_image_file(for_size: TextureSize) -> &'static [u8] {
- match for_size {
- TextureSize::Full => SPRITESHEET_IMAGE_FILE_FULL,
- TextureSize::Scaled375 => SPRITESHEET_IMAGE_FILE_375,
- TextureSize::Scaled125 => SPRITESHEET_IMAGE_FILE_125
- }
-}>
\ No newline at end of file
D client/src/textures/loader_slow.rs => client/src/textures/loader_slow.rs +0 -20
@@ 1,20 0,0 @@
-use crate::textures::{TextureManager, TextureSize};
-use std::error::Error;
-use log::debug;
-
-#[derive(Debug)]
-pub struct TextureLoader {
- size: TextureSize
-}
-impl TextureManager for TextureLoader {
- fn load(size: TextureSize) -> Result<Self, Box<dyn Error>> where Self: Sized {
- debug!("Using slow texture loader, textures will be loaded on-the-fly");
- Ok(TextureLoader {
- size
- })
- }
-
- fn get_texture(&self, texture_id: &str) -> Option<String> {
- Some(format!("/assets/final/{}/{}.png", self.size.to_string(), texture_id))
- }
-}>
\ No newline at end of file
D client/src/textures/mod.rs => client/src/textures/mod.rs +0 -48
@@ 1,48 0,0 @@
-use std::error::Error;
-use std::str::FromStr;
-
-#[cfg(all(feature = "textures-fast", feature = "textures-slow"))]
-compile_error!("Mutually exclusive modules textures-fast and textures-slow selected.");
-#[cfg(not(any(feature = "textures-fast", feature = "textures-slow")))]
-compile_error!("Required feature textures not specified. Please specify one of textures-fast, textures-slow");
-
-#[cfg(feature = "textures-fast")]
-#[path = "loader_fast.rs"]
-pub mod loader;
-
-#[cfg(feature = "textures-slow")]
-#[path = "loader_slow.rs"]
-pub mod loader;
-
-pub trait TextureManager {
- fn load(size: TextureSize) -> Result<Self, Box<dyn Error>> where Self: Sized;
- fn get_texture(&self, texture_id: &str) -> Option<String>;
-}
-
-#[derive(Debug, Copy, Clone)]
-pub enum TextureSize {
- Full,
- Scaled375,
- Scaled125
-}
-impl ToString for TextureSize {
- fn to_string(&self) -> String {
- match self {
- TextureSize::Full => "full".to_string(),
- TextureSize::Scaled375 => "375".to_string(),
- TextureSize::Scaled125 => "125".to_string()
- }
- }
-}
-impl FromStr for TextureSize {
- type Err = ();
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- match s {
- "full" => Ok(TextureSize::Full),
- "375" => Ok(TextureSize::Scaled375),
- "125" => Ok(TextureSize::Scaled125),
- _ => Err(())
- }
- }
-}>
\ No newline at end of file