aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xCargo.lock51
-rwxr-xr-xREADME.md18
-rwxr-xr-xazalea-client/Cargo.toml1
-rwxr-xr-xazalea-client/README.md2
-rwxr-xr-xazalea-client/src/connect.rs332
-rwxr-xr-xazalea-client/src/lib.rs4
-rw-r--r--azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs1
-rw-r--r--bot/src/main.rs12
8 files changed, 266 insertions, 155 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 144a840b..a1e4d3f0 100755
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -68,9 +68,9 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "azalea-auth"
@@ -104,6 +104,7 @@ version = "0.1.0"
dependencies = [
"azalea-auth",
"azalea-protocol",
+ "tokio",
]
[[package]]
@@ -532,7 +533,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
- "wasi",
+ "wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
@@ -596,7 +597,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
dependencies = [
- "socket2",
+ "socket2 0.3.19",
"widestring",
"winapi",
"winreg",
@@ -640,9 +641,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.109"
+version = "0.2.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01"
+checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
[[package]]
name = "linked-hash-map"
@@ -652,10 +653,11 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
-version = "0.4.5"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
+checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
+ "autocfg",
"scopeguard",
]
@@ -716,14 +718,15 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.7.14"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
+checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
"winapi",
]
@@ -843,6 +846,12 @@ dependencies = [
]
[[package]]
+name = "once_cell"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+
+[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1204,6 +1213,16 @@ dependencies = [
]
[[package]]
+name = "socket2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
name = "syn"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1270,16 +1289,18 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
-version = "1.15.0"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
+checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
+ "once_cell",
"pin-project-lite",
+ "socket2 0.4.4",
"tokio-macros",
"winapi",
]
@@ -1435,6 +1456,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
name = "wasm-bindgen"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/README.md b/README.md
index 6502b0ae..10e94325 100755
--- a/README.md
+++ b/README.md
@@ -17,15 +17,15 @@ I named this Azalea because it sounds like a cool word and this is a cool librar
Note that this doesn't work yet, it's just how I want the API to look.
```rs
-use azalea::{Bot, Event};
+use azalea::{Account, Event};
-let bot = Bot::offline("bot");
-// or let bot = azalea::Bot::microsoft("access token").await;
+let account = Account::offline("bot");
+// or let account = azalea::Account::microsoft("access token").await;
-bot.join("localhost".try_into().unwrap()).await.unwrap();
+let bot = account.join("localhost".try_into().unwrap()).await.unwrap();
loop {
- match bot.recv().await {
+ match bot.next().await {
Event::Message(m) {
if m.username == bot.username { return };
bot.chat(m.message).await;
@@ -42,17 +42,17 @@ loop {
You can use the `azalea::Bots` struct to control many bots as one unit.
```rs
-use azalea::{Bot, Bots, Event, pathfinder};
+use azalea::{Account, Accounts, Event, pathfinder};
#[tokio::main]
async fn main() {
- let bots = Bots::new();
+ let accounts = Accounts::new();
for i in 0..10 {
- bots.add(Bot::offline(format!("bot{}", i)));
+ accounts.add(Account::offline(format!("bot{}", i)));
}
- bots.join("localhost".try_into().unwrap()).await.unwrap();
+ let bots = accounts.join("localhost".try_into().unwrap()).await.unwrap();
bots.goto(pathfinder::GotoGoal(azalea::BlockCoord(0, 70, 0))).await;
diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml
index 5769f289..d8477ea1 100755
--- a/azalea-client/Cargo.toml
+++ b/azalea-client/Cargo.toml
@@ -8,3 +8,4 @@ version = "0.1.0"
[dependencies]
azalea-auth = {path = "../azalea-auth"}
azalea-protocol = {path = "../azalea-protocol"}
+tokio = {version = "1.18.0", features = ["sync"]}
diff --git a/azalea-client/README.md b/azalea-client/README.md
index dd42a6a4..9b51356e 100755
--- a/azalea-client/README.md
+++ b/azalea-client/README.md
@@ -1,3 +1,3 @@
# Azalea Client
-A library that can mimic everything a normal Minecraft client can do. If you want to make a bot, you should use `azalea` instead since it contains utilities for that.
+A library that can mimic everything a normal Minecraft client can do. If you want to make a bot with higher-level functions, you should use `azalea` instead.
diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs
index d7345f68..2098d6a3 100755
--- a/azalea-client/src/connect.rs
+++ b/azalea-client/src/connect.rs
@@ -1,6 +1,5 @@
-///! Connect to Minecraft servers.
use azalea_protocol::{
- connect::HandshakeConnection,
+ connect::{GameConnection, HandshakeConnection},
packets::{
game::GamePacket,
handshake::client_intention_packet::ClientIntentionPacket,
@@ -12,140 +11,219 @@ use azalea_protocol::{
},
resolver, ServerAddress,
};
+use futures::FutureExt;
+use std::{
+ borrow::BorrowMut,
+ cell::RefCell,
+ future::Future,
+ pin::Pin,
+ sync::{Arc, Mutex, Weak},
+};
+use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
-pub async fn join_server(address: &ServerAddress) -> Result<(), String> {
- let username = "bot".to_string();
+///! Connect to Minecraft servers.
- let resolved_address = resolver::resolve_address(address).await?;
+/// Something that can join Minecraft servers.
+pub struct Account {
+ username: String,
+}
- let mut conn = HandshakeConnection::new(&resolved_address).await?;
+pub struct ClientState {
+ // placeholder
+ pub health: u16,
+}
- // handshake
- conn.write(
- ClientIntentionPacket {
- protocol_version: PROTOCOL_VERSION,
- hostname: address.host.clone(),
- port: address.port,
- intention: ConnectionProtocol::Login,
- }
- .get(),
- )
- .await;
- let mut conn = conn.login();
-
- // login
- conn.write(ServerboundHelloPacket { username }.get()).await;
-
- let mut conn = loop {
- let packet_result = conn.read().await;
- match packet_result {
- Ok(packet) => match packet {
- LoginPacket::ClientboundHelloPacket(p) => {
- println!("Got encryption request {:?} {:?}", p.nonce, p.public_key);
- let e = azalea_auth::encryption::encrypt(&p.public_key, &p.nonce).unwrap();
-
- // TODO: authenticate with the server here (authenticateServer)
- println!("Sending encryption response {:?}", e);
-
- conn.write(
- ServerboundKeyPacket {
- nonce: e.encrypted_nonce.into(),
- shared_secret: e.encrypted_public_key.into(),
- }
- .get(),
- )
- .await;
- conn.set_encryption_key(e.secret_key);
- }
- LoginPacket::ClientboundLoginCompressionPacket(p) => {
- println!("Got compression request {:?}", p.compression_threshold);
- conn.set_compression_threshold(p.compression_threshold);
- }
- LoginPacket::ClientboundGameProfilePacket(p) => {
- println!("Got profile {:?}", p.game_profile);
- break conn.game();
- }
- LoginPacket::ServerboundHelloPacket(p) => {
- println!("Got hello {:?}", p);
- }
- LoginPacket::ClientboundLoginDisconnectPacket(p) => {
- println!("Got disconnect {:?}", p);
- }
- LoginPacket::ClientboundCustomQueryPacket(p) => {
- println!("Got custom query {:?}", p);
- }
- LoginPacket::ServerboundKeyPacket(_) => todo!(),
- },
- Err(e) => {
- panic!("Error: {:?}", e);
+/// A player that you can control that is currently in a Minecraft server.
+pub struct Client {
+ event_receiver: UnboundedReceiver<Event>,
+ conn: GameConnection,
+ state: ClientState,
+}
+
+pub enum Event {}
+
+impl Client {
+ async fn join(account: &Account, address: &ServerAddress) -> Result<Arc<Mutex<Self>>, String> {
+ let resolved_address = resolver::resolve_address(address).await?;
+
+ let mut conn = HandshakeConnection::new(&resolved_address).await?;
+
+ // handshake
+ conn.write(
+ ClientIntentionPacket {
+ protocol_version: PROTOCOL_VERSION,
+ hostname: address.host.clone(),
+ port: address.port,
+ intention: ConnectionProtocol::Login,
}
- }
- };
-
- // game
- loop {
- let packet_result = conn.read().await;
- match packet_result {
- Ok(packet) => match packet {
- GamePacket::ClientboundLoginPacket(p) => {
- println!("Got login packet {:?}", p);
- }
- GamePacket::ClientboundUpdateViewDistancePacket(p) => {
- println!("Got view distance packet {:?}", p);
- }
- GamePacket::ClientboundCustomPayloadPacket(p) => {
- println!("Got custom payload packet {:?}", p);
- }
- GamePacket::ClientboundChangeDifficultyPacket(p) => {
- println!("Got difficulty packet {:?}", p);
- }
- GamePacket::ClientboundDeclareCommandsPacket(p) => {
- println!("Got declare commands packet");
- }
- GamePacket::ClientboundPlayerAbilitiesPacket(p) => {
- println!("Got player abilities packet {:?}", p);
- }
- GamePacket::ClientboundSetCarriedItemPacket(p) => {
- println!("Got set carried item packet {:?}", p);
- }
- GamePacket::ClientboundUpdateTagsPacket(p) => {
- println!("Got update tags packet");
- }
- GamePacket::ClientboundDisconnectPacket(p) => {
- println!("Got login disconnect packet {:?}", p);
- }
- GamePacket::ClientboundUpdateRecipesPacket(p) => {
- println!("Got update recipes packet");
- }
- GamePacket::ClientboundEntityEventPacket(p) => {
- println!("Got entity event packet {:?}", p);
- }
- GamePacket::ClientboundRecipePacket(p) => {
- println!("Got recipe packet");
- }
- GamePacket::ClientboundPlayerPositionPacket(p) => {
- // TODO: reply with teleport confirm
- println!("Got player position packet {:?}", p);
- }
- GamePacket::ClientboundPlayerInfoPacket(p) => {
- println!("Got player info packet {:?}", p);
- }
- GamePacket::ClientboundSetChunkCacheCenterPacket(p) => {
- println!("Got chunk cache center packet {:?}", p);
- }
- GamePacket::ClientboundLevelChunkWithLightPacket(p) => {
- println!("Got chunk with light packet");
- }
- GamePacket::ClientboundLightUpdatePacket(p) => {
- println!("Got light update packet {:?}", p);
+ .get(),
+ )
+ .await;
+ let mut conn = conn.login();
+
+ // login
+ conn.write(
+ ServerboundHelloPacket {
+ username: account.username.clone(),
+ }
+ .get(),
+ )
+ .await;
+
+ let conn = loop {
+ let packet_result = conn.read().await;
+ match packet_result {
+ Ok(packet) => match packet {
+ LoginPacket::ClientboundHelloPacket(p) => {
+ println!("Got encryption request {:?} {:?}", p.nonce, p.public_key);
+ let e = azalea_auth::encryption::encrypt(&p.public_key, &p.nonce).unwrap();
+
+ // TODO: authenticate with the server here (authenticateServer)
+ println!("Sending encryption response {:?}", e);
+
+ conn.write(
+ ServerboundKeyPacket {
+ nonce: e.encrypted_nonce.into(),
+ shared_secret: e.encrypted_public_key.into(),
+ }
+ .get(),
+ )
+ .await;
+ conn.set_encryption_key(e.secret_key);
+ }
+ LoginPacket::ClientboundLoginCompressionPacket(p) => {
+ println!("Got compression request {:?}", p.compression_threshold);
+ conn.set_compression_threshold(p.compression_threshold);
+ }
+ LoginPacket::ClientboundGameProfilePacket(p) => {
+ println!("Got profile {:?}", p.game_profile);
+ break conn.game();
+ }
+ LoginPacket::ServerboundHelloPacket(p) => {
+ println!("Got hello {:?}", p);
+ }
+ LoginPacket::ClientboundLoginDisconnectPacket(p) => {
+ println!("Got disconnect {:?}", p);
+ }
+ LoginPacket::ClientboundCustomQueryPacket(p) => {
+ println!("Got custom query {:?}", p);
+ }
+ LoginPacket::ServerboundKeyPacket(_) => todo!(),
+ },
+ Err(e) => {
+ panic!("Error: {:?}", e);
}
- },
- Err(e) => {
- panic!("Error: {:?}", e);
+ }
+ };
+
+ let (tx, rx) = mpsc::unbounded_channel();
+
+ // we got the GameConnection, so the server is now connected :)
+ let client = Client {
+ event_receiver: rx,
+ conn,
+ state: ClientState { health: 20 },
+ };
+ // let client = Arc::new(Mutex::new(client));
+ // let weak_client = Arc::<_>::downgrade(&client);
+
+ // just start up the game loop and we're ready!
+ // tokio::spawn(Self::game_loop(weak_client, tx));
+
+ Ok(client)
+ }
+
+ // async fn game_loop(weak_client: Weak<Mutex<Client>>, tx: UnboundedSender<Event>) {
+ // loop {
+ // let client_option = weak_client.upgrade();
+ // match client_option {
+ // Some(client) => {
+ // let mut client = client.lock().unwrap();
+
+ // match client.conn.read().await {
+ // Ok(packet) => client.handle(&packet, &tx),
+ // Err(e) => {
+ // panic!("Error: {:?}", e);
+ // }
+ // };
+ // }
+ // // the client was dropped, so we're done
+ // None => break,
+ // }
+ // }
+ // }
+
+ fn handle(&self, packet: &GamePacket, tx: &UnboundedSender<Event>) {
+ match packet {
+ GamePacket::ClientboundLoginPacket(p) => {
+ println!("Got login packet {:?}", p);
+ }
+ GamePacket::ClientboundUpdateViewDistancePacket(p) => {
+ println!("Got view distance packet {:?}", p);
+ }
+ GamePacket::ClientboundCustomPayloadPacket(p) => {
+ println!("Got custom payload packet {:?}", p);
+ }
+ GamePacket::ClientboundChangeDifficultyPacket(p) => {
+ println!("Got difficulty packet {:?}", p);
+ }
+ GamePacket::ClientboundDeclareCommandsPacket(p) => {
+ println!("Got declare commands packet");
+ }
+ GamePacket::ClientboundPlayerAbilitiesPacket(p) => {
+ println!("Got player abilities packet {:?}", p);
+ }
+ GamePacket::ClientboundSetCarriedItemPacket(p) => {
+ println!("Got set carried item packet {:?}", p);
+ }
+ GamePacket::ClientboundUpdateTagsPacket(p) => {
+ println!("Got update tags packet");
+ }
+ GamePacket::ClientboundDisconnectPacket(p) => {
+ println!("Got login disconnect packet {:?}", p);
+ }
+ GamePacket::ClientboundUpdateRecipesPacket(p) => {
+ println!("Got update recipes packet");
+ }
+ GamePacket::ClientboundEntityEventPacket(p) => {
+ println!("Got entity event packet {:?}", p);
+ }
+ GamePacket::ClientboundRecipePacket(p) => {
+ println!("Got recipe packet");
+ }
+ GamePacket::ClientboundPlayerPositionPacket(p) => {
+ // TODO: reply with teleport confirm
+ println!("Got player position packet {:?}", p);
+ }
+ GamePacket::ClientboundPlayerInfoPacket(p) => {
+ println!("Got player info packet {:?}", p);
+ }
+ GamePacket::ClientboundSetChunkCacheCenterPacket(p) => {
+ println!("Got chunk cache center packet {:?}", p);
+ }
+ GamePacket::ClientboundLevelChunkWithLightPacket(p) => {
+ println!("Got chunk with light packet");
+ }
+ GamePacket::ClientboundLightUpdatePacket(p) => {
+ println!("Got light update packet {:?}", p);
}
}
println!();
}
- Ok(())
+ pub async fn next(&mut self) -> Option<Event> {
+ self.event_receiver.recv().await
+ }
+}
+
+impl Account {
+ pub fn offline(username: &str) -> Self {
+ Self {
+ username: username.to_string(),
+ }
+ }
+
+ pub async fn join(&self, address: &ServerAddress) -> Result<Arc<Mutex<Client>>, String> {
+ Client::join(&self, address).await
+ }
}
diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs
index 8c1bcfe9..0e1b8c16 100755
--- a/azalea-client/src/lib.rs
+++ b/azalea-client/src/lib.rs
@@ -1,8 +1,10 @@
//! Significantly abstract azalea-protocol so it's actually useable for bots.
-pub mod connect;
+mod connect;
pub mod ping;
+pub use connect::{Account, ServerClient};
+
#[cfg(test)]
mod tests {
#[test]
diff --git a/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs b/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs
index 6882d255..b916cb8e 100644
--- a/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs
@@ -13,6 +13,7 @@ pub struct ClientboundLevelChunkWithLightPacket {
#[derive(Clone, Debug, McBufReadable, McBufWritable)]
pub struct ClientboundLevelChunkPacketData {
heightmaps: azalea_nbt::Tag,
+ // we can't parse the data in azalea-protocol because it dependso on context from other packets
data: Vec<u8>,
block_entities: Vec<BlockEntity>,
}
diff --git a/bot/src/main.rs b/bot/src/main.rs
index 14fc9656..8ae4ba1e 100644
--- a/bot/src/main.rs
+++ b/bot/src/main.rs
@@ -3,15 +3,17 @@ async fn main() {
println!("Hello, world!");
// let address = "95.111.249.143:10000";
- // let address = "localhost:52467";
- let address = "localhost:25566";
+ let address = "localhost:53193";
// let response = azalea_client::ping::ping_server(&address.try_into().unwrap())
// .await
// .unwrap();
// println!("{}", response.description.to_ansi(None));
- let _response = azalea_client::connect::join_server(&address.try_into().unwrap())
- .await
- .unwrap();
+ let account = azalea_client::Account::offline("bot");
+ let client = account.join(&address.try_into().unwrap()).await.unwrap();
println!("connected");
+
+ // loop {
+ // match client.next().await {}
+ // }
}