initial commit
This commit is contained in:
commit
0cdc71ab8a
12 changed files with 1292 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
230
Cargo.lock
generated
Normal file
230
Cargo.lock
generated
Normal file
|
@ -0,0 +1,230 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hematite-nbt"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670d0784ee67cfb57393dc1837867d2951f9a59ca7db99a653499c854f745739"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cesu8",
|
||||
"flate2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mlua"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4351dbcc863fb6249c81b3bd0c8001214e9d4d44d22cabda17026353a77fe612"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"cc",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"pkg-config",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quectocraft"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hematite-nbt",
|
||||
"mlua",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "quectocraft"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
hematite-nbt = "0.5"
|
||||
serde_json = "1.0"
|
||||
serde = "1.0"
|
||||
mlua = { version = "0.8", features = ["lua54"] }
|
||||
uuid = "1.2"
|
23
plugins/example_plugin/main.lua
Normal file
23
plugins/example_plugin/main.lua
Normal file
|
@ -0,0 +1,23 @@
|
|||
local plugin = {
|
||||
id = "example_plugin",
|
||||
name = "Example Plugin",
|
||||
version = "0.1.0",
|
||||
}
|
||||
|
||||
function plugin.init()
|
||||
print("PLUGIN init")
|
||||
end
|
||||
|
||||
function plugin.playerJoin(name, uuid)
|
||||
print("PLUGIN player joined: " .. name .. " uuid " .. uuid)
|
||||
end
|
||||
|
||||
function plugin.playerLeave(name, uuid)
|
||||
print("PLUGIN player left: " .. name .. " uuid " .. uuid)
|
||||
end
|
||||
|
||||
function plugin.chatMessage(message, author, authorUuid)
|
||||
print("PLUGIN message from " .. author .. ": " .. message)
|
||||
end
|
||||
|
||||
return plugin
|
30
src/main.rs
Normal file
30
src/main.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use mlua::Lua;
|
||||
use network::NetworkServer;
|
||||
use plugins::{Plugin, Plugins};
|
||||
|
||||
mod plugins;
|
||||
mod protocol;
|
||||
mod network;
|
||||
|
||||
fn main() {
|
||||
let lua = Lua::new();
|
||||
let plugin = Plugin::load("plugins/example_plugin/main.lua".into(), &lua).unwrap();
|
||||
let mut plugins = Plugins::new(&lua).unwrap();
|
||||
plugins.add_plugin(plugin);
|
||||
|
||||
let mut server = NetworkServer::new("127.0.0.1:25565".to_owned(), plugins);
|
||||
let sleep_dur = Duration::from_millis(5);
|
||||
let mut i = 0;
|
||||
loop {
|
||||
server.get_new_clients();
|
||||
server.handle_connections();
|
||||
std::thread::sleep(sleep_dur);
|
||||
if i % 1024 == 0 {
|
||||
server.send_keep_alive();
|
||||
i = 0;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
243
src/network/mod.rs
Normal file
243
src/network/mod.rs
Normal file
|
@ -0,0 +1,243 @@
|
|||
use std::{net::{TcpStream, TcpListener, Shutdown}, thread, io::Write, sync::mpsc::{Receiver, Sender, channel, TryRecvError}, time::Duration};
|
||||
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{protocol::{data::{PacketDecoder, PacketEncoder}, serverbound::*, clientbound::*, NetworkState}, plugins::Plugins};
|
||||
|
||||
pub struct NetworkServer<'lua> {
|
||||
plugins: Plugins<'lua>,
|
||||
new_clients: Receiver<NetworkClient>,
|
||||
clients: Vec<NetworkClient>,
|
||||
}
|
||||
|
||||
impl <'lua> NetworkServer<'lua> {
|
||||
pub fn new(addr: String, plugins: Plugins<'lua>) -> Self {
|
||||
let (send, recv) = channel();
|
||||
thread::spawn(move || Self::listen(&addr, send));
|
||||
plugins.init();
|
||||
Self {
|
||||
plugins,
|
||||
new_clients: recv,
|
||||
clients: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn listen(addr: &str, send_clients: Sender<NetworkClient>) {
|
||||
println!("listening for connections");
|
||||
let listener = TcpListener::bind(addr).unwrap();
|
||||
for (id, stream) in listener.incoming().enumerate() {
|
||||
let stream = stream.unwrap();
|
||||
println!("got connection from {} (id {})", stream.peer_addr().unwrap(), id);
|
||||
let stream_2 = stream.try_clone().unwrap();
|
||||
let (send, recv) = channel();
|
||||
thread::spawn(|| NetworkClient::listen(stream_2, send));
|
||||
let client = NetworkClient {
|
||||
id: id as i32,
|
||||
play: false,
|
||||
closed: false,
|
||||
stream,
|
||||
serverbound: recv,
|
||||
player: None,
|
||||
};
|
||||
send_clients.send(client).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_new_clients(&mut self) {
|
||||
while let Ok(client) = self.new_clients.try_recv() {
|
||||
self.clients.push(client);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_keep_alive(&mut self) {
|
||||
let mut closed = Vec::new();
|
||||
for client in self.clients.iter_mut() {
|
||||
if client.play {
|
||||
if let Err(_) = client.send_packet(ClientBoundPacket::KeepAlive(0)) {
|
||||
client.close();
|
||||
if let Some(pl) = &client.player {
|
||||
self.plugins.player_leave(pl);
|
||||
}
|
||||
closed.push(client.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.clients.retain(|x| !closed.contains(&x.id));
|
||||
}
|
||||
|
||||
pub fn handle_connections(&mut self) {
|
||||
let mut closed = Vec::new();
|
||||
for i in 0..self.clients.len() {
|
||||
let client: &mut NetworkClient = unsafe {
|
||||
&mut *(self.clients.get_unchecked_mut(i) as *mut _)
|
||||
};
|
||||
let mut alive = true;
|
||||
while let Some(packet) = client.recv_packet(&mut alive) {
|
||||
if let Err(_) = self.handle_packet(client, packet) {
|
||||
alive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !alive && !client.closed {
|
||||
closed.push(client.id);
|
||||
if let Some(pl) = &client.player {
|
||||
self.plugins.player_leave(pl);
|
||||
}
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
self.clients.retain(|x| !closed.contains(&x.id));
|
||||
}
|
||||
|
||||
fn handle_packet(&mut self, client: &mut NetworkClient, packet: ServerBoundPacket) -> std::io::Result<()> {
|
||||
match packet {
|
||||
ServerBoundPacket::Ignored(_) => (),
|
||||
ServerBoundPacket::Unknown(id) => println!("unknown: {}", id),
|
||||
ServerBoundPacket::Handshake(_) => (),
|
||||
ServerBoundPacket::StatusRequest()
|
||||
=> client.send_packet(ClientBoundPacket::StatusResponse(
|
||||
r#"{"version":{"name":"1.19.2","protocol":760}}"#.to_owned()
|
||||
))?,
|
||||
ServerBoundPacket::PingRequest(n) => {
|
||||
client.send_packet(ClientBoundPacket::PingResponse(n))?;
|
||||
client.close();
|
||||
}
|
||||
ServerBoundPacket::LoginStart(login_start) => {
|
||||
client.player = Some(Player {
|
||||
name: login_start.name.clone(),
|
||||
uuid: login_start.uuid.unwrap(),
|
||||
});
|
||||
client.play = true;
|
||||
client.send_packet(ClientBoundPacket::LoginSuccess(LoginSuccess {
|
||||
name: login_start.name,
|
||||
uuid: login_start.uuid.unwrap(),
|
||||
}))?;
|
||||
self.plugins.player_join(client.player.as_ref().unwrap());
|
||||
self.post_login(client)?;
|
||||
}
|
||||
ServerBoundPacket::ChatMessage(msg) => {
|
||||
self.plugins.chat_message(client.player.as_ref().unwrap(), &msg.message);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_login(&mut self, client: &mut NetworkClient) -> std::io::Result<()> {
|
||||
client.send_packet(ClientBoundPacket::LoginPlay(LoginPlay {
|
||||
eid: client.id,
|
||||
is_hardcore: false,
|
||||
gamemode: 1,
|
||||
prev_gamemode: 1,
|
||||
dimensions: vec![
|
||||
"minecraft:world".to_owned(),
|
||||
],
|
||||
registry_codec: include_bytes!("../resources/registry_codec.nbt").to_vec(),
|
||||
dimension_type: "minecraft:the_end".to_owned(),
|
||||
dimension_name: "minecraft:world".to_owned(),
|
||||
seed_hash: 0,
|
||||
max_players: 0,
|
||||
view_distance: 8,
|
||||
sim_distance: 8,
|
||||
reduced_debug_info: false,
|
||||
respawn_screen: false,
|
||||
is_debug: false,
|
||||
is_flat: false,
|
||||
death_location: None,
|
||||
}))?;
|
||||
client.send_packet(ClientBoundPacket::PluginMessage(PluginMessage {
|
||||
channel: "minecraft:brand".to_owned(),
|
||||
data: {
|
||||
let mut data = Vec::new();
|
||||
data.write_string(32767, "QuectoCraft");
|
||||
data
|
||||
}
|
||||
}))?;
|
||||
client.send_packet(ClientBoundPacket::PlayerAbilities(0x0d, 0.05, 0.1))?;
|
||||
let mut chunk_data: Vec<u8> = Vec::new();
|
||||
for _ in 0..(384 / 16) {
|
||||
// number of non-air blocks
|
||||
chunk_data.write_short(0);
|
||||
// block states
|
||||
chunk_data.write_ubyte(0);
|
||||
chunk_data.write_varint(0);
|
||||
chunk_data.write_varint(0);
|
||||
// biomes
|
||||
chunk_data.write_ubyte(0);
|
||||
chunk_data.write_varint(0);
|
||||
chunk_data.write_varint(0);
|
||||
}
|
||||
let hmdata = vec![0i64; 37];
|
||||
let mut heightmap = nbt::Blob::new();
|
||||
heightmap.insert("MOTION_BLOCKING", hmdata).unwrap();
|
||||
client.send_packet(ClientBoundPacket::ChunkData(ChunkData {
|
||||
x: 0,
|
||||
z: 0,
|
||||
heightmap,
|
||||
chunk_data,
|
||||
}))?;
|
||||
client.send_packet(ClientBoundPacket::SyncPlayerPosition(SyncPlayerPosition {
|
||||
x: 0.0,
|
||||
y: 64.0,
|
||||
z: 0.0,
|
||||
yaw: 0.0,
|
||||
pitch: 0.0,
|
||||
flags: 0,
|
||||
teleport_id: 0,
|
||||
dismount: false
|
||||
}))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Player {
|
||||
pub name: String,
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
||||
struct NetworkClient {
|
||||
pub id: i32,
|
||||
pub play: bool,
|
||||
pub closed: bool,
|
||||
stream: TcpStream,
|
||||
serverbound: Receiver<ServerBoundPacket>,
|
||||
player: Option<Player>,
|
||||
}
|
||||
|
||||
impl NetworkClient {
|
||||
pub fn listen(mut stream: TcpStream, send: Sender<ServerBoundPacket>) {
|
||||
let mut state = NetworkState::Handshake;
|
||||
let dur = Duration::from_millis(5);
|
||||
loop {
|
||||
if let Some(decoder) = PacketDecoder::decode(&mut stream) {
|
||||
let packet = ServerBoundPacket::decode(&mut state, decoder);
|
||||
send.send(packet).unwrap();
|
||||
}
|
||||
thread::sleep(dur)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_packet(&mut self, alive: &mut bool) -> Option<ServerBoundPacket> {
|
||||
match self.serverbound.try_recv() {
|
||||
Ok(packet) => Some(packet),
|
||||
Err(TryRecvError::Empty) => None,
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
*alive = false;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_packet(&mut self, packet: ClientBoundPacket) -> std::io::Result<()> {
|
||||
self.stream.write_all(&packet.encode())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
println!("closed connection with {}", self.id);
|
||||
let _ = self.stream.shutdown(Shutdown::Both);
|
||||
self.closed = true;
|
||||
}
|
||||
}
|
128
src/plugins.rs
Normal file
128
src/plugins.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
use std::{path::{Path, PathBuf}, str::FromStr};
|
||||
|
||||
use mlua::{Function, Lua, Error, Table};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::network::Player;
|
||||
|
||||
pub struct EventHandlers<'lua> {
|
||||
init: Option<Function<'lua>>,
|
||||
player_join: Option<Function<'lua>>,
|
||||
player_leave: Option<Function<'lua>>,
|
||||
chat_message: Option<Function<'lua>>,
|
||||
}
|
||||
|
||||
pub struct Plugin<'lua> {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub event_handlers: EventHandlers<'lua>,
|
||||
}
|
||||
|
||||
impl <'lua> Plugin<'lua> {
|
||||
pub fn load(path: &str, lua: &'lua Lua) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let path = PathBuf::from_str(path).unwrap();
|
||||
let chunk = lua.load(&path);
|
||||
let module: Table = chunk.eval()?;
|
||||
|
||||
let id: String = module.get("id")?;
|
||||
let name: String = module.get("name").unwrap_or_else(|_| id.clone());
|
||||
let version: String = module.get("version").unwrap_or_else(|_| "?".to_owned());
|
||||
|
||||
let init: Option<Function<'lua>> = module.get("init").ok();
|
||||
let player_join: Option<Function<'lua>> = module.get("playerJoin").ok();
|
||||
let player_leave: Option<Function<'lua>> = module.get("playerLeave").ok();
|
||||
let chat_message: Option<Function<'lua>> = module.get("chatMessage").ok();
|
||||
|
||||
let event_handlers = EventHandlers { init, player_join, player_leave, chat_message };
|
||||
Ok(Plugin { id, name, version, event_handlers })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Plugins<'lua> {
|
||||
lua: &'lua Lua,
|
||||
plugins: Vec<Plugin<'lua>>
|
||||
}
|
||||
|
||||
impl <'lua> Plugins<'lua> {
|
||||
pub fn new(lua: &'lua Lua) -> Result<Self, mlua::Error> {
|
||||
let server = lua.create_table()?;
|
||||
let players = lua.create_table()?;
|
||||
server.set("players", players)?;
|
||||
let fn_send = lua.create_function(|_, (uuid, message): (String, String)| {
|
||||
Ok(())
|
||||
})?;
|
||||
server.set("send", fn_send)?;
|
||||
lua.globals().set("server", server)?;
|
||||
Ok(Self {
|
||||
lua,
|
||||
plugins: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_plugin(&mut self, pl: Plugin<'lua>) {
|
||||
self.plugins.push(pl);
|
||||
}
|
||||
|
||||
pub fn init(&self) {
|
||||
for pl in &self.plugins {
|
||||
if let Some(init) = &pl.event_handlers.init {
|
||||
if let Err(e) = init.call::<_, ()>(()) {
|
||||
println!("Error in plugin {}: {}", pl.name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn player_join(&self, player: &Player) {
|
||||
if let Err(e) = self.add_player(player) {
|
||||
println!("Error adding player: {}", e);
|
||||
return
|
||||
}
|
||||
for pl in &self.plugins {
|
||||
if let Some(init) = &pl.event_handlers.player_join {
|
||||
if let Err(e) = init.call::<_, ()>((player.name.as_str(), player.uuid.to_string())) {
|
||||
println!("Error in plugin {}: {}", pl.name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_player(&self, player: &Player) -> Result<(), mlua::Error> {
|
||||
let server: Table = self.lua.globals().get("server")?;
|
||||
let players: Table = server.get("players")?;
|
||||
players.set(player.uuid.to_string(), player.name.as_str())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn player_leave(&self, player: &Player) {
|
||||
if let Err(e) = self.remove_player(player.uuid) {
|
||||
println!("Error removing player: {}", e);
|
||||
return
|
||||
}
|
||||
for pl in &self.plugins {
|
||||
if let Some(func) = &pl.event_handlers.player_leave {
|
||||
if let Err(e) = func.call::<_, ()>((player.name.as_str(), player.uuid.to_string())) {
|
||||
println!("Error in plugin {}: {}", pl.name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_player(&self, uuid: Uuid) -> Result<(), mlua::Error> {
|
||||
let server: Table = self.lua.globals().get("server")?;
|
||||
let players: Table = server.get("players")?;
|
||||
players.set(uuid.to_string(), mlua::Nil)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn chat_message(&self, player: &Player, message: &str) {
|
||||
for pl in &self.plugins {
|
||||
if let Some(func) = &pl.event_handlers.chat_message {
|
||||
if let Err(e) = func.call::<_, ()>((message, player.name.as_str(), player.uuid.to_string())) {
|
||||
println!("Error in plugin {}: {}", pl.name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
204
src/protocol/clientbound.rs
Normal file
204
src/protocol/clientbound.rs
Normal file
|
@ -0,0 +1,204 @@
|
|||
use uuid::Uuid;
|
||||
|
||||
use super::{data::{PacketEncoder, finalize_packet}, Position};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoginSuccess {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl LoginSuccess {
|
||||
pub fn encode(self, encoder: &mut impl PacketEncoder) {
|
||||
encoder.write_uuid(self.uuid);
|
||||
encoder.write_string(16, &self.name);
|
||||
encoder.write_varint(0);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoginPlay {
|
||||
pub eid: i32,
|
||||
pub is_hardcore: bool,
|
||||
pub gamemode: u8,
|
||||
pub prev_gamemode: u8,
|
||||
pub dimensions: Vec<String>,
|
||||
pub registry_codec: Vec<u8>,
|
||||
pub dimension_type: String,
|
||||
pub dimension_name: String,
|
||||
pub seed_hash: i64,
|
||||
pub max_players: i32,
|
||||
pub view_distance: i32,
|
||||
pub sim_distance: i32,
|
||||
pub reduced_debug_info: bool,
|
||||
pub respawn_screen: bool,
|
||||
pub is_debug: bool,
|
||||
pub is_flat: bool,
|
||||
pub death_location: Option<(String, Position)>
|
||||
}
|
||||
|
||||
impl LoginPlay {
|
||||
pub fn encode(self, encoder: &mut impl PacketEncoder) {
|
||||
encoder.write_int(self.eid);
|
||||
encoder.write_bool(self.is_hardcore);
|
||||
encoder.write_ubyte(self.gamemode);
|
||||
encoder.write_ubyte(self.prev_gamemode);
|
||||
encoder.write_varint(self.dimensions.len() as i32);
|
||||
for dim in self.dimensions {
|
||||
encoder.write_string(32767, &dim);
|
||||
}
|
||||
encoder.write_bytes(&self.registry_codec);
|
||||
encoder.write_string(32767, &self.dimension_type);
|
||||
encoder.write_string(32767, &self.dimension_name);
|
||||
encoder.write_long(self.seed_hash);
|
||||
encoder.write_varint(self.max_players);
|
||||
encoder.write_varint(self.view_distance);
|
||||
encoder.write_varint(self.sim_distance);
|
||||
encoder.write_bool(self.reduced_debug_info);
|
||||
encoder.write_bool(self.respawn_screen);
|
||||
encoder.write_bool(self.is_debug);
|
||||
encoder.write_bool(self.is_flat);
|
||||
encoder.write_bool(self.death_location.is_some());
|
||||
if let Some(dl) = self.death_location {
|
||||
encoder.write_string(32767, &dl.0);
|
||||
encoder.write_position(dl.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PluginMessage {
|
||||
pub channel: String,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl PluginMessage {
|
||||
pub fn encode(self, encoder: &mut impl PacketEncoder) {
|
||||
encoder.write_string(32767, &self.channel);
|
||||
encoder.write_bytes(&self.data);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SyncPlayerPosition {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub z: f64,
|
||||
pub yaw: f32,
|
||||
pub pitch: f32,
|
||||
pub flags: i8,
|
||||
pub teleport_id: i32,
|
||||
pub dismount: bool,
|
||||
}
|
||||
|
||||
impl SyncPlayerPosition {
|
||||
pub fn encode(self, encoder: &mut impl PacketEncoder) {
|
||||
encoder.write_double(self.x);
|
||||
encoder.write_double(self.y);
|
||||
encoder.write_double(self.z);
|
||||
encoder.write_float(self.yaw);
|
||||
encoder.write_float(self.pitch);
|
||||
encoder.write_byte(self.flags);
|
||||
encoder.write_varint(self.teleport_id);
|
||||
encoder.write_bool(self.dismount);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChunkData {
|
||||
pub x: i32,
|
||||
pub z: i32,
|
||||
pub heightmap: nbt::Blob,
|
||||
pub chunk_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ChunkData {
|
||||
pub fn encode(self, encoder: &mut impl PacketEncoder) {
|
||||
encoder.write_int(self.x);
|
||||
encoder.write_int(self.z);
|
||||
self.heightmap.to_writer(encoder).unwrap();
|
||||
encoder.write_varint(self.chunk_data.len() as i32);
|
||||
encoder.write_bytes(&self.chunk_data);
|
||||
// number of block entities
|
||||
encoder.write_varint(0);
|
||||
// trust edges
|
||||
encoder.write_bool(true);
|
||||
// light masks
|
||||
encoder.write_varint(0);
|
||||
encoder.write_varint(0);
|
||||
encoder.write_varint(0);
|
||||
encoder.write_varint(0);
|
||||
// sky light array
|
||||
encoder.write_varint(0);
|
||||
// block light array
|
||||
encoder.write_varint(0);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ClientBoundPacket {
|
||||
// status
|
||||
StatusResponse(String),
|
||||
PingResponse(i64),
|
||||
// login
|
||||
LoginSuccess(LoginSuccess),
|
||||
// play
|
||||
LoginPlay(LoginPlay),
|
||||
PluginMessage(PluginMessage),
|
||||
SyncPlayerPosition(SyncPlayerPosition),
|
||||
ChunkData(ChunkData),
|
||||
KeepAlive(i64),
|
||||
PlayerAbilities(i8, f32, f32),
|
||||
SystemChatMessage(serde_json::Value, bool),
|
||||
}
|
||||
|
||||
impl ClientBoundPacket {
|
||||
pub fn encode(self) -> Vec<u8> {
|
||||
let mut packet = Vec::new();
|
||||
match self {
|
||||
Self::StatusResponse(status) => {
|
||||
packet.write_string(32767, &status);
|
||||
finalize_packet(packet, 0)
|
||||
},
|
||||
Self::PingResponse(n) => {
|
||||
packet.write_long(n);
|
||||
finalize_packet(packet, 1)
|
||||
},
|
||||
Self::LoginSuccess(login_success) => {
|
||||
login_success.encode(&mut packet);
|
||||
finalize_packet(packet, 2)
|
||||
}
|
||||
Self::PluginMessage(plugin_message) => {
|
||||
plugin_message.encode(&mut packet);
|
||||
finalize_packet(packet, 22)
|
||||
}
|
||||
Self::SyncPlayerPosition(sync_player_position) => {
|
||||
sync_player_position.encode(&mut packet);
|
||||
finalize_packet(packet, 57)
|
||||
}
|
||||
Self::LoginPlay(login_play) => {
|
||||
login_play.encode(&mut packet);
|
||||
finalize_packet(packet, 37)
|
||||
}
|
||||
Self::ChunkData(chunk_data) => {
|
||||
chunk_data.encode(&mut packet);
|
||||
finalize_packet(packet, 33)
|
||||
}
|
||||
Self::KeepAlive(n) => {
|
||||
packet.write_long(n);
|
||||
finalize_packet(packet, 32)
|
||||
}
|
||||
Self::PlayerAbilities(flags, speed, view) => {
|
||||
packet.write_byte(flags);
|
||||
packet.write_float(speed);
|
||||
packet.write_float(view);
|
||||
finalize_packet(packet, 49)
|
||||
}
|
||||
Self::SystemChatMessage(msg, overlay) => {
|
||||
packet.write_string(262144, &msg.to_string());
|
||||
packet.write_bool(overlay);
|
||||
finalize_packet(packet, 98)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
284
src/protocol/data.rs
Normal file
284
src/protocol/data.rs
Normal file
|
@ -0,0 +1,284 @@
|
|||
use std::io::{Write, Read};
|
||||
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::Position;
|
||||
|
||||
pub trait PacketEncoder: Write {
|
||||
fn write_bytes(&mut self, data: &[u8]) {
|
||||
self.write_all(data).unwrap();
|
||||
}
|
||||
|
||||
fn write_bool(&mut self, data: bool) {
|
||||
self.write_all(&[data as u8]).unwrap();
|
||||
}
|
||||
|
||||
fn write_byte(&mut self, data: i8) {
|
||||
self.write_all(&[data as u8]).unwrap();
|
||||
}
|
||||
|
||||
fn write_ubyte(&mut self, data: u8) {
|
||||
self.write_all(&[data]).unwrap();
|
||||
}
|
||||
|
||||
fn write_short(&mut self, data: i16) {
|
||||
self.write_all(&data.to_be_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn write_ushort(&mut self, data: u16) {
|
||||
self.write_all(&data.to_be_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn write_int(&mut self, data: i32) {
|
||||
self.write_all(&data.to_be_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn write_long(&mut self, data: i64) {
|
||||
self.write_all(&data.to_be_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn write_uuid(&mut self, data: Uuid) {
|
||||
self.write_all(&data.as_u128().to_be_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn write_float(&mut self, data: f32) {
|
||||
self.write_all(&data.to_be_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn write_double(&mut self, data: f64) {
|
||||
self.write_all(&data.to_be_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn write_varint(&mut self, mut data: i32) {
|
||||
loop {
|
||||
let mut byte = (data & 0b11111111) as u8;
|
||||
data = data >> 7;
|
||||
if data != 0 {
|
||||
byte |= 0b10000000;
|
||||
}
|
||||
self.write_all(&[byte]).unwrap();
|
||||
if data == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_varlong(&mut self, mut data: i64) {
|
||||
loop {
|
||||
let mut byte = (data & 0b11111111) as u8;
|
||||
data = data >> 7;
|
||||
if data != 0 {
|
||||
byte |= 0b10000000;
|
||||
}
|
||||
self.write_all(&[byte]).unwrap();
|
||||
if data == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_position(&mut self, position: Position) {
|
||||
self.write_long(
|
||||
(((position.x & 0x3ffffff) as i64) << 38)
|
||||
| (((position.z & 0x3ffffff) as i64) << 12)
|
||||
| (position.y & 0xfff) as i64
|
||||
)
|
||||
}
|
||||
|
||||
fn write_nbt(&mut self, nbt: &impl Serialize) {
|
||||
nbt::to_writer(self, nbt, None).unwrap();
|
||||
}
|
||||
|
||||
fn write_string(&mut self, max_len: usize, val: &str) {
|
||||
if val.len() > max_len * 4 + 3 {
|
||||
panic!("exceeded max string length")
|
||||
}
|
||||
self.write_varint(val.len() as i32);
|
||||
self.write_all(val.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
fn to_data(self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
impl PacketEncoder for Vec<u8> {
|
||||
fn to_data(self) -> Vec<u8> { self }
|
||||
}
|
||||
|
||||
fn encode_varint(mut data: i32) -> Vec<u8> {
|
||||
let mut res = Vec::new();
|
||||
loop {
|
||||
let mut byte = (data & 0b11111111) as u8;
|
||||
data = data >> 7;
|
||||
if data != 0 {
|
||||
byte |= 0b10000000;
|
||||
}
|
||||
res.push(byte);
|
||||
if data == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn finalize_packet(packet: impl PacketEncoder, packet_id: i32) -> Vec<u8> {
|
||||
let mut id = encode_varint(packet_id);
|
||||
let mut data = packet.to_data();
|
||||
let mut result = encode_varint((id.len() + data.len()) as i32);
|
||||
result.append(&mut id);
|
||||
result.append(&mut data);
|
||||
result
|
||||
}
|
||||
|
||||
pub struct PacketDecoder {
|
||||
data: Vec<u8>,
|
||||
idx: usize,
|
||||
packet_id: i32,
|
||||
}
|
||||
|
||||
impl PacketDecoder {
|
||||
pub fn decode(read: &mut impl Read) -> Option<PacketDecoder> {
|
||||
let size = read_varint(read)? as usize;
|
||||
let mut data = vec![0; size];
|
||||
read.read_exact(&mut data).unwrap();
|
||||
let mut decoder = PacketDecoder {
|
||||
data,
|
||||
idx: 0,
|
||||
packet_id: 0
|
||||
};
|
||||
decoder.packet_id = decoder.read_varint();
|
||||
return Some(decoder);
|
||||
}
|
||||
|
||||
pub fn packet_id(&self) -> i32 {
|
||||
return self.packet_id;
|
||||
}
|
||||
|
||||
pub fn read_bytes(&mut self, n: usize) -> &[u8] {
|
||||
let ret = &self.data[self.idx..self.idx+n];
|
||||
self.idx += n;
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn read_bool(&mut self) -> bool {
|
||||
self.idx += 1;
|
||||
self.data[self.idx-1] != 0
|
||||
}
|
||||
|
||||
pub fn read_byte(&mut self) -> i8 {
|
||||
self.idx += 1;
|
||||
self.data[self.idx-1] as i8
|
||||
}
|
||||
|
||||
pub fn read_ubyte(&mut self) -> u8 {
|
||||
self.idx += 1;
|
||||
self.data[self.idx-1]
|
||||
}
|
||||
|
||||
pub fn read_short(&mut self) -> i16 {
|
||||
let mut buf = [0; 2];
|
||||
buf.copy_from_slice(&self.data[self.idx..self.idx+2]);
|
||||
self.idx += 2;
|
||||
i16::from_be_bytes(buf)
|
||||
}
|
||||
|
||||
pub fn read_ushort(&mut self) -> u16 {
|
||||
let mut buf = [0; 2];
|
||||
buf.copy_from_slice(&self.data[self.idx..self.idx+2]);
|
||||
self.idx += 2;
|
||||
u16::from_be_bytes(buf)
|
||||
}
|
||||
|
||||
pub fn read_int(&mut self) -> i32 {
|
||||
let mut buf = [0; 4];
|
||||
buf.copy_from_slice(&self.data[self.idx..self.idx+4]);
|
||||
self.idx += 4;
|
||||
i32::from_be_bytes(buf)
|
||||
}
|
||||
|
||||
pub fn read_long(&mut self) -> i64 {
|
||||
let mut buf = [0; 8];
|
||||
buf.copy_from_slice(&self.data[self.idx..self.idx+8]);
|
||||
self.idx += 8;
|
||||
i64::from_be_bytes(buf)
|
||||
}
|
||||
|
||||
pub fn read_uuid(&mut self) -> Uuid {
|
||||
let mut buf = [0; 16];
|
||||
buf.copy_from_slice(&self.data[self.idx..self.idx+16]);
|
||||
self.idx += 16;
|
||||
Uuid::from_u128(u128::from_be_bytes(buf))
|
||||
}
|
||||
|
||||
pub fn read_float(&mut self) -> f32 {
|
||||
let mut buf = [0; 4];
|
||||
buf.copy_from_slice(&self.data[self.idx..self.idx+4]);
|
||||
self.idx += 4;
|
||||
f32::from_be_bytes(buf)
|
||||
}
|
||||
|
||||
pub fn read_double(&mut self) -> f64 {
|
||||
let mut buf = [0; 8];
|
||||
buf.copy_from_slice(&self.data[self.idx..self.idx+8]);
|
||||
self.idx += 8;
|
||||
f64::from_be_bytes(buf)
|
||||
}
|
||||
|
||||
pub fn read_varint(&mut self) -> i32 {
|
||||
let mut result = 0;
|
||||
let mut count = 0;
|
||||
loop {
|
||||
let byte = self.read_ubyte();
|
||||
result |= ((byte & 0x7f) as i32) << (7 * count);
|
||||
count += 1;
|
||||
if count > 5 {
|
||||
panic!("varint too long")
|
||||
}
|
||||
if byte & 0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn read_varlong(&mut self) -> i64 {
|
||||
let mut result = 0;
|
||||
let mut count = 0;
|
||||
loop {
|
||||
let byte = self.read_ubyte();
|
||||
result |= ((byte & 0x7f) as i64) << (7 * count);
|
||||
count += 1;
|
||||
if count > 10 {
|
||||
panic!("varint too long")
|
||||
}
|
||||
if byte & 0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn read_string(&mut self) -> String {
|
||||
let len = self.read_varint() as usize;
|
||||
String::from_utf8(self.read_bytes(len).to_vec()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn read_varint(read: &mut impl Read) -> Option<i32> {
|
||||
let mut result = 0;
|
||||
let mut count = 0;
|
||||
loop {
|
||||
let mut byte = [0];
|
||||
read.read_exact(&mut byte).ok()?;
|
||||
let byte = byte[0];
|
||||
result |= ((byte & 0x7f) as i32) << (7 * count);
|
||||
count += 1;
|
||||
if count > 5 {
|
||||
panic!("varint too long")
|
||||
}
|
||||
if byte & 0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
Some(result)
|
||||
}
|
18
src/protocol/mod.rs
Normal file
18
src/protocol/mod.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
pub mod data;
|
||||
pub mod serverbound;
|
||||
pub mod clientbound;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum NetworkState {
|
||||
Handshake,
|
||||
Status,
|
||||
Login,
|
||||
Play
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Position {
|
||||
x: i32,
|
||||
y: i16,
|
||||
z: i32
|
||||
}
|
118
src/protocol/serverbound.rs
Normal file
118
src/protocol/serverbound.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use uuid::Uuid;
|
||||
|
||||
use super::{data::PacketDecoder, NetworkState};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Handshake {
|
||||
pub version: i32,
|
||||
pub addr: String,
|
||||
pub port: u16,
|
||||
pub next_state: i32,
|
||||
}
|
||||
|
||||
impl Handshake {
|
||||
pub fn decode(mut decoder: PacketDecoder) -> Self {
|
||||
let version = decoder.read_varint();
|
||||
let addr = decoder.read_string();
|
||||
let port = decoder.read_ushort();
|
||||
let next_state = decoder.read_varint();
|
||||
Self { version, addr, port, next_state }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SigData {
|
||||
pub timestamp: i64,
|
||||
pub pubkey: Vec<u8>,
|
||||
pub sig: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoginStart {
|
||||
pub name: String,
|
||||
pub sig_data: Option<SigData>,
|
||||
pub uuid: Option<Uuid>,
|
||||
}
|
||||
|
||||
impl LoginStart {
|
||||
pub fn decode(mut decoder: PacketDecoder) -> Self {
|
||||
let name = decoder.read_string();
|
||||
let has_sig_data = decoder.read_bool();
|
||||
let sig_data = if has_sig_data {
|
||||
let timestamp = decoder.read_long();
|
||||
let pubkey_len = decoder.read_varint();
|
||||
let pubkey = decoder.read_bytes(pubkey_len as usize).to_vec();
|
||||
let sig_len = decoder.read_varint();
|
||||
let sig = decoder.read_bytes(sig_len as usize).to_vec();
|
||||
Some(SigData { timestamp, pubkey, sig })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let has_uuid = decoder.read_bool();
|
||||
let uuid = if has_uuid {
|
||||
Some(decoder.read_uuid())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Self { name, sig_data, uuid }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChatMessage {
|
||||
pub message: String,
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
impl ChatMessage {
|
||||
pub fn decode(mut decoder: PacketDecoder) -> Self {
|
||||
let message = decoder.read_string();
|
||||
let timestamp = decoder.read_long();
|
||||
// TODO read rest of packet
|
||||
Self { message, timestamp }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ServerBoundPacket {
|
||||
Unknown(i32),
|
||||
Ignored(i32),
|
||||
// handshake
|
||||
Handshake(Handshake),
|
||||
// status
|
||||
StatusRequest(),
|
||||
PingRequest(i64),
|
||||
// login
|
||||
LoginStart(LoginStart),
|
||||
// play
|
||||
ChatMessage(ChatMessage),
|
||||
}
|
||||
|
||||
impl ServerBoundPacket {
|
||||
pub fn decode(state: &mut NetworkState, mut decoder: PacketDecoder) -> ServerBoundPacket {
|
||||
use NetworkState as NS;
|
||||
match (*state, decoder.packet_id()) {
|
||||
(NS::Handshake, 0) => {
|
||||
let hs = Handshake::decode(decoder);
|
||||
match hs.next_state {
|
||||
1 => *state = NS::Status,
|
||||
2 => *state = NS::Login,
|
||||
state => panic!("invalid next state: {}", state)
|
||||
}
|
||||
ServerBoundPacket::Handshake(hs)
|
||||
},
|
||||
(NS::Status, 0)
|
||||
=> ServerBoundPacket::StatusRequest(),
|
||||
(NS::Status, 1)
|
||||
=> ServerBoundPacket::PingRequest(decoder.read_long()),
|
||||
(NS::Login, 0) => {
|
||||
*state = NS::Play;
|
||||
ServerBoundPacket::LoginStart(LoginStart::decode(decoder))
|
||||
},
|
||||
(NS::Play, 5) => ServerBoundPacket::ChatMessage(ChatMessage::decode(decoder)),
|
||||
(NS::Play, id @ (18 | 20 | 21 | 22 | 30)) => ServerBoundPacket::Ignored(id),
|
||||
(_, id) => ServerBoundPacket::Unknown(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
src/resources/registry_codec.nbt
Normal file
BIN
src/resources/registry_codec.nbt
Normal file
Binary file not shown.
Loading…
Reference in a new issue