From 63975cc0a233df1da0d741ce5e246b08059d20e4 Mon Sep 17 00:00:00 2001 From: c0repwn3r Date: Thu, 13 Apr 2023 10:43:01 -0400 Subject: [PATCH] extend module system in client --- client/Cargo.toml | 6 +- client/src/rendering/mod.rs | 13 +++ .../src/rendering/renderer_canvascentric.rs | 110 ++++++++++++++++++ ...{renderer.rs => renderer_playercentric.rs} | 2 +- client/src/rendering/util.rs | 8 ++ client/src/textures/mod.rs | 4 +- server/src/planet.rs | 3 +- spacetime | 22 ++-- spacetime_py/spacetime.py | 22 ++-- 9 files changed, 165 insertions(+), 25 deletions(-) create mode 100644 client/src/rendering/renderer_canvascentric.rs rename client/src/rendering/{renderer.rs => renderer_playercentric.rs} (98%) create mode 100644 client/src/rendering/util.rs diff --git a/client/Cargo.toml b/client/Cargo.toml index 530ad1924408e6715d450fb60416635779a586ca..a3729fe7c9c191026bf630313d0a390f12df98c5 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -54,6 +54,8 @@ features = [ ] [features] -default = ['textures-slow'] textures-slow = [] -textures-fast = ['image', 'ron', 'base64'] \ No newline at end of file +textures-fast = ['image', 'ron', 'base64'] + +renderer-canvascentric = [] +renderer-playercentric = [] \ No newline at end of file diff --git a/client/src/rendering/mod.rs b/client/src/rendering/mod.rs index d53c3c98e09d951cf7c0c692e84b5eeef7376ec5..e54bda979a3c9ce2802fb1870f69d26859f53f3c 100644 --- a/client/src/rendering/mod.rs +++ b/client/src/rendering/mod.rs @@ -1,8 +1,21 @@ 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> where Self: Sized; diff --git a/client/src/rendering/renderer_canvascentric.rs b/client/src/rendering/renderer_canvascentric.rs new file mode 100644 index 0000000000000000000000000000000000000000..8183bcc7313ad678a656ad97b715269cdff5369d --- /dev/null +++ b/client/src/rendering/renderer_canvascentric.rs @@ -0,0 +1,110 @@ +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> { + Ok(Self { + canvas_element_id: canvas_element_id.to_string() + }) + } + + async fn render_frame(&self, _time_delta_ms: f64) -> Result<(), Box> { + // 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::().map_err(|_| ()).unwrap(); + let context = typed_canvas_element.get_context("2d").unwrap().unwrap().dyn_into::().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(()) + } +} diff --git a/client/src/rendering/renderer.rs b/client/src/rendering/renderer_playercentric.rs similarity index 98% rename from client/src/rendering/renderer.rs rename to client/src/rendering/renderer_playercentric.rs index ecfeb518533d25109660884165efb63056d9121b..9f9a29d0beaa0fd4d78965c2a44c67c2ef4a6ddc 100644 --- a/client/src/rendering/renderer.rs +++ b/client/src/rendering/renderer_playercentric.rs @@ -21,7 +21,7 @@ impl Renderer for WebRenderer { } async fn render_frame(&self, _time_delta_ms: f64) -> Result<(), Box> { - // TODO - core is working on this, please no touchy without telling him + // 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")?; diff --git a/client/src/rendering/util.rs b/client/src/rendering/util.rs new file mode 100644 index 0000000000000000000000000000000000000000..a6864cee49d6bb7037e814cad56c2bb303334f15 --- /dev/null +++ b/client/src/rendering/util.rs @@ -0,0 +1,8 @@ +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::().unwrap() +} \ No newline at end of file diff --git a/client/src/textures/mod.rs b/client/src/textures/mod.rs index 7dd7fb712dc1bf5056c01d44e48943b7368ad197..dfcb5ede664076305e5e6c49ef361e57b93375ac 100644 --- a/client/src/textures/mod.rs +++ b/client/src/textures/mod.rs @@ -2,9 +2,9 @@ use std::error::Error; use std::str::FromStr; #[cfg(all(feature = "textures-fast", feature = "textures-slow"))] -compile_error!("You cannot use both texture loaders. Please only enable one of the textures-fast or textures-slow feature."); +compile_error!("Mutually exclusive modules textures-fast and textures-slow selected."); #[cfg(not(any(feature = "textures-fast", feature = "textures-slow")))] -compile_error!("You need to enable a texture loader. Please enable one of the textures-fast, textures-slow feature."); +compile_error!("Required feature textures not specified. Please specify one of textures-fast, textures-slow"); #[cfg(feature = "textures-fast")] #[path = "loader_fast.rs"] diff --git a/server/src/planet.rs b/server/src/planet.rs index 188bfd0f932e1ab11c7f739146d3932a10a69ae3..5381e6141e3a34912ccdf26ce24619e59b422fcf 100644 --- a/server/src/planet.rs +++ b/server/src/planet.rs @@ -4,7 +4,8 @@ use starkingdoms_protocol::{PlanetType, ProtocolPlanet}; use crate::{SCALE, manager::ClientHandlerMessage}; -const GRAVITY: f64 = 0.001; +//const GRAVITY: f64 = 0.001; +const GRAVITY: f64 = 0.000006674; #[derive(Clone)] pub struct Planet { diff --git a/spacetime b/spacetime index d162bb9f907583b183f1d0a642eb1e348cd92e52..8f3c7616acade30d3b2537147b74d079a371ccef 100755 --- a/spacetime +++ b/spacetime @@ -4,11 +4,13 @@ set -e SCRIPT_PATH=$(readlink -f "${BASH_SOURCE:-$0}") SCRIPT_DIR=$(dirname "$SCRIPT_PATH") +CLIENT_MODS=${STK_CLIENT_MODULES:-"textures-slow renderer-playercentric"} +SERVER_MODS=${STK_SERVER_MODULES:=""} exec_spacetime() { - # args: target, environment, build root, verbose? + # args: target, environment, build root, modules echo "[*] Running configure command: 'python3 $SCRIPT_DIR/spacetime_py/spacetime.py $1 $2 $SCRIPT_DIR $SPACETIME_VERBOSE'" - python3 "$SCRIPT_DIR/spacetime_py/spacetime.py" "$1" "$2" "$SCRIPT_DIR" "$SPACETIME_VERBOSE" + python3 "$SCRIPT_DIR/spacetime_py/spacetime.py" "$1" "$2" "$SCRIPT_DIR" "$SPACETIME_VERBOSE" "$4" } exec_ninja() { @@ -79,50 +81,50 @@ sub_install_tooling() { sub_build_client_bundle() { check_all - exec_spacetime client dev "$SCRIPT_DIR" + exec_spacetime client dev "$SCRIPT_DIR" "$CLIENT_MODS" exec_ninja client } sub_run_http() { check_all - exec_spacetime client dev "$SCRIPT_DIR" + exec_spacetime client dev "$SCRIPT_DIR" "$CLIENT_MODS" exec_ninja client cd web && python3 -m http.server } sub_build_client_bundle_prod() { check_all - exec_spacetime client prod "$SCRIPT_DIR" + exec_spacetime client prod "$SCRIPT_DIR" "$CLIENT_MODS" exec_ninja client } sub_run_http_prod() { check_all - exec_spacetime client prod "$SCRIPT_DIR" + exec_spacetime client prod "$SCRIPT_DIR" "$CLIENT_MODS" exec_ninja client cd web && python3 -m http.server } sub_build_server() { check_all - exec_spacetime server dev "$SCRIPT_DIR" + exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS" exec_ninja server } sub_run_server() { check_all - exec_spacetime server dev "$SCRIPT_DIR" + exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS" exec_ninja server exec "$SCRIPT_DIR/target/debug/starkingdoms-server" } sub_build_server_prod() { check_all - exec_spacetime server prod "$SCRIPT_DIR" + exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS" exec_ninja server } sub_run_server_prod() { check_all - exec_spacetime server prod "$SCRIPT_DIR" + exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS" exec_ninja server exec "$SCRIPT_DIR/target/release/starkingdoms-server" } diff --git a/spacetime_py/spacetime.py b/spacetime_py/spacetime.py index 008d8743522cefd99e677d62fa893f0394cb00ab..d8611e3a9dfa430fdd897a8a6525f910745aad5a 100644 --- a/spacetime_py/spacetime.py +++ b/spacetime_py/spacetime.py @@ -58,13 +58,13 @@ def gen_inkscape_rules_for_asset(root, asset, writer, files_375, files_full, fil writer.build([out_125], rule_125, [in_file]) -def gen_rules_for_client(root, env, writer): +def gen_rules_for_client(root, env, writer, modules): if env == 'dev': - writer.rule('cargo-client', 'wasm-pack build --target web client', + writer.rule('cargo-client', f'wasm-pack build --target web client --features "{modules}"', depfile=f'{root}/target/wasm32-unknown-unknown/release/starkingdoms_client.d', pool='console') elif env == 'prod': writer.rule('cargo-client', - 'wasm-pack build --target web client --no-default-features --features textures-fast', + f'wasm-pack build --target web client --features "{modules}"', depfile=f'{root}/target/wasm32-unknown-unknown/release/starkingdoms_client.d', pool='console') writer.build( [ @@ -115,14 +115,14 @@ def gen_rules_for_client(root, env, writer): writer.build(['client'], 'phony', [f'{root}/web/dist/starkingdoms_client.js']) -def gen_rules_for_server(root, env, writer): +def gen_rules_for_server(root, env, writer, modules): if env == 'dev': out_dir = 'debug' - writer.rule('cargo-server', 'cargo build --bin starkingdoms-server', + writer.rule('cargo-server', f'cargo build --bin starkingdoms-server --features "{modules}"', depfile=f'{root}/target/debug/starkingdoms-server.d', pool='console') elif env == 'prod': out_dir = 'release' - writer.rule('cargo-server', 'cargo build --bin starkingdoms-server --release', + writer.rule('cargo-server', f'cargo build --bin starkingdoms-server --release --features "{modules}"', depfile=f'{root}/target/release/starkingdoms-server.d', pool='console') writer.build([f'{root}/target/{out_dir}/starkingdoms-server'], 'cargo-server', ['server/Cargo.toml']) @@ -172,13 +172,17 @@ def main(): target = sys.argv[1] env = sys.argv[2] root = sys.argv[3] + if len(sys.argv) > 5: + modules = sys.argv[5] + else: + modules = "" verbose = False if len(sys.argv) > 4: if sys.argv[4] == '-v': verbose = True - print(f'[spacetime] Configuring ninja for PRIMARY_TARGET={target} with ENV={env}, BUILDROOT={root}') + print(f'[spacetime] Configuring ninja for PRIMARY_TARGET={target} with ENV={env}, BUILDROOT={root}, MODULES={modules}') with open(f'{root}/build.ninja', 'w') as f: writer = Writer(f) @@ -193,9 +197,9 @@ def main(): generate_assets_build_command(root, assets, writer) if target == 'client': - gen_rules_for_client(root, env, writer) + gen_rules_for_client(root, env, writer, modules) elif target == 'server': - gen_rules_for_server(root, env, writer) + gen_rules_for_server(root, env, writer, modules) print(f'[spacetime] Configured build')