~starkingdoms/starkingdoms

1286ad4253f09c965ae14ff9a2694acd6d9e2396 — core 8 months ago cf5bc7e
format
M crates/client/src/components.rs => crates/client/src/components.rs +21 -7
@@ 15,7 15,9 @@ pub struct Transform {
}
impl Transform {
    pub fn to_matrix(&self) -> Matrix4<f32> {
        self.translation.to_homogeneous() * self.rotation.to_homogeneous().to_homogeneous() * self.scale.to_homogeneous()
        self.translation.to_homogeneous()
            * self.rotation.to_homogeneous().to_homogeneous()
            * self.scale.to_homogeneous()
    }
}



@@ 30,7 32,7 @@ pub struct Camera {
    pub x: f32,
    pub y: f32,
    pub zoom: f32,
    pub width: u32, // screen width (these are for aspect ratio)
    pub width: u32,  // screen width (these are for aspect ratio)
    pub height: u32, // screen height
}
impl Camera {


@@ 38,17 40,29 @@ impl Camera {
        let x_scale = self.zoom / self.width as f32 * 2.0;
        let y_scale = self.zoom / self.height as f32 * 2.0;
        Matrix4::from_vec(vec![
            x_scale,        0.0,            0.0,    0.0,
            0.0,            y_scale,        0.0,    0.0,
            0.0,            0.0,            1.0,    0.0,
            self.x*x_scale, self.y*y_scale, 0.0,    1.0,
            x_scale,
            0.0,
            0.0,
            0.0,
            0.0,
            y_scale,
            0.0,
            0.0,
            0.0,
            0.0,
            1.0,
            0.0,
            self.x * x_scale,
            self.y * y_scale,
            0.0,
            1.0,
        ])
    }
    pub fn to_cursor_matrix(&self) -> Matrix3<f32> {
        let x = -(self.width as f32 / 2.0);
        let y = -(self.height as f32 / 2.0);
        Translation2::new(-self.x, -self.y).to_homogeneous()
            * Scale2::new(1.0/self.zoom, 1.0/self.zoom).to_homogeneous()
            * Scale2::new(1.0 / self.zoom, 1.0 / self.zoom).to_homogeneous()
            * Translation2::new(x, y).to_homogeneous()
    }
}

M crates/client/src/lib.rs => crates/client/src/lib.rs +16 -7
@@ 14,9 14,9 @@ pub mod platform;
#[path = "native/mod.rs"]
pub mod platform;

pub mod rendering;
pub mod components;
pub mod networking;
pub mod rendering;

// Hi, you've found the real main function! This is called AFTER platform-specific initialization code.
pub fn start() {


@@ 47,14 47,23 @@ pub fn start() {
    let mut send_packet_events = Events::<SendPacket>::default();
    let recv_packet_events = Events::<RecvPacket>::default();

    world.spawn((Transform {
        translation: Translation3::new(0.0, 0.0, 0.0),
        rotation: Rotation2::new(0.0),
        scale: Scale3::new(25.0, 25.0, 1.0),
    }, Texture { name: "hearty.svg".to_string() }, Player, Part(false)));
    world.spawn((
        Transform {
            translation: Translation3::new(0.0, 0.0, 0.0),
            rotation: Rotation2::new(0.0),
            scale: Scale3::new(25.0, 25.0, 1.0),
        },
        Texture {
            name: "hearty.svg".to_string(),
        },
        Player,
        Part(false),
    ));

    let event_loop = EventLoop::new().unwrap();
    event_loop.set_control_flow(ControlFlow::Wait);

    event_loop.run_app(&mut App::new(world, send_packet_events, recv_packet_events)).unwrap();
    event_loop
        .run_app(&mut App::new(world, send_packet_events, recv_packet_events))
        .unwrap();
}

M crates/client/src/networking/mod.rs => crates/client/src/networking/mod.rs +71 -32
@@ 1,10 1,18 @@
use std::collections::HashMap;

use bevy_ecs::{entity::Entity, event::Events, query::{QuerySingleError, With}, world::World};
use bevy_ecs::{
    entity::Entity,
    event::Events,
    query::{QuerySingleError, With},
    world::World,
};
use nalgebra::{Rotation2, Scale2, Scale3, Translation3};
use starkingdoms_common::{packet::Packet, PartType, PlanetType};

use crate::components::{Camera, Part, Planet, Player, RecvPacket, SendPacket, ServerId, SpriteBundle, Texture, Transform};
use crate::components::{
    Camera, Part, Planet, Player, RecvPacket, SendPacket, ServerId, SpriteBundle, Texture,
    Transform,
};

#[cfg(target_arch = "wasm32")]
#[path = "ws_wasm.rs"]


@@ 23,7 31,8 @@ fn texture_name(part_type: PartType, attached: bool) -> String {
            Hub => "hub_on.svg",
            LandingThruster => "landingthruster_on.svg",
            LandingThrusterSuspension => "landingleg.svg",
        }.to_string()
        }
        .to_string()
    } else {
        match part_type {
            Placeholder => panic!("AHHHH PLACEHOLDER PANIC"),


@@ 32,7 41,8 @@ fn texture_name(part_type: PartType, attached: bool) -> String {
            Hub => "hub_off.svg",
            LandingThruster => "landingthruster_off.svg",
            LandingThrusterSuspension => "landingleg.svg",
        }.to_string()
        }
        .to_string()
    }
}



