~starkingdoms/starkingdoms

d128f26234cf10b506de2e05f524a941d5691df3 — c0repwn3r 2 years ago 41c180e
translate server to protobuf protocol
M Cargo.lock => Cargo.lock +0 -1
@@ 1585,7 1585,6 @@ dependencies = [
 "log",
 "nalgebra",
 "rapier2d-f64",
 "rmp-serde",
 "serde",
 "serde_json",
 "simple_logger",

D client/src/input/mod.rs => client/src/input/mod.rs +0 -37
@@ 1,37 0,0 @@
use std::error::Error;
use async_trait::async_trait;

/*
Functions the input subsystem needs to perform:
- init
- register and unregister event handlers for:
    - mouse movement
    - keydown, keyup, keypress
    - clicks
 */

#[async_trait]
pub trait InputSubsystem {
    async fn init() -> Result<Self, Box<dyn Error>> where Self: Sized;

    async fn register_on_mouse_move(&mut self, id: &str, callback: dyn FnMut(OnMouseMoveEvent)) -> Result<(), Box<dyn Error>>;
    async fn unregister_on_mouse_move(&mut self, id: &str) -> Result<(), Box<dyn Error>>;

    async fn register_on_key_down(&mut self, id: &str, callback: dyn FnMut(OnKeyDownEvent)) -> Result<(), Box<dyn Error>>;
    async fn unregister_on_key_down(&mut self, id: &str) -> Result<(), Box<dyn Error>>;

    async fn register_on_key_up(&mut self, id: &str, callback: dyn FnMut(OnKeyUpEvent)) -> Result<(), Box<dyn Error>>;
    async fn unregister_on_key_up(&mut self, id: &str) -> Result<(), Box<dyn Error>>;

    async fn register_on_keypress(&mut self, id: &str, callback: dyn FnMut(OnKeypressEvent)) -> Result<(), Box<dyn Error>>;
    async fn unregister_on_keypress(&mut self, id: &str) -> Result<(), Box<dyn Error>>;

    async fn register_on_click(&mut self, id: &str, callback: dyn FnMut(OnClickEvent)) -> Result<(), Box<dyn Error>>;
    async fn unregister_on_click(&mut self, id: &str) -> Result<(), Box<dyn Error>>;
}

pub struct OnMouseMoveEvent {}
pub struct OnKeyDownEvent {}
pub struct OnKeyUpEvent {}
pub struct OnKeypressEvent {}
pub struct OnClickEvent {}
\ No newline at end of file

M client/src/lib.rs => client/src/lib.rs +8 -2
@@ 16,7 16,6 @@ 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;


@@ 33,7 32,13 @@ pub mod macros;
pub mod chat;
pub mod rendering;
pub mod textures;
pub mod input;

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

#[wasm_bindgen]
extern {


@@ 70,6 75,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 const MAX_CONNECTION_TRIES: i32 = 10;

M protocol/src/lib.rs => protocol/src/lib.rs +148 -1
@@ 1,1 1,148 @@
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
\ No newline at end of file
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::starkingdoms_protocol::PacketWrapper;
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));

pub const PROTOCOL_VERSION: u32 = 1;

#[derive(Debug)]
pub enum MessageC2S {
    Hello(MessageC2SHello),
    Goodbye(MessageC2SGoodbye),
    Chat(MessageC2SChat),
    Ping(MessageC2SPing)
}

#[derive(Debug)]
pub enum MessageS2C {
    Hello(MessageS2CHello),
    Goodbye(MessageS2CGoodbye),
    Chat(MessageS2CChat),
    Pong(MessageS2CPong),
    PlayersUpdate(MessageS2CPlayersUpdate),
    PlanetData(MessageS2CPlanetData)
}

impl TryFrom<&[u8]> for MessageC2S {
    type Error = Box<dyn Error>;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        let pkt = starkingdoms_protocol::PacketWrapper::parse_from_bytes(value)?;

