~starkingdoms/starkingdoms

30280fe01d0e80cfe0420eba2d9809d845c01ab0 — core 2 years ago cde0f4a
animation framework
M client/src/lib.rs => client/src/lib.rs +20 -7
@@ 2,11 2,11 @@ use std::error::Error;
use std::str::FromStr;

use futures::stream::{SplitSink, SplitStream};
use futures::StreamExt;
use futures::{StreamExt, TryFutureExt};
use log::{debug, error, info, Level, trace, warn};
use wasm_bindgen::prelude::*;
use ws_stream_wasm::{WsErr, WsMessage, WsMeta, WsStream};
use starkingdoms_protocol::{Planet, State};
use starkingdoms_protocol::{ProtocolPlanet, State};
use starkingdoms_protocol::PROTOCOL_VERSION;
use starkingdoms_protocol::MessageS2C;
use starkingdoms_protocol::MessageC2S;


@@ 20,6 20,7 @@ use futures::FutureExt;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Window};
use starkingdoms_protocol::GoodbyeReason::PingPongTimeout;
use crate::rendering::Renderer;
use crate::rendering::renderer::WebRenderer;
use crate::textures::loader::TextureLoader;
use crate::textures::{TextureManager, TextureSize};


@@ 39,7 40,7 @@ extern {
#[derive(Debug)]
pub struct Client {
    pub client_data: Option<ClientData>,
    pub planets: Vec<Planet>,
    pub planets: Vec<ProtocolPlanet>,
    pub x: f64,
    pub y: f64
}


@@ 50,7 51,8 @@ pub struct ClientData {
    pub tx: SplitSink<WsStream, WsMessage>,
    pub rx: SplitStream<WsStream>,
    pub pong_timeout: u64,
    pub textures: TextureLoader
    pub textures: TextureLoader,
    pub renderer: WebRenderer
}

pub const PONG_MAX_TIMEOUT: u64 = 5;


@@ 81,7 83,7 @@ pub async fn rust_init(gateway: &str, username: &str, texture_size: &str) -> Res

    let textures = TextureLoader::load(TextureSize::from_str(texture_size).unwrap()).map_err(|e| e.to_string())?;

    match main(gateway, username, 1, textures).await {
    match main(gateway, username, 1, textures, WebRenderer::get("canvas").await.unwrap()).await {
        Ok(c) => c,
        Err(e) => {
            error!("Error initializing gateway client: {}", e);


@@ 95,7 97,7 @@ pub async fn rust_init(gateway: &str, username: &str, texture_size: &str) -> Res
}

#[async_recursion(?Send)]
pub async fn main(gateway: &str, username: &str, backoff: i32, textures: TextureLoader) -> Result<(), Box<dyn Error>> {
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;


@@ 119,7 121,7 @@ pub async fn main(gateway: &str, username: &str, backoff: i32, textures: Texture
        Err(e) => {
            return match e {
                WsErr::ConnectionFailed { .. } => {
                    main(gateway, username, backoff + 1, textures).await
                    main(gateway, username, backoff + 1, textures, renderer).await
                },
                _ => {
                    Err(e.into())


@@ 136,6 138,7 @@ pub async fn main(gateway: &str, username: &str, backoff: i32, textures: Texture
        rx,
        pong_timeout: (js_sys::Date::now() as u64 / 1000) + 5,
        textures,
        renderer
    };

    trace!("Split stream, handshaking with server");


@@ 286,4 289,14 @@ pub fn get_texture(texture_id: &str) -> Option<String> {
    } 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"))
    }
}
\ No newline at end of file

M client/src/rendering/mod.rs => client/src/rendering/mod.rs +1 -0
@@ 6,4 6,5 @@ pub mod renderer;
#[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

M client/src/rendering/renderer.rs => client/src/rendering/renderer.rs +12 -7
@@ 4,22 4,27 @@ use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
use crate::rendering::Renderer;
use wasm_bindgen::JsCast;

#[derive(Debug)]
pub struct WebRenderer {
    canvas: HtmlCanvasElement,
    context: CanvasRenderingContext2d
    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
        // 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(canvas_element_id).ok_or("canvas element does not 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();
        Ok(Self {
            canvas: typed_canvas_element,
            context
        })
        Ok(())
    }
}
\ No newline at end of file

M web/play.html => web/play.html +17 -4
@@ 2,10 2,8 @@
<html lang="en-US">
<head>
    <title>StarKingdoms.TK</title>
    <link rel="stylesheet" href="/static/css/stylemain.css"></link>
    <link rel="favicon" href="/static/img/favicon.ico"></link>
    <link rel="stylesheet" href="/static/css/play.css"></link>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>

    <body>


@@ 27,7 25,7 @@
        <script type="module">
            // If you're getting build errors here | you need to run `just build_client_bundle` first, to compile client code
            //                                     v
            import init, { rust_init, send_chat, update_socket, set_status, get_texture } from "./dist/starkingdoms_client.js";
            import init, { rust_init, send_chat, update_socket, set_status, get_texture, render_frame } from "./dist/starkingdoms_client.js";
            init().then(() => {
                const urlSearchParams = new URLSearchParams(window.location.search);



@@ 45,6 43,20 @@
                        });
                    }, 5);

                    let start;
                    function animateFrame(time) {
                        if (start === undefined) {
                            start = time;
                        }
                        let delta = time - start;

                        render_frame(delta);
                        start = time;
                        requestAnimationFrame(animateFrame)
                    }

                    requestAnimationFrame(animateFrame);

                    if (urlSearchParams.has("showTextures")) {
                        let textures = ["autoplr_cfg", "autoplr_error", "autoplr_on", "cargo_off", "cargo_on", "earth", "ecothruster_on", "hearty", "hub_off", "hub_on", "landingleg", "landingthruster_off", "landingthruster_on", "powerhub_off", "powerhub_on", "superthruster_off", "superthruster_on", "thruster_off", "thruster_on"];



@@ 53,6 65,7 @@

                            let fieldset_elem = document.createElement("fieldset");
                            fieldset_elem.style = "width: min-content;";
                            fieldset_elem.classList.add("texturebox")
                            let legend_elem = document.createElement("legend");
                            legend_elem.innerText = texture;


A web/static/css/play.css => web/static/css/play.css +4 -0
@@ 0,0 1,4 @@
.texturebox {
    display: inline;
    margin: 5px;
}
\ No newline at end of file