use bytes::Bytes;
use ghast_protocol::types::ByteString;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Vec3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl Vec3 {
#[must_use]
pub const fn new(x: f64, y: f64, z: f64) -> Self {
Vec3 { x, y, z }
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Pose {
pub pos: Vec3,
pub yaw: f32,
pub pitch: f32,
pub motion: Vec3,
pub on_ground: bool,
pub flying: bool,
pub sneaking: bool,
pub sprinting: bool,
}
impl Pose {
#[must_use]
pub fn eyes(&self, eye_height: f64) -> Vec3 {
Vec3::new(self.pos.x, self.pos.y + eye_height, self.pos.z)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Controls {
pub forward: bool,
pub back: bool,
pub left: bool,
pub right: bool,
pub jump: bool,
pub sneak: bool,
pub sprint: bool,
}
impl Controls {
pub const NONE: Controls = Controls {
forward: false,
back: false,
left: false,
right: false,
jump: false,
sneak: false,
sprint: false,
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ChatPosition {
Chat,
System,
ActionBar,
Other(i8),
}
impl ChatPosition {
#[must_use]
pub fn from_wire(v: i8) -> Self {
match v {
0 => Self::Chat,
1 => Self::System,
2 => Self::ActionBar,
v => Self::Other(v),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChatMessage {
pub json: ByteString,
pub position: ChatPosition,
pub sender: Option<Uuid>,
}
impl ChatMessage {
#[must_use]
pub fn text(&self) -> String {
ghast_protocol::chat::to_plain_text(&self.json)
}
#[must_use]
pub fn plain(&self) -> String {
ghast_protocol::chat::strip_formatting(&self.text())
}
}
#[must_use]
pub fn chat_to_plain(json: &str) -> String {
ghast_protocol::chat::strip_formatting(&ghast_protocol::chat::to_plain_text(json))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GameMode {
#[default]
Survival,
Creative,
Adventure,
Spectator,
Other(u8),
}
impl GameMode {
#[must_use]
pub fn from_wire(v: u8) -> Self {
match v & 0x07 {
0 => Self::Survival,
1 => Self::Creative,
2 => Self::Adventure,
3 => Self::Spectator,
_ => Self::Other(v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub struct Abilities {
pub flags: i8,
pub flying_speed: f32,
pub walking_speed: f32,
}
impl Default for Abilities {
fn default() -> Self {
Abilities {
flags: 0,
flying_speed: 0.05,
walking_speed: 0.1,
}
}
}
impl Abilities {
#[must_use]
pub fn may_fly(&self) -> bool {
self.flags & 4 != 0
}
#[must_use]
pub fn flying(&self) -> bool {
self.flags & 2 != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub struct Health {
pub health: f32,
pub food: i32,
pub saturation: f32,
}
impl Default for Health {
fn default() -> Self {
Health {
health: 20.0,
food: 20,
saturation: 5.0,
}
}
}
pub mod teleport_flags {
pub const X: i8 = 0x01;
pub const Y: i8 = 0x02;
pub const Z: i8 = 0x04;
pub const YAW: i8 = 0x08;
pub const PITCH: i8 = 0x10;
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub struct Teleport {
pub x: f64,
pub y: f64,
pub z: f64,
pub yaw: f32,
pub pitch: f32,
pub flags: i8,
pub teleport_id: Option<i32>,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum LoginInbound {
Disconnect(ByteString),
EncryptionRequest {
server_id: ByteString,
public_key: Bytes,
verify_token: Bytes,
},
Compress(i32),
Success {
uuid: Option<Uuid>,
username: ByteString,
},
PluginRequest {
message_id: i32,
channel: ByteString,
},
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct JoinGame {
pub entity_id: i32,
pub game_mode: GameMode,
pub dimension: String,
pub hardcore: bool,
pub dimension_nbt: Option<(ghast_protocol::nbt::RawNbt, ghast_protocol::nbt::RawNbt)>,
pub respawn_screen: bool,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Inbound {
KeepAlive(i64),
JoinGame(JoinGame),
Respawn {
dimension: String,
game_mode: GameMode,
},
Teleport(Teleport),
Chat(ChatMessage),
Disconnect(ByteString),
Health(Health),
Abilities(Abilities),
HeldSlot(i8),
GameState {
reason: u8,
value: f32,
},
Transaction {
window_id: i8,
action: i16,
accepted: bool,
},
OpenWindow(i32),
CloseWindow(i32),
SetCompression(i32),
Velocity {
entity_id: i32,
motion: Vec3,
},
ExplosionPush(Vec3),
MovementSpeed {
entity_id: i32,
base: f64,
modifiers: Vec<(Uuid, f64, i8)>,
},
Effect {
entity_id: i32,
id: i8,
amplifier: i8,
},
EffectRemoved {
entity_id: i32,
id: i8,
},
EntityStatus {
entity_id: i32,
status: i8,
},
Died {
entity_id: i32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum EntityAction {
StartSneaking,
StopSneaking,
StartSprinting,
StopSprinting,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MovePacket {
Flying {
on_ground: bool,
},
Position {
pos: Vec3,
on_ground: bool,
},
Look {
yaw: f32,
pitch: f32,
on_ground: bool,
},
PositionLook {
pos: Vec3,
yaw: f32,
pitch: f32,
on_ground: bool,
},
}
impl MovePacket {
#[must_use]
pub fn trace_id(&self) -> &'static str {
match self {
Self::Flying { .. } => "C03",
Self::Position { .. } => "C04",
Self::Look { .. } => "C05",
Self::PositionLook { .. } => "C06",
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct ClientSettings {
pub locale: String,
pub view_distance: i8,
pub chat_mode: i8,
pub chat_colors: bool,
pub skin_parts: u8,
pub main_hand: i32,
}
impl ClientSettings {
#[must_use]
pub fn with_locale(mut self, locale: impl Into<String>) -> Self {
self.locale = locale.into();
self
}
#[must_use]
pub const fn with_view_distance(mut self, chunks: i8) -> Self {
self.view_distance = chunks;
self
}
#[must_use]
pub const fn with_chat_mode(mut self, mode: i8) -> Self {
self.chat_mode = mode;
self
}
#[must_use]
pub const fn with_chat_colors(mut self, on: bool) -> Self {
self.chat_colors = on;
self
}
#[must_use]
pub const fn with_skin_parts(mut self, mask: u8) -> Self {
self.skin_parts = mask;
self
}
#[must_use]
pub const fn with_main_hand(mut self, hand: i32) -> Self {
self.main_hand = hand;
self
}
}
impl Default for ClientSettings {
fn default() -> Self {
ClientSettings {
locale: "en_US".to_owned(),
view_distance: 8,
chat_mode: 0,
chat_colors: true,
skin_parts: 0x7f,
main_hand: 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Face {
Down,
Up,
North,
South,
West,
East,
}
impl Face {
#[must_use]
pub const fn id(self) -> i8 {
match self {
Face::Down => 0,
Face::Up => 1,
Face::North => 2,
Face::South => 3,
Face::West => 4,
Face::East => 5,
}
}
#[must_use]
pub const fn from_id(id: i8) -> Option<Face> {
Some(match id {
0 => Face::Down,
1 => Face::Up,
2 => Face::North,
3 => Face::South,
4 => Face::West,
5 => Face::East,
_ => return None,
})
}
#[must_use]
pub const fn offset(self, p: ghast_protocol::BlockPos) -> ghast_protocol::BlockPos {
let (x, y, z) = match self {
Face::Down => (0, -1, 0),
Face::Up => (0, 1, 0),
Face::North => (0, 0, -1),
Face::South => (0, 0, 1),
Face::West => (-1, 0, 0),
Face::East => (1, 0, 0),
};
ghast_protocol::BlockPos::new(p.x + x, p.y + y, p.z + z)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum EntityUse {
Interact,
Attack,
InteractAt(Vec3),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DigStatus {
Start,
Abort,
Finish,
ReleaseUseItem,
}
impl DigStatus {
#[must_use]
pub const fn id(self) -> i32 {
match self {
DigStatus::Start => 0,
DigStatus::Abort => 1,
DigStatus::Finish => 2,
DigStatus::ReleaseUseItem => 5,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Outbound {
KeepAlive(i64),
TeleportConfirm(i32),
Move(MovePacket),
EntityAction {
entity_id: i32,
action: EntityAction,
},
Chat(String),
Respawn,
Transaction {
window_id: i8,
action: i16,
accepted: bool,
},
Abilities {
flags: i8,
flying_speed: f32,
walking_speed: f32,
},
HeldSlot(i16),
Swing,
Dig {
status: DigStatus,
pos: ghast_protocol::BlockPos,
face: Face,
},
Place {
pos: ghast_protocol::BlockPos,
face: Face,
cursor: Vec3,
held: Option<ghast_inventory::Stack>,
off_hand: bool,
},
UseItem {
held: Option<ghast_inventory::Stack>,
off_hand: bool,
},
SwingOffHand,
UseEntity {
target: i32,
action: EntityUse,
off_hand: bool,
sneaking: bool,
},
Settings(ClientSettings),
Inventory(ghast_inventory::Outgoing),
Brand(String),
}