@@ 53,36 63,52 @@ pub fn process_packets(
                let entity = player_query.single(world);
                world.entity_mut(entity).insert(ServerId(*id));
            }
            PlayerList { players } => { // existing players
            PlayerList { players } => {
                // existing players
                // username sync, eventually
                for player in players {
                    world.spawn((Transform {
                        translation: Translation3::new(0.0, 0.0, 0.0),
                        rotation: Rotation2::new(0.0),
                        scale: Scale3::new(25.0, 25.0, 1.0),
                    }, Texture {
                        name: "hearty.svg".to_string(),
                    }, ServerId(player.0), Part(false)));
                    world.spawn((
                        Transform {
                            translation: Translation3::new(0.0, 0.0, 0.0),
                            rotation: Rotation2::new(0.0),
                            scale: Scale3::new(25.0, 25.0, 1.0),
                        },
                        Texture {
                            name: "hearty.svg".to_string(),
                        },
                        ServerId(player.0),
                        Part(false),
                    ));
                }
            }
            SpawnPlayer { id, username } => {
                // username sync, eventually
                world.spawn((Transform {
                    translation: Translation3::new(0.0, 0.0, 0.0),
                    rotation: Rotation2::new(0.0),
                    scale: Scale3::new(25.0, 25.0, 1.0),
                }, Texture {
                    name: "hearty.svg".to_string(),
                }, ServerId(*id), Part(false)));
                world.spawn((
                    Transform {
                        translation: Translation3::new(0.0, 0.0, 0.0),
                        rotation: Rotation2::new(0.0),
                        scale: Scale3::new(25.0, 25.0, 1.0),
                    },
                    Texture {
                        name: "hearty.svg".to_string(),
                    },
                    ServerId(*id),
                    Part(false),
                ));
            }
            SpawnPart { id, part } => {
                world.spawn((Transform {
                    translation: Translation3::new(part.transform.x, part.transform.y, 0.0),
                    rotation: Rotation2::new(part.transform.rot),
                    scale: Scale3::new(25.0, 25.0, 1.0),
                }, Texture {
                    name: texture_name(part.part_type, part.flags.attached),
                }, ServerId(*id), Part(part.flags.attached)));
                world.spawn((
                    Transform {
                        translation: Translation3::new(part.transform.x, part.transform.y, 0.0),
                        rotation: Rotation2::new(part.transform.rot),
                        scale: Scale3::new(25.0, 25.0, 1.0),
                    },
                    Texture {
                        name: texture_name(part.part_type, part.flags.attached),
                    },
                    ServerId(*id),
                    Part(part.flags.attached),
                ));
            }
            PartPositions { parts } => {
                for (id, part) in parts {


@@ 97,7 123,7 @@ pub fn process_packets(
                                QuerySingleError::MultipleEntities(s) => {
                                    panic!("There should never multiple players marked as players");
                                }
                            }
                            },
                        };
                        if server_id.0 == *id {
                            let mut camera = world.resource_mut::<Camera>();


@@ 105,8 131,13 @@ pub fn process_packets(
                            camera.y = -part.transform.y;
                        }
                    }
                    let mut part_query = world.query::<(Entity, &ServerId, &mut Transform, &mut Texture, &mut Part)>();
                    for (entity, server_id, mut transform, mut texture, mut bevy_part) in part_query.iter_mut(world) {
                    let mut part_query =
                        world
                            .query::<(Entity, &ServerId, &mut Transform, &mut Texture, &mut Part)>(
                            );
                    for (entity, server_id, mut transform, mut texture, mut bevy_part) in
                        part_query.iter_mut(world)
                    {
                        if server_id.0 == *id {
                            transform.translation.x = part.transform.x;
                            transform.translation.y = part.transform.y;


@@ 129,7 160,11 @@ pub fn process_packets(
                    if !planet_types.contains_key(&planet.planet_type) {
                        let entity = world.spawn(SpriteBundle {
                            transform: Transform {
                                translation: Translation3::new(planet.transform.x, planet.transform.y, 0.0),
                                translation: Translation3::new(
                                    planet.transform.x,
                                    planet.transform.y,
                                    0.0,
                                ),
                                rotation: Rotation2::new(planet.transform.rot),
                                scale: Scale3::new(planet.radius, planet.radius, 1.0),
                            },


@@ 157,14 192,18 @@ pub fn process_packets(
                                    PlanetType::Uranus => "venus.svg",
                                    PlanetType::Neptune => "mars.svg",
                                    PlanetType::Pluto => "earth.svg",
                                }.to_string()
                                }
                                .to_string(),
                            },
                        });
                        planet_types.insert(planet.planet_type, (entity.id(), *server_id));
                    }
                }
            }
            EnergyUpdate { amount: m_amount, max: m_max } => {
            EnergyUpdate {
                amount: m_amount,
                max: m_max,
            } => {
                *amount = *m_amount;
                *max = *m_max;
            }

M crates/client/src/networking/ws_native.rs => crates/client/src/networking/ws_native.rs +22 -5
@@ 1,4 1,7 @@
use std::{net::TcpStream, sync::{Arc, Mutex}};
use std::{
    net::TcpStream,
    sync::{Arc, Mutex},
};

use bevy_ecs::system::Resource;
use crossbeam::channel::{unbounded, Receiver, Sender};


@@ 22,7 25,11 @@ impl PacketMessageConvert for Packet {
        }
    }
    fn into_message(&self) -> Message {
        Message::Text(serde_json::to_string(self).expect("failed to serialize packet to json").into())
        Message::Text(
            serde_json::to_string(self)
                .expect("failed to serialize packet to json")
                .into(),
        )
    }
}