        let deser_pkt = match pkt.packet_id {
            _id if _id == message_c2s::message_c2shello::Packet_info::type_.value() as i64 => {
                MessageC2S::Hello(MessageC2SHello::parse_from_bytes(&pkt.packet_data)?)
            },
            _id if _id == message_c2s::message_c2sgoodbye::Packet_info::type_.value() as i64 => {
                MessageC2S::Goodbye(MessageC2SGoodbye::parse_from_bytes(&pkt.packet_data)?)
            },
            _id if _id == message_c2s::message_c2schat::Packet_info::type_.value() as i64 => {
                MessageC2S::Chat(MessageC2SChat::parse_from_bytes(&pkt.packet_data)?)
            },
            _id if _id == message_c2s::message_c2sping::Packet_info::type_.value() as i64 => {
                MessageC2S::Ping(MessageC2SPing::parse_from_bytes(&pkt.packet_data)?)
            }
            _ => { return Err("Not a C2S packet".into()); }
        };

        Ok(deser_pkt)
    }
}

impl TryInto<Vec<u8>> for MessageC2S {
    type Error = Box<dyn Error>;

    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
        let (pkt_id, pkt_bytes) = match self {
            MessageC2S::Hello(p) => {
                (message_c2s::message_c2shello::Packet_info::type_.value(), p.write_to_bytes()?)
            }
            MessageC2S::Goodbye(p) => {
                (message_c2s::message_c2sgoodbye::Packet_info::type_.value(), p.write_to_bytes()?)
            }
            MessageC2S::Chat(p) => {
                (message_c2s::message_c2schat::Packet_info::type_.value(), p.write_to_bytes()?)
            }
            MessageC2S::Ping(p) => {
                (message_c2s::message_c2sping::Packet_info::type_.value(), p.write_to_bytes()?)
            }
        };

        let pkt = PacketWrapper {
            packet_id: pkt_id as i64,
            packet_data: pkt_bytes,
            special_fields: Default::default(),
        };

        Ok(pkt.write_to_bytes()?)
    }
}

impl TryFrom<&[u8]> for MessageS2C {
    type Error = Box<dyn Error>;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        let pkt = PacketWrapper::parse_from_bytes(value)?;

        let deser_pkt = match pkt.packet_id {
            _id if _id == message_s2c::message_s2chello::Packet_info::type_.value() as i64 => {
                MessageS2C::Hello(MessageS2CHello::parse_from_bytes(&pkt.packet_data)?)
            },
            _id if _id == message_s2c::message_s2cgoodbye::Packet_info::type_.value() as i64 => {
                MessageS2C::Goodbye(MessageS2CGoodbye::parse_from_bytes(&pkt.packet_data)?)
            },
            _id if _id == message_s2c::message_s2cchat::Packet_info::type_.value() as i64 => {
                MessageS2C::Chat(MessageS2CChat::parse_from_bytes(&pkt.packet_data)?)
            },
            _id if _id == message_s2c::message_s2cpong::Packet_info::type_.value() as i64 => {
                MessageS2C::Pong(MessageS2CPong::parse_from_bytes(&pkt.packet_data)?)
            },
            _id if _id == message_s2c::message_s2cplayers_update::Packet_info::type_.value() as i64 => {
                MessageS2C::PlayersUpdate(MessageS2CPlayersUpdate::parse_from_bytes(&pkt.packet_data)?)
            },
            _id if _id == message_s2c::message_s2cplanet_data::Packet_info::type_.value() as i64 => {
                MessageS2C::PlanetData(MessageS2CPlanetData::parse_from_bytes(&pkt.packet_data)?)
            },
            _ => { return Err("Not a S2C packet".into()); }
        };

        Ok(deser_pkt)
    }
}

impl TryInto<Vec<u8>> for MessageS2C {
    type Error = Box<dyn Error>;

    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
        let (pkt_id, pkt_bytes) = match self {
            MessageS2C::Hello(p) => {
                (message_s2c::message_s2chello::Packet_info::type_.value(), p.write_to_bytes()?)
            }
            MessageS2C::Goodbye(p) => {
                (message_s2c::message_s2cgoodbye::Packet_info::type_.value(), p.write_to_bytes()?)
            }
            MessageS2C::Chat(p) => {
                (message_s2c::message_s2cchat::Packet_info::type_.value(), p.write_to_bytes()?)
            }
            MessageS2C::Pong(p) => {
                (message_s2c::message_s2cpong::Packet_info::type_.value(), p.write_to_bytes()?)
            }
            MessageS2C::PlayersUpdate(p) => {
                (message_s2c::message_s2cplayers_update::Packet_info::type_.value(), p.write_to_bytes()?)
            }
            MessageS2C::PlanetData(p) => {
                (message_s2c::message_s2cplanet_data::Packet_info::type_.value(), p.write_to_bytes()?)
            }
        };

