aboutsummaryrefslogtreecommitdiff
path: root/azalea/src/swarm/chat.rs
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2022-11-27 16:25:07 -0600
committerGitHub <noreply@github.com>2022-11-27 16:25:07 -0600
commit631ed63dbdc7167df4de02a55b5c2ef1cea909e9 (patch)
tree104e567c332f2aeb30ea6acefef8c73f9b2f158b /azalea/src/swarm/chat.rs
parent962b9fcaae917c7e5bef718469fba31f6ff7c3cb (diff)
downloadazalea-drasl-631ed63dbdc7167df4de02a55b5c2ef1cea909e9.tar.xz
Swarm (#36)
* make azalea-pathfinder dir * start writing d* lite impl * more work on d* lite * work more on implementing d* lite * full d* lite impl * updated edges * add next() function * add NoPathError * why does dstar lite not work * fix d* lite implementation * make the test actually check the coords * replace while loop with if statement * fix clippy complaints * make W only have to be PartialOrd * fix PartialOrd issues * implement mtd* lite * add a test to mtd* lite * remove normal d* lite * make heuristic only take in one arg * add `success` function * Update README.md * evil black magic to make .entity not need dimension * start adding moves * slightly improve the vec3/position situation new macro that implements all the useful functions * moves stuff * make it compile * update deps in az-pathfinder * make it compile again * more pathfinding stuff * add Bot::look_at * replace EntityMut and EntityRef with just Entity * block pos pathfinding stuff * rename movedirection to walkdirection * execute path every tick * advance path * change az-pf version * make azalea_client keep plugin state * fix Plugins::get * why does it think there is air * start debugging incorrect air * update some From methods to use rem_euclid * start adding swarm * fix deadlock i still don't understand why it was happening but the solution was to keep the Client::player lock for shorter so it didn't overlap with the Client::dimension lock * make lookat actually work probably * fix going too fast * Update main.rs * make a thing immutable * direction_looking_at * fix rotations * import swarm in an example * fix stuff from merge * remove azalea_pathfinder import * delete azalea-pathfinder crate already in azalea::pathfinder module * swarms * start working on shared dimensions * Shared worlds work * start adding Swarm::add_account * add_account works * change "client" to "bot" in some places * Fix issues from merge * Update world.rs * add SwarmEvent::Disconnect(Account) * almost add SwarmEvent::Chat and new plugin system it panics rn * make plugins have to provide the State associated type * improve comments * make fn build slightly cleaner * fix SwarmEvent::Chat * change a println in bot/main.rs * Client::shutdown -> disconnect * polish fix clippy warnings + improve some docs a bit * fix shared worlds* *there's a bug that entities and bots will have their positions exaggerated because the relative movement packet is applied for every entity once per bot * i am being trolled by rust for some reason some stuff is really slow for literally no reason and it makes no sense i am going insane * make world an RwLock again * remove debug messages * fix skipping event ticks unfortunately now sending events is `.send().await?` instead of just `.send()` * fix deadlock + warnings * turns out my floor_mod impl was wrong and i32::rem_euclid has the correct behavior LOL * still errors with lots of bots * make swarm iter & fix new chunks not loading * improve docs * start fixing tests * fix all the tests except the examples i don't know how to exclude them from the tests * improve docs some more
Diffstat (limited to 'azalea/src/swarm/chat.rs')
-rw-r--r--azalea/src/swarm/chat.rs147
1 files changed, 147 insertions, 0 deletions
diff --git a/azalea/src/swarm/chat.rs b/azalea/src/swarm/chat.rs
new file mode 100644
index 00000000..a39632f5
--- /dev/null
+++ b/azalea/src/swarm/chat.rs
@@ -0,0 +1,147 @@
+//! Implements SwarmEvent::Chat
+
+// How the chat event works (to avoid firing the event multiple times):
+// ---
+// There's a shared queue of all the chat messages
+// Each bot contains an index of the farthest message we've seen
+// When a bot receives a chat messages, it looks into the queue to find the
+// earliest instance of the message content that's after the bot's chat index.
+// If it finds it, then its personal index is simply updated. Otherwise, fire
+// the event and add to the queue.
+//
+// To make sure the queue doesn't grow too large, we keep a `chat_min_index`
+// in Swarm that's set to the smallest index of all the bots, and we remove all
+// messages from the queue that are before that index.
+
+use crate::{Swarm, SwarmEvent};
+use async_trait::async_trait;
+use azalea_client::{ChatPacket, Client, Event};
+use parking_lot::Mutex;
+use std::{collections::VecDeque, sync::Arc};
+use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
+
+#[derive(Clone)]
+pub struct Plugin {
+ pub swarm_state: SwarmState,
+ pub tx: UnboundedSender<ChatPacket>,
+}
+
+impl crate::Plugin for Plugin {
+ type State = State;
+
+ fn build(&self) -> State {
+ State {
+ farthest_chat_index: Arc::new(Mutex::new(0)),
+ swarm_state: self.swarm_state.clone(),
+ tx: self.tx.clone(),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct State {
+ pub farthest_chat_index: Arc<Mutex<usize>>,
+ pub tx: UnboundedSender<ChatPacket>,
+ pub swarm_state: SwarmState,
+}
+
+#[derive(Clone)]
+pub struct SwarmState {
+ pub chat_queue: Arc<Mutex<VecDeque<ChatPacket>>>,
+ pub chat_min_index: Arc<Mutex<usize>>,
+ pub rx: Arc<tokio::sync::Mutex<UnboundedReceiver<ChatPacket>>>,
+}
+
+#[async_trait]
+impl crate::PluginState for State {
+ async fn handle(self: Box<Self>, event: Event, _bot: Client) {
+ // we're allowed to access Plugin::swarm_state since it's shared for every bot
+ if let Event::Chat(m) = event {
+ // When a bot receives a chat messages, it looks into the queue to find the
+ // earliest instance of the message content that's after the bot's chat index.
+ // If it finds it, then its personal index is simply updated. Otherwise, fire
+ // the event and add to the queue.
+
+ let mut chat_queue = self.swarm_state.chat_queue.lock();
+ let chat_min_index = self.swarm_state.chat_min_index.lock();
+ let mut farthest_chat_index = self.farthest_chat_index.lock();
+
+ let actual_vec_index = *farthest_chat_index - *chat_min_index;
+
+ // go through the queue and find the first message that's after the bot's index
+ let mut found = false;
+ for (i, msg) in chat_queue.iter().enumerate().skip(actual_vec_index) {
+ if msg == &m {
+ // found the message, update the index
+ *farthest_chat_index = i + *chat_min_index + 1;
+ found = true;
+ break;
+ }
+ }
+
+ if !found {
+ // didn't find the message, so fire the swarm event and add to the queue
+ self.tx
+ .send(m.clone())
+ .expect("failed to send chat message to swarm");
+ chat_queue.push_back(m);
+ *farthest_chat_index = chat_queue.len() - 1 + *chat_min_index;
+ }
+ }
+ }
+}
+
+impl SwarmState {
+ pub fn new<S>(swarm: Swarm<S>) -> (Self, UnboundedSender<ChatPacket>)
+ where
+ S: Send + Sync + Clone + 'static,
+ {
+ let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
+
+ let swarm_state = SwarmState {
+ chat_queue: Arc::new(Mutex::new(VecDeque::new())),
+ chat_min_index: Arc::new(Mutex::new(0)),
+ rx: Arc::new(tokio::sync::Mutex::new(rx)),
+ };
+ tokio::spawn(swarm_state.clone().start(swarm));
+
+ (swarm_state, tx)
+ }
+ async fn start<S>(self, swarm: Swarm<S>)
+ where
+ S: Send + Sync + Clone + 'static,
+ {
+ // it should never be locked unless we reused the same plugin for two swarms (bad)
+ let mut rx = self.rx.lock().await;
+ while let Some(m) = rx.recv().await {
+ swarm.swarm_tx.send(SwarmEvent::Chat(m)).unwrap();
+
+ // To make sure the queue doesn't grow too large, we keep a `chat_min_index`
+ // in Swarm that's set to the smallest index of all the bots, and we remove all
+ // messages from the queue that are before that index.
+
+ let chat_min_index = *self.chat_min_index.lock();
+ let mut new_chat_min_index = usize::MAX;
+ for (bot, _) in swarm.bot_datas.lock().iter() {
+ let this_farthest_chat_index = *bot
+ .plugins
+ .get::<State>()
+ .expect("Chat plugin not installed")
+ .farthest_chat_index
+ .lock();
+ if this_farthest_chat_index < new_chat_min_index {
+ new_chat_min_index = this_farthest_chat_index;
+ }
+ }
+
+ let mut chat_queue = self.chat_queue.lock();
+ // remove all messages from the queue that are before the min index
+ for _ in 0..(new_chat_min_index - chat_min_index) {
+ chat_queue.pop_front();
+ }
+
+ // update the min index
+ *self.chat_min_index.lock() = new_chat_min_index;
+ }
+ }
+}