~starkingdoms/starkingdoms

bb92723517eb6968fc446dc1654ee6c27eceb751 — c0repwn3r 2 years ago d128f26
translate client to protobuf protocol
M client/src/chat.rs => client/src/chat.rs +7 -3
@@ 1,7 1,9 @@
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


@@ 10,9 12,11 @@ pub async fn send_chat(message: &str) -> Result<(), JsError> {
    let client_data = &mut CLIENT.write()?.client_data;

    if let Some(data) = client_data {
        send!(data.tx, &MessageC2S::Chat {
            message: message.to_string()
        }).await?;
        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"));
    }

M client/src/lib.rs => client/src/lib.rs +44 -46
@@ 1,17 1,11 @@
use std::error::Error;
use std::panic;
use std::str::FromStr;

use futures::stream::{SplitSink, SplitStream};
use futures::{StreamExt, TryFutureExt};
use log::{debug, error, info, Level, trace, warn};
use futures::{StreamExt};
use log::{error, info, Level, trace, warn};
use wasm_bindgen::prelude::*;
use web_sys::console::debug;
use ws_stream_wasm::{WsErr, WsMessage, WsMeta, WsStream};
use starkingdoms_protocol::{ProtocolPlanet, ProtocolPlayer, State};
use starkingdoms_protocol::PROTOCOL_VERSION;
use starkingdoms_protocol::MessageS2C;
use starkingdoms_protocol::MessageC2S;
use futures::SinkExt;
use lazy_static::lazy_static;
use std::sync::Arc;


@@ 20,7 14,13 @@ use async_recursion::async_recursion;
use futures::FutureExt;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Window};
use starkingdoms_protocol::GoodbyeReason::PingPongTimeout;
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;


@@ 33,13 33,6 @@ pub mod chat;
pub mod rendering;
pub mod textures;

pub struct Buttons {
    up: bool,
    right: bool,
    down: bool,
    left: bool
}

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);


@@ 48,10 41,10 @@ extern {
#[derive(Debug)]
pub struct Client {
    pub client_data: Option<ClientData>,
    pub planets: Vec<ProtocolPlanet>,
    pub planets: Vec<Planet>,
    pub x: f64,
    pub y: f64,
    pub players: Vec<ProtocolPlayer>
    pub players: Vec<Player>
}

#[derive(Debug)]


@@ 75,7 68,7 @@ lazy_static! {
        y: 0f64,
        players: vec![]
    }));
    pub static ref BUTTONS: Arc<Buttons> = Arc::new(Buttons { up: false, left: false, right: false, down: false });
    //pub static ref BUTTONS: Arc<Buttons> = Arc::new(Buttons { up: false, left: false, right: false, down: false });
}

pub const MAX_CONNECTION_TRIES: i32 = 10;


