placeholder-project

Temp project
git clone git://archive.git.mtrnord.blog/MTRNord/placeholder-project.git
Log | Files | Refs

server.rs (6957B)


      1 use bevy::app::PluginGroupBuilder;
      2 use bevy::prelude::*;
      3 use bevy::utils::Duration;
      4 
      5 use lightyear::prelude::server::*;
      6 use lightyear::prelude::*;
      7 
      8 use crate::networking::shared_movement_behaviour;
      9 
     10 use super::{protocol::*, shared_config, SharedSettings};
     11 
     12 // Plugin group to add all server-related plugins
     13 pub struct ServerPluginGroup {
     14     pub(crate) lightyear: ServerPlugin<MatrixRPGGameProto>,
     15 }
     16 
     17 impl ServerPluginGroup {
     18     pub(crate) fn new(
     19         transport_configs: Vec<TransportConfig>,
     20         shared_settings: SharedSettings,
     21     ) -> ServerPluginGroup {
     22         // Step 1: create the io (transport + link conditioner)
     23         let link_conditioner = LinkConditionerConfig {
     24             incoming_latency: Duration::from_millis(200),
     25             incoming_jitter: Duration::from_millis(20),
     26             incoming_loss: 0.05,
     27         };
     28         let mut net_configs = vec![];
     29         for transport_config in transport_configs {
     30             net_configs.push(NetConfig::Netcode {
     31                 config: NetcodeConfig::default()
     32                     .with_protocol_id(shared_settings.protocol_id)
     33                     .with_key(shared_settings.private_key),
     34                 io: IoConfig::from_transport(transport_config)
     35                     .with_conditioner(link_conditioner.clone()),
     36             });
     37         }
     38 
     39         // Step 2: define the server configuration
     40         let config = ServerConfig {
     41             shared: shared_config(),
     42             net: net_configs,
     43             ..default()
     44         };
     45 
     46         // Step 3: create the plugin
     47         let plugin_config = PluginConfig::new(config, protocol());
     48         ServerPluginGroup {
     49             lightyear: ServerPlugin::new(plugin_config),
     50         }
     51     }
     52 }
     53 
     54 impl PluginGroup for ServerPluginGroup {
     55     fn build(self) -> PluginGroupBuilder {
     56         PluginGroupBuilder::start::<Self>()
     57             .add(self.lightyear)
     58             .add(MatrixRPGServerPlugin)
     59             .add(super::SharedPlugin)
     60     }
     61 }
     62 
     63 // Plugin for server-specific logic
     64 pub struct MatrixRPGServerPlugin;
     65 
     66 impl Plugin for MatrixRPGServerPlugin {
     67     fn build(&self, app: &mut App) {
     68         app.add_systems(Startup, init);
     69         // Re-adding Replicate components to client-replicated entities must be done in this set for proper handling.
     70         app.add_systems(
     71             PreUpdate,
     72             (replicate_players).in_set(MainSet::ClientReplication),
     73         );
     74         // the physics/FixedUpdates systems that consume inputs should be run in this set
     75         app.add_systems(FixedUpdate, movement);
     76         //app.add_systems(Update, send_message);
     77         app.add_systems(Update, handle_disconnections);
     78     }
     79 }
     80 
     81 pub(crate) fn init(mut commands: Commands) {
     82     commands.spawn(Camera2dBundle::default());
     83     commands.spawn(TextBundle::from_section(
     84         "Server",
     85         TextStyle {
     86             font_size: 30.0,
     87             color: Color::WHITE,
     88             ..default()
     89         },
     90     ));
     91 }
     92 
     93 /// Server disconnection system, delete all player entities upon disconnection
     94 pub(crate) fn handle_disconnections(
     95     mut disconnections: EventReader<DisconnectEvent>,
     96     mut commands: Commands,
     97     player_entities: Query<(Entity, &PlayerId)>,
     98 ) {
     99     for disconnection in disconnections.read() {
    100         let client_id = disconnection.context();
    101         for (entity, player_id) in player_entities.iter() {
    102             if player_id.0 == *client_id {
    103                 commands.entity(entity).despawn();
    104             }
    105         }
    106     }
    107 }
    108 
    109 /// Read client inputs and move players
    110 pub(crate) fn movement(
    111     mut position_query: Query<(&mut PlayerPosition, &PlayerId)>,
    112     mut input_reader: EventReader<InputEvent<Inputs>>,
    113     tick_manager: Res<TickManager>,
    114 ) {
    115     for input in input_reader.read() {
    116         let client_id = input.context();
    117         if let Some(input) = input.input() {
    118             debug!(
    119                 "Receiving input: {:?} from client: {:?} on tick: {:?}",
    120                 input,
    121                 client_id,
    122                 tick_manager.tick()
    123             );
    124 
    125             for (position, player_id) in position_query.iter_mut() {
    126                 if player_id.0 == *client_id {
    127                     // NOTE: be careful to directly pass Mut<PlayerPosition>
    128                     // getting a mutable reference triggers change detection, unless you use `as_deref_mut()`
    129                     shared_movement_behaviour(position, input);
    130                 }
    131             }
    132         }
    133     }
    134 }
    135 
    136 // // NOTE: you can use either:
    137 // // - ServerMut (which is a wrapper around a bunch of resources used in lightyear)
    138 // // - ResMut<ConnectionManager>, which is the actual resource used to send the message in this case. This is more optimized
    139 // //   because it enables more parallelism
    140 // /// Send messages from server to clients (only in non-headless mode, because otherwise we run with minimal plugins
    141 // /// and cannot do input handling)
    142 // pub(crate) fn send_message(
    143 //     mut server: ResMut<ServerConnectionManager>,
    144 //     input: Option<Res<ButtonInput<KeyCode>>>,
    145 // ) {
    146 //     if input.is_some_and(|input| input.pressed(KeyCode::KeyM)) {
    147 //         let message = Message1(5);
    148 //         info!("Send message: {:?}", message);
    149 //         server
    150 //             .send_message_to_target::<Channel1, Message1>(Message1(5), NetworkTarget::All)
    151 //             .unwrap_or_else(|e| {
    152 //                 error!("Failed to send message: {:?}", e);
    153 //             });
    154 //     }
    155 // }
    156 
    157 // Replicate the pre-spawned entities back to the client
    158 // Note that this needs to run before FixedUpdate, since we handle client inputs in the FixedUpdate schedule (subject to change)
    159 // And we want to handle deletion properly
    160 pub(crate) fn replicate_players(
    161     mut commands: Commands,
    162     mut player_spawn_reader: EventReader<ComponentInsertEvent<PlayerPosition>>,
    163 ) {
    164     for event in player_spawn_reader.read() {
    165         debug!("received player spawn event: {:?}", event);
    166         let client_id = event.context();
    167         let entity = event.entity();
    168 
    169         // for all cursors we have received, add a Replicate component so that we can start replicating it
    170         // to other clients
    171         if let Some(mut e) = commands.get_entity(entity) {
    172             e.insert(Replicate {
    173                 // we want to replicate back to the original client, since they are using a pre-spawned entity
    174                 replication_target: NetworkTarget::All,
    175                 // NOTE: even with a pre-spawned Predicted entity, we need to specify who will run prediction
    176                 // NOTE: Be careful to not override the pre-spawned prediction! we do not need to enable prediction
    177                 //  because there is a pre-spawned predicted entity
    178                 prediction_target: NetworkTarget::Only(vec![*client_id]),
    179                 // we want the other clients to apply interpolation for the player
    180                 interpolation_target: NetworkTarget::AllExcept(vec![*client_id]),
    181                 ..default()
    182             });
    183         }
    184     }
    185 }