bevy-nest 0.7.0

🪹 A telnet plugin for getting MUDdy in Bevy.
Documentation
use std::time::Duration;

use bevy::app::ScheduleRunnerPlugin;
use bevy::log::LogPlugin;
use bevy::prelude::*;
use bevy::time::TimePlugin;
use bevy_nest::prelude::*;

#[derive(Component)]
struct Player(ClientId);

#[derive(Resource)]
struct WhoTimer(Timer);

fn setup_network(server: Res<Server>) {
    server.listen("127.0.0.1:4000");
}

fn handle_events(
    mut commands: Commands,
    mut events: MessageReader<NetworkEvent>,
    mut outbox: MessageWriter<Outbox>,
    players: Query<(Entity, &Player)>,
) {
    for event in events.read() {
        match event {
            NetworkEvent::Connected(id) => {
                commands.spawn(Player(*id));

                for (_, player) in players.iter() {
                    outbox.write_text(player.0, format!("{id:?} connected"));
                }
            }
            NetworkEvent::Disconnected(id) => {
                if let Some((entity, _)) = players.iter().find(|(_, c)| c.0 == *id) {
                    commands.entity(entity).despawn();

                    for (_, player) in players.iter() {
                        outbox.write_text(player.0, format!("{id:?} disconnected"));
                    }
                }
            }
            NetworkEvent::Error(error) => {
                error!("Network Error: {error:?}");
            }
        }
    }
}

fn handle_frames(
    mut inbox: MessageReader<Inbox>,
    mut outbox: MessageWriter<Outbox>,
    players: Query<(Entity, &Player)>,
) {
    for frame in inbox.read() {
        if let Frame::Text(text) = &frame.content {
            let clean = text.trim_end_matches(['\r', '\n']);

            if clean.is_empty() {
                continue;
            }

            for (_, player) in players.iter() {
                outbox.write_text(player.0, format!("{:?}: {clean}", frame.from));
            }
        }
    }
}

struct OnlineCount(usize);

impl From<OnlineCount> for Payload {
    fn from(online_count: OnlineCount) -> Self {
        Payload {
            package: "chat".into(),
            subpackage: Some("who".into()),
            data: Some(online_count.0.to_string()),
        }
    }
}

fn who_online(
    time: Res<Time>,
    mut who_timer: ResMut<WhoTimer>,
    mut outbox: MessageWriter<Outbox>,
    players: Query<&Player>,
) {
    if who_timer.0.tick(time.delta()).just_finished() {
        for player in players.iter() {
            outbox.write_gmcp(player.0, OnlineCount(players.iter().len()).into());
        }
    }
}

fn main() {
    App::new()
        .insert_resource(WhoTimer(Timer::new(
            Duration::from_secs(3),
            TimerMode::Repeating,
        )))
        .add_plugins((
            LogPlugin::default(),
            TaskPoolPlugin::default(),
            TimePlugin,
            ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1.0 / 60.0)),
            NestPlugin,
        ))
        .add_systems(Startup, setup_network)
        .add_systems(Update, (handle_events, handle_frames, who_online))
        .run();
}