@@ 43,9 50,15 @@ impl Ws {
        std::thread::spawn(move || {
            let socket = socket_clone;
            loop {
                let message = socket.lock().unwrap().read().expect("Failed to reading message");
                let message = socket
                    .lock()
                    .unwrap()
                    .read()
                    .expect("Failed to reading message");
                let packet = Packet::from_message(&message).expect("Server sent invalid packet");
                packet_sender.send(packet).expect("Couldn't send packet to server");
                packet_sender
                    .send(packet)
                    .expect("Couldn't send packet to server");
            }
        });
        Ws {


@@ 56,6 69,10 @@ impl Ws {
        }
    }
    pub fn send_packet(&mut self, packet: &Packet) {
        self.socket.lock().unwrap().send(packet.into_message()).expect("Couldn't send packet to server");
        self.socket
            .lock()
            .unwrap()
            .send(packet.into_message())
            .expect("Couldn't send packet to server");
    }
}

M crates/client/src/networking/ws_wasm.rs => crates/client/src/networking/ws_wasm.rs +37 -10
@@ 2,11 2,14 @@ use std::thread::yield_now;

use bevy_ecs::system::Resource;
//use crossbeam::channel::{unbounded, Receiver, Sender};
use futures::{channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, SinkExt};
use futures::{
    channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender},
    SinkExt,
};
use starkingdoms_common::packet::Packet;
use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
use wasm_bindgen_futures::spawn_local;
use web_sys::{MessageEvent, WebSocket};
use starkingdoms_common::packet::Packet;

const PORT: u16 = 3000;



@@ 26,8 29,12 @@ pub struct Ws {
impl Ws {
    pub fn new() -> Self {
        let window = web_sys::window().unwrap();
        let ws = WebSocket::new(&format!("ws://{}:{}",
                window.location().hostname().unwrap(), PORT)).expect("Couldn't connect to server");
        let ws = WebSocket::new(&format!(
            "ws://{}:{}",
            window.location().hostname().unwrap(),
            PORT
        ))
        .expect("Couldn't connect to server");
        let (packet_sender, receiver) = unbounded();
        //let packet_sender = Rc::new(RwLock::new(packet_sender));
        let (sender, packet_receiver) = unbounded();


@@ 40,17 47,28 @@ impl Ws {
                jwt: None,
            };

            ws_clone.send_with_str(&serde_json::to_string(&packet).expect("Couldn't convert packet to string")).expect("Failed to send packet");
            ws_clone
                .send_with_str(
                    &serde_json::to_string(&packet).expect("Couldn't convert packet to string"),
                )
                .expect("Failed to send packet");
        });
        ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref()));
        onopen_callback.forget();
        let onmessage_callback = Closure::<dyn FnMut(_)>::new(move |e: MessageEvent| {
            //tracing::error!("{}", ws.ready_state());
            let data = e.data().as_string().expect("Expected string, found some other type");
            let data: Packet = serde_json::from_str(&data).expect("Received invalid json from server");
            let data = e
                .data()
                .as_string()
                .expect("Expected string, found some other type");
            let data: Packet =
                serde_json::from_str(&data).expect("Received invalid json from server");
            let mut sender_clone = packet_sender.clone();
            spawn_local(async move {
                sender_clone.send(data).await.expect("Couldn't transmit packet to client");
                sender_clone
                    .send(data)
                    .await
                    .expect("Couldn't transmit packet to client");
            });
        });
        ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));


@@ 65,14 83,23 @@ impl Ws {
    pub fn send_all_packets_from_channel(&mut self) {
        //for packet in self.packet_receiver.iter() {
        while let Ok(Some(packet)) = self.packet_receiver.try_next() {
            self.socket.0.send_with_str(&serde_json::to_string(&packet).expect("Couldn't convert packet to json")).expect("Couldn't send packet to server");
            self.socket
                .0
                .send_with_str(
                    &serde_json::to_string(&packet).expect("Couldn't convert packet to json"),
                )
                .expect("Couldn't send packet to server");
        }
    }
    pub fn send_packet(&mut self, packet: Packet) {
        let socket = self.socket.0.clone();
        spawn_local(async move {
            //while socket.ready_state() != 1 {  }
            socket.send_with_str(&serde_json::to_string(&packet).expect("Couldn't convert packet to json")).expect("Couldn't send packet to server");
            socket
                .send_with_str(
                    &serde_json::to_string(&packet).expect("Couldn't convert packet to json"),
                )
                .expect("Couldn't send packet to server");
        });
    }
}

M crates/client/src/rendering/assets_native.rs => crates/client/src/rendering/assets_native.rs +15 -9
@@ 11,12 11,10 @@ pub struct ImgData {
}

#[derive(Resource)]
pub struct Assets {

}
pub struct Assets {}
impl Assets {
    pub fn new() -> Self {
        Assets {  }
        Assets {}
    }
    pub fn get(&self, local_path: impl Into<String>) -> Option<ImgData> {
        let local_path = local_path.into();


@@ 26,18 24,27 @@ impl Assets {
                default_size: usvg::Size::from_wh(20.0, 20.0).unwrap(),
                ..Default::default()
            };
            let tree = usvg::Tree::from_data(&bytes, &opt).expect(&format!("Couldn't parse svg {}", local_path));
            let tree = usvg::Tree::from_data(&bytes, &opt)
                .expect(&format!("Couldn't parse svg {}", local_path));
            let tree_size = tree.size().to_int_size();
            let size = usvg::Size::from_wh(200.0, 200.0).unwrap().to_int_size();
            assert!(size.width() > 0 && size.height() > 0);
            let mut pixmap = tiny_skia::Pixmap::new(size.width(), size.height()).expect("Failed to construct pixmap");
            resvg::render(&tree, tiny_skia::Transform::from_scale((size.width() as f32)/(tree_size.height() as f32), (size.height() as f32)/(tree_size.height() as f32)), &mut pixmap.as_mut());
            let mut pixmap = tiny_skia::Pixmap::new(size.width(), size.height())
                .expect("Failed to construct pixmap");
            resvg::render(
                &tree,
                tiny_skia::Transform::from_scale(
                    (size.width() as f32) / (tree_size.height() as f32),
                    (size.height() as f32) / (tree_size.height() as f32),
                ),
                &mut pixmap.as_mut(),
            );
            let data = ImgData {
                bytes: pixmap.data().to_vec(),
                width: size.width(),
                height: size.height(),
            };
            

            Some(data)
        } else if local_path.ends_with(".png") {
            let img = image::load_from_memory(&bytes).unwrap();


@@ 53,4 60,3 @@ impl Assets {
        }
    }
}


