Client/Server Feature Split (#63)
This commit is contained in:
@@ -4,11 +4,13 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
client = ["lightyear/client"]
|
||||
server = ["lightyear/server"]
|
||||
dbg = []
|
||||
|
||||
[dependencies]
|
||||
avian3d = { workspace = true }
|
||||
bevy = { workspace = true }
|
||||
bevy = { workspace = true, default-features = false }
|
||||
bevy-inspector-egui = { workspace = true, optional = true }
|
||||
bevy-steamworks = { workspace = true }
|
||||
bevy-ui-gradients = { workspace = true }
|
||||
|
||||
@@ -6,14 +6,11 @@ use crate::{
|
||||
physics_layers::GameLayer,
|
||||
protocol::GltfSceneRoot,
|
||||
tb_entities::EnemySpawn,
|
||||
utils::{
|
||||
auto_rotate::AutoRotation,
|
||||
commands::{CommandExt, EntityCommandExt},
|
||||
global_observer,
|
||||
},
|
||||
utils::{auto_rotate::AutoRotation, commands::CommandExt, global_observer},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
#[cfg(feature = "server")]
|
||||
use lightyear::prelude::{NetworkTarget, Replicate};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
@@ -63,29 +60,30 @@ fn on_trigger_missile(
|
||||
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
transform.translation += transform.forward().as_vec3() * 2.0;
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
Name::new("projectile-missile"),
|
||||
CurverProjectile {
|
||||
time: time.elapsed_secs(),
|
||||
damage: head.damage,
|
||||
},
|
||||
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
Sensor,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
transform,
|
||||
children![(
|
||||
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
||||
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
|
||||
GltfSceneRoot::Projectile(head.projectile.clone()),
|
||||
),],
|
||||
))
|
||||
.insert_server(Replicate::to_clients(NetworkTarget::All));
|
||||
let mut _projectile = commands.spawn((
|
||||
Name::new("projectile-missile"),
|
||||
CurverProjectile {
|
||||
time: time.elapsed_secs(),
|
||||
damage: head.damage,
|
||||
},
|
||||
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
Sensor,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
transform,
|
||||
children![(
|
||||
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
||||
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
|
||||
GltfSceneRoot::Projectile(head.projectile.clone()),
|
||||
),],
|
||||
));
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
_projectile.insert(Replicate::to_clients(NetworkTarget::All));
|
||||
}
|
||||
|
||||
fn enemy_hit(
|
||||
|
||||
@@ -6,15 +6,11 @@ use crate::{
|
||||
physics_layers::GameLayer,
|
||||
protocol::GltfSceneRoot,
|
||||
sounds::PlaySound,
|
||||
utils::{
|
||||
commands::{CommandExt, EntityCommandExt},
|
||||
explosions::Explosion,
|
||||
global_observer,
|
||||
trail::Trail,
|
||||
},
|
||||
utils::{commands::CommandExt, explosions::Explosion, global_observer, trail::Trail},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
#[cfg(feature = "server")]
|
||||
use lightyear::prelude::{NetworkTarget, Replicate};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
@@ -63,46 +59,46 @@ fn on_trigger_missile(
|
||||
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
transform.translation += transform.forward().as_vec3() * 2.0;
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
Name::new("projectile-missile"),
|
||||
MissileProjectile {
|
||||
time: time.elapsed_secs(),
|
||||
damage: head.damage,
|
||||
},
|
||||
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
let mut _projectile = commands.spawn((
|
||||
Name::new("projectile-missile"),
|
||||
MissileProjectile {
|
||||
time: time.elapsed_secs(),
|
||||
damage: head.damage,
|
||||
},
|
||||
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
Sensor,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
transform,
|
||||
children![
|
||||
(
|
||||
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
||||
GltfSceneRoot::Projectile("missile".to_string()),
|
||||
),
|
||||
Sensor,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
transform,
|
||||
children![
|
||||
(
|
||||
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
||||
GltfSceneRoot::Projectile("missile".to_string()),
|
||||
),
|
||||
(
|
||||
Trail::new(
|
||||
12,
|
||||
LinearRgba::rgb(1., 0.0, 0.),
|
||||
LinearRgba::rgb(0.9, 0.9, 0.)
|
||||
)
|
||||
.with_pos(transform.translation),
|
||||
Gizmo {
|
||||
handle: gizmo_assets.add(GizmoAsset::default()),
|
||||
line_config: GizmoLineConfig {
|
||||
width: 10.,
|
||||
..default()
|
||||
},
|
||||
(
|
||||
Trail::new(
|
||||
12,
|
||||
LinearRgba::rgb(1., 0.0, 0.),
|
||||
LinearRgba::rgb(0.9, 0.9, 0.)
|
||||
)
|
||||
.with_pos(transform.translation),
|
||||
Gizmo {
|
||||
handle: gizmo_assets.add(GizmoAsset::default()),
|
||||
line_config: GizmoLineConfig {
|
||||
width: 10.,
|
||||
..default()
|
||||
},
|
||||
)
|
||||
],
|
||||
))
|
||||
.insert_server(Replicate::to_clients(NetworkTarget::All));
|
||||
..default()
|
||||
},
|
||||
)
|
||||
],
|
||||
));
|
||||
#[cfg(feature = "server")]
|
||||
_projectile.insert(Replicate::to_clients(NetworkTarget::All));
|
||||
}
|
||||
|
||||
fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) {
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
physics_layers::GameLayer,
|
||||
player::{Player, PlayerBodyMesh},
|
||||
sounds::PlaySound,
|
||||
utils::{billboards::Billboard, commands::IsServer, sprite_3d_animation::AnimationTimer},
|
||||
utils::{billboards::Billboard, sprite_3d_animation::AnimationTimer},
|
||||
};
|
||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||
@@ -126,11 +126,10 @@ fn on_trigger_state(
|
||||
player: Query<(&ActiveHead, &ActionState<ControlState>), With<Player>>,
|
||||
headdb: Res<HeadsDatabase>,
|
||||
time: Res<Time>,
|
||||
is_server: Option<Res<IsServer>>,
|
||||
client: Query<&Client>,
|
||||
) {
|
||||
if let Ok(client) = client.single()
|
||||
&& (client.state == ClientState::Connected && is_server.is_none())
|
||||
&& (client.state == ClientState::Connected && cfg!(not(feature = "server")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -7,15 +7,13 @@ use crate::{
|
||||
protocol::GltfSceneRoot,
|
||||
sounds::PlaySound,
|
||||
utils::{
|
||||
auto_rotate::AutoRotation,
|
||||
commands::{CommandExt, EntityCommandExt},
|
||||
explosions::Explosion,
|
||||
global_observer,
|
||||
auto_rotate::AutoRotation, commands::CommandExt, explosions::Explosion, global_observer,
|
||||
},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
use bevy_ballistic::launch_velocity;
|
||||
#[cfg(feature = "server")]
|
||||
use lightyear::prelude::{NetworkTarget, Replicate};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::f32::consts::PI;
|
||||
@@ -62,31 +60,32 @@ fn on_trigger_thrown(
|
||||
//TODO: projectile db?
|
||||
let explosion_animation = !matches!(state.head, 8 | 16);
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
Transform::from_translation(pos),
|
||||
Name::new("projectile-thrown"),
|
||||
ThrownProjectile {
|
||||
impact_animation: explosion_animation,
|
||||
damage: head.damage,
|
||||
},
|
||||
Collider::sphere(0.4),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
RigidBody::Dynamic,
|
||||
CollisionEventsEnabled,
|
||||
Mass(0.01),
|
||||
LinearVelocity(vel),
|
||||
Visibility::default(),
|
||||
Sensor,
|
||||
))
|
||||
.insert_server(Replicate::to_clients(NetworkTarget::All))
|
||||
.with_child((
|
||||
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
|
||||
GltfSceneRoot::Projectile(head.projectile.clone()),
|
||||
));
|
||||
let mut projectile = commands.spawn((
|
||||
Transform::from_translation(pos),
|
||||
Name::new("projectile-thrown"),
|
||||
ThrownProjectile {
|
||||
impact_animation: explosion_animation,
|
||||
damage: head.damage,
|
||||
},
|
||||
Collider::sphere(0.4),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
RigidBody::Dynamic,
|
||||
CollisionEventsEnabled,
|
||||
Mass(0.01),
|
||||
LinearVelocity(vel),
|
||||
Visibility::default(),
|
||||
Sensor,
|
||||
));
|
||||
projectile.with_child((
|
||||
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
|
||||
GltfSceneRoot::Projectile(head.projectile.clone()),
|
||||
));
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
projectile.insert(Replicate::to_clients(NetworkTarget::All));
|
||||
}
|
||||
|
||||
fn shot_collision(
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::{
|
||||
animation::AnimationFlags,
|
||||
control::{controller_common::MovementSpeedFactor, controls::ControllerSettings},
|
||||
player::PlayerBodyMesh,
|
||||
utils::commands::IsServer,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use happy_feet::prelude::{Grounding, KinematicVelocity, MoveInput};
|
||||
@@ -50,7 +49,6 @@ fn apply_controls(
|
||||
&ActionState<ControlState>,
|
||||
)>,
|
||||
rig_transform_q: Option<Single<&GlobalTransform, With<PlayerBodyMesh>>>,
|
||||
is_server: Option<Res<IsServer>>,
|
||||
) {
|
||||
let Ok((
|
||||
mut move_input,
|
||||
@@ -80,7 +78,7 @@ fn apply_controls(
|
||||
move_input.set(direction * move_factor.0);
|
||||
|
||||
if controls.jump && grounding.is_grounded() {
|
||||
if is_server.is_some() {
|
||||
if cfg!(feature = "server") {
|
||||
flags.jumping = true;
|
||||
flags.jump_count += 1;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use super::{ControlState, Controls};
|
||||
#[cfg(feature = "client")]
|
||||
use crate::player::Player;
|
||||
use crate::{
|
||||
GameState,
|
||||
control::{CharacterInputEnabled, ControllerSet},
|
||||
player::Player,
|
||||
};
|
||||
use bevy::{
|
||||
input::{
|
||||
@@ -12,7 +13,8 @@ use bevy::{
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
use lightyear::prelude::{client::input::InputSet::WriteClientInputs, input::native::ActionState};
|
||||
#[cfg(feature = "client")]
|
||||
use lightyear::prelude::input::native::ActionState;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
@@ -38,7 +40,14 @@ pub fn plugin(app: &mut App) {
|
||||
),
|
||||
);
|
||||
|
||||
app.add_systems(FixedPreUpdate, buffer_inputs.in_set(WriteClientInputs));
|
||||
#[cfg(feature = "client")]
|
||||
{
|
||||
use lightyear::prelude::client::input::InputSet;
|
||||
app.add_systems(
|
||||
FixedPreUpdate,
|
||||
buffer_inputs.in_set(InputSet::WriteClientInputs),
|
||||
);
|
||||
}
|
||||
|
||||
app.add_systems(
|
||||
Update,
|
||||
@@ -53,6 +62,7 @@ pub struct ControllerSettings {
|
||||
pub jump_force: f32,
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
/// Write inputs from combined keyboard/gamepad state into the networked input buffer
|
||||
/// for the local player.
|
||||
fn buffer_inputs(
|
||||
|
||||
@@ -52,5 +52,6 @@ pub enum GameState {
|
||||
#[default]
|
||||
AssetLoading,
|
||||
MapLoading,
|
||||
Connecting,
|
||||
Playing,
|
||||
}
|
||||
|
||||
@@ -15,9 +15,17 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
))
|
||||
.observe(
|
||||
|_t: Trigger<SceneCollidersReady>,
|
||||
mut next_game_state: ResMut<NextState<GameState>>| {
|
||||
#[cfg(any(feature = "client", feature = "server"))] mut next_game_state: ResMut<
|
||||
NextState<GameState>,
|
||||
>| {
|
||||
info!("map loaded");
|
||||
|
||||
assert!(cfg!(feature = "client") ^ cfg!(feature = "server"));
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
next_game_state.set(GameState::Connecting);
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
next_game_state.set(GameState::Playing);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -12,13 +12,14 @@ use crate::{
|
||||
loading_assets::GameAssets,
|
||||
sounds::PlaySound,
|
||||
tb_entities::EnemySpawn,
|
||||
utils::{
|
||||
billboards::Billboard,
|
||||
commands::{EntityCommandExt, IsServer},
|
||||
},
|
||||
utils::billboards::Billboard,
|
||||
};
|
||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||
use lightyear::prelude::{Client, Connected, Disconnected, NetworkTarget, Replicate};
|
||||
use lightyear::prelude::Disconnected;
|
||||
#[cfg(feature = "client")]
|
||||
use lightyear::prelude::{Client, Connected};
|
||||
#[cfg(feature = "server")]
|
||||
use lightyear::prelude::{NetworkTarget, Replicate};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -56,20 +57,26 @@ pub fn plugin(app: &mut App) {
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
is_server: Option<Res<IsServer>>,
|
||||
client: Query<(Option<&Connected>, Option<&Disconnected>), With<Client>>,
|
||||
#[cfg(feature = "client")] client: Query<
|
||||
(Option<&Connected>, Option<&Disconnected>),
|
||||
With<Client>,
|
||||
>,
|
||||
mut spawned: Local<bool>,
|
||||
) {
|
||||
if *spawned {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_server.is_some() {
|
||||
#[cfg(feature = "server")]
|
||||
{
|
||||
commands.init_resource::<NpcSpawning>();
|
||||
commands.trigger(OnCheckSpawns { on_client: false });
|
||||
|
||||
*spawned = true;
|
||||
} else if let Ok((connected, disconnected)) = client.single()
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
if let Ok((connected, disconnected)) = client.single()
|
||||
&& (connected.is_some() || disconnected.is_some())
|
||||
{
|
||||
commands.init_resource::<NpcSpawning>();
|
||||
@@ -87,9 +94,8 @@ fn on_spawn_check(
|
||||
query: Query<(Entity, &EnemySpawn, &Transform), Without<Npc>>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
spawning: Res<NpcSpawning>,
|
||||
is_server: Option<Res<IsServer>>,
|
||||
) {
|
||||
if is_server.is_none() && !trigger.event().on_client {
|
||||
if cfg!(not(feature = "server")) && !trigger.event().on_client {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,8 +113,8 @@ fn on_spawn_check(
|
||||
}
|
||||
|
||||
let id = names[&spawn.head];
|
||||
commands
|
||||
.entity(e)
|
||||
let mut ecommands = commands.entity(e);
|
||||
ecommands
|
||||
.insert((
|
||||
Hitpoints::new(100),
|
||||
Npc,
|
||||
@@ -122,9 +128,10 @@ fn on_spawn_check(
|
||||
]),
|
||||
))
|
||||
.insert_if(Ai, || !spawn.disable_ai)
|
||||
.insert_server(Replicate::to_clients(NetworkTarget::All))
|
||||
.with_child((Name::from("body-rig"), AnimatedCharacter::new(id)))
|
||||
.observe(on_kill);
|
||||
#[cfg(feature = "server")]
|
||||
ecommands.insert(Replicate::to_clients(NetworkTarget::All));
|
||||
|
||||
commands.trigger(SpawnCharacter(transform.translation));
|
||||
commands.trigger(PlaySound::Beaming);
|
||||
@@ -133,7 +140,6 @@ fn on_spawn_check(
|
||||
|
||||
fn on_kill(
|
||||
trigger: Trigger<Kill>,
|
||||
is_server: Option<Res<IsServer>>,
|
||||
disconnected: Option<Single<&Disconnected>>,
|
||||
mut commands: Commands,
|
||||
query: Query<(&Transform, &EnemySpawn, &ActiveHead)>,
|
||||
@@ -150,7 +156,7 @@ fn on_kill(
|
||||
|
||||
commands.trigger(HeadDrops::new(transform.translation, head.0));
|
||||
commands.trigger(OnCheckSpawns {
|
||||
on_client: is_server.is_some() || disconnected.is_some(),
|
||||
on_client: cfg!(feature = "server") || disconnected.is_some(),
|
||||
});
|
||||
|
||||
commands.entity(trigger.target()).despawn();
|
||||
|
||||
@@ -15,7 +15,6 @@ use crate::{
|
||||
npc::SpawnCharacter,
|
||||
sounds::PlaySound,
|
||||
tb_entities::SpawnPoint,
|
||||
utils::commands::EntityCommandExt,
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{
|
||||
@@ -23,9 +22,9 @@ use bevy::{
|
||||
prelude::*,
|
||||
window::{CursorGrabMode, PrimaryWindow},
|
||||
};
|
||||
use lightyear::prelude::{
|
||||
ControlledBy, Lifetime, NetworkTarget, PredictionTarget, Replicate, input::native::ActionState,
|
||||
};
|
||||
use lightyear::prelude::input::native::ActionState;
|
||||
#[cfg(feature = "server")]
|
||||
use lightyear::prelude::{ControlledBy, Lifetime, NetworkTarget, PredictionTarget, Replicate};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Component, Default, Serialize, Deserialize, PartialEq)]
|
||||
@@ -53,7 +52,7 @@ pub fn plugin(app: &mut App) {
|
||||
|
||||
pub fn spawn(
|
||||
mut commands: Commands,
|
||||
owner: Entity,
|
||||
#[cfg(feature = "server")] owner: Entity,
|
||||
query: Query<&Transform, With<SpawnPoint>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
@@ -64,17 +63,18 @@ pub fn spawn(
|
||||
|
||||
let transform = Transform::from_translation(spawn.translation + Vec3::new(0., 3., 0.));
|
||||
|
||||
commands
|
||||
.spawn(player_bundle(transform, &heads_db))
|
||||
.insert_server((
|
||||
Replicate::to_clients(NetworkTarget::All),
|
||||
PredictionTarget::to_clients(NetworkTarget::All),
|
||||
ControlledBy {
|
||||
owner,
|
||||
lifetime: Lifetime::SessionBased,
|
||||
},
|
||||
))
|
||||
.observe(on_kill);
|
||||
let mut player = commands.spawn(player_bundle(transform, &heads_db));
|
||||
player.observe(on_kill);
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
player.insert((
|
||||
Replicate::to_clients(NetworkTarget::All),
|
||||
PredictionTarget::to_clients(NetworkTarget::All),
|
||||
ControlledBy {
|
||||
owner,
|
||||
lifetime: Lifetime::SessionBased,
|
||||
},
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
AudioPlayer::new(asset_server.load("sfx/heads/angry demonstrator.ogg")),
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
use bevy::ecs::{
|
||||
bundle::Bundle,
|
||||
event::Event,
|
||||
resource::Resource,
|
||||
system::{Commands, EntityCommands},
|
||||
world::{EntityWorldMut, World},
|
||||
};
|
||||
use lightyear::prelude::Disconnected;
|
||||
|
||||
#[derive(Default, Resource)]
|
||||
pub struct IsServer;
|
||||
|
||||
pub trait CommandExt {
|
||||
fn trigger_server(&mut self, event: impl Event) -> &mut Self;
|
||||
}
|
||||
@@ -18,7 +13,7 @@ impl<'w, 's> CommandExt for Commands<'w, 's> {
|
||||
fn trigger_server(&mut self, event: impl Event) -> &mut Self {
|
||||
self.queue(|world: &mut World| {
|
||||
let mut query_state = world.query::<&Disconnected>();
|
||||
if world.contains_resource::<IsServer>() || !query_state.query(world).is_empty() {
|
||||
if cfg!(feature = "server") || !query_state.query(world).is_empty() {
|
||||
world.trigger(event);
|
||||
}
|
||||
});
|
||||
@@ -27,23 +22,14 @@ impl<'w, 's> CommandExt for Commands<'w, 's> {
|
||||
}
|
||||
|
||||
pub trait EntityCommandExt {
|
||||
fn insert_server(&mut self, bundle: impl Bundle) -> &mut Self;
|
||||
|
||||
fn trigger_server(&mut self, event: impl Event) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<'w> EntityCommandExt for EntityCommands<'w> {
|
||||
fn insert_server(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||
self.queue(|mut entity: EntityWorldMut| {
|
||||
if entity.world().contains_resource::<IsServer>() {
|
||||
entity.insert(bundle);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn trigger_server(&mut self, event: impl Event) -> &mut Self {
|
||||
self.queue(|mut entity: EntityWorldMut| {
|
||||
if entity.world().contains_resource::<IsServer>() {
|
||||
let mut query_state = entity.world_scope(|world| world.query::<&Disconnected>());
|
||||
if cfg!(feature = "server") || !query_state.query(entity.world()).is_empty() {
|
||||
entity.trigger(event);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::utils::commands::IsServer;
|
||||
use bevy::{ecs::system::SystemParam, prelude::*};
|
||||
use lightyear::prelude::{AppTriggerExt, Channel, NetworkDirection, RemoteTrigger, TriggerSender};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -6,12 +5,11 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(SystemParam)]
|
||||
pub struct ServerMultiTriggerSender<'w, 's, M: Event + Clone> {
|
||||
senders: Query<'w, 's, &'static mut TriggerSender<M>>,
|
||||
is_server: Option<Res<'w, IsServer>>,
|
||||
}
|
||||
|
||||
impl<'w, 's, M: Event + Clone> ServerMultiTriggerSender<'w, 's, M> {
|
||||
pub fn server_trigger_targets<C: Channel>(&mut self, trigger: M, target: &[Entity]) {
|
||||
if self.is_server.is_none() {
|
||||
if cfg!(not(feature = "server")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user