From 6306763f0fa038c9fe8cb3fb74b2774ab5a27b5f Mon Sep 17 00:00:00 2001 From: veronoicc <64193056+veronoicc@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:06:27 +0000 Subject: [PATCH] This is VERY likely not a good way to do it, but it works for now --- azalea-auth/src/account.rs | 2 +- azalea/src/lib.rs | 23 ++++++------- azalea/src/swarm/chat.rs | 16 +++++---- azalea/src/swarm/mod.rs | 68 ++++++++++++++++++++------------------ 4 files changed, 56 insertions(+), 53 deletions(-) diff --git a/azalea-auth/src/account.rs b/azalea-auth/src/account.rs index 7172cec6e..8c962eb15 100644 --- a/azalea-auth/src/account.rs +++ b/azalea-auth/src/account.rs @@ -5,7 +5,7 @@ use uuid::Uuid; use crate::{certs::{Certificates, FetchCertificatesError}, sessionserver::ClientSessionServerError}; -pub trait Account: Clone + Component { +pub trait Account: Send + Sync + Clone + Component { fn join( &self, public_key: &[u8], diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index 84c215d54..0cf2f64ed 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -73,19 +73,20 @@ pub enum StartError { /// # Ok(()) /// # } /// ``` -pub struct ClientBuilder +pub struct ClientBuilder where S: Default + Send + Sync + Clone + Component + 'static, + A: Send + Sync + Clone + auth::account::Account + 'static, { /// Internally, ClientBuilder is just a wrapper over SwarmBuilder since it's /// technically just a subset of it so we can avoid duplicating code this /// way. - swarm: SwarmBuilder, + swarm: SwarmBuilder, } -impl ClientBuilder { +impl ClientBuilder where A: Send + Sync + Clone + auth::account::Account + 'static, { /// Start building a client that can join the world. #[must_use] - pub fn new() -> ClientBuilder { + pub fn new() -> ClientBuilder { Self::new_without_plugins() .add_plugins(DefaultPlugins) .add_plugins(DefaultBotPlugins) @@ -115,7 +116,7 @@ impl ClientBuilder { /// # } /// ``` #[must_use] - pub fn new_without_plugins() -> ClientBuilder { + pub fn new_without_plugins() -> ClientBuilder { Self { swarm: SwarmBuilder::new_without_plugins(), } @@ -138,7 +139,7 @@ impl ClientBuilder { /// } /// ``` #[must_use] - pub fn set_handler(self, handler: HandleFn) -> ClientBuilder + pub fn set_handler(self, handler: HandleFn) -> ClientBuilder where S: Default + Send + Sync + Clone + Component + 'static, Fut: Future> + Send + 'static, @@ -148,9 +149,10 @@ impl ClientBuilder { } } } -impl ClientBuilder +impl ClientBuilder where S: Default + Send + Sync + Clone + Component + 'static, + A: Send + Sync + Clone + auth::account::Account + 'static, { /// Set the client state instead of initializing defaults. #[must_use] @@ -180,7 +182,7 @@ where /// [`ServerAddress`]: azalea_protocol::ServerAddress pub async fn start( mut self, - account: Account, + account: A, address: impl TryInto, ) -> Result { self.swarm.accounts = vec![account]; @@ -190,11 +192,6 @@ where self.swarm.start(address).await } } -impl Default for ClientBuilder { - fn default() -> Self { - Self::new() - } -} /// A marker that can be used in place of a State in [`ClientBuilder`] or /// [`SwarmBuilder`]. You probably don't need to use this manually since the diff --git a/azalea/src/swarm/chat.rs b/azalea/src/swarm/chat.rs index 7425293bf..052596edb 100644 --- a/azalea/src/swarm/chat.rs +++ b/azalea/src/swarm/chat.rs @@ -19,6 +19,7 @@ use crate::ecs::{ schedule::IntoSystemConfigs, system::{Commands, Query, Res, ResMut, Resource}, }; +use azalea_auth::account::Account; use azalea_client::chat::{ChatPacket, ChatReceivedEvent}; use bevy_app::{App, Plugin, Update}; use bevy_ecs::prelude::Event; @@ -27,13 +28,16 @@ use std::collections::VecDeque; use super::{Swarm, SwarmEvent}; #[derive(Clone)] -pub struct SwarmChatPlugin; -impl Plugin for SwarmChatPlugin { +pub struct SwarmChatPlugin where A: Send + Sync + Clone + Account + 'static { + pub account: std::marker::PhantomData, +} + +impl Plugin for SwarmChatPlugin where A: Send + Sync + Clone + Account + 'static { fn build(&self, app: &mut App) { app.add_event::() .add_systems( Update, - (chat_listener, update_min_index_and_shrink_queue).chain(), + (chat_listener, update_min_index_and_shrink_queue::).chain(), ) .insert_resource(GlobalChatState { chat_queue: VecDeque::new(), @@ -113,12 +117,12 @@ fn chat_listener( } } -fn update_min_index_and_shrink_queue( +fn update_min_index_and_shrink_queue( query: Query<&ClientChatState>, mut global_chat_state: ResMut, mut events: EventReader, - swarm: Option>, -) { + swarm: Option>>, +) where A: Send + Sync + Clone + Account + 'static { for event in events.read() { if let Some(swarm) = &swarm { // it should also work if Swarm isn't present (so the tests don't need it) diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index cf557e09e..3dcd7564d 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -14,7 +14,7 @@ use bevy_app::{App, PluginGroup, PluginGroupBuilder, Plugins}; use bevy_ecs::{component::Component, entity::Entity, system::Resource, world::World}; use futures::future::{join_all, BoxFuture}; use parking_lot::{Mutex, RwLock}; -use std::{collections::HashMap, future::Future, net::SocketAddr, sync::Arc, time::Duration}; +use std::{collections::HashMap, future::Future, marker::PhantomData, net::SocketAddr, sync::Arc, time::Duration}; use tokio::sync::mpsc; use tracing::error; @@ -28,7 +28,7 @@ use crate::{BoxHandleFn, DefaultBotPlugins, HandleFn, NoState, StartError}; /// The `S` type parameter is the type of the state for individual bots. /// It's used to make the [`Swarm::add`] function work. #[derive(Clone, Resource)] -pub struct Swarm { +pub struct Swarm where A: Send + Sync + Clone + Account + 'static, { pub ecs_lock: Arc>, bots: Arc>>, @@ -40,7 +40,7 @@ pub struct Swarm { pub instance_container: Arc>, bots_tx: mpsc::UnboundedSender<(Option, Client)>, - swarm_tx: mpsc::UnboundedSender, + swarm_tx: mpsc::UnboundedSender>, run_schedule_sender: mpsc::UnboundedSender<()>, } @@ -50,7 +50,7 @@ pub struct SwarmBuilder where S: Send + Sync + Clone + Component + 'static, SS: Default + Send + Sync + Clone + Resource + 'static, - A: Account + A: Send + Sync + Clone + Account + 'static, { pub(crate) app: App, /// The accounts that are going to join the server. @@ -64,7 +64,7 @@ where pub(crate) handler: Option>, /// The function that's called every time the swarm receives a /// [`SwarmEvent`]. - pub(crate) swarm_handler: Option>, + pub(crate) swarm_handler: Option>, /// How long we should wait between each bot joining the server. Set to /// None to have every bot connect at the same time. None is different than @@ -72,14 +72,14 @@ where /// the previous one to be ready. pub(crate) join_delay: Option, } -impl SwarmBuilder { +impl SwarmBuilder where A: Send + Sync + Clone + Account + 'static { /// Start creating the swarm. #[must_use] - pub fn new() -> SwarmBuilder { + pub fn new() -> SwarmBuilder { Self::new_without_plugins() .add_plugins(DefaultPlugins) .add_plugins(DefaultBotPlugins) - .add_plugins(DefaultSwarmPlugins) + .add_plugins(DefaultSwarmPlugins { _account: PhantomData:: }) } /// [`Self::new`] but without adding the plugins by default. This is useful @@ -108,7 +108,7 @@ impl SwarmBuilder { /// # } /// ``` #[must_use] - pub fn new_without_plugins() -> SwarmBuilder { + pub fn new_without_plugins() -> SwarmBuilder { SwarmBuilder { // we create the app here so plugins can add onto it. // the schedules won't run until [`Self::start`] is called. @@ -123,9 +123,10 @@ impl SwarmBuilder { } } -impl SwarmBuilder +impl SwarmBuilder where SS: Default + Send + Sync + Clone + Resource + 'static, + A: Send + Sync + Clone + Account + 'static, { /// Set the function that's called every time a bot receives an [`Event`]. /// This is the way to handle normal per-bot events. @@ -154,7 +155,7 @@ where /// # } /// ``` #[must_use] - pub fn set_handler(self, handler: HandleFn) -> SwarmBuilder + pub fn set_handler(self, handler: HandleFn) -> SwarmBuilder where Fut: Future> + Send + 'static, S: Send + Sync + Clone + Component + Default + 'static, @@ -170,9 +171,10 @@ where } } -impl SwarmBuilder +impl SwarmBuilder where S: Send + Sync + Clone + Component + 'static, + A: Send + Sync + Clone + Account + 'static { /// Set the function that's called every time the swarm receives a /// [`SwarmEvent`]. This is the way to handle global swarm events. @@ -202,10 +204,11 @@ where /// } /// ``` #[must_use] - pub fn set_swarm_handler(self, handler: SwarmHandleFn) -> SwarmBuilder + pub fn set_swarm_handler(self, handler: SwarmHandleFn) -> SwarmBuilder where SS: Default + Send + Sync + Clone + Resource + 'static, Fut: Future> + Send + 'static, + A: Send + Sync + Clone + Account + 'static, { SwarmBuilder { handler: self.handler, @@ -221,10 +224,11 @@ where } } -impl SwarmBuilder +impl SwarmBuilder where S: Send + Sync + Clone + Component + 'static, SS: Default + Send + Sync + Clone + Resource + 'static, + A: Send + Sync + Clone + Account + 'static, { /// Add a vec of [`Account`]s to the swarm. /// @@ -235,7 +239,7 @@ where /// By default every account will join at the same time, you can add a delay /// with [`Self::join_delay`]. #[must_use] - pub fn add_accounts(mut self, accounts: Vec) -> Self + pub fn add_accounts(mut self, accounts: Vec) -> Self where S: Default, { @@ -250,7 +254,7 @@ where /// This will make the state for this client be the default, use /// [`Self::add_account_with_state`] to avoid that. #[must_use] - pub fn add_account(self, account: impl Account) -> Self + pub fn add_account(self, account: A) -> Self where S: Default, { @@ -259,7 +263,7 @@ where /// Add an account with a custom initial state. Use just /// [`Self::add_account`] to use the Default implementation for the state. #[must_use] - pub fn add_account_with_state(mut self, account: impl Account, state: S) -> Self { + pub fn add_account_with_state(mut self, account: A, state: S) -> Self { self.accounts.push(account); self.states.push(state); self @@ -427,15 +431,10 @@ where } } -impl Default for SwarmBuilder { - fn default() -> Self { - Self::new() - } -} /// An event about something that doesn't have to do with a single bot. #[derive(Clone, Debug)] -pub enum SwarmEvent { +pub enum SwarmEvent where A: Send + Sync + Clone + Account + 'static, { /// All the bots in the swarm have successfully joined the server. Login, /// The swarm was created. This is only fired once, and it's guaranteed to @@ -445,14 +444,14 @@ pub enum SwarmEvent { /// /// You can implement an auto-reconnect by calling [`Swarm::add`] /// with the account from this event. - Disconnect(Box), + Disconnect(Box), /// At least one bot received a chat message. Chat(ChatPacket), } -pub type SwarmHandleFn = fn(Swarm, SwarmEvent, SS) -> Fut; -pub type BoxSwarmHandleFn = - Box BoxFuture<'static, Result<(), anyhow::Error>> + Send>; +pub type SwarmHandleFn = fn(Swarm, SwarmEvent, SS) -> Fut; +pub type BoxSwarmHandleFn = + Box, SwarmEvent, SS) -> BoxFuture<'static, Result<(), anyhow::Error>> + Send>; /// Make a bot [`Swarm`]. /// @@ -515,7 +514,7 @@ pub type BoxSwarmHandleFn = /// Ok(()) /// } -impl Swarm { +impl Swarm where A: Send + Sync + Clone + Account + 'static, { /// Add a new account to the swarm. You can remove it later by calling /// [`Client::disconnect`]. /// @@ -560,7 +559,7 @@ impl Swarm { } cloned_bots.lock().remove(&bot.entity); let account = cloned_bot - .get_component::() + .get_component::() .expect("bot is missing required Account component"); swarm_tx .send(SwarmEvent::Disconnect(Box::new(account))) @@ -597,7 +596,7 @@ impl Swarm { } } -impl IntoIterator for Swarm { +impl IntoIterator for Swarm where A: Send + Sync + Clone + Account + 'static, { type Item = Client; type IntoIter = std::vec::IntoIter; @@ -626,12 +625,15 @@ impl IntoIterator for Swarm { /// This plugin group will add all the default plugins necessary for swarms to /// work. -pub struct DefaultSwarmPlugins; +pub struct DefaultSwarmPlugins where A: Send + Sync + Clone + Account + 'static { + _account: PhantomData, + +} -impl PluginGroup for DefaultSwarmPlugins { +impl PluginGroup for DefaultSwarmPlugins where A: Send + Sync + Clone + Account + 'static { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() - .add(chat::SwarmChatPlugin) + .add(chat::SwarmChatPlugin { account: PhantomData:: }) .add(events::SwarmPlugin) } }