M crates/client/src/rendering/assets_wasm.rs => crates/client/src/rendering/assets_wasm.rs +38 -14
@@ 1,4 1,8 @@
use std::{collections::HashMap, fmt::Display, sync::{Arc, Mutex}};
use std::{
    collections::HashMap,
    fmt::Display,
    sync::{Arc, Mutex},
};

use bevy_ecs::system::Resource;
use image::EncodableLayout;


@@ 42,40 46,54 @@ impl Assets {
    }
    pub fn get(&self, local_path: impl Into<String>) -> Option<ImgData> {
        let local_path = local_path.into();
        let contains_texture = {
            self.textures.lock().unwrap().contains_key(&local_path)
        };
        let contains_texture = { self.textures.lock().unwrap().contains_key(&local_path) };
        let contains_texture_promise = {
            self.texture_promises.lock().unwrap().contains_key(&local_path)
            self.texture_promises
                .lock()
                .unwrap()
                .contains_key(&local_path)
        };
        if !contains_texture && !contains_texture_promise {
            let local_path_clone = local_path.clone();
            let request_promise = poll_promise::Promise::spawn_local(async move {
                let window = web_sys::window().unwrap();
                let request = ehttp::Request::get(format!("{}/src/assets/{}", window.location().origin().unwrap(), local_path_clone));
                let request = ehttp::Request::get(format!(
                    "{}/src/assets/{}",
                    window.location().origin().unwrap(),
                    local_path_clone
                ));
                let response = match ehttp::fetch_async(request).await {
                    Ok(resp) => resp,
                    Err(e) => {
                        panic!("{}", e);
                    },
                    }
                };
                if local_path_clone.ends_with(".svg") {
                    let opt = usvg::Options {
                        default_size: usvg::Size::from_wh(20.0, 20.0).unwrap(),
                        ..Default::default()
                    };
                    let tree = usvg::Tree::from_data(&response.bytes, &opt).expect(&format!("Couldn't parse svg {}", local_path_clone));
                    let tree = usvg::Tree::from_data(&response.bytes, &opt)
                        .expect(&format!("Couldn't parse svg {}", local_path_clone));
                    let tree_size = tree.size().to_int_size();
                    let size = usvg::Size::from_wh(512.0, 512.0).unwrap().to_int_size();
                    assert!(size.width() > 0 && size.height() > 0);
                    let mut pixmap = tiny_skia::Pixmap::new(size.width(), size.height()).expect("Failed to construct pixmap");
                    resvg::render(&tree, tiny_skia::Transform::from_scale((size.width() as f32)/(tree_size.height() as f32), (size.height() as f32)/(tree_size.height() as f32)), &mut pixmap.as_mut());
                    let mut pixmap = tiny_skia::Pixmap::new(size.width(), size.height())
                        .expect("Failed to construct pixmap");
                    resvg::render(
                        &tree,
                        tiny_skia::Transform::from_scale(
                            (size.width() as f32) / (tree_size.height() as f32),
                            (size.height() as f32) / (tree_size.height() as f32),
                        ),
                        &mut pixmap.as_mut(),
                    );
                    let data = ImgData {
                        bytes: pixmap.data().to_vec(),
                        width: size.width(),
                        height: size.height(),
                    };
                    

                    data
                } else if local_path_clone.ends_with(".png") {
                    let img = image::load_from_memory(&response.bytes).unwrap();


@@ 91,7 109,10 @@ impl Assets {
                }
            });
            {
                self.texture_promises.lock().unwrap().insert(local_path.clone(), request_promise);
                self.texture_promises
                    .lock()
                    .unwrap()
                    .insert(local_path.clone(), request_promise);
            }
            None
        } else if !contains_texture {


@@ 99,11 120,14 @@ impl Assets {
            let promise = texture_promises.get_mut(&local_path).unwrap();
            let mut returned_value = None;
            if let Some(texture) = promise.ready() {
                self.textures.lock().unwrap().insert(local_path.clone(), texture.clone());
                self.textures
                    .lock()
                    .unwrap()
                    .insert(local_path.clone(), texture.clone());
                returned_value = Some(texture.clone());
                texture_promises.remove(&local_path);
            }
            return returned_value
            return returned_value;
        } else {
            self.textures.lock().unwrap().get(&local_path).cloned()
        }

M crates/client/src/rendering/mod.rs => crates/client/src/rendering/mod.rs +283 -118
@@ 11,9 11,14 @@ use egui::{Label, ProgressBar};
use egui_glow::EguiGlow;
use glow::{HasContext, PixelUnpackData};
#[cfg(not(target_arch = "wasm32"))]
use glutin::surface::{Surface, WindowSurface, GlSurface, SwapInterval};
use glutin::surface::{GlSurface, Surface, SwapInterval, WindowSurface};
#[cfg(not(target_arch = "wasm32"))]
use glutin::{config::{ConfigTemplateBuilder, GlConfig}, context::{ContextApi, ContextAttributesBuilder, PossiblyCurrentContext}, display::GetGlDisplay, prelude::{GlDisplay, NotCurrentGlContext}};
use glutin::{
    config::{ConfigTemplateBuilder, GlConfig},
    context::{ContextApi, ContextAttributesBuilder, PossiblyCurrentContext},
    display::GetGlDisplay,
    prelude::{GlDisplay, NotCurrentGlContext},
};
#[cfg(not(target_arch = "wasm32"))]
use glutin_winit::{DisplayBuilder, GlWindow};
use nalgebra::{Vector3, Vector4};


@@ 29,15 34,22 @@ use winit::event_loop::ControlFlow;
use winit::keyboard::{KeyCode, PhysicalKey};
#[cfg(target_arch = "wasm32")]
use winit::platform::web::{WindowAttributesExtWebSys, WindowExtWebSys};
use winit::{application::ApplicationHandler, dpi::LogicalSize, event::WindowEvent, event_loop::ActiveEventLoop, raw_window_handle::HasWindowHandle, window::{Window, WindowAttributes}};
use winit::{
    application::ApplicationHandler,
    dpi::LogicalSize,
    event::WindowEvent,
    event_loop::ActiveEventLoop,
    raw_window_handle::HasWindowHandle,
    window::{Window, WindowAttributes},
};

use crate::components::{Camera, Player, RecvPacket, SendPacket, Texture, Transform};
use crate::networking::process_packets;
use crate::networking::ws::Ws;
#[cfg(not(target_arch="wasm32"))]
#[cfg(not(target_arch = "wasm32"))]
#[path = "assets_native.rs"]
pub mod assets;
#[cfg(target_arch="wasm32")]
#[cfg(target_arch = "wasm32")]
#[path = "assets_wasm.rs"]
pub mod assets;



@@ 73,21 85,15 @@ pub struct App {
}

const VERTICES: [f32; 16] = [
    -1.0, -1.0,     0.0, 1.0,
    1.0, -1.0,      1.0, 1.0,
    1.0, 1.0,       1.0, 0.0,
    -1.0, 1.0,      0.0, 0.0,
];
const INDICES: [u32; 6] = [
    0, 1, 2,
    2, 3, 0,
    -1.0, -1.0, 0.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 0.0,
];
const INDICES: [u32; 6] = [0, 1, 2, 2, 3, 0];

impl App {
    pub fn new(
        world: World,
        send_packet_events: Events<SendPacket>,
        recv_packet_events: Events<RecvPacket>
        recv_packet_events: Events<RecvPacket>,
    ) -> Self {
        Self {
            world,


@@ 104,28 110,53 @@ impl ApplicationHandler for App {
        let attributes = {
            let document = web_sys::window().unwrap().document().unwrap();
            let canvas = document.get_element_by_id("canvas").unwrap();
            let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>()
                .map_err(|_| ()).unwrap();
            canvas.set_width(web_sys::window().unwrap().inner_width().unwrap().as_f64().unwrap() as u32);
            canvas.set_height(web_sys::window().unwrap().inner_height().unwrap().as_f64().unwrap() as u32);
            let canvas = canvas
                .dyn_into::<web_sys::HtmlCanvasElement>()
                .map_err(|_| ())
                .unwrap();
            canvas.set_width(
                web_sys::window()
                    .unwrap()
                    .inner_width()
                    .unwrap()
                    .as_f64()
                    .unwrap() as u32,
            );
            canvas.set_height(
                web_sys::window()
                    .unwrap()
                    .inner_height()
                    .unwrap()
                    .as_f64()
                    .unwrap() as u32,
            );
            Window::default_attributes()
                .with_title("StarKingdoms.TK")
                .with_canvas(Some(canvas))
        };
        #[cfg(not(target_arch = "wasm32"))]
        let attributes = {
            Window::default_attributes().with_transparent(true).with_title("StarKingdoms.TK")
            Window::default_attributes()
                .with_transparent(true)
                .with_title("StarKingdoms.TK")
                .with_inner_size(LogicalSize::new(400, 300))
        };
        self.window = Some(event_loop.create_window(attributes.clone()).unwrap());
        let window = self.window.as_ref().unwrap();
        #[cfg(target_arch = "wasm32")]
        let context = window.canvas().unwrap().get_context("webgl2")
            .unwrap().unwrap()
        let context = window
            .canvas()
            .unwrap()
            .get_context("webgl2")
            .unwrap()
            .unwrap()
            .dyn_into::<web_sys::WebGl2RenderingContext>()
            .unwrap();
        #[cfg(target_arch = "wasm32")]
        let (gl, shader_version) = (Arc::new(glow::Context::from_webgl2_context(context)), "#version 300 es");
        let (gl, shader_version) = (
            Arc::new(glow::Context::from_webgl2_context(context)),
            "#version 300 es",
        );

        #[cfg(not(target_arch = "wasm32"))]
        let (gl, shader_version) = unsafe {


@@ 133,36 164,55 @@ impl ApplicationHandler for App {

            let display_builder = DisplayBuilder::new().with_window_attributes(Some(attributes));

            let (window, gl_config) = display_builder.build(event_loop, template, |configs| {
                configs.reduce(|accum, config| {
                    let supports_transparency = config.supports_transparency().unwrap_or(false)
                        && !accum.supports_transparency().unwrap_or(false);
                    if supports_transparency || config.num_samples() > accum.num_samples() {
                        config
                    } else {
                        accum
                    }
                }).unwrap()
            }).unwrap();
            let raw_handle = window.as_ref().map(|window| window.window_handle().unwrap().window_handle().unwrap().as_raw());
            let (window, gl_config) = display_builder
                .build(event_loop, template, |configs| {
                    configs
                        .reduce(|accum, config| {
                            let supports_transparency =
                                config.supports_transparency().unwrap_or(false)
                                    && !accum.supports_transparency().unwrap_or(false);
                            if supports_transparency || config.num_samples() > accum.num_samples() {
                                config
                            } else {
                                accum
                            }
                        })
                        .unwrap()
                })
                .unwrap();
            let raw_handle = window.as_ref().map(|window| {
                window
                    .window_handle()
                    .unwrap()
                    .window_handle()
                    .unwrap()
                    .as_raw()
            });
            let gl_display = gl_config.display();
            let context_attributes = ContextAttributesBuilder::new()
                .with_context_api(ContextApi::OpenGl(Some(glutin::context::Version {
                    major: 3,
                    minor: 0,
                }))).build(raw_handle);
                })))
                .build(raw_handle);

            let not_current_gl_context = gl_display.create_context(&gl_config, &context_attributes).unwrap();
            let not_current_gl_context = gl_display
                .create_context(&gl_config, &context_attributes)
                .unwrap();

            let window = window.unwrap();
            let surface_attributes = window.build_surface_attributes(Default::default()).unwrap();
            let gl_surface = gl_display.create_window_surface(&gl_config, &surface_attributes).unwrap();
            let gl_surface = gl_display
                .create_window_surface(&gl_config, &surface_attributes)
                .unwrap();

            let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap();

            let gl = glow::Context::from_loader_function_cstr(|s| gl_display.get_proc_address(s));

            gl_surface.set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())).unwrap();
            gl_surface
                .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
                .unwrap();

            self.gl_surface = Some(gl_surface);
            self.gl_context = Some(gl_context);


@@ 171,17 221,31 @@ impl ApplicationHandler for App {
        };
        unsafe {
            let shaders = [
                ("vertex", include_str!("../shaders/vertex.glsl"), glow::VERTEX_SHADER),
                ("fragment", include_str!("../shaders/fragment.glsl"), glow::FRAGMENT_SHADER),
                (
                    "vertex",
                    include_str!("../shaders/vertex.glsl"),
                    glow::VERTEX_SHADER,
                ),
                (
                    "fragment",
                    include_str!("../shaders/fragment.glsl"),
                    glow::FRAGMENT_SHADER,
                ),
            ];
            let program = gl.create_program().expect("Failed to create program");

            for (name, source, shader_type) in shaders {
                let shader = gl.create_shader(shader_type).expect("Failed to create vertex shader");
                let shader = gl
                    .create_shader(shader_type)
                    .expect("Failed to create vertex shader");
                gl.shader_source(shader, &format!("{}\n{}", shader_version, source));
                gl.compile_shader(shader);
                if !gl.get_shader_compile_status(shader) {
                    tracing::error!("error in {} shader: {}", name, gl.get_shader_info_log(shader));
                    tracing::error!(
                        "error in {} shader: {}",
                        name,
                        gl.get_shader_info_log(shader)
                    );
                }
                gl.attach_shader(program, shader);
                gl.delete_shader(shader);


@@ 190,27 254,44 @@ impl ApplicationHandler for App {

            gl.use_program(Some(program));

            let vertex_array = gl.create_vertex_array().expect("Failed to create vertex array");
            let vertex_array = gl
                .create_vertex_array()
                .expect("Failed to create vertex array");
            gl.bind_vertex_array(Some(vertex_array));
            let vertex_buffer = gl.create_buffer().expect("Failed to create vertex buffer");
            gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
            let element_buffer = gl.create_buffer().expect("Failed to create element buffer");
            gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(element_buffer));
            gl.buffer_data_u8_slice(glow::ARRAY_BUFFER,
            gl.buffer_data_u8_slice(
                glow::ARRAY_BUFFER,
                std::slice::from_raw_parts(VERTICES.as_ptr() as *const u8, size_of_val(&VERTICES)),
                glow::STATIC_DRAW);
            gl.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER,
                glow::STATIC_DRAW,
            );
            gl.buffer_data_u8_slice(
                glow::ELEMENT_ARRAY_BUFFER,
                std::slice::from_raw_parts(INDICES.as_ptr() as *const u8, size_of_val(&INDICES)),
                glow::STATIC_DRAW);
                glow::STATIC_DRAW,
            );

            gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 4*size_of::<f32>() as i32, 0);
            gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 4 * size_of::<f32>() as i32, 0);
            gl.enable_vertex_attrib_array(0);
            gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, 4*size_of::<f32>() as i32, 2*size_of::<f32>() as i32);
            gl.vertex_attrib_pointer_f32(
                1,
                2,
                glow::FLOAT,
                false,
                4 * size_of::<f32>() as i32,
                2 * size_of::<f32>() as i32,
            );
            gl.enable_vertex_attrib_array(1);


            gl.clear_color(0.1, 0.1, 0.1, 1.0);
            gl.viewport(0, 0, window.inner_size().width as i32, window.inner_size().height as i32);
            gl.viewport(
                0,
                0,
                window.inner_size().width as i32,
                window.inner_size().height as i32,
            );
            gl.enable(glow::BLEND);
            gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);



