refactored

This commit is contained in:
TriMill 2022-12-18 00:43:01 -05:00
parent a3375c53a4
commit 278e28ad18
5 changed files with 321 additions and 227 deletions

View file

@ -43,8 +43,8 @@ impl NetworkClient {
}
}
pub fn send_packet(&mut self, packet: ClientBoundPacket) -> std::io::Result<()> {
self.stream.write_all(&packet.encode())?;
pub fn send_packet(&mut self, packet: impl ClientBoundPacket) -> std::io::Result<()> {
self.stream.write_all(&encode_packet(packet))?;
Ok(())
}

View file

@ -68,7 +68,7 @@ impl <'lua> NetworkServer<'lua> {
let mut closed = Vec::new();
for client in self.clients.iter_mut() {
if client.player.is_some() {
let result = client.send_packet(ClientBoundPacket::KeepAlive(0));
let result = client.send_packet(KeepAlive { data: 0 });
if result.is_err() {
client.close();
self.plugins.player_leave(client.player.as_ref().unwrap());
@ -114,7 +114,7 @@ impl <'lua> NetworkServer<'lua> {
for client in self.clients.iter_mut() {
if let Some(p) = &client.player {
if p.name == player || p.uuid.to_string() == player {
client.send_packet(ClientBoundPacket::SystemChatMessage(message, false))?;
client.send_packet(SystemChatMessage { message, overlay: false })?;
break
}
}
@ -123,7 +123,7 @@ impl <'lua> NetworkServer<'lua> {
Response::Broadcast { message } => {
for client in self.clients.iter_mut() {
if client.player.is_some() {
client.send_packet(ClientBoundPacket::SystemChatMessage(message.clone(), false))?;
client.send_packet(SystemChatMessage { message: message.clone(), overlay: false })?;
}
}
},
@ -131,7 +131,7 @@ impl <'lua> NetworkServer<'lua> {
for client in self.clients.iter_mut() {
if let Some(pl) = &client.player {
if pl.name == player || pl.uuid.to_string() == player {
client.send_packet(ClientBoundPacket::Disconnect(reason.clone()))?;
client.send_packet(Disconnect { reason: reason.clone() })?;
}
}
}
@ -147,88 +147,21 @@ impl <'lua> NetworkServer<'lua> {
ServerBoundPacket::Unknown(id) => warn!("Unknown packet: {}", id),
ServerBoundPacket::Handshake(_) => (),
ServerBoundPacket::StatusRequest()
=> client.send_packet(ClientBoundPacket::StatusResponse(
r#"{"version":{"name":"1.19.3","protocol":761}}"#.to_owned()
))?,
=> client.send_packet(StatusResponse {
data: r#"{"version":{"name":"1.19.3","protocol":761}}"#.to_owned()
})?,
ServerBoundPacket::PingRequest(n) => {
client.send_packet(ClientBoundPacket::PingResponse(n))?;
client.send_packet(PingResponse { data: n })?;
client.close();
}
ServerBoundPacket::LoginStart(login_start) => {
if self.clients.iter().filter_map(|x| x.player.as_ref()).any(|x| x.uuid == login_start.uuid) {
client.send_packet(ClientBoundPacket::LoginDisconnect(json!({
"translate": "multiplayer.disconnect.duplicate_login"
})))?;
client.close();
return Ok(())
}
client.player = Some(Player {
name: login_start.name.clone(),
uuid: login_start.uuid,
});
match self.config.login {
LoginMode::Offline => {
client.verified = true;
client.send_packet(ClientBoundPacket::LoginPluginRequest{
id: -1,
channel: "qc:init".to_owned(),
data: Vec::new()
})?;
},
LoginMode::Velocity => {
client.send_packet(ClientBoundPacket::LoginPluginRequest{
id: 10,
channel: "velocity:player_info".to_owned(),
data: vec![1],
})?
}
}
}
// Finalize login
ServerBoundPacket::LoginPluginResponse { id: -1, .. } => {
if !client.verified {
client.send_packet(ClientBoundPacket::Disconnect(json!({
"text": "Failed to verify your connection",
"color": "red",
})))?;
client.close();
}
client.send_packet(ClientBoundPacket::LoginSuccess(LoginSuccess {
name: client.player.as_ref().unwrap().name.to_owned(),
uuid: client.player.as_ref().unwrap().uuid,
}))?;
self.plugins.player_join(client.player.as_ref().unwrap());
self.post_login(client)?;
}
// Velocity login
ServerBoundPacket::LoginPluginResponse { id: 10, data } => {
let Some(data) = data else {
client.send_packet(ClientBoundPacket::LoginDisconnect(json!({
"text": "This server can only be connected to via a Velocity proxy",
"color": "red"
})))?;
client.close();
return Ok(());
};
let (sig, data) = data.split_at(32);
let mut mac = Hmac::<Sha256>::new_from_slice(self.config.velocity_secret.clone().unwrap().as_bytes())?;
mac.update(data);
if let Err(_) = mac.verify_slice(sig) {
client.send_packet(ClientBoundPacket::Disconnect(json!({
"text": "Could not verify secret. Ensure that the secrets configured for Velocity and Quectocraft match."
})))?;
client.close();
return Ok(())
}
client.verified = true;
client.send_packet(ClientBoundPacket::LoginPluginRequest{
id: -1,
channel: "qc:init".to_owned(),
data: Vec::new()
})?;
}
ServerBoundPacket::LoginStart(login_start)
=> self.start_login(client, login_start)?,
ServerBoundPacket::LoginPluginResponse(LoginPluginResponse { id: 10, data })
=> self.velocity_login(client, data)?,
ServerBoundPacket::LoginPluginResponse(LoginPluginResponse{ id: -1, .. })
=> self.login(client)?,
ServerBoundPacket::LoginPluginResponse { .. } => {
client.send_packet(ClientBoundPacket::LoginDisconnect(json!({"text": "bad plugin response"})))?;
client.send_packet(LoginDisconnect { reason: json!({"text": "bad plugin response"}) })?;
client.close();
}
ServerBoundPacket::ChatMessage(msg) => {
@ -238,10 +171,10 @@ impl <'lua> NetworkServer<'lua> {
let mut parts = msg.message.splitn(1, " ");
if let Some(cmd) = parts.next() {
if cmd == "qc" {
client.send_packet(ClientBoundPacket::SystemChatMessage(json!({
client.send_packet(SystemChatMessage { message: json!({
"text": format!("QuectoCraft version {}", VERSION),
"color": "green"
}), false))?;
}), overlay: false })?;
} else {
let args = parts.next().unwrap_or_default();
self.plugins.command(client.player.as_ref().unwrap(), cmd, args);
@ -252,12 +185,85 @@ impl <'lua> NetworkServer<'lua> {
Ok(())
}
fn post_login(&mut self, client: &mut NetworkClient) -> std::io::Result<()> {
client.send_packet(ClientBoundPacket::LoginPlay(LoginPlay {
fn start_login(&mut self, client: &mut NetworkClient, login_start: LoginStart) -> Result<(), Box<dyn std::error::Error>> {
if self.clients.iter().filter_map(|x| x.player.as_ref()).any(|x| x.uuid == login_start.uuid) {
client.send_packet(LoginDisconnect { reason: json!({
"translate": "multiplayer.disconnect.duplicate_login"
})})?;
client.close();
return Ok(())
}
client.player = Some(Player {
name: login_start.name.clone(),
uuid: login_start.uuid,
});
match self.config.login {
LoginMode::Offline => {
client.verified = true;
client.send_packet(LoginPluginRequest{
id: -1,
channel: "qc:init".to_owned(),
data: Vec::new()
})?;
},
LoginMode::Velocity => {
client.send_packet(LoginPluginRequest{
id: 10,
channel: "velocity:player_info".to_owned(),
data: vec![1],
})?
}
}
Ok(())
}
fn velocity_login(&mut self, client: &mut NetworkClient, data: Option<Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
let Some(data) = data else {
client.send_packet(LoginDisconnect { reason: json!({
"text": "This server can only be connected to via a Velocity proxy",
"color": "red"
})})?;
client.close();
return Ok(());
};
let (sig, data) = data.split_at(32);
let mut mac = Hmac::<Sha256>::new_from_slice(self.config.velocity_secret.clone().unwrap().as_bytes())?;
mac.update(data);
if let Err(_) = mac.verify_slice(sig) {
client.send_packet(Disconnect { reason: json!({
"text": "Could not verify secret. Ensure that the secrets configured for Velocity and Quectocraft match."
})})?;
client.close();
return Ok(())
}
client.verified = true;
client.send_packet(LoginPluginRequest{
id: -1,
channel: "qc:init".to_owned(),
data: Vec::new()
})?;
Ok(())
}
fn login(&mut self, client: &mut NetworkClient) -> std::io::Result<()> {
if !client.verified {
client.send_packet(Disconnect { reason: json!({
"text": "Failed to verify your connection",
"color": "red",
})})?;
client.close();
return Ok(())
}
client.send_packet(LoginSuccess {
name: client.player.as_ref().unwrap().name.to_owned(),
uuid: client.player.as_ref().unwrap().uuid,
})?;
self.plugins.player_join(client.player.as_ref().unwrap());
client.send_packet(LoginPlay {
eid: client.id,
is_hardcore: false,
gamemode: 1,
prev_gamemode: 1,
gamemode: 3,
prev_gamemode: 3,
dimensions: vec![
"qc:world".to_owned(),
],
@ -273,16 +279,16 @@ impl <'lua> NetworkServer<'lua> {
is_debug: false,
is_flat: false,
death_location: None,
}))?;
client.send_packet(ClientBoundPacket::PluginMessage(PluginMessage {
})?;
client.send_packet(PluginMessage {
channel: "minecraft:brand".to_owned(),
data: {
let mut data = Vec::new();
data.write_string(32767, &format!("Quectocraft {}", VERSION));
data
}
}))?;
client.send_packet(ClientBoundPacket::Commands(self.commands.clone()))?;
})?;
client.send_packet(self.commands.clone())?;
let mut chunk_data: Vec<u8> = Vec::new();
for _ in 0..(384 / 16) {
// number of non-air blocks
@ -299,16 +305,16 @@ impl <'lua> NetworkServer<'lua> {
let hmdata = vec![0i64; 37];
let mut heightmap = nbt::Blob::new();
heightmap.insert("MOTION_BLOCKING", hmdata).unwrap();
client.send_packet(ClientBoundPacket::ChunkData(ChunkData {
client.send_packet(ChunkData {
x: 0,
z: 0,
heightmap,
chunk_data,
}))?;
client.send_packet(ClientBoundPacket::SetDefaultSpawnPosition(
Position { x: 0, y: 0, z: 0 }, 0.0
))?;
client.send_packet(ClientBoundPacket::SyncPlayerPosition(SyncPlayerPosition {
})?;
client.send_packet(SetDefaultSpawnPosition {
pos: Position { x: 0, y: 0, z: 0 }, angle: 0.0
})?;
client.send_packet(SyncPlayerPosition {
x: 0.0,
y: 64.0,
z: 0.0,
@ -317,7 +323,7 @@ impl <'lua> NetworkServer<'lua> {
flags: 0,
teleport_id: 0,
dismount: false
}))?;
})?;
// TODO why doesn't this work with quilt?
// client.send_packet(ClientBoundPacket::PlayerAbilities(0x0f, 0.05, 0.1))?;
Ok(())

View file

@ -1,6 +1,50 @@
use uuid::Uuid;
use super::{data::{PacketEncoder, finalize_packet}, Position, command::Commands};
use super::{data::{PacketEncoder, finalize_packet}, Position};
pub trait ClientBoundPacket: std::fmt::Debug {
fn encode(&self, encoder: &mut impl PacketEncoder);
fn packet_id(&self) -> i32;
}
//////////////////
// //
// Status //
// //
//////////////////
#[derive(Debug)]
pub struct PingResponse {
pub data: i64
}
impl ClientBoundPacket for PingResponse {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_long(self.data)
}
fn packet_id(&self) -> i32 { 0x01 }
}
#[derive(Debug)]
pub struct StatusResponse {
pub data: String
}
impl ClientBoundPacket for StatusResponse {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_string(32767, &self.data)
}
fn packet_id(&self) -> i32 { 0x00 }
}
/////////////////
// //
// Login //
// //
/////////////////
#[derive(Debug)]
pub struct LoginSuccess {
@ -8,14 +52,52 @@ pub struct LoginSuccess {
pub name: String,
}
impl LoginSuccess {
pub fn encode(self, encoder: &mut impl PacketEncoder) {
impl ClientBoundPacket for LoginSuccess {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_uuid(self.uuid);
encoder.write_string(16, &self.name);
encoder.write_varint(0);
}
fn packet_id(&self) -> i32 { 0x02 }
}
#[derive(Debug)]
pub struct LoginPluginRequest {
pub id: i32,
pub channel: String,
pub data: Vec<u8>,
}
impl ClientBoundPacket for LoginPluginRequest {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_varint(self.id);
encoder.write_string(32767, &self.channel);
encoder.write_bytes(&self.data);
}
fn packet_id(&self) -> i32 { 0x04 }
}
#[derive(Debug)]
pub struct LoginDisconnect {
pub reason: serde_json::Value
}
impl ClientBoundPacket for LoginDisconnect {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_string(262144, &self.reason.to_string())
}
fn packet_id(&self) -> i32 { 0x00 }
}
////////////////
// //
// Play //
// //
////////////////
#[derive(Debug)]
pub struct LoginPlay {
pub eid: i32,
@ -37,14 +119,14 @@ pub struct LoginPlay {
pub death_location: Option<(String, Position)>
}
impl LoginPlay {
pub fn encode(self, encoder: &mut impl PacketEncoder) {
impl ClientBoundPacket for LoginPlay {
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 {
for dim in &self.dimensions {
encoder.write_string(32767, &dim);
}
encoder.write_bytes(&self.registry_codec);
@ -59,11 +141,13 @@ impl LoginPlay {
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 {
if let Some(dl) = &self.death_location {
encoder.write_string(32767, &dl.0);
encoder.write_position(dl.1);
}
}
fn packet_id(&self) -> i32 { 0x24 }
}
#[derive(Debug)]
@ -72,11 +156,13 @@ pub struct PluginMessage {
pub data: Vec<u8>,
}
impl PluginMessage {
pub fn encode(self, encoder: &mut impl PacketEncoder) {
impl ClientBoundPacket for PluginMessage {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_string(32767, &self.channel);
encoder.write_bytes(&self.data);
}
fn packet_id(&self) -> i32 { 0x15 }
}
#[derive(Debug)]
@ -91,8 +177,8 @@ pub struct SyncPlayerPosition {
pub dismount: bool,
}
impl SyncPlayerPosition {
pub fn encode(self, encoder: &mut impl PacketEncoder) {
impl ClientBoundPacket for SyncPlayerPosition {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_double(self.x);
encoder.write_double(self.y);
encoder.write_double(self.z);
@ -102,6 +188,8 @@ impl SyncPlayerPosition {
encoder.write_varint(self.teleport_id);
encoder.write_bool(self.dismount);
}
fn packet_id(&self) -> i32 { 0x38 }
}
#[derive(Debug)]
@ -112,8 +200,8 @@ pub struct ChunkData {
pub chunk_data: Vec<u8>,
}
impl ChunkData {
pub fn encode(self, encoder: &mut impl PacketEncoder) {
impl ClientBoundPacket for ChunkData {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_int(self.x);
encoder.write_int(self.z);
self.heightmap.to_writer(encoder).unwrap();
@ -133,104 +221,86 @@ impl ChunkData {
// block light array
encoder.write_varint(0);
}
fn packet_id(&self) -> i32 { 0x20 }
}
#[allow(unused)]
#[derive(Debug)]
pub enum ClientBoundPacket {
// status
StatusResponse(String),
PingResponse(i64),
// login
LoginPluginRequest { id: i32, channel: String, data: Vec<u8> },
LoginSuccess(LoginSuccess),
LoginDisconnect(serde_json::Value),
// play
LoginPlay(LoginPlay),
PluginMessage(PluginMessage),
Commands(Commands),
ChunkData(ChunkData),
SyncPlayerPosition(SyncPlayerPosition),
KeepAlive(i64),
PlayerAbilities(i8, f32, f32),
Disconnect(serde_json::Value),
SetDefaultSpawnPosition(Position, f32),
SystemChatMessage(serde_json::Value, bool),
pub struct KeepAlive {
pub data: i64
}
impl ClientBoundPacket {
pub fn encode(self) -> Vec<u8> {
let mut packet = Vec::new();
match self {
// Status
Self::StatusResponse(status) => {
packet.write_string(32767, &status);
finalize_packet(packet, 0)
},
Self::PingResponse(n) => {
packet.write_long(n);
finalize_packet(packet, 1)
},
// Login
Self::LoginDisconnect(message) => {
packet.write_string(262144, &message.to_string());
finalize_packet(packet, 0)
impl ClientBoundPacket for KeepAlive {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_long(self.data)
}
Self::LoginPluginRequest { id, channel, data } => {
packet.write_varint(id);
packet.write_string(32767, &channel);
packet.write_bytes(&data);
finalize_packet(packet, 4)
fn packet_id(&self) -> i32 { 0x1f }
}
Self::LoginSuccess(login_success) => {
login_success.encode(&mut packet);
finalize_packet(packet, 2)
#[derive(Debug)]
pub struct PlayerAbilities {
pub flags: i8,
pub fly_speed: f32,
pub fov_modifier: f32,
}
// Play
Self::Disconnect(message) => {
packet.write_string(262144, &message.to_string());
finalize_packet(packet, 23)
impl ClientBoundPacket for PlayerAbilities {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_byte(self.flags);
encoder.write_float(self.fly_speed);
encoder.write_float(self.fov_modifier);
}
Self::LoginPlay(login_play) => {
login_play.encode(&mut packet);
finalize_packet(packet, 36)
fn packet_id(&self) -> i32 { 0x30 }
}
Self::PluginMessage(plugin_message) => {
plugin_message.encode(&mut packet);
finalize_packet(packet, 21)
#[derive(Debug)]
pub struct Disconnect {
pub reason: serde_json::Value
}
Self::Commands(commands) => {
commands.encode(&mut packet);
finalize_packet(packet, 14)
impl ClientBoundPacket for Disconnect {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_string(262144, &self.reason.to_string())
}
Self::ChunkData(chunk_data) => {
chunk_data.encode(&mut packet);
finalize_packet(packet, 32)
fn packet_id(&self) -> i32 { 0x17 }
}
Self::SyncPlayerPosition(sync_player_position) => {
sync_player_position.encode(&mut packet);
finalize_packet(packet, 56)
#[derive(Debug)]
pub struct SetDefaultSpawnPosition {
pub pos: Position,
pub angle: f32
}
Self::KeepAlive(n) => {
packet.write_long(n);
finalize_packet(packet, 31)
impl ClientBoundPacket for SetDefaultSpawnPosition {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_position(self.pos);
encoder.write_float(self.angle);
}
Self::SetDefaultSpawnPosition(pos, angle) => {
packet.write_position(pos);
packet.write_float(angle);
finalize_packet(packet, 76)
fn packet_id(&self) -> i32 { 0x4c }
}
Self::PlayerAbilities(flags, speed, view) => {
packet.write_byte(flags);
packet.write_float(speed);
packet.write_float(view);
finalize_packet(packet, 48)
}
Self::SystemChatMessage(msg, overlay) => {
packet.write_string(262144, &msg.to_string());
packet.write_bool(overlay);
finalize_packet(packet, 96)
#[derive(Debug)]
pub struct SystemChatMessage {
pub message: serde_json::Value,
pub overlay: bool
}
impl ClientBoundPacket for SystemChatMessage {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_string(262144, &self.message.to_string());
encoder.write_bool(self.overlay);
}
fn packet_id(&self) -> i32 { 0x60 }
}
pub fn encode_packet(packet: impl ClientBoundPacket) -> Vec<u8> {
let mut buffer = Vec::new();
packet.encode(&mut buffer);
finalize_packet(buffer, packet.packet_id())
}

View file

@ -1,10 +1,23 @@
use super::data::PacketEncoder;
use super::{data::PacketEncoder, clientbound::ClientBoundPacket};
#[derive(Debug, Clone)]
pub struct Commands {
nodes: Vec<CommandNode>,
}
impl ClientBoundPacket for Commands {
fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_varint(self.nodes.len() as i32);
for node in &self.nodes {
node.encode(encoder);
}
// root node
encoder.write_varint(0);
}
fn packet_id(&self) -> i32 { 0x0e }
}
impl Commands {
pub fn new() -> Self {
let root = CommandNode {
@ -67,14 +80,6 @@ impl Commands {
self.nodes[node as usize].children.push(child);
}
pub fn encode(&self, encoder: &mut impl PacketEncoder) {
encoder.write_varint(self.nodes.len() as i32);
for node in &self.nodes {
node.encode(encoder);
}
// root node
encoder.write_varint(0);
}
}
pub enum NodeError {

View file

@ -60,6 +60,25 @@ impl ChatMessage {
}
}
#[derive(Debug)]
pub struct LoginPluginResponse {
pub id: i32,
pub data: Option<Vec<u8>>,
}
impl LoginPluginResponse {
pub fn decode(mut decoder: PacketDecoder) -> Self {
let id = decoder.read_varint();
let success = decoder.read_bool();
let data = if success {
Some(decoder.read_to_end().to_vec())
} else {
None
};
Self { id, data }
}
}
#[derive(Debug)]
pub enum ServerBoundPacket {
Unknown(i32),
@ -71,7 +90,7 @@ pub enum ServerBoundPacket {
PingRequest(i64),
// login
LoginStart(LoginStart),
LoginPluginResponse { id: i32, data: Option<Vec<u8>> },
LoginPluginResponse(LoginPluginResponse),
// play
ChatMessage(ChatMessage),
ChatCommand(ChatMessage),
@ -98,17 +117,11 @@ impl ServerBoundPacket {
ServerBoundPacket::LoginStart(LoginStart::decode(decoder))
},
(NS::Login, 2) => {
let id = decoder.read_varint();
let success = decoder.read_bool();
let data = if success {
Some(decoder.read_to_end().to_vec())
} else {
None
};
if id == -1 {
let lpr = LoginPluginResponse::decode(decoder);
if lpr.id == -1 {
*state = NetworkState::Play;
}
ServerBoundPacket::LoginPluginResponse { id, data }
ServerBoundPacket::LoginPluginResponse(lpr)
},
(NS::Play, 4) => ServerBoundPacket::ChatCommand(ChatMessage::decode(decoder)),
(NS::Play, 5) => ServerBoundPacket::ChatMessage(ChatMessage::decode(decoder)),