mirror of https://github.com/zyllian/classics.git synced 2025-05-10 12:16:40 -07:00

initial commit

This commit is contained in:
Zoey 2024-04-18 19:43:27 -07:00
commit ca94ec10f2
No known key found for this signature in database
GPG key ID: 8611B896D1AAFAF2
12 changed files with 1624 additions and 0 deletions

133
src/packet/client.rs Normal file
View file

@ -0,0 +1,133 @@
use half::f16;
/// enum for a packet which can be received by the client
#[derive(Debug, Clone)]
pub enum ClientPacket {
/// packet sent by a client to identify itself to the server
PlayerIdentification {
/// should always be 0x07 for classic clients >= 0.28
protocol_version: u8,
username: String,
/// currently unverified, original minecraft auth for classic is gone anyway
/// TODO: use verification key field as password protection? investigate
verification_key: String,
_unused: u8,
},
/// packet sent when a client changes a block
/// because changes are reflected immediately, to restrict changes, server must send back its own SetBlock packet with the original block
SetBlock {
x: i16,
y: i16,
z: i16,
/// 0x00 for destroy, 0x01 for create
mode: u8,
block_type: u8,
},
/// sent to update the player's current position and orientation with the server
PositionOrientation {
/// should always be 0xff (-1), referring to the player who sent it
_player_id: i8,
x: f16,
y: f16,
z: f16,
yaw: u8,
pitch: u8,
},
/// packet for the client to send chat messages
Message {
/// should always be 0xff (-1), referring to the player who sent it
player_id: i8,
message: String,
},
}
impl ClientPacket {
// unused currently, so disabled
// /// gets the packet's id
// pub fn get_id(&self) -> u8 {
// match self {
// Self::PlayerIdentification { .. } => 0x00,
// Self::SetBlock { .. } => 0x05,
// Self::PositionOrientation { .. } => 0x08,
// Self::Message { .. } => 0x0d,
// }
// }
/// reads the packet
pub fn read(id: u8, packet: &mut super::PacketReader) -> Option<Self> {
Some(match id {
0x00 => Self::PlayerIdentification {
protocol_version: packet.next_u8()?,
username: packet.next_string()?,
verification_key: packet.next_string()?,
_unused: packet.next_u8()?,
},
0x05 => Self::SetBlock {
x: packet.next_i16()?,
y: packet.next_i16()?,
z: packet.next_i16()?,
mode: packet.next_u8()?,
block_type: packet.next_u8()?,
},
0x08 => Self::PositionOrientation {
_player_id: packet.next_i8()?,
x: packet.next_f16()?,
y: packet.next_f16()?,
z: packet.next_f16()?,
yaw: packet.next_u8()?,
pitch: packet.next_u8()?,
},
0x0d => Self::Message {
player_id: packet.next_i8()?,
message: packet.next_string()?,
},
_ => return None,
})
}
// only needed on the client, so disabled for now
// /// writes the packet
// pub fn write(&self, writer: super::PacketWriter) -> super::PacketWriter {
// match self {
// Self::PlayerIdentification {
// protocol_version,
// username,
// verification_key,
// _unused,
// } => writer
// .write_u8(*protocol_version)
// .write_string(username)
// .write_string(verification_key)
// .write_u8(*_unused),
// Self::SetBlock {
// x,
// y,
// z,
// mode,
// block_type,
// } => writer
// .write_i16(*x)
// .write_i16(*y)
// .write_i16(*z)
// .write_u8(*mode)
// .write_u8(*block_type),
// Self::PositionOrientation {
// player_id,
// x,
// y,
// z,
// yaw,
// pitch,
// } => writer
// .write_i8(*player_id)
// .write_f16(*x)
// .write_f16(*y)
// .write_f16(*z)
// .write_u8(*yaw)
// .write_u8(*pitch),
// Self::Message { player_id, message } => {
// writer.write_i8(*player_id).write_string(message)
// }
// }
// }
}

262
src/packet/server.rs Normal file
View file

@ -0,0 +1,262 @@
use half::f16;
use crate::player::PlayerType;
#[derive(Debug, Clone)]
#[allow(unused)]
pub enum ServerPacket {
/// packet sent as a response to joining clients
ServerIdentification {
/// should be 0x07
protocol_version: u8,
server_name: String,
server_motd: String,
user_type: PlayerType,
},
/// since clients do not notify the server when leaving, the ping packet is used to check if the client is still connected
/// TODO: implement pinging? classicube works fine without it
Ping {},
/// informs clients that there is incoming level data
LevelInitialize {},
/// packet to send a chunk (not minecraft chunk) of gzipped level data
LevelDataChunk {
chunk_length: i16,
chunk_data: Vec<u8>,
percent_complete: u8,
},
/// packet sent after chunk data is finished sending containing the level dimensions
LevelFinalize {
x_size: i16,
y_size: i16,
z_size: i16,
},
/// indicates a block change
/// when a player changes a block, their own change is echoed back to them
SetBlock {
x: i16,
y: i16,
z: i16,
block_type: u8,
},
/// packet sent when a new player spawns
/// also contains their spawn point
SpawnPlayer {
player_id: i8,
player_name: String,
x: f16,
y: f16,
z: f16,
yaw: u8,
pitch: u8,
},
/// packet to set a player's position and orientation
SetPositionOrientation {
player_id: i8,
x: f16,
y: f16,
z: f16,
yaw: u8,
pitch: u8,
},
/// packet to update a player's position and orientation
/// TODO: implement?
UpdatePositionOrientation {
player_id: i8,
x_change: f16,
y_change: f16,
z_change: f16,
yaw: u8,
pitch: u8,
},
/// packet to update a player's position
/// TODO: implement?
UpdatePosition {
player_id: i8,
x_change: f16,
y_change: f16,
z_change: f16,
},
/// packet to update a player's orientation
/// TODO: implement?
UpdateOrientation { player_id: i8, yaw: u8, pitch: u8 },
/// packet sent when a player is despawned from the world (i.e. when leaving)
DespawnPlayer { player_id: i8 },
/// packet sent when there's a chat message to go out
Message { player_id: i8, message: String },
/// informs a client that they're being disconnected from the server and why
DisconnectPlayer { disconnect_reason: String },
/// packet sent to a user to inform them that their user type has changed
UpdateUserType {
/// 0x00 for normal, 0x64 for op
user_type: PlayerType,
},
}
impl ServerPacket {
/// gets the packet's id
pub fn get_id(&self) -> u8 {
match self {
Self::ServerIdentification { .. } => 0x00,
Self::Ping {} => 0x01,
Self::LevelInitialize {} => 0x02,
Self::LevelDataChunk { .. } => 0x03,
Self::LevelFinalize { .. } => 0x04,
Self::SetBlock { .. } => 0x06,
Self::SpawnPlayer { .. } => 0x07,
Self::SetPositionOrientation { .. } => 0x08,
Self::UpdatePositionOrientation { .. } => 0x09,
Self::UpdatePosition { .. } => 0x0a,
Self::UpdateOrientation { .. } => 0x0b,
Self::DespawnPlayer { .. } => 0x0c,
Self::Message { .. } => 0x0d,
Self::DisconnectPlayer { .. } => 0x0e,
Self::UpdateUserType { .. } => 0x0f,
}
}
/// writes the packet
pub fn write(&self, writer: super::PacketWriter) -> super::PacketWriter {
match self {
Self::ServerIdentification {
protocol_version,
server_name,
server_motd,
user_type,
} => writer
.write_u8(*protocol_version)
.write_string(server_name)
.write_string(server_motd)
.write_u8(*user_type as u8),
Self::Ping {} => writer,
Self::LevelInitialize {} => writer,
Self::LevelDataChunk {
chunk_length,
chunk_data,
percent_complete,
} => writer
.write_i16(*chunk_length)
.write_array(chunk_data)
.write_u8(*percent_complete),
Self::LevelFinalize {
x_size,
y_size,
z_size,
} => writer
.write_i16(*x_size)
.write_i16(*y_size)
.write_i16(*z_size),
Self::SetBlock {
x,
y,
z,
block_type,
} => writer
.write_i16(*x)
.write_i16(*y)
.write_i16(*z)
.write_u8(*block_type),
Self::SpawnPlayer {
player_id,
player_name,
x,
y,
z,
yaw,
pitch,
} => writer
.write_i8(*player_id)
.write_string(player_name)
.write_f16(*x)
.write_f16(*y)
.write_f16(*z)
.write_u8(*yaw)
.write_u8(*pitch),
Self::SetPositionOrientation {
player_id,
x,
y,
z,
yaw,
pitch,
} => writer
.write_i8(*player_id)
.write_f16(*x)
.write_f16(*y)
.write_f16(*z)
.write_u8(*yaw)
.write_u8(*pitch),
Self::UpdatePositionOrientation {
player_id,
x_change,
y_change,
z_change,
yaw,
pitch,
} => writer
.write_i8(*player_id)
.write_f16(*x_change)
.write_f16(*y_change)
.write_f16(*z_change)
.write_u8(*yaw)
.write_u8(*pitch),
Self::UpdatePosition {
player_id,
x_change,
y_change,
z_change,
} => writer
.write_i8(*player_id)
.write_f16(*x_change)
.write_f16(*y_change)
.write_f16(*z_change),
Self::UpdateOrientation {
player_id,
yaw,
pitch,
} => writer.write_i8(*player_id).write_u8(*yaw).write_u8(*pitch),
Self::DespawnPlayer { player_id } => writer.write_i8(*player_id),
Self::Message { player_id, message } => {
writer.write_i8(*player_id).write_string(message)
}
Self::DisconnectPlayer { disconnect_reason } => writer.write_string(disconnect_reason),
Self::UpdateUserType { user_type } => writer.write_u8(*user_type as u8),
}
}
/// gets the player id contained in the packet, if any
pub fn get_player_id(&self) -> Option<i8> {
Some(match self {
Self::SpawnPlayer { player_id, .. } => *player_id,
Self::SetPositionOrientation { player_id, .. } => *player_id,
Self::UpdatePositionOrientation { player_id, .. } => *player_id,
Self::UpdatePosition { player_id, .. } => *player_id,
Self::UpdateOrientation { player_id, .. } => *player_id,
Self::DespawnPlayer { player_id, .. } => *player_id,
Self::Message { player_id, .. } => *player_id,
_ => return None,
})
}
/// sets the player id in the packet if possible
pub fn set_player_id(&mut self, new_player_id: i8) {
match self {
Self::SpawnPlayer { player_id, .. } => *player_id = new_player_id,
Self::SetPositionOrientation { player_id, .. } => *player_id = new_player_id,
Self::UpdatePositionOrientation { player_id, .. } => *player_id = new_player_id,
Self::UpdatePosition { player_id, .. } => *player_id = new_player_id,
Self::UpdateOrientation { player_id, .. } => *player_id = new_player_id,
Self::DespawnPlayer { player_id, .. } => *player_id = new_player_id,
Self::Message { player_id, .. } => *player_id = new_player_id,
_ => {}
}
}
/// gets whether this packet should echo back to the current player
pub fn should_echo(&self) -> bool {
matches!(
self,
Self::SetBlock { .. } | Self::SpawnPlayer { .. } | Self::Message { .. }
)
}
}