@@ 220,14 301,35 @@ impl ApplicationHandler for App {
            self.element_buffer = Some(element_buffer);
        }
        #[cfg(target_arch = "wasm32")]
        web_sys::window().unwrap().set_onresize(Some(Closure::<dyn Fn(Event)>::new(move |_| {
            let document = web_sys::window().unwrap().document().unwrap();
            let canvas = document.get_element_by_id("canvas").unwrap();
            let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>()
                .map_err(|_| ()).unwrap();
            canvas.set_width(web_sys::window().unwrap().inner_width().unwrap().as_f64().unwrap() as u32);
            canvas.set_height(web_sys::window().unwrap().inner_height().unwrap().as_f64().unwrap() as u32);
        }).into_js_value().as_ref().unchecked_ref()));
        web_sys::window().unwrap().set_onresize(Some(
            Closure::<dyn Fn(Event)>::new(move |_| {
                let document = web_sys::window().unwrap().document().unwrap();
                let canvas = document.get_element_by_id("canvas").unwrap();
                let canvas = canvas
                    .dyn_into::<web_sys::HtmlCanvasElement>()
                    .map_err(|_| ())
                    .unwrap();
                canvas.set_width(
                    web_sys::window()
                        .unwrap()
                        .inner_width()
                        .unwrap()
                        .as_f64()
                        .unwrap() as u32,
                );
                canvas.set_height(
                    web_sys::window()
                        .unwrap()
                        .inner_height()
                        .unwrap()
                        .as_f64()
                        .unwrap() as u32,
                );
            })
            .into_js_value()
            .as_ref()
            .unchecked_ref(),
        ));

        let egui_glow = egui_glow::EguiGlow::new(event_loop, gl.clone(), None, None, true);
        self.egui_glow = Some(egui_glow);


