mirror of
https://github.com/zyllian/classics.git
synced 2025-01-18 11:47:14 -08:00
This commit is contained in:
parent
d53f37d664
commit
be81b8d581
11 changed files with 199 additions and 45 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -126,6 +126,7 @@ dependencies = [
|
||||||
"safer-bytes",
|
"safer-bytes",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"strum",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -191,6 +192,12 @@ dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -424,6 +431,12 @@ version = "0.1.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
@ -503,6 +516,28 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.60"
|
version = "2.0.60"
|
||||||
|
|
|
@ -17,4 +17,5 @@ rand = "0.8"
|
||||||
safer-bytes = "0.2"
|
safer-bytes = "0.2"
|
||||||
serde = {version = "1", features = ["derive"]}
|
serde = {version = "1", features = ["derive"]}
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
tokio = {version = "1", features = ["full"]}
|
tokio = {version = "1", features = ["full"]}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
packet::{server::ServerPacket, STRING_LENGTH},
|
packet::{server::ServerPacket, ExtBitmask, STRING_LENGTH},
|
||||||
player::PlayerType,
|
player::PlayerType,
|
||||||
server::{
|
server::{
|
||||||
config::{ConfigCoordinatesWithOrientation, ServerProtectionMode},
|
config::{ConfigCoordinatesWithOrientation, ServerProtectionMode},
|
||||||
|
network::set_player_inventory,
|
||||||
ServerData,
|
ServerData,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -87,7 +88,10 @@ impl<'m> Command<'m> {
|
||||||
CMD_SAY => Self::Say { message: arguments },
|
CMD_SAY => Self::Say { message: arguments },
|
||||||
CMD_SETPERM => Self::SetPermissions {
|
CMD_SETPERM => Self::SetPermissions {
|
||||||
player_username: Self::next_string(&mut arguments)?,
|
player_username: Self::next_string(&mut arguments)?,
|
||||||
permissions: arguments.trim().try_into()?,
|
permissions: arguments
|
||||||
|
.trim()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| format!("&cUnknown permissions type: {arguments}"))?,
|
||||||
},
|
},
|
||||||
CMD_KICK => {
|
CMD_KICK => {
|
||||||
let username = Self::next_string(&mut arguments)?;
|
let username = Self::next_string(&mut arguments)?;
|
||||||
|
@ -297,7 +301,7 @@ impl<'m> Command<'m> {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
let perm_string = serde_json::to_string(&permissions).expect("should never fail");
|
let perm_string: &'static str = permissions.into();
|
||||||
|
|
||||||
if let Some(current) = data.config.player_perms.get(player_username) {
|
if let Some(current) = data.config.player_perms.get(player_username) {
|
||||||
if *current >= player_perms {
|
if *current >= player_perms {
|
||||||
|
@ -329,6 +333,15 @@ impl<'m> Command<'m> {
|
||||||
player_id: p.id,
|
player_id: p.id,
|
||||||
message: format!("Your permissions have been set to {perm_string}"),
|
message: format!("Your permissions have been set to {perm_string}"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if p.extensions.contains(ExtBitmask::InventoryOrder) {
|
||||||
|
set_player_inventory(
|
||||||
|
p.permissions,
|
||||||
|
p.extensions,
|
||||||
|
p.custom_blocks_support_level,
|
||||||
|
&mut p.packets_to_send,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
messages.push(format!(
|
messages.push(format!(
|
||||||
"Set permissions for {player_username} to {perm_string}"
|
"Set permissions for {player_username} to {perm_string}"
|
||||||
|
|
|
@ -4,6 +4,9 @@ use internment::Intern;
|
||||||
|
|
||||||
use crate::player::PlayerType;
|
use crate::player::PlayerType;
|
||||||
|
|
||||||
|
/// the level of custom blocks supported by the server
|
||||||
|
pub const CUSTOM_BLOCKS_SUPPORT_LEVEL: u8 = 1;
|
||||||
|
|
||||||
/// information about all blocks implemented
|
/// information about all blocks implemented
|
||||||
pub static BLOCK_INFO: LazyLock<BTreeMap<u8, BlockInfo>> = LazyLock::new(|| {
|
pub static BLOCK_INFO: LazyLock<BTreeMap<u8, BlockInfo>> = LazyLock::new(|| {
|
||||||
[
|
[
|
||||||
|
@ -19,7 +22,7 @@ pub static BLOCK_INFO: LazyLock<BTreeMap<u8, BlockInfo>> = LazyLock::new(|| {
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
0x07,
|
0x07,
|
||||||
BlockInfo::new("bedrock").perm(PlayerType::Operator, PlayerType::Operator),
|
BlockInfo::new("bedrock").perm(PlayerType::Moderator, PlayerType::Moderator),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
0x08,
|
0x08,
|
||||||
|
@ -28,13 +31,13 @@ pub static BLOCK_INFO: LazyLock<BTreeMap<u8, BlockInfo>> = LazyLock::new(|| {
|
||||||
stationary: 0x09,
|
stationary: 0x09,
|
||||||
ticks_to_spread: 3,
|
ticks_to_spread: 3,
|
||||||
})
|
})
|
||||||
.perm(PlayerType::Operator, PlayerType::Normal),
|
.perm(PlayerType::Moderator, PlayerType::Normal),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
0x09,
|
0x09,
|
||||||
BlockInfo::new("water_stationary")
|
BlockInfo::new("water_stationary")
|
||||||
.block_type(BlockType::FluidStationary { moving: 0x08 })
|
.block_type(BlockType::FluidStationary { moving: 0x08 })
|
||||||
.perm(PlayerType::Operator, PlayerType::Normal),
|
.perm(PlayerType::Moderator, PlayerType::Normal),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
0x0a,
|
0x0a,
|
||||||
|
@ -43,13 +46,13 @@ pub static BLOCK_INFO: LazyLock<BTreeMap<u8, BlockInfo>> = LazyLock::new(|| {
|
||||||
stationary: 0x0b,
|
stationary: 0x0b,
|
||||||
ticks_to_spread: 15,
|
ticks_to_spread: 15,
|
||||||
})
|
})
|
||||||
.perm(PlayerType::Operator, PlayerType::Normal),
|
.perm(PlayerType::Moderator, PlayerType::Normal),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
0x0b,
|
0x0b,
|
||||||
BlockInfo::new("lava_stationary")
|
BlockInfo::new("lava_stationary")
|
||||||
.block_type(BlockType::FluidStationary { moving: 0x0a })
|
.block_type(BlockType::FluidStationary { moving: 0x0a })
|
||||||
.perm(PlayerType::Operator, PlayerType::Normal),
|
.perm(PlayerType::Moderator, PlayerType::Normal),
|
||||||
),
|
),
|
||||||
(0x0c, BlockInfo::new("sand")),
|
(0x0c, BlockInfo::new("sand")),
|
||||||
(0x0d, BlockInfo::new("gravel")),
|
(0x0d, BlockInfo::new("gravel")),
|
||||||
|
|
|
@ -205,6 +205,9 @@ impl ExtBitmask {
|
||||||
Self::EnvWeatherType => {
|
Self::EnvWeatherType => {
|
||||||
ExtInfo::new("EnvWeatherType".to_string(), 1, Self::EnvWeatherType)
|
ExtInfo::new("EnvWeatherType".to_string(), 1, Self::EnvWeatherType)
|
||||||
}
|
}
|
||||||
|
Self::InventoryOrder => {
|
||||||
|
ExtInfo::new("InventoryOrder".to_string(), 1, Self::InventoryOrder)
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ pub enum ExtendedClientPacket {
|
||||||
},
|
},
|
||||||
/// packet containing a supported extension name and version
|
/// packet containing a supported extension name and version
|
||||||
ExtEntry { ext_name: String, version: i32 },
|
ExtEntry { ext_name: String, version: i32 },
|
||||||
|
/// packet containing the support level for custom blocks from the client
|
||||||
|
CustomBlockSupportLevel { support_level: u8 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtendedClientPacket {
|
impl ExtendedClientPacket {
|
||||||
|
@ -18,6 +20,7 @@ impl ExtendedClientPacket {
|
||||||
Some(match id {
|
Some(match id {
|
||||||
0x10 => STRING_LENGTH + 2,
|
0x10 => STRING_LENGTH + 2,
|
||||||
0x11 => STRING_LENGTH + 4,
|
0x11 => STRING_LENGTH + 4,
|
||||||
|
0x13 => 1,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -36,6 +39,9 @@ impl ExtendedClientPacket {
|
||||||
ext_name: buf.try_get_string().ok()?,
|
ext_name: buf.try_get_string().ok()?,
|
||||||
version: buf.try_get_i32().ok()?,
|
version: buf.try_get_i32().ok()?,
|
||||||
},
|
},
|
||||||
|
0x13 => Self::CustomBlockSupportLevel {
|
||||||
|
support_level: buf.try_get_u8().ok()?,
|
||||||
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use half::f16;
|
use half::f16;
|
||||||
|
|
||||||
use crate::{level::WeatherType, player::PlayerType, SERVER_NAME};
|
use crate::{
|
||||||
|
level::{block::CUSTOM_BLOCKS_SUPPORT_LEVEL, WeatherType},
|
||||||
|
player::PlayerType,
|
||||||
|
SERVER_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
use super::ExtBitmask;
|
use super::ExtBitmask;
|
||||||
|
|
||||||
|
@ -17,9 +21,9 @@ pub enum ServerPacket {
|
||||||
},
|
},
|
||||||
/// since clients do not notify the server when leaving, the ping packet is used to check if the client is still connected
|
/// 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
|
/// TODO: implement pinging? classicube works fine without it
|
||||||
Ping {},
|
Ping,
|
||||||
/// informs clients that there is incoming level data
|
/// informs clients that there is incoming level data
|
||||||
LevelInitialize {},
|
LevelInitialize,
|
||||||
/// packet to send a chunk (not minecraft chunk) of gzipped level data
|
/// packet to send a chunk (not minecraft chunk) of gzipped level data
|
||||||
LevelDataChunk {
|
LevelDataChunk {
|
||||||
chunk_length: i16,
|
chunk_length: i16,
|
||||||
|
@ -96,13 +100,17 @@ pub enum ServerPacket {
|
||||||
|
|
||||||
// extension packets
|
// extension packets
|
||||||
/// packet to send info about the server's extensions
|
/// packet to send info about the server's extensions
|
||||||
ExtInfo {},
|
ExtInfo,
|
||||||
/// packet to send info about an extension on the server
|
/// packet to send info about an extension on the server
|
||||||
ExtEntry { ext_name: String, version: i32 },
|
ExtEntry { ext_name: String, version: i32 },
|
||||||
|
/// packet to send the server's supported custom blocks
|
||||||
|
CustomBlockSupportLevel,
|
||||||
/// packet to set a player's currently held block
|
/// packet to set a player's currently held block
|
||||||
HoldThis { block: u8, prevent_change: bool },
|
HoldThis { block: u8, prevent_change: bool },
|
||||||
/// informs the client that it should update the current weather
|
/// informs the client that it should update the current weather
|
||||||
EnvWeatherType { weather_type: WeatherType },
|
EnvWeatherType { weather_type: WeatherType },
|
||||||
|
/// packet to set a block's position in the client's inventory
|
||||||
|
SetInventoryOrder { order: u8, block: u8 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerPacket {
|
impl ServerPacket {
|
||||||
|
@ -110,8 +118,8 @@ impl ServerPacket {
|
||||||
pub fn get_id(&self) -> u8 {
|
pub fn get_id(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
Self::ServerIdentification { .. } => 0x00,
|
Self::ServerIdentification { .. } => 0x00,
|
||||||
Self::Ping {} => 0x01,
|
Self::Ping => 0x01,
|
||||||
Self::LevelInitialize {} => 0x02,
|
Self::LevelInitialize => 0x02,
|
||||||
Self::LevelDataChunk { .. } => 0x03,
|
Self::LevelDataChunk { .. } => 0x03,
|
||||||
Self::LevelFinalize { .. } => 0x04,
|
Self::LevelFinalize { .. } => 0x04,
|
||||||
Self::SetBlock { .. } => 0x06,
|
Self::SetBlock { .. } => 0x06,
|
||||||
|
@ -125,10 +133,12 @@ impl ServerPacket {
|
||||||
Self::DisconnectPlayer { .. } => 0x0e,
|
Self::DisconnectPlayer { .. } => 0x0e,
|
||||||
Self::UpdateUserType { .. } => 0x0f,
|
Self::UpdateUserType { .. } => 0x0f,
|
||||||
|
|
||||||
Self::ExtInfo {} => 0x10,
|
Self::ExtInfo => 0x10,
|
||||||
Self::ExtEntry { .. } => 0x11,
|
Self::ExtEntry { .. } => 0x11,
|
||||||
|
Self::CustomBlockSupportLevel { .. } => 0x13,
|
||||||
Self::HoldThis { .. } => 0x14,
|
Self::HoldThis { .. } => 0x14,
|
||||||
Self::EnvWeatherType { .. } => 0x1f,
|
Self::EnvWeatherType { .. } => 0x1f,
|
||||||
|
Self::SetInventoryOrder { .. } => 0x2c,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,8 +155,8 @@ impl ServerPacket {
|
||||||
.write_string(server_name)
|
.write_string(server_name)
|
||||||
.write_string(server_motd)
|
.write_string(server_motd)
|
||||||
.write_u8(user_type.into()),
|
.write_u8(user_type.into()),
|
||||||
Self::Ping {} => writer,
|
Self::Ping => writer,
|
||||||
Self::LevelInitialize {} => writer,
|
Self::LevelInitialize => writer,
|
||||||
Self::LevelDataChunk {
|
Self::LevelDataChunk {
|
||||||
chunk_length,
|
chunk_length,
|
||||||
chunk_data,
|
chunk_data,
|
||||||
|
@ -239,17 +249,19 @@ impl ServerPacket {
|
||||||
Self::DisconnectPlayer { disconnect_reason } => writer.write_string(disconnect_reason),
|
Self::DisconnectPlayer { disconnect_reason } => writer.write_string(disconnect_reason),
|
||||||
Self::UpdateUserType { user_type } => writer.write_u8(user_type.into()),
|
Self::UpdateUserType { user_type } => writer.write_u8(user_type.into()),
|
||||||
|
|
||||||
Self::ExtInfo {} => writer
|
Self::ExtInfo => writer
|
||||||
.write_string(SERVER_NAME)
|
.write_string(SERVER_NAME)
|
||||||
.write_i16(ExtBitmask::all().all_contained_info().len() as i16),
|
.write_i16(ExtBitmask::all().all_contained_info().len() as i16),
|
||||||
Self::ExtEntry { ext_name, version } => {
|
Self::ExtEntry { ext_name, version } => {
|
||||||
writer.write_string(ext_name).write_i32(*version)
|
writer.write_string(ext_name).write_i32(*version)
|
||||||
}
|
}
|
||||||
|
Self::CustomBlockSupportLevel => writer.write_u8(CUSTOM_BLOCKS_SUPPORT_LEVEL),
|
||||||
Self::HoldThis {
|
Self::HoldThis {
|
||||||
block,
|
block,
|
||||||
prevent_change,
|
prevent_change,
|
||||||
} => writer.write_u8(*block).write_bool(*prevent_change),
|
} => writer.write_u8(*block).write_bool(*prevent_change),
|
||||||
Self::EnvWeatherType { weather_type } => writer.write_u8(weather_type.into()),
|
Self::EnvWeatherType { weather_type } => writer.write_u8(weather_type.into()),
|
||||||
|
Self::SetInventoryOrder { order, block } => writer.write_u8(*order).write_u8(*block),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ pub struct Player {
|
||||||
pub _addr: SocketAddr,
|
pub _addr: SocketAddr,
|
||||||
/// the player's supported extensions
|
/// the player's supported extensions
|
||||||
pub extensions: ExtBitmask,
|
pub extensions: ExtBitmask,
|
||||||
|
/// the level of custom blocks this client supports
|
||||||
|
pub custom_blocks_support_level: u8,
|
||||||
/// queue of packets to be sent to this player
|
/// queue of packets to be sent to this player
|
||||||
pub packets_to_send: Vec<ServerPacket>,
|
pub packets_to_send: Vec<ServerPacket>,
|
||||||
/// whether this player should be kicked and the message to give
|
/// whether this player should be kicked and the message to give
|
||||||
|
@ -36,7 +38,20 @@ pub struct Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// enum describing types of players
|
/// enum describing types of players
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
strum::EnumString,
|
||||||
|
strum::IntoStaticStr,
|
||||||
|
)]
|
||||||
|
#[strum(ascii_case_insensitive)]
|
||||||
pub enum PlayerType {
|
pub enum PlayerType {
|
||||||
/// a normal player
|
/// a normal player
|
||||||
Normal,
|
Normal,
|
||||||
|
@ -61,16 +76,3 @@ impl From<&PlayerType> for u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for PlayerType {
|
|
||||||
type Error = String;
|
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
||||||
Ok(match value.to_lowercase().as_str() {
|
|
||||||
"normal" => Self::Normal,
|
|
||||||
"moderator" => Self::Moderator,
|
|
||||||
"operator" => Self::Operator,
|
|
||||||
value => return Err(format!("Unknown permissions type: {value}")),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod config;
|
pub mod config;
|
||||||
mod network;
|
pub(crate) mod network;
|
||||||
|
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
client::ClientPacket, server::ServerPacket, ExtBitmask, PacketWriter, ARRAY_LENGTH,
|
client::ClientPacket, server::ServerPacket, ExtBitmask, PacketWriter, ARRAY_LENGTH,
|
||||||
EXTENSION_MAGIC_NUMBER,
|
EXTENSION_MAGIC_NUMBER,
|
||||||
},
|
},
|
||||||
player::Player,
|
player::{Player, PlayerType},
|
||||||
server::config::ServerProtectionMode,
|
server::config::ServerProtectionMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,6 +49,32 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// gets the packets needed to update a player's inventory
|
||||||
|
pub(crate) fn set_player_inventory(
|
||||||
|
perms: PlayerType,
|
||||||
|
extensions: ExtBitmask,
|
||||||
|
custom_blocks_support_level: u8,
|
||||||
|
packets_queue: &mut Vec<ServerPacket>,
|
||||||
|
) {
|
||||||
|
let custom_blocks =
|
||||||
|
extensions.contains(ExtBitmask::CustomBlocks) && custom_blocks_support_level == 1;
|
||||||
|
assert!(
|
||||||
|
custom_blocks_support_level <= 1,
|
||||||
|
"support not implemented for additional custom block levels"
|
||||||
|
);
|
||||||
|
for (id, info) in &*BLOCK_INFO {
|
||||||
|
if !custom_blocks && *id > 49 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let block = if info.place_permissions <= perms {
|
||||||
|
*id
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
packets_queue.push(ServerPacket::SetInventoryOrder { order: *id, block });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) async fn handle_stream(
|
pub(super) async fn handle_stream(
|
||||||
mut stream: TcpStream,
|
mut stream: TcpStream,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
|
@ -185,13 +211,17 @@ async fn handle_stream_inner(
|
||||||
pitch: 0,
|
pitch: 0,
|
||||||
permissions: player_type,
|
permissions: player_type,
|
||||||
extensions: ExtBitmask::none(),
|
extensions: ExtBitmask::none(),
|
||||||
|
custom_blocks_support_level: 0,
|
||||||
packets_to_send: Vec::new(),
|
packets_to_send: Vec::new(),
|
||||||
should_be_kicked: None,
|
should_be_kicked: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if magic_number == EXTENSION_MAGIC_NUMBER {
|
if magic_number == EXTENSION_MAGIC_NUMBER {
|
||||||
player.extensions = extensions::get_supported_extensions(stream).await?;
|
(player.extensions, player.custom_blocks_support_level) =
|
||||||
|
extensions::get_supported_extensions(stream).await?;
|
||||||
}
|
}
|
||||||
|
let extensions = player.extensions;
|
||||||
|
let custom_blocks_support_level = player.custom_blocks_support_level;
|
||||||
|
|
||||||
reply_queue.push(ServerPacket::ServerIdentification {
|
reply_queue.push(ServerPacket::ServerIdentification {
|
||||||
protocol_version: 0x07,
|
protocol_version: 0x07,
|
||||||
|
@ -201,9 +231,12 @@ async fn handle_stream_inner(
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("generating level packets");
|
println!("generating level packets");
|
||||||
reply_queue.extend(build_level_packets(&data.level).into_iter());
|
reply_queue.extend(
|
||||||
|
build_level_packets(&data.level, extensions, custom_blocks_support_level)
|
||||||
|
.into_iter(),
|
||||||
|
);
|
||||||
|
|
||||||
if player.extensions.contains(ExtBitmask::EnvWeatherType) {
|
if extensions.contains(ExtBitmask::EnvWeatherType) {
|
||||||
reply_queue.push(ServerPacket::EnvWeatherType {
|
reply_queue.push(ServerPacket::EnvWeatherType {
|
||||||
weather_type: data.level.weather,
|
weather_type: data.level.weather,
|
||||||
});
|
});
|
||||||
|
@ -263,6 +296,15 @@ async fn handle_stream_inner(
|
||||||
reply_queue.push(ServerPacket::UpdateUserType {
|
reply_queue.push(ServerPacket::UpdateUserType {
|
||||||
user_type: player_type,
|
user_type: player_type,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if extensions.contains(ExtBitmask::InventoryOrder) {
|
||||||
|
set_player_inventory(
|
||||||
|
player_type,
|
||||||
|
extensions,
|
||||||
|
custom_blocks_support_level,
|
||||||
|
&mut reply_queue,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ClientPacket::SetBlock {
|
ClientPacket::SetBlock {
|
||||||
x,
|
x,
|
||||||
|
@ -422,14 +464,30 @@ async fn handle_stream_inner(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// helper to put together packets that need to be sent to send full level data for the given level
|
/// helper to put together packets that need to be sent to send full level data for the given level
|
||||||
fn build_level_packets(level: &Level) -> Vec<ServerPacket> {
|
fn build_level_packets(
|
||||||
|
level: &Level,
|
||||||
|
extensions: ExtBitmask,
|
||||||
|
custom_blocks_support_level: u8,
|
||||||
|
) -> Vec<ServerPacket> {
|
||||||
let mut packets: Vec<ServerPacket> = vec![ServerPacket::LevelInitialize {}];
|
let mut packets: Vec<ServerPacket> = vec![ServerPacket::LevelInitialize {}];
|
||||||
|
|
||||||
// TODO: the type conversions in here may be weird idk
|
let custom_blocks =
|
||||||
|
extensions.contains(ExtBitmask::CustomBlocks) && custom_blocks_support_level >= 1;
|
||||||
|
|
||||||
let volume = level.x_size * level.y_size * level.z_size;
|
let volume = level.x_size * level.y_size * level.z_size;
|
||||||
let mut data = Vec::with_capacity(volume + 4);
|
let mut data = Vec::with_capacity(volume + 4);
|
||||||
data.extend_from_slice(&(volume as i32).to_be_bytes());
|
data.extend_from_slice(&(volume as i32).to_be_bytes());
|
||||||
data.extend_from_slice(&level.blocks);
|
data.extend(level.blocks.iter().copied().map(|b| {
|
||||||
|
if custom_blocks || b <= 49 {
|
||||||
|
b
|
||||||
|
} else {
|
||||||
|
BLOCK_INFO
|
||||||
|
.get(&b)
|
||||||
|
.expect("missing block")
|
||||||
|
.fallback
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
let mut e = GzEncoder::new(Vec::new(), Compression::best());
|
let mut e = GzEncoder::new(Vec::new(), Compression::best());
|
||||||
e.write_all(&data).expect("failed to gzip level data");
|
e.write_all(&data).expect("failed to gzip level data");
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
use crate::packet::{
|
use crate::{
|
||||||
client::ClientPacket, client_extended::ExtendedClientPacket, server::ServerPacket, ExtBitmask,
|
level::block::CUSTOM_BLOCKS_SUPPORT_LEVEL,
|
||||||
ExtInfo,
|
packet::{
|
||||||
|
client::ClientPacket, client_extended::ExtendedClientPacket, server::ServerPacket,
|
||||||
|
ExtBitmask, ExtInfo,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{next_packet, write_packets};
|
use super::{next_packet, write_packets};
|
||||||
|
|
||||||
pub async fn get_supported_extensions(stream: &mut TcpStream) -> std::io::Result<ExtBitmask> {
|
pub async fn get_supported_extensions(stream: &mut TcpStream) -> std::io::Result<(ExtBitmask, u8)> {
|
||||||
let extensions = ExtBitmask::all().all_contained_info();
|
let extensions = ExtBitmask::all().all_contained_info();
|
||||||
|
|
||||||
write_packets(
|
write_packets(
|
||||||
|
@ -61,5 +64,23 @@ pub async fn get_supported_extensions(stream: &mut TcpStream) -> std::io::Result
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(ExtBitmask::none(), |acc, ext| acc | ext.bitmask);
|
.fold(ExtBitmask::none(), |acc, ext| acc | ext.bitmask);
|
||||||
|
|
||||||
Ok(final_bitmask)
|
let custom_blocks_support_level = if final_bitmask.contains(ExtBitmask::CustomBlocks) {
|
||||||
|
write_packets(
|
||||||
|
stream,
|
||||||
|
Some(ServerPacket::CustomBlockSupportLevel).into_iter(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
if let Some(ClientPacket::Extended(ExtendedClientPacket::CustomBlockSupportLevel {
|
||||||
|
support_level,
|
||||||
|
})) = next_packet(stream).await?
|
||||||
|
{
|
||||||
|
support_level.min(CUSTOM_BLOCKS_SUPPORT_LEVEL)
|
||||||
|
} else {
|
||||||
|
panic!("expected CustomBlockSupportLevel packet!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((final_bitmask, custom_blocks_support_level))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue