mod incoming_parts;
mod incoming_planets;
mod key_input;
mod starfield;
use crate::client::incoming_parts::incoming_parts_plugin;
use crate::client::incoming_planets::incoming_planets_plugin;
use crate::client::key_input::key_input_plugin;
use crate::client::starfield::starfield_plugin;
use crate::ecs::{CursorWorldCoordinates, MainCamera, Player};
use bevy::core_pipeline::fxaa::Fxaa;
use bevy::prelude::*;
use bevy::window::PrimaryWindow;
use bevy_replicon::prelude::RepliconChannels;
use bevy_replicon::shared::server_entity_map::ServerEntityMap;
use bevy_replicon_renet2::RenetChannelsExt;
#[cfg(not(target_arch = "wasm32"))]
use bevy_replicon_renet2::netcode::NativeSocket;
use bevy_replicon_renet2::netcode::{ClientAuthentication, NetcodeClientTransport};
#[cfg(target_arch = "wasm32")]
use bevy_replicon_renet2::netcode::{ClientSocket, WebSocketClient, WebSocketClientConfig};
use bevy_replicon_renet2::renet2::{ConnectionConfig, RenetClient};
use std::net::{IpAddr, SocketAddr, UdpSocket};
use std::time::SystemTime;
pub struct ClientPlugin {
#[cfg(target_arch = "wasm32")]
pub server: url::Url,
#[cfg(not(target_arch = "wasm32"))]
pub server: SocketAddr,
}
impl Plugin for ClientPlugin {
fn build(&self, app: &mut App) {
let server = self.server;
app.insert_resource(CursorWorldCoordinates(None))
.add_systems(
Startup,
move |mut commands: Commands, channels: Res<RepliconChannels>| {
let current_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
let client_id = current_time.as_millis() as u64;
#[cfg(target_arch = "wasm32")]
{
let socket_config = WebSocketClientConfig {
server_url: server.clone(),
};
let socket = WebSocketClient::new(socket_config).unwrap();
let client = RenetClient::new(
ConnectionConfig::from_channels(
channels.server_configs(),
channels.client_configs(),
),
socket.is_reliable(),
);
let authentication = ClientAuthentication::Unsecure {
socket_id: 1,
server_addr: socket.server_address(),
client_id: current_time.as_millis() as u64,
user_data: None,
protocol_id: 0,
};
let mut transport =
NetcodeClientTransport::new(current_time, authentication, socket)
.unwrap();
commands.insert_resource(client);
commands.insert_resource(transport);
}
#[cfg(not(target_arch = "wasm32"))]
{
let bind = match server.ip() {
IpAddr::V4(_) => "127.0.0.1:0",
IpAddr::V6(_) => "[::1]:0",
};
let client_socket =
NativeSocket::new(UdpSocket::bind(bind).unwrap()).unwrap();
let authentication = ClientAuthentication::Unsecure {
socket_id: 0,
server_addr: server,
client_id: current_time.as_millis() as u64,
user_data: None,
protocol_id: 0,
};
let client = RenetClient::new(
ConnectionConfig::from_channels(
channels.server_configs(),
channels.client_configs(),
),
false,
);
let transport = NetcodeClientTransport::new(
current_time,
authentication,
client_socket,
)
.unwrap();
commands.insert_resource(client);
commands.insert_resource(transport);
}
},
)
.add_systems(Startup, setup_graphics)
.add_systems(Update, update_cursor_position)
.add_systems(Update, follow_camera)
.add_systems(Update, find_me)
.add_plugins(incoming_planets_plugin)
.add_plugins(incoming_parts_plugin)
.add_plugins(key_input_plugin)
.add_plugins(starfield_plugin);
}
}
#[derive(Component)]
pub struct Me;
fn find_me(
mut commands: Commands,
q_clients: Query<(Entity, &Player), Added<Player>>,
entity_map: Res<ServerEntityMap>,
) {
for (entity, player) in q_clients.iter() {
let this_id_clientside = entity_map.to_client().get(&player.client).unwrap();
if *this_id_clientside == entity {
commands.entity(entity).insert(Me);
}
}
}
fn setup_graphics(mut commands: Commands) {
commands
.spawn(Camera2d)
.insert(Camera { ..default() })
.insert(Fxaa::default())
.insert(MainCamera);
}
fn follow_camera(
mut camera: Query<&mut Transform, (With<MainCamera>, Without<Me>)>,
player: Query<&Transform, With<Me>>,
) {
let mut camera = camera.single_mut().unwrap();
let Ok(player) = player.single() else {
return;
};
camera.translation = player.translation;
}
fn update_cursor_position(
q_windows: Query<&Window, With<PrimaryWindow>>,
q_camera: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
mut coords: ResMut<CursorWorldCoordinates>,
) {
let (camera, camera_transform) = q_camera.single().unwrap();
let window = q_windows.single().unwrap();
if let Some(world_position) = window
.cursor_position()
.and_then(|cursor| camera.viewport_to_world(camera_transform, cursor).ok())
.map(|ray| ray.origin.truncate())
{
coords.0 = Some(world_position);
} else {
coords.0 = None;
}
}