@@ 245,32 347,33 @@ impl ApplicationHandler for App {
            }
            WindowEvent::Resized(size) => {
                #[cfg(not(target_arch = "wasm32"))]
                self.gl_surface.as_ref().unwrap().resize(self.gl_context.as_ref().unwrap(),
                    NonZeroU32::new(size.width).unwrap(), NonZeroU32::new(size.height).unwrap());
                self.gl_surface.as_ref().unwrap().resize(
                    self.gl_context.as_ref().unwrap(),
                    NonZeroU32::new(size.width).unwrap(),
                    NonZeroU32::new(size.height).unwrap(),
                );

                let mut camera = self.world.get_resource_mut::<Camera>().unwrap();
                camera.width = size.width;
                camera.height = size.height;

                unsafe {
                    self.gl.as_ref().unwrap().viewport(0, 0, size.width as i32, size.height as i32);
                    self.gl
                        .as_ref()
                        .unwrap()
                        .viewport(0, 0, size.width as i32, size.height as i32);
                }
            }
            WindowEvent::MouseWheel { delta, .. } => {
                let mut camera = self.world.get_resource_mut::<Camera>().unwrap();
                let raw_delta = match delta {
                    MouseScrollDelta::PixelDelta(pos) => {
                        pos.y as f32
                    }
                    MouseScrollDelta::LineDelta(y, .. ) => {
                        y
                    }
                    MouseScrollDelta::PixelDelta(pos) => pos.y as f32,
                    MouseScrollDelta::LineDelta(y, ..) => y,
                };
                let delta = 1.1;
                if raw_delta < 0.0 {
                    camera.zoom *= 1.0 / delta;
                } else {

                    camera.zoom *= delta;
                }
            }