@@ 158,11 151,13 @@ pub async fn main(gateway: &str, username: &str, backoff: i32, textures: Texture

    set_status("Handshaking with server...");

    send!(client_data.tx, &MessageC2S::Hello {
        next_state: State::Play,
    let msg = MessageC2S::Hello(MessageC2SHello {
        version: PROTOCOL_VERSION,
        requested_username: username.to_string()
    }).await?;
        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");



@@ 170,13 165,13 @@ pub async fn main(gateway: &str, username: &str, backoff: i32, textures: Texture
        let typed_msg: MessageS2C = msg;

        match typed_msg {
            MessageS2C::Hello { version, given_username, next_state } => {
                info!("FAST CONNECT - connected to server protocol {} given username {}, switching to state {:?}", version, given_username, next_state);
                client_data.state = next_state;
            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 { reason } => {
                error!("server disconnected before finishing handshake: {:?}", reason);
                return Err(format!("disconnected by server: {:?}", reason).into());
            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);


@@ 203,7 198,8 @@ pub async fn send_ping_pong() -> Result<(), JsError> {

    let client_data = client.client_data.as_mut().unwrap();

    send!(client_data.tx, &MessageC2S::Ping {}).await?;

    send!(client_data.tx, MessageC2S::Ping(MessageC2SPing {special_fields: Default::default()}).try_into().map_err(|_| JsError::new("I/O Error"))?).await?;

    Ok(())
}


@@ 221,9 217,11 @@ pub async fn update_socket() -> Result<(), JsError> {

    if client_data.pong_timeout < (js_sys::Date::now() as u64 / 1000) {
        error!("Connection timed out");
        send!(client_data.tx, &MessageC2S::Goodbye {
                reason: PingPongTimeout
            }).await?;
        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"));


@@ 238,13 236,13 @@ pub async fn update_socket() -> Result<(), JsError> {

    if let Some(msg) = maybe_msg {
        match msg {
            MessageS2C::Goodbye { reason } => {
                info!("server sent disconnect: {:?}", reason);
            MessageS2C::Goodbye(pkt) => {
                info!("server sent disconnect: {:?}", pkt.reason);
                client.client_data = None;
                return Err(JsError::new("disconnected by server"));
            }
            MessageS2C::Chat { from, message } => {
                info!("[CHAT] {}: {}", from, message);
            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");


@@ 252,25 250,25 @@ pub async fn update_socket() -> Result<(), JsError> {

                let new_elem = document.create_element("div").expect("could not create element");

                let msg_formatted = markdown::to_html(&format!("**[{}]** {}", from, message));
                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 {} => {
            MessageS2C::Pong(_) => {
                client_data.pong_timeout = (js_sys::Date::now() as u64 / 1000) + PONG_MAX_TIMEOUT
            },
            MessageS2C::PlanetData { planets } => {
                client.planets = planets;
            MessageS2C::PlanetData(pkt) => {
                client.planets = pkt.planets;
            },
            MessageS2C::PlayersUpdate { players } => {
                let me = players.iter().find(|i| i.username == client_data.username);
            MessageS2C::PlayersUpdate(pkt) => {
                let me = pkt.players.iter().find(|i| i.username == client_data.username);
                if let Some(me) = me {
                    client.x = me.x;
                    client.y = me.y;
                    client.x = me.x as f64;
                    client.y = me.y as f64;
                }
                client.players = players;
                client.players = pkt.players;
            }
            _ => {
                warn!("server sent unexpected packet {:?}, ignoring", msg);

M client/src/macros.rs => client/src/macros.rs +5 -6
@@ 1,10 1,9 @@
use serde::{Serialize};
use ws_stream_wasm::WsMessage;

#[macro_export]
macro_rules! send {
    ($writer:expr,$pkt:expr) => {
        $writer.send($crate::macros::__generic_packet_to_message($pkt).unwrap())
        $writer.send($crate::macros::__generic_packet_to_message($pkt))
    };
}



@@ 15,7 14,7 @@ macro_rules! recv {
            if let Some(future_result) = $reader.next().now_or_never() {
                if let Some(msg) = future_result {
                    if let WsMessage::Binary(msg) = msg {
                        match rmp_serde::from_slice(&msg) {
                        match MessageS2C::try_from(msg.as_slice()) {
                            Ok(d) => Ok(Some(d)),
                            Err(e) => {
                                log::error!("error deserializing message: {}", e);


@@ 42,7 41,7 @@ macro_rules! recv_now {
        {
            if let Some(msg) = $reader.next().await {
                if let WsMessage::Binary(msg) = msg {
                    match rmp_serde::from_slice(&msg) {
                    match MessageS2C::try_from(msg.as_slice()) {
                        Ok(d) => Ok(Some(d)),
                        Err(e) => {
                            log::error!("error deserializing message: {}", e);


@@ 60,6 59,6 @@ macro_rules! recv_now {
    };
}

pub fn __generic_packet_to_message<T: Serialize>(pkt: &T) -> Result<WsMessage, rmp_serde::encode::Error> {
    rmp_serde::to_vec(&pkt).map(WsMessage::from)
pub fn __generic_packet_to_message(pkt: Vec<u8>) -> WsMessage {
    WsMessage::from(pkt)
}
\ No newline at end of file

M client/src/rendering/renderer_playercentric.rs => client/src/rendering/renderer_playercentric.rs +7 -7
@@ 1,11 1,11 @@
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::textures::TextureManager;

// TODO: Remove all the f32s

pub const STARFIELD_RENDER_SCALE: f64 = 1.0;



@@ 35,7 35,7 @@ impl Renderer for WebRenderer {
        if client.client_data.is_none() {
            return Err("client not yet initialized".into());
        }
        let client_data = client.client_data.as_ref().unwrap();
        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;


@@ 58,10 58,10 @@ impl Renderer for WebRenderer {
            //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.as_texture_id())).unwrap().dyn_into::<HtmlImageElement>().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, planet.y - planet.radius - client.y, planet.radius * 2f64, planet.radius * 2f64).map_err(|e: JsValue| e.as_string().unwrap())?;
            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();
        }


@@ 71,14 71,14 @@ impl Renderer for WebRenderer {

            //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 - client.x, player.y - client.y).map_err(|e: JsValue| e.as_string().unwrap())?; // fwip
            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).map_err(|e: JsValue| e.as_string().unwrap())?; // fwip
            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())?;

M protocol/src/lib.rs => protocol/src/lib.rs +9 -0
@@ 2,6 2,7 @@ use std::error::Error;
use protobuf::{Enum, Message};
use crate::message_c2s::{MessageC2SChat, MessageC2SGoodbye, MessageC2SHello, MessageC2SPing};
use crate::message_s2c::{MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong};
use crate::planet::PlanetType;
use crate::starkingdoms_protocol::PacketWrapper;
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));



@@ 145,4 146,12 @@ impl TryInto<Vec<u8>> for MessageS2C {

        Ok(pkt.write_to_bytes()?)
    }
}

impl planet::PlanetType {
    pub fn as_texture_id(&self) -> String {
        match self {
            PlanetType::Earth => "earth".to_string()
        }
    }
}
\ No newline at end of file