From 87665fcf6a0162d09d148802b899b4a76439c6cb Mon Sep 17 00:00:00 2001 From: ghostlyzsh Date: Thu, 20 Apr 2023 19:13:02 -0500 Subject: [PATCH] added loose modules, rendering is stupid --- client/src/gateway.ts | 5 + client/src/index.ts | 38 ++++++- client/src/protocol/message_c2s.ts | 30 +++++- client/src/protocol/message_s2c.ts | 98 +++++++++++++++++ client/src/protocol/module.ts | 162 +++++++++++++++++++++++++++++ server/src/entity.rs | 23 +++- server/src/handler.rs | 12 ++- server/src/manager.rs | 8 ++ server/src/timer.rs | 76 ++++++++++++-- 9 files changed, 439 insertions(+), 13 deletions(-) create mode 100644 client/src/protocol/module.ts diff --git a/client/src/gateway.ts b/client/src/gateway.ts index 3d9ca94a69f2dd03ec0b86df09ff3015aecd5e30..a4b83eac2aaf01dd6231e8c91b63c74dbcf8a247 100644 --- a/client/src/gateway.ts +++ b/client/src/gateway.ts @@ -14,6 +14,8 @@ import { MessageS2CGoodbye_packetInfo, MessageS2CHello, MessageS2CHello_packetInfo, + MessageS2CModulesUpdate, + MessageS2CModulesUpdate_packetInfo, MessageS2CPlanetData, MessageS2CPlanetData_packetInfo, MessageS2CPlayersUpdate, MessageS2CPlayersUpdate_packetInfo, @@ -143,6 +145,9 @@ export async function gateway_connect(gateway_url: string, username: string): Ga } else if (pkt_id == MessageS2CPlanetData_packetInfo.type) { let pkt = MessageS2CPlanetData.decode(pkt_data); global.planets = pkt.planets; + } else if (pkt_id == MessageS2CModulesUpdate_packetInfo.type) { + let pkt = MessageS2CModulesUpdate.decode(pkt_data); + global.modules = pkt.modules; } else { logger.warn(`server sent unexpected packet ${pkt_id} for state Play`); } diff --git a/client/src/index.ts b/client/src/index.ts index 2403045b6d4db083efdf14e8864e381dfbed6b4d..adb5bd2b184874c325977de6b2e717489a857816 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -2,6 +2,7 @@ import {Logger, logSetup} from "./logger"; import {gateway_connect, GatewayClient} from "./gateway"; import {Player} from "./protocol/player"; import {Planet, PlanetType} from "./protocol/planet"; +import {Module, ModuleType} from "./protocol/module"; import { MessageC2SAuthenticateAndBeamOut, MessageC2SAuthenticateAndBeamOut_packetInfo, @@ -17,6 +18,7 @@ export interface GlobalData { client: GatewayClient | null, players: Player[], planets: Planet[], + modules: Module[], me: Player | null, canvas: HTMLCanvasElement, spritesheet_img: HTMLImageElement | null, @@ -38,6 +40,7 @@ export const global: GlobalData = { client: null, players: [], planets: [], + modules: [], me: null, canvas: document.getElementById("canvas"), spritesheet_img: null, @@ -207,6 +210,29 @@ async function client_main(server: string, username: string, texture_quality: st } } + for (let i = 0; i < global.modules.length; i++) { + let module = global.modules[i]; + console.log(module); + // @ts-ignore + let tex = global.spritesheet!["frames"][module_type_to_tex_id(module.moduleType)]; + + global.context.save(); + + global.context.rotate(module.rotation); + + global.context.translate(module.x, module.y); + + global.context.drawImage(global.spritesheet_img!, + tex.frame.x, + tex.frame.y, + tex.frame.w, + tex.frame.h, + (module.x - 25 - global.me?.x!), + (module.y - 25 - global.me?.y!), 50, 50); + + global.context.restore(); + } + for (let i = 0; i < global.players.length; i++) { let player = global.players[i]; // @ts-ignore @@ -253,4 +279,14 @@ function planet_type_to_tex_id(ty: PlanetType): string { return "moon.png" } return "unknown.png" -} \ No newline at end of file +} +function module_type_to_tex_id(ty: ModuleType): string { + if (ty == ModuleType.Cargo) { + return "cargo_off.png" + } else if (ty == ModuleType.LandingThruster) { + return "landingthruster_off.png" + } else if (ty == ModuleType.Hub) { + return "hub_off.png" + } + return "unknown.png" +} diff --git a/client/src/protocol/message_c2s.ts b/client/src/protocol/message_c2s.ts index d0780ae9a452190917ab0eedec8c282a288eb98b..db501617ad798047e98f0da5f7cd398548861fdb 100644 --- a/client/src/protocol/message_c2s.ts +++ b/client/src/protocol/message_c2s.ts @@ -12,6 +12,8 @@ export interface MessageC2SHello { requestedUsername: string; /** The state the connection will go into after the handshake. */ nextState: State; + token?: string | undefined; + user?: string | undefined; } export enum MessageC2SHello_packetInfo { @@ -244,7 +246,7 @@ export function messageC2SAuthenticateAndBeamOut_packetInfoToJSON( } function createBaseMessageC2SHello(): MessageC2SHello { - return { version: 0, requestedUsername: "", nextState: 0 }; + return { version: 0, requestedUsername: "", nextState: 0, token: undefined, user: undefined }; } export const MessageC2SHello = { @@ -258,6 +260,12 @@ export const MessageC2SHello = { if (message.nextState !== 0) { writer.uint32(24).int32(message.nextState); } + if (message.token !== undefined) { + writer.uint32(34).string(message.token); + } + if (message.user !== undefined) { + writer.uint32(42).string(message.user); + } return writer; }, @@ -289,6 +297,20 @@ export const MessageC2SHello = { message.nextState = reader.int32() as any; continue; + case 4: + if (tag != 34) { + break; + } + + message.token = reader.string(); + continue; + case 5: + if (tag != 42) { + break; + } + + message.user = reader.string(); + continue; } if ((tag & 7) == 4 || tag == 0) { break; @@ -303,6 +325,8 @@ export const MessageC2SHello = { version: isSet(object.version) ? Number(object.version) : 0, requestedUsername: isSet(object.requestedUsername) ? String(object.requestedUsername) : "", nextState: isSet(object.nextState) ? stateFromJSON(object.nextState) : 0, + token: isSet(object.token) ? String(object.token) : undefined, + user: isSet(object.user) ? String(object.user) : undefined, }; }, @@ -311,6 +335,8 @@ export const MessageC2SHello = { message.version !== undefined && (obj.version = Math.round(message.version)); message.requestedUsername !== undefined && (obj.requestedUsername = message.requestedUsername); message.nextState !== undefined && (obj.nextState = stateToJSON(message.nextState)); + message.token !== undefined && (obj.token = message.token); + message.user !== undefined && (obj.user = message.user); return obj; }, @@ -323,6 +349,8 @@ export const MessageC2SHello = { message.version = object.version ?? 0; message.requestedUsername = object.requestedUsername ?? ""; message.nextState = object.nextState ?? 0; + message.token = object.token ?? undefined; + message.user = object.user ?? undefined; return message; }, }; diff --git a/client/src/protocol/message_s2c.ts b/client/src/protocol/message_s2c.ts index 35155d47feb6c68d38b4ebbc51f72591bb85cd15..810bc93259a47a7ad5d9efcafd538d5fdbadaf1b 100644 --- a/client/src/protocol/message_s2c.ts +++ b/client/src/protocol/message_s2c.ts @@ -1,6 +1,7 @@ /* eslint-disable */ import * as _m0 from "protobufjs/minimal"; import { GoodbyeReason, goodbyeReasonFromJSON, goodbyeReasonToJSON } from "./goodbye_reason"; +import { Module } from "./module"; import { Planet } from "./planet"; import { Player } from "./player"; import { State, stateFromJSON, stateToJSON } from "./state"; @@ -239,6 +240,43 @@ export function messageS2CPlanetData_packetInfoToJSON(object: MessageS2CPlanetDa } } +export interface MessageS2CModulesUpdate { + modules: Module[]; +} + +export enum MessageS2CModulesUpdate_packetInfo { + unknown = 0, + type = 11, + UNRECOGNIZED = -1, +} + +export function messageS2CModulesUpdate_packetInfoFromJSON(object: any): MessageS2CModulesUpdate_packetInfo { + switch (object) { + case 0: + case "unknown": + return MessageS2CModulesUpdate_packetInfo.unknown; + case 11: + case "type": + return MessageS2CModulesUpdate_packetInfo.type; + case -1: + case "UNRECOGNIZED": + default: + return MessageS2CModulesUpdate_packetInfo.UNRECOGNIZED; + } +} + +export function messageS2CModulesUpdate_packetInfoToJSON(object: MessageS2CModulesUpdate_packetInfo): string { + switch (object) { + case MessageS2CModulesUpdate_packetInfo.unknown: + return "unknown"; + case MessageS2CModulesUpdate_packetInfo.type: + return "type"; + case MessageS2CModulesUpdate_packetInfo.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + function createBaseMessageS2CHello(): MessageS2CHello { return { version: 0, givenUsername: "", nextState: 0 }; } @@ -614,6 +652,66 @@ export const MessageS2CPlanetData = { }, }; +function createBaseMessageS2CModulesUpdate(): MessageS2CModulesUpdate { + return { modules: [] }; +} + +export const MessageS2CModulesUpdate = { + encode(message: MessageS2CModulesUpdate, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.modules) { + Module.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MessageS2CModulesUpdate { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMessageS2CModulesUpdate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag != 10) { + break; + } + + message.modules.push(Module.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) == 4 || tag == 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): MessageS2CModulesUpdate { + return { modules: Array.isArray(object?.modules) ? object.modules.map((e: any) => Module.fromJSON(e)) : [] }; + }, + + toJSON(message: MessageS2CModulesUpdate): unknown { + const obj: any = {}; + if (message.modules) { + obj.modules = message.modules.map((e) => e ? Module.toJSON(e) : undefined); + } else { + obj.modules = []; + } + return obj; + }, + + create, I>>(base?: I): MessageS2CModulesUpdate { + return MessageS2CModulesUpdate.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): MessageS2CModulesUpdate { + const message = createBaseMessageS2CModulesUpdate(); + message.modules = object.modules?.map((e) => Module.fromPartial(e)) || []; + return message; + }, +}; + type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; export type DeepPartial = T extends Builtin ? T diff --git a/client/src/protocol/module.ts b/client/src/protocol/module.ts new file mode 100644 index 0000000000000000000000000000000000000000..97dae4c6d3f6d6ef19e7562bb3617df9b9d2e8ee --- /dev/null +++ b/client/src/protocol/module.ts @@ -0,0 +1,162 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; + +export const protobufPackage = "protocol.module"; + +export enum ModuleType { + Cargo = 0, + LandingThruster = 1, + Hub = 2, + UNRECOGNIZED = -1, +} + +export function moduleTypeFromJSON(object: any): ModuleType { + switch (object) { + case 0: + case "Cargo": + return ModuleType.Cargo; + case 1: + case "LandingThruster": + return ModuleType.LandingThruster; + case 2: + case "Hub": + return ModuleType.Hub; + case -1: + case "UNRECOGNIZED": + default: + return ModuleType.UNRECOGNIZED; + } +} + +export function moduleTypeToJSON(object: ModuleType): string { + switch (object) { + case ModuleType.Cargo: + return "Cargo"; + case ModuleType.LandingThruster: + return "LandingThruster"; + case ModuleType.Hub: + return "Hub"; + case ModuleType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export interface Module { + moduleType: ModuleType; + rotation: number; + x: number; + y: number; +} + +function createBaseModule(): Module { + return { moduleType: 0, rotation: 0, x: 0, y: 0 }; +} + +export const Module = { + encode(message: Module, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.moduleType !== 0) { + writer.uint32(8).int32(message.moduleType); + } + if (message.rotation !== 0) { + writer.uint32(21).float(message.rotation); + } + if (message.x !== 0) { + writer.uint32(29).float(message.x); + } + if (message.y !== 0) { + writer.uint32(37).float(message.y); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Module { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseModule(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag != 8) { + break; + } + + message.moduleType = reader.int32() as any; + continue; + case 2: + if (tag != 21) { + break; + } + + message.rotation = reader.float(); + continue; + case 3: + if (tag != 29) { + break; + } + + message.x = reader.float(); + continue; + case 4: + if (tag != 37) { + break; + } + + message.y = reader.float(); + continue; + } + if ((tag & 7) == 4 || tag == 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Module { + return { + moduleType: isSet(object.moduleType) ? moduleTypeFromJSON(object.moduleType) : 0, + rotation: isSet(object.rotation) ? Number(object.rotation) : 0, + x: isSet(object.x) ? Number(object.x) : 0, + y: isSet(object.y) ? Number(object.y) : 0, + }; + }, + + toJSON(message: Module): unknown { + const obj: any = {}; + message.moduleType !== undefined && (obj.moduleType = moduleTypeToJSON(message.moduleType)); + message.rotation !== undefined && (obj.rotation = message.rotation); + message.x !== undefined && (obj.x = message.x); + message.y !== undefined && (obj.y = message.y); + return obj; + }, + + create, I>>(base?: I): Module { + return Module.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): Module { + const message = createBaseModule(); + message.moduleType = object.moduleType ?? 0; + message.rotation = object.rotation ?? 0; + message.x = object.x ?? 0; + message.y = object.y ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/server/src/entity.rs b/server/src/entity.rs index 66b3309e7b4b1dc5b53b601cad92b66ec8fd2ed5..3173b70ca2f3e94e779618c75a697a997c046299 100644 --- a/server/src/entity.rs +++ b/server/src/entity.rs @@ -3,7 +3,7 @@ use std::{sync::atomic::AtomicU32, collections::HashMap, net::SocketAddr}; use nalgebra::Vector2; use starkingdoms_protocol::planet::PlanetType; -use crate::{planet::Planet, SCALE, manager::{ClientHandlerMessage, Player}}; +use crate::{planet::Planet, SCALE, manager::{ClientHandlerMessage, Player, Module}}; pub type EntityId = u32; pub type Entities = HashMap; @@ -11,7 +11,7 @@ static mut ENTITY_ID_COUNT: AtomicU32 = AtomicU32::new(0); pub fn get_entity_id() -> EntityId { let last_entity_id = unsafe { &ENTITY_ID_COUNT }; let id = last_entity_id.fetch_add(1, std::sync::atomic::Ordering::AcqRel); - if id > 2_147_483_600 { panic!("No remaining entity ids") }; + if id > 4_147_483_600 { panic!("No remaining entity ids") }; id } @@ -78,6 +78,24 @@ impl EntityHandler { } Some(players[0].clone().1) } + pub fn get_modules(&self) -> Vec { + let mut modules = Vec::new(); + for entity in self.entities.values() { + if let Entity::Module(module) = entity { + modules.push(module.clone()); + } + } + modules + } + pub fn get_module_count(&self) -> u32 { + let mut module_count = 0; + for entity in self.entities.values() { + if let Entity::Module(_module) = entity { + module_count += 1; + } + } + module_count + } pub fn gravity(&self, position: (f64, f64), mass: f64) -> (f64, f64) { let mut direction = Vector2::zeros(); @@ -114,4 +132,5 @@ impl EntityHandler { pub enum Entity { Player(Player), Planet(Planet), + Module(Module), } diff --git a/server/src/handler.rs b/server/src/handler.rs index 05eb13569ed3fecb4b05bcb774d3506c4be1fcbc..f680e3e92d021adb10deef970d5fd267356a519e 100644 --- a/server/src/handler.rs +++ b/server/src/handler.rs @@ -11,7 +11,7 @@ use rand::Rng; use rapier2d_f64::prelude::{RigidBodyBuilder, RigidBodyType, ColliderBuilder, MassProperties, Collider}; use tungstenite::Message; use starkingdoms_protocol::goodbye_reason::GoodbyeReason; -use starkingdoms_protocol::message_s2c::{MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong}; +use starkingdoms_protocol::message_s2c::{MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong, MessageS2CModulesUpdate}; use starkingdoms_protocol::{MessageS2C, MessageC2S, PROTOCOL_VERSION}; use starkingdoms_protocol::state::State; use crate::entity::{EntityHandler, get_entity_id, Entity}; @@ -20,7 +20,6 @@ use crate::{send, recv, SCALE}; use async_std::{sync::RwLock, channel::Receiver}; use async_std::net::TcpStream; use async_tungstenite::WebSocketStream; -use starkingdoms_protocol::api::APISavedPlayerData; use crate::api::{load_player_data_from_api, save_player_data_to_api}; pub async fn handle_client(mgr: ClientManager, entities: Arc>, data: Arc>, @@ -63,6 +62,15 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc { + if matches!(state, State::Play) { + let msg = MessageS2C::ModulesUpdate(MessageS2CModulesUpdate { + modules, + special_fields: Default::default(), + }).try_into()?; + send!(client_tx, msg).await?; + } + } } } else { info!("channel closed, shutting down"); diff --git a/server/src/manager.rs b/server/src/manager.rs index 94eff3bb062172f9f2eeb01cddaa99c9506baf49..39bc912875a63a19e2a0a8d3473b9e2df61dadb9 100644 --- a/server/src/manager.rs +++ b/server/src/manager.rs @@ -6,6 +6,7 @@ use rapier2d_f64::prelude::{IntegrationParameters, PhysicsPipeline, IslandManage use async_std::sync::RwLock; use async_std::channel::Sender; use starkingdoms_protocol::api::APISavedPlayerData; +use starkingdoms_protocol::module::ModuleType; #[derive(Clone)] pub struct ClientManager { @@ -31,6 +32,12 @@ impl Player { } } +#[derive(Clone)] +pub struct Module { + pub handle: RigidBodyHandle, + pub module_type: ModuleType, +} + #[derive(Default, Clone)] pub struct PlayerInput { pub up: bool, @@ -81,4 +88,5 @@ pub enum ClientHandlerMessage { ChatMessage { from: String, message: String }, PlayersUpdate { players: Vec }, PlanetData { planets: Vec }, + ModulesUpdate { modules: Vec }, } diff --git a/server/src/timer.rs b/server/src/timer.rs index 2fe7909bd51f0147ae6da2a7a8f8ae7d7ff6afa3..727d70fa7a663e929e151be0a39c7d48b6b9a2bd 100644 --- a/server/src/timer.rs +++ b/server/src/timer.rs @@ -1,25 +1,29 @@ -use std::{time::Duration, sync::Arc}; +use std::{time::Duration, sync::Arc, f64::consts::PI}; use log::{debug, warn}; use nalgebra::{vector, point}; -use rapier2d_f64::prelude::{PhysicsPipeline}; +use rand::Rng; +use rapier2d_f64::prelude::{PhysicsPipeline, ColliderBuilder, RigidBodyBuilder}; use async_std::sync::RwLock; use async_std::task::sleep; -use starkingdoms_protocol::{player::Player, planet::PlanetType}; -use crate::{manager::{ClientHandlerMessage, ClientManager, PhysicsData}, SCALE, planet::{Planets, Planet}, entity::{Entities, Entity, EntityHandler}}; +use starkingdoms_protocol::{player::Player, planet::PlanetType, module::ModuleType}; +use crate::{manager::{ClientHandlerMessage, ClientManager, PhysicsData, Module}, SCALE, planet::{Planets, Planet}, entity::{Entities, Entity, EntityHandler, get_entity_id}}; use crate::orbit::constants::{EARTH_MASS, GAME_ORBITS_ENABLED, MOON_APOAPSIS, MOON_MASS, MOON_ORBIT_TIME, MOON_PERIAPSIS}; use crate::orbit::orbit::{calculate_point_on_orbit, calculate_vector_of_orbit, calculate_world_position_of_orbit}; pub const ROTATIONAL_FORCE: f64 = 100.0; pub const LATERAL_FORCE: f64 = 100.0; +pub const MODULE_SPAWN: f64 = 1000.0; +pub const MODULE_MAX: u32 = 10; -pub async fn timer_main(mgr: ClientManager, physics_data: Arc>, entities: Arc>) { +pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc>, entities: Arc>) { let mut pipeline = PhysicsPipeline::new(); let mut time = 0.0; + let mut module_timer = 0.0; let planet_ids; { - let mut data_handle = physics_data.write().await; + let mut data_handle = physics_data_orig.write().await; let mut rigid_body_set = data_handle.rigid_body_set.clone(); let mut collider_set = data_handle.collider_set.clone(); @@ -33,8 +37,9 @@ pub async fn timer_main(mgr: ClientManager, physics_data: Arc 1.0 && entities.read().await.get_module_count() < MODULE_MAX { + debug!("module spawn"); + module_timer = 0.; + + let mut rigid_body_set = physics_data.rigid_body_set.clone(); + let mut collider_set = physics_data.collider_set.clone(); + + let module_collider = ColliderBuilder::cuboid(25. / SCALE, 25. / SCALE); + let angle: f64 = { + let mut rng = rand::thread_rng(); + rng.gen::() * PI * 2. + }; + let module_body = RigidBodyBuilder::dynamic() + .translation(vector![angle.cos() * 2050. / SCALE, angle.sin() * 2050.0/SCALE]) + .build(); + let module_handler = rigid_body_set.insert(module_body); + collider_set.insert_with_parent(module_collider, module_handler, &mut rigid_body_set); + + physics_data.rigid_body_set = rigid_body_set; + physics_data.collider_set = collider_set; + + let module = Module { + handle: module_handler, + module_type: ModuleType::Cargo, + }; + entities.write().await.entities.insert(get_entity_id(), Entity::Module(module)); + } + for module in entities.read().await.get_modules().iter() { + let module_handle = module.handle; + let module_body = physics_data.rigid_body_set.get_mut(module_handle).unwrap(); + module_body.reset_forces(true); + module_body.reset_torques(true); + let planets = entities.read().await; + let grav_force = planets.gravity((module_body.translation().x, module_body.translation().y), module_body.mass()); + module_body.apply_impulse(vector![grav_force.0, grav_force.1], true); + } + } + { for (player_id, player) in entities.read().await.get_players().iter() { let player_handle = player.handle; @@ -179,6 +223,24 @@ pub async fn timer_main(mgr: ClientManager, physics_data: Arc = modules.iter() + .map(|module| { + let body = physics_data.rigid_body_set.get(module.handle).unwrap(); + return starkingdoms_protocol::module::Module { + module_type: module.module_type.into(), + rotation: body.rotation().angle() as f32, + x: (body.translation().x * SCALE) as f32, + y: (body.translation().y * SCALE) as f32, + special_fields: Default::default(), + }; + }).collect(); + match client_thread.tx.send(ClientHandlerMessage::ModulesUpdate { modules: protocol_modules.clone() }).await { + Ok(_) => (), + Err(e) => { + warn!("unable to send module position packet: {}", e); + } + }; let world = entities.read().await; let planet_data = world.to_protocol();