        let pkt = PacketWrapper {
            packet_id: pkt_id as i64,
            packet_data: pkt_bytes,
            special_fields: Default::default(),
        };

        Ok(pkt.write_to_bytes()?)
    }
}
\ No newline at end of file

M protocol/src/pbuf/planet.proto => protocol/src/pbuf/planet.proto +1 -1
@@ 2,7 2,7 @@ syntax = "proto3";
package protocol.planet;

message Planet {
  PlanetType type = 1;  // Type of the planet
  PlanetType planet_type = 1;  // Type of the planet
  float x = 2;          // Translation on the X axis, in game units
  float y = 3;          // Translation on the Y axis, in game units
  float radius = 4;     // The radius of the planet extending out from (x, y)

M server/Cargo.toml => server/Cargo.toml +0 -1
@@ 12,7 12,6 @@ slp-description = "A StarKingdoms.TK server"
[dependencies]
tokio = { version = "1.27", features = ["macros", "sync", "rt-multi-thread"] }
serde = { version = "1", features = ["derive"] }
rmp-serde = { version = "1" }
serde_json = "1"
futures = { version = "0.3", default-features = false }
hyper = { version = "0.14", features = ["server", "http1", "http2", "tcp"] }

M server/src/handler.rs => server/src/handler.rs +79 -56
@@ 12,8 12,10 @@ use tokio::sync::RwLock;
use tokio::sync::mpsc::Receiver;
use tokio_tungstenite::WebSocketStream;
use tungstenite::Message;
use starkingdoms_protocol::{GoodbyeReason, MessageC2S, MessageS2C, PROTOCOL_VERSION, State};
use starkingdoms_protocol::GoodbyeReason::PingPongTimeout;
use starkingdoms_protocol::goodbye_reason::GoodbyeReason;
use starkingdoms_protocol::message_s2c::{MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong};
use starkingdoms_protocol::{MessageS2C, MessageC2S, PROTOCOL_VERSION};
use starkingdoms_protocol::state::State;
use crate::manager::{ClientHandlerMessage, ClientManager, PhysicsData, Player};
use crate::{send, recv, SCALE};



@@ 29,24 31,30 @@ pub async fn handle_client(mgr: ClientManager, data: Arc<RwLock<PhysicsData>>, r
                ClientHandlerMessage::Tick => {} // this intentionally does nothing,
                ClientHandlerMessage::ChatMessage { from, message } => {
                    if matches!(state, State::Play) {
                        send!(client_tx, &MessageS2C::Chat {
                        let msg = MessageS2C::Chat(MessageS2CChat {
                            from,
                            message,
                            from
                        }).await?;
                            special_fields: Default::default(),
                        }).try_into()?;
                        send!(client_tx, msg).await?;
                    }
                }
                ClientHandlerMessage::PlayersUpdate { players } => {
                    if matches!(state, State::Play) {
                        send!(client_tx, &MessageS2C::PlayersUpdate {
                            players
                        }).await?;
                        let msg = MessageS2C::PlayersUpdate(MessageS2CPlayersUpdate {
                            players,
                            special_fields: Default::default(),
                        }).try_into()?;
                        send!(client_tx, msg).await?;
                    }
                }
                ClientHandlerMessage::PlanetData { planets } => {
                    if matches!(state, State::Play) {
                        send!(client_tx, &MessageS2C::PlanetData {
                            planets
                        }).await?;
                        let msg = MessageS2C::PlanetData(MessageS2CPlanetData {
                            planets,
                            special_fields: Default::default(),
                        }).try_into()?;
                        send!(client_tx, msg).await?;
                    }
                }
            }


@@ 57,9 65,11 @@ pub async fn handle_client(mgr: ClientManager, data: Arc<RwLock<PhysicsData>>, r

        if ping_timeout < SystemTime::now() {
            error!("[{}] ping timeout", remote_addr);
            send!(client_tx, &MessageS2C::Goodbye {
                reason: PingPongTimeout
            }).await?;
            let msg = MessageS2C::Goodbye(MessageS2CGoodbye {
                reason: GoodbyeReason::PingPongTimeout.into(),
                special_fields: Default::default(),
            }).try_into()?;
            send!(client_tx, msg).await?;
            break;
        }



@@ 67,51 77,57 @@ pub async fn handle_client(mgr: ClientManager, data: Arc<RwLock<PhysicsData>>, r
            match state {
                State::Handshake => {
                    match pkt {
                        MessageC2S::Hello { version, requested_username, next_state } => {
                            if !matches!(next_state, State::Play) {
                                error!("client sent unexpected state {:?} (expected: Play)", next_state);
                                send!(client_tx, &MessageS2C::Goodbye {
                                        reason: GoodbyeReason::UnexpectedNextState,
                                    }).await?;
                        MessageC2S::Hello(pkt) => {
                            if !matches!(pkt.next_state.unwrap(), State::Play) {
                                error!("client sent unexpected state {:?} (expected: Play)", pkt.next_state);
                                let msg = MessageS2C::Goodbye(MessageS2CGoodbye {
                                    reason: GoodbyeReason::UnexpectedNextState.into(),
                                    special_fields: Default::default(),
                                }).try_into()?;
                                send!(client_tx, msg).await?;
                                break;
                            }

                            // check version
                            if version != PROTOCOL_VERSION {
                                error!("client sent incompatible version {} (expected: {})", version, PROTOCOL_VERSION);
                                send!(client_tx, &MessageS2C::Goodbye {
                                        reason: GoodbyeReason::UnsupportedProtocol {
                                            supported: PROTOCOL_VERSION,
                                            got: version,
                                        },
                                    }).await?;
                            if pkt.version != PROTOCOL_VERSION {
                                error!("client sent incompatible version {} (expected: {})", pkt.version, PROTOCOL_VERSION);
                                let msg = MessageS2C::Goodbye(MessageS2CGoodbye {
                                    reason: GoodbyeReason::UnsupportedProtocol.into(),
                                    special_fields: Default::default(),
                                }).try_into()?;
                                send!(client_tx, msg).await?;
                                break;
                            }

                            // determine if we can give them that username
                            {
                                if mgr.usernames.read().await.values().any(|u| *u == requested_username) {
                                    error!("client requested username {} but it is in use", requested_username);
                                    send!(client_tx, &MessageS2C::Goodbye {
                                            reason: GoodbyeReason::UsernameTaken,
                                        }).await?;
                                if mgr.usernames.read().await.values().any(|u| *u == pkt.requested_username) {
                                    error!("client requested username {} but it is in use", pkt.requested_username);
                                    let msg: Vec<u8> = MessageS2C::Goodbye(MessageS2CGoodbye {
                                        reason: GoodbyeReason::UsernameTaken.into(),
                                        special_fields: Default::default(),
                                    }).try_into()?;
                                    send!(client_tx, msg).await?;
                                    break;
                                }
                            }

                            // username is fine
                            {
                                mgr.usernames.write().await.insert(remote_addr, requested_username.clone());
                                mgr.usernames.write().await.insert(remote_addr, pkt.requested_username.clone());
                            }

                            send!(client_tx, &MessageS2C::Hello {
                                    version,
                                    given_username: requested_username.clone(),
                                    next_state,
                                }).await?;
                            let msg = MessageS2C::Hello(MessageS2CHello {
                                version: pkt.version,
                                given_username: pkt.requested_username.clone(),
                                special_fields: Default::default(),
                                next_state: pkt.next_state,
                            }).try_into()?;

                            state = next_state;
                            username = requested_username;
                            send!(client_tx, msg).await?;

                            state = pkt.next_state.unwrap();
                            username = pkt.requested_username;

                            // make player rigidbody
                            {


@@ 134,15 150,17 @@ pub async fn handle_client(mgr: ClientManager, data: Arc<RwLock<PhysicsData>>, r
                                mgr.players.write().await.insert(remote_addr, Player { handle: player_handle });
                            }
                        },
                        MessageC2S::Goodbye { reason } => {
                            info!("client sent goodbye: {:?}", reason);
                        MessageC2S::Goodbye(pkt) => {
                            info!("client sent goodbye: {:?}", pkt.reason);
                            break;
                        },
                        _ => {
                            error!("client sent unexpected packet {:?} for state {:?}", pkt, state);
                            send!(client_tx, &MessageS2C::Goodbye {
                                    reason: GoodbyeReason::UnexpectedPacket,
                                }).await?;
                            let msg = MessageS2C::Goodbye(MessageS2CGoodbye {
                                reason: GoodbyeReason::UnexpectedPacket.into(),
                                special_fields: Default::default(),
                            }).try_into()?;
                            send!(client_tx, msg).await?;
                            break;
                        }
                    }


@@ 151,20 169,22 @@ pub async fn handle_client(mgr: ClientManager, data: Arc<RwLock<PhysicsData>>, r
                    match pkt {
                        MessageC2S::Hello { .. } => {
                            error!("client sent unexpected packet {:?} for state {:?}", pkt, state);
                            send!(client_tx, &MessageS2C::Goodbye {
                                    reason: GoodbyeReason::UnexpectedPacket,
                                }).await?;
                            let msg = MessageS2C::Goodbye(MessageS2CGoodbye {
                                reason: GoodbyeReason::UnexpectedPacket.into(),
                                special_fields: Default::default(),
                            }).try_into()?;
                            send!(client_tx, msg).await?;
                            break;
                        },
                        MessageC2S::Goodbye { reason } => {
                            info!("client sent goodbye: {:?}", reason);
                        MessageC2S::Goodbye(pkt) => {
                            info!("client sent goodbye: {:?}", pkt.reason);
                            break;
                        },
                        MessageC2S::Chat { message } => {
                            info!("[{}] CHAT: [{}] {}", remote_addr, username, message);
                        MessageC2S::Chat(pkt) => {
                            info!("[{}] CHAT: [{}] {}", remote_addr, username, pkt.message);

                            for (_addr, client_thread) in mgr.handlers.read().await.iter() {
                                match client_thread.tx.send(ClientHandlerMessage::ChatMessage { from: username.clone(), message: message.clone() }).await {
                                match client_thread.tx.send(ClientHandlerMessage::ChatMessage { from: username.clone(), message: pkt.message.clone() }).await {
                                    Ok(_) => (),
                                    Err(e) => {
                                        error!("unable to update a client thread: {}", e);


@@ 172,8 192,11 @@ pub async fn handle_client(mgr: ClientManager, data: Arc<RwLock<PhysicsData>>, r
                                }
                            }
                        },
                        MessageC2S::Ping {} => {
                            send!(client_tx, &MessageS2C::Pong {}).await?;
                        MessageC2S::Ping(_) => {
                            let msg = MessageS2C::Pong(MessageS2CPong {
                                special_fields: Default::default(),
                            }).try_into()?;
                            send!(client_tx, msg).await?;
                            ping_timeout = SystemTime::now() + Duration::from_secs(10);
                        }
                    }

M server/src/macros.rs => server/src/macros.rs +7 -8
@@ 1,13 1,16 @@
use serde::{Serialize};
use tungstenite::Message;

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

pub fn _generic_pkt_into(p: Vec<u8>) -> Message {
    Message::from(p)
}

#[macro_export]
macro_rules! recv {
    ($reader:expr) => {


@@ 17,7 20,7 @@ macro_rules! recv {
                    match msg {
                        Ok(msg) => {
                            if msg.is_binary() {
                                match rmp_serde::from_slice(&msg.into_data()) {
                                match MessageC2S::try_from(msg.into_data().as_slice()) {
                                    Ok(d) => Ok(Some(d)),
                                    Err(e) => {
                                        log::error!("error deserializing message: {}", e);


@@ 52,7 55,7 @@ macro_rules! recv_now {
                match msg {
                    Ok(msg) => {
                        if msg.is_binary() {
                            match rmp_serde::from_slice(&msg.into_data()) {
                            match MessageC2S::try_from(&msg.into_data()) {
                                Ok(d) => Ok(Some(d)),
                                Err(e) => {
                                    log::error!("error deserializing message: {}", e);


@@ 74,8 77,4 @@ macro_rules! recv_now {
            }
        }
    };
}

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

M server/src/main.rs => server/src/main.rs +1 -1
@@ 6,7 6,7 @@ use hyper::service::{make_service_fn, service_fn};
use manager::PhysicsData;
use nalgebra::vector;
use planet::Planets;
use rapier2d_f64::prelude::{MultibodyJointSet, ImpulseJointSet, ColliderSet, RigidBodySet, NarrowPhase, BroadPhase, IslandManager, CCDSolver, IntegrationParameters, ColliderBuilder, RigidBodyBuilder, RigidBodyHandle};
use rapier2d_f64::prelude::{MultibodyJointSet, ImpulseJointSet, ColliderSet, RigidBodySet, NarrowPhase, BroadPhase, IslandManager, CCDSolver, IntegrationParameters};
use tokio_tungstenite::WebSocketStream;
use tungstenite::{handshake};
use futures::stream::StreamExt;

M server/src/manager.rs => server/src/manager.rs +2 -5
@@ 1,11 1,8 @@
use std::collections::HashMap;

use std::net::SocketAddr;
use std::sync::Arc;

use rapier2d_f64::na::{Vector2};
use rapier2d_f64::prelude::{IntegrationParameters, PhysicsPipeline, IslandManager, BroadPhase, NarrowPhase, ImpulseJointSet, MultibodyJointSet, CCDSolver, RigidBodySet, ColliderSet, RigidBodyHandle};
use starkingdoms_protocol::{ProtocolPlanet, ProtocolPlayer};
use tokio::sync::mpsc::Sender;
use tokio::sync::RwLock;



@@ 61,6 58,6 @@ impl PhysicsData {
pub enum ClientHandlerMessage {
    Tick,
    ChatMessage { from: String, message: String },
    PlayersUpdate { players: Vec<ProtocolPlayer> },
    PlanetData { planets: Vec<ProtocolPlanet> },
    PlayersUpdate { players: Vec<starkingdoms_protocol::player::Player> },
    PlanetData { planets: Vec<starkingdoms_protocol::planet::Planet> },
}

M server/src/planet.rs => server/src/planet.rs +8 -6
@@ 1,6 1,6 @@
use nalgebra::{Vector2, vector};
use rapier2d_f64::prelude::{RigidBodyHandle, RigidBodySet, ColliderBuilder, RigidBodyBuilder, ColliderSet};
use starkingdoms_protocol::{PlanetType, ProtocolPlanet};
use starkingdoms_protocol::planet::PlanetType;

use crate::{SCALE, manager::ClientHandlerMessage};



@@ 71,11 71,13 @@ impl Planets {
        let mut planets = vec![];

        for planet in self.planets.clone() {
            planets.push(ProtocolPlanet {
                planet_type: planet.planet_type,
                x: planet.position.0 * SCALE,
                y: planet.position.1 * SCALE,
                radius: planet.radius, // DO NOT * SCALE
            // TODO: Adjust codegen to use f64
            planets.push(starkingdoms_protocol::planet::Planet {
                planet_type: planet.planet_type.into(),
                x: (planet.position.0 * SCALE) as f32,
                y: (planet.position.1 * SCALE) as f32,
                radius: planet.radius as f32, // DO NOT * SCALE
                special_fields: Default::default(),
            });
        }


M server/src/timer.rs => server/src/timer.rs +8 -8
@@ 1,11 1,9 @@

use std::{time::Duration, sync::Arc};
use log::{error};

use nalgebra::vector;
use rapier2d_f64::prelude::{PhysicsPipeline, RigidBodyHandle};
use starkingdoms_protocol::{ProtocolPlanet, PlanetType, ProtocolPlayer};
use rapier2d_f64::prelude::{PhysicsPipeline};
use tokio::{time::sleep, sync::RwLock};
use starkingdoms_protocol::player::Player;
use crate::{manager::{ClientHandlerMessage, ClientManager, PhysicsData}, SCALE, planet::Planets};

pub async fn timer_main(mgr: ClientManager, physics_data: Arc<RwLock<PhysicsData>>, world_data: Arc<RwLock<Planets>>) {


@@ 35,11 33,13 @@ pub async fn timer_main(mgr: ClientManager, physics_data: Arc<RwLock<PhysicsData
                    username = usernames.get(player_id).unwrap().clone();
                }

                protocol_players.push(ProtocolPlayer {
                    rotation: rotation.angle(),
                    x: translation.x * SCALE,
                    y: translation.y * SCALE,
                // TODO: Figure out how to adjust codegen to use f64
                protocol_players.push(Player {
                    rotation: rotation.angle() as f32,
                    x: (translation.x * SCALE) as f32,
                    y: (translation.y * SCALE) as f32,
                    username,
                    special_fields: Default::default(),
                });
            }
        }