@@ 296,8 399,8 @@ impl ApplicationHandler for App {
                                    self.right = true;
                                    matched = true;
                                }
                                _ => matched = false
                            }
                                _ => matched = false,
                            },
                            ElementState::Released => match key {
                                KeyCode::KeyW => {
                                    self.up = false;


@@ 315,16 418,17 @@ impl ApplicationHandler for App {
                                    self.right = false;
                                    matched = true;
                                }
                                _ => matched = false
                            }
                                _ => matched = false,
                            },
                        }
                        if matched {
                            self.send_packet_events.send(SendPacket(Packet::PlayerInput {
                                up: self.up,
                                down: self.down,
                                left: self.left,
                                right: self.right,
                            }));
                            self.send_packet_events
                                .send(SendPacket(Packet::PlayerInput {
                                    up: self.up,
                                    down: self.down,
                                    left: self.left,
                                    right: self.right,
                                }));
                        }
                    }
                    PhysicalKey::Unidentified(_) => {} // unsupported


@@ 340,22 444,30 @@ impl ApplicationHandler for App {
                };
                let camera = self.world.get_resource::<Camera>().unwrap();
                let view = camera.to_cursor_matrix();
                let pos = view * Vector3::new(self.mouse_pos.x as f32, self.mouse_pos.y as f32, 1.0);
                let pos =
                    view * Vector3::new(self.mouse_pos.x as f32, self.mouse_pos.y as f32, 1.0);
                let pos = pos / pos.z;
                self.send_packet_events.send(SendPacket(Packet::PlayerMouseInput {
                    x: pos.x,
                    y: pos.y,
                    released: !state.is_pressed(),
                    button,
                }));
                self.send_packet_events
                    .send(SendPacket(Packet::PlayerMouseInput {
                        x: pos.x,
                        y: pos.y,
                        released: !state.is_pressed(),
                        button,
                    }));
            }
            _ => {}
        }
        let event_response = self.egui_glow.as_mut().unwrap()
        let event_response = self
            .egui_glow
            .as_mut()
            .unwrap()
            .on_window_event(self.window.as_ref().unwrap(), &event);
    }
    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
        let mut ws = self.world.get_resource_mut::<Ws>().expect("Failed to get Ws resource");
        let mut ws = self
            .world
            .get_resource_mut::<Ws>()
            .expect("Failed to get Ws resource");
        #[cfg(target_arch = "wasm32")]
        while let Ok(Some(packet)) = ws.receiver.try_next() {
            self.recv_packet_events.send(RecvPacket(packet));


@@ 367,36 479,61 @@ impl ApplicationHandler for App {
        self.send_packet_events.update();
        self.recv_packet_events.update();

        process_packets(&mut self.world, &mut self.send_packet_events,
            &mut self.recv_packet_events, &mut self.planet_types,
            &mut self.amount, &mut self.max);
        process_packets(
            &mut self.world,
            &mut self.send_packet_events,
            &mut self.recv_packet_events,
            &mut self.planet_types,
            &mut self.amount,
            &mut self.max,
        );

        let window = self.window.as_ref().unwrap();
        let gl = self.gl.as_ref().unwrap();

        let mut player = self.world.query_filtered::<&Transform, With<Player>>();
        let player = player.single(&self.world);
        self.egui_glow.as_mut().unwrap().run(
            self.window.as_ref().unwrap(),
            |ctx| {
                egui::Window::new("Main Menu").resizable(false).show(ctx, |ui| {
                    ui.heading("Starkingdoms.tk");

                    ui.add(Label::new("Fuel:"));
                    ui.add(ProgressBar::new((self.amount as f32)/(self.max as f32)).corner_radius(0));
                    ui.add(Label::new(format!("Pos: {}, {}", player.translation.x as u32, player.translation.y as u32)));
                });
            },
        );
        self.egui_glow
            .as_mut()
            .unwrap()
            .run(self.window.as_ref().unwrap(), |ctx| {
                egui::Window::new("Main Menu")
                    .resizable(false)
                    .show(ctx, |ui| {
                        ui.heading("Starkingdoms.tk");

                        ui.add(Label::new("Fuel:"));
                        ui.add(
                            ProgressBar::new((self.amount as f32) / (self.max as f32))
                                .corner_radius(0),
                        );
                        ui.add(Label::new(format!(
                            "Pos: {}, {}",
                            player.translation.x as u32, player.translation.y as u32
                        )));
                    });
            });

        let camera = self.world.get_resource::<Camera>().unwrap();
        let x_scale = camera.zoom / camera.width as f32 * 2.0;
        let y_scale = camera.zoom / camera.height as f32 * 2.0;
        let view = &[
            x_scale,    0.0,    0.0,    camera.x*x_scale,
            0.0,        y_scale,0.0,    camera.y*y_scale,
            0.0,        0.0,    1.0,    0.0,
            0.0,        0.0,    0.0,    1.0,
            x_scale,
            0.0,
            0.0,
            camera.x * x_scale,
            0.0,
            y_scale,
            0.0,
            camera.y * y_scale,
            0.0,
            0.0,
            1.0,
            0.0,
            0.0,
            0.0,
            0.0,
            1.0,
        ];

        let mut sprite_query = self.world.query::<(&Transform, &mut Texture)>();


@@ 413,7 550,7 @@ impl ApplicationHandler for App {
            gl.bind_buffer(glow::ARRAY_BUFFER, self.vertex_buffer);
            gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, self.element_buffer);
            gl.active_texture(glow::TEXTURE0);
            

            let view_loc = gl.get_uniform_location(self.program.unwrap(), "view");
            gl.uniform_matrix_4_f32_slice(view_loc.as_ref(), true, view);
            let model_loc = gl.get_uniform_location(self.program.unwrap(), "model");


@@ 423,15 560,29 @@ impl ApplicationHandler for App {
                    let assets = self.world.resource::<Assets>();
                    let image = match assets.get(texture.name.clone()) {
                        Some(t) => t,
                        None => continue
                        None => continue,
                    };

                    let texture_object = gl.create_texture().expect("Failed to create texture object");
                    let texture_object = gl
                        .create_texture()
                        .expect("Failed to create texture object");
                    gl.bind_texture(glow::TEXTURE_2D, Some(texture_object));
                    gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR_MIPMAP_LINEAR as i32);
                    gl.tex_image_2d(glow::TEXTURE_2D, 0, glow::RGBA as i32,
                        image.width as i32, image.height as i32, 0, glow::RGBA,
                        glow::UNSIGNED_BYTE, PixelUnpackData::Slice(Some(&image.bytes)));
                    gl.tex_parameter_i32(
                        glow::TEXTURE_2D,
                        glow::TEXTURE_MIN_FILTER,
                        glow::LINEAR_MIPMAP_LINEAR as i32,
                    );
                    gl.tex_image_2d(
                        glow::TEXTURE_2D,
                        0,
                        glow::RGBA as i32,
                        image.width as i32,
                        image.height as i32,
                        0,
                        glow::RGBA,
                        glow::UNSIGNED_BYTE,
                        PixelUnpackData::Slice(Some(&image.bytes)),
                    );
                    gl.generate_mipmap(glow::TEXTURE_2D);

                    self.textures.insert(texture.name.clone(), texture_object);


@@ 447,17 598,31 @@ impl ApplicationHandler for App {
            }
        }

        self.egui_glow.as_mut().unwrap().paint(self.window.as_ref().unwrap());
        self.egui_glow
            .as_mut()
            .unwrap()
            .paint(self.window.as_ref().unwrap());

        #[cfg(not(target_arch = "wasm32"))]
        self.gl_surface.as_ref().unwrap().swap_buffers(self.gl_context.as_ref().unwrap()).unwrap();
        self.gl_surface
            .as_ref()
            .unwrap()
            .swap_buffers(self.gl_context.as_ref().unwrap())
            .unwrap();

        let mut ws = self.world.get_resource_mut::<Ws>().expect("Failed to get Ws resource");
        let mut ws = self
            .world
            .get_resource_mut::<Ws>()
            .expect("Failed to get Ws resource");
        let mut send_event_cursor = self.send_packet_events.get_cursor();
        for event in send_event_cursor.read(&self.send_packet_events) {
            ws.send_packet(event.0.clone());
        }

        event_loop.set_control_flow(ControlFlow::WaitUntil(web_time::Instant::now().checked_add(web_time::Duration::from_millis(16)).unwrap()));
        event_loop.set_control_flow(ControlFlow::WaitUntil(
            web_time::Instant::now()
                .checked_add(web_time::Duration::from_millis(16))
                .unwrap(),
        ));
    }
}