mirror of
https://github.com/zyllian/classics.git
synced 2025-01-18 03:32:41 -08:00
implement commands system
This commit is contained in:
parent
37c82d8755
commit
19116ee308
5 changed files with 248 additions and 57 deletions
77
src/command.rs
Normal file
77
src/command.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
use crate::player::PlayerType;
|
||||||
|
|
||||||
|
const CMD_ME: &str = "me";
|
||||||
|
const CMD_SAY: &str = "say";
|
||||||
|
const CMD_SET_PERM: &str = "set-perm";
|
||||||
|
|
||||||
|
/// enum for possible commands
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Command<'m> {
|
||||||
|
/// for rp, prefixes `action` with `*<username>`
|
||||||
|
///
|
||||||
|
/// i.e. `/me says hello` becomes `*<username> says hello`
|
||||||
|
Me { action: &'m str },
|
||||||
|
/// sends a message prefixed with `[SERVER]` instead of the player's username
|
||||||
|
Say { message: &'m str },
|
||||||
|
/// sets permissions for a player
|
||||||
|
SetPermissions {
|
||||||
|
player_username: &'m str,
|
||||||
|
permissions: PlayerType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'m> Command<'m> {
|
||||||
|
pub const PREFIX: char = '/';
|
||||||
|
|
||||||
|
pub fn parse(input: &'m str) -> Result<Command, String> {
|
||||||
|
let (command_name, mut arguments) = input.split_once(' ').unwrap_or((input, ""));
|
||||||
|
Ok(match command_name {
|
||||||
|
CMD_ME => Self::Me { action: arguments },
|
||||||
|
CMD_SAY => Self::Say { message: arguments },
|
||||||
|
CMD_SET_PERM => Self::SetPermissions {
|
||||||
|
player_username: Self::next_string(&mut arguments)?,
|
||||||
|
permissions: arguments.trim().try_into()?,
|
||||||
|
},
|
||||||
|
_ => return Err(format!("Unknown command: {command_name}")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn perms_required(&self) -> PlayerType {
|
||||||
|
match self {
|
||||||
|
Self::Me { .. } => PlayerType::Normal,
|
||||||
|
_ => PlayerType::Moderator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_string(args: &mut &'m str) -> Result<&'m str, String> {
|
||||||
|
if args.is_empty() {
|
||||||
|
return Err("Missing argument".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (start_index, end_index, extra) = if args.starts_with('"') {
|
||||||
|
let mut end_index = 1;
|
||||||
|
let mut extra = 1;
|
||||||
|
while end_index < args.len() {
|
||||||
|
if let Some(index) = args[end_index..].find('"') {
|
||||||
|
end_index += index;
|
||||||
|
if &args[end_index - 1..=end_index - 1] == "\\" {
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
end_index = args.len();
|
||||||
|
extra = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(1, end_index, extra)
|
||||||
|
} else {
|
||||||
|
(0, args.find(' ').unwrap_or(args.len()), 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = &args[start_index..end_index];
|
||||||
|
*args = &args[end_index + extra..];
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ use server::{
|
||||||
Server,
|
Server,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod command;
|
||||||
mod level;
|
mod level;
|
||||||
mod packet;
|
mod packet;
|
||||||
mod player;
|
mod player;
|
||||||
|
|
|
@ -127,7 +127,7 @@ impl ServerPacket {
|
||||||
.write_u8(*protocol_version)
|
.write_u8(*protocol_version)
|
||||||
.write_string(server_name)
|
.write_string(server_name)
|
||||||
.write_string(server_motd)
|
.write_string(server_motd)
|
||||||
.write_u8(*user_type as u8),
|
.write_u8(user_type.into()),
|
||||||
Self::Ping {} => writer,
|
Self::Ping {} => writer,
|
||||||
Self::LevelInitialize {} => writer,
|
Self::LevelInitialize {} => writer,
|
||||||
Self::LevelDataChunk {
|
Self::LevelDataChunk {
|
||||||
|
@ -220,7 +220,7 @@ impl ServerPacket {
|
||||||
writer.write_i8(*player_id).write_string(message)
|
writer.write_i8(*player_id).write_string(message)
|
||||||
}
|
}
|
||||||
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 as u8),
|
Self::UpdateUserType { user_type } => writer.write_u8(user_type.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,13 @@ 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)]
|
||||||
#[repr(u8)]
|
|
||||||
pub enum PlayerType {
|
pub enum PlayerType {
|
||||||
/// a normal player
|
/// a normal player
|
||||||
Normal = 0x00,
|
Normal,
|
||||||
|
/// moderator of the server
|
||||||
|
Moderator,
|
||||||
/// a player who's an operator
|
/// a player who's an operator
|
||||||
Operator = 0x64,
|
Operator,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PlayerType {
|
impl Default for PlayerType {
|
||||||
|
@ -47,16 +48,25 @@ impl Default for PlayerType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u8> for PlayerType {
|
impl From<&PlayerType> for u8 {
|
||||||
type Error = ();
|
fn from(val: &PlayerType) -> Self {
|
||||||
|
match val {
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
PlayerType::Normal => 0,
|
||||||
if value == Self::Normal as u8 {
|
PlayerType::Moderator => 0x64,
|
||||||
Ok(Self::Normal)
|
PlayerType::Operator => 0x64,
|
||||||
} else if value == Self::Operator as u8 {
|
|
||||||
Ok(Self::Operator)
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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}")),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use tokio::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
command::Command,
|
||||||
level::{block::BLOCK_INFO, BlockUpdate, Level},
|
level::{block::BLOCK_INFO, BlockUpdate, Level},
|
||||||
packet::{client::ClientPacket, server::ServerPacket, PacketWriter, ARRAY_LENGTH},
|
packet::{client::ClientPacket, server::ServerPacket, PacketWriter, ARRAY_LENGTH},
|
||||||
player::{Player, PlayerType},
|
player::{Player, PlayerType},
|
||||||
|
@ -72,6 +73,24 @@ async fn handle_stream_inner(
|
||||||
let mut read_buf;
|
let mut read_buf;
|
||||||
let mut id_buf;
|
let mut id_buf;
|
||||||
|
|
||||||
|
macro_rules! msg {
|
||||||
|
($message:expr) => {
|
||||||
|
reply_queue.push_back(ServerPacket::Message {
|
||||||
|
player_id: -1,
|
||||||
|
message: $message,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! spread_packet {
|
||||||
|
($data:expr, $packet:expr) => {
|
||||||
|
let packet = $packet;
|
||||||
|
for player in &mut $data.players {
|
||||||
|
player.packets_to_send.push(packet.clone());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let ready = stream
|
let ready = stream
|
||||||
.ready(Interest::READABLE | Interest::WRITABLE)
|
.ready(Interest::READABLE | Interest::WRITABLE)
|
||||||
|
@ -211,10 +230,7 @@ async fn handle_stream_inner(
|
||||||
player.packets_to_send.push(message_packet.clone());
|
player.packets_to_send.push(message_packet.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reply_queue.push_back(ServerPacket::Message {
|
msg!("&dWelcome to the server! Enjoyyyyyy".to_string());
|
||||||
player_id: *own_id,
|
|
||||||
message: "Welcome to the server! Enjoyyyyyy".to_string(),
|
|
||||||
});
|
|
||||||
reply_queue.push_back(ServerPacket::UpdateUserType {
|
reply_queue.push_back(ServerPacket::UpdateUserType {
|
||||||
user_type: PlayerType::Operator,
|
user_type: PlayerType::Operator,
|
||||||
});
|
});
|
||||||
|
@ -241,13 +257,7 @@ async fn handle_stream_inner(
|
||||||
|
|
||||||
let new_block_info = BLOCK_INFO.get(&block_type);
|
let new_block_info = BLOCK_INFO.get(&block_type);
|
||||||
if new_block_info.is_none() {
|
if new_block_info.is_none() {
|
||||||
reply_queue.push_back(ServerPacket::Message {
|
msg!(format!("&cUnknown block ID: 0x{:0x}", block_type));
|
||||||
player_id: -1,
|
|
||||||
message: format!(
|
|
||||||
"Unknown block ID: 0x{:0x}",
|
|
||||||
block_type
|
|
||||||
),
|
|
||||||
});
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let new_block_info = new_block_info.expect("will never fail");
|
let new_block_info = new_block_info.expect("will never fail");
|
||||||
|
@ -266,16 +276,10 @@ async fn handle_stream_inner(
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if player_type < new_block_info.place_permissions {
|
if player_type < new_block_info.place_permissions {
|
||||||
cancel = true;
|
cancel = true;
|
||||||
reply_queue.push_back(ServerPacket::Message {
|
msg!("&cNot allow to place this block.".to_string());
|
||||||
player_id: -1,
|
|
||||||
message: "Not allowed to place this block.".to_string(),
|
|
||||||
});
|
|
||||||
} else if player_type < block_info.break_permissions {
|
} else if player_type < block_info.break_permissions {
|
||||||
cancel = true;
|
cancel = true;
|
||||||
reply_queue.push_back(ServerPacket::Message {
|
msg!("&cNot allowed to break this block.".to_string());
|
||||||
player_id: -1,
|
|
||||||
message: "Not allowed to break this block.".to_string(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if cancel {
|
if cancel {
|
||||||
|
@ -305,33 +309,132 @@ async fn handle_stream_inner(
|
||||||
yaw,
|
yaw,
|
||||||
pitch,
|
pitch,
|
||||||
} => {
|
} => {
|
||||||
let packet = ServerPacket::SetPositionOrientation {
|
|
||||||
player_id: *own_id,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
z,
|
|
||||||
yaw,
|
|
||||||
pitch,
|
|
||||||
};
|
|
||||||
let mut data = data.write().await;
|
let mut data = data.write().await;
|
||||||
for player in &mut data.players {
|
spread_packet!(
|
||||||
player.packets_to_send.push(packet.clone());
|
data,
|
||||||
}
|
ServerPacket::SetPositionOrientation {
|
||||||
|
player_id: *own_id,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ClientPacket::Message { player_id, message } => {
|
ClientPacket::Message { player_id, message } => {
|
||||||
let mut data = data.write().await;
|
let mut data = data.write().await;
|
||||||
println!("{message}");
|
|
||||||
let message = format!(
|
if let Some(message) = message.strip_prefix(Command::PREFIX) {
|
||||||
"&f<{}> {message}",
|
match Command::parse(message) {
|
||||||
data.players
|
Ok(cmd) => {
|
||||||
.iter()
|
let player = data
|
||||||
.find(|p| p.id == *own_id)
|
.players
|
||||||
.expect("should never fail")
|
.iter()
|
||||||
.username
|
.find(|p| p.id == *own_id)
|
||||||
);
|
.expect("missing player");
|
||||||
let packet = ServerPacket::Message { player_id, message };
|
|
||||||
for player in &mut data.players {
|
if cmd.perms_required() > player.player_type {
|
||||||
player.packets_to_send.push(packet.clone());
|
msg!("Permissions do not allow you to use this command".to_string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match cmd {
|
||||||
|
Command::Me { action } => {
|
||||||
|
let message = format!(
|
||||||
|
"&f*{} {action}",
|
||||||
|
data.players
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.id == *own_id)
|
||||||
|
.expect("missing player")
|
||||||
|
.username
|
||||||
|
);
|
||||||
|
spread_packet!(
|
||||||
|
data,
|
||||||
|
ServerPacket::Message {
|
||||||
|
player_id,
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Command::Say { message } => {
|
||||||
|
let message =
|
||||||
|
format!("&d[SERVER] &f{message}");
|
||||||
|
spread_packet!(
|
||||||
|
data,
|
||||||
|
ServerPacket::Message {
|
||||||
|
player_id,
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Command::SetPermissions {
|
||||||
|
player_username,
|
||||||
|
permissions,
|
||||||
|
} => {
|
||||||
|
let player_perms = player.player_type;
|
||||||
|
if player_username == player.username {
|
||||||
|
msg!("Cannot change your own permissions".to_string());
|
||||||
|
continue;
|
||||||
|
} else if permissions >= player_perms {
|
||||||
|
msg!("Cannot set permissions higher or equal to your own".to_string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let perm_string =
|
||||||
|
serde_json::to_string(&permissions)
|
||||||
|
.expect("should never fail");
|
||||||
|
|
||||||
|
let current = data
|
||||||
|
.config
|
||||||
|
.player_perms
|
||||||
|
.entry(player_username.to_string())
|
||||||
|
.or_default();
|
||||||
|
if *current >= player_perms {
|
||||||
|
msg!("This player outranks you"
|
||||||
|
.to_string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*current = permissions;
|
||||||
|
if let Some(p) = data
|
||||||
|
.players
|
||||||
|
.iter_mut()
|
||||||
|
.find(|p| p.username == player_username)
|
||||||
|
{
|
||||||
|
p.player_type = permissions;
|
||||||
|
p.packets_to_send.push(
|
||||||
|
ServerPacket::UpdateUserType {
|
||||||
|
user_type: p.player_type,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
p.packets_to_send.push(ServerPacket::Message {
|
||||||
|
player_id: p.id,
|
||||||
|
message: format!("Your permissions have been set to {perm_string}")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
msg!(format!("Set permissions for {player_username} to {perm_string}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(msg) => {
|
||||||
|
msg!(format!("&c{msg}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("{message}");
|
||||||
|
let message = format!(
|
||||||
|
"&f<{}> {message}",
|
||||||
|
data.players
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.id == *own_id)
|
||||||
|
.expect("should never fail")
|
||||||
|
.username
|
||||||
|
);
|
||||||
|
spread_packet!(
|
||||||
|
data,
|
||||||
|
ServerPacket::Message { player_id, message }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue