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,
|
||||
};
|
||||
|
||||
mod command;
|
||||
mod level;
|
||||
mod packet;
|
||||
mod player;
|
||||
|
|
|
@ -127,7 +127,7 @@ impl ServerPacket {
|
|||
.write_u8(*protocol_version)
|
||||
.write_string(server_name)
|
||||
.write_string(server_motd)
|
||||
.write_u8(*user_type as u8),
|
||||
.write_u8(user_type.into()),
|
||||
Self::Ping {} => writer,
|
||||
Self::LevelInitialize {} => writer,
|
||||
Self::LevelDataChunk {
|
||||
|
@ -220,7 +220,7 @@ impl ServerPacket {
|
|||
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),
|
||||
Self::UpdateUserType { user_type } => writer.write_u8(user_type.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,12 +33,13 @@ pub struct Player {
|
|||
|
||||
/// enum describing types of players
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[repr(u8)]
|
||||
pub enum PlayerType {
|
||||
/// a normal player
|
||||
Normal = 0x00,
|
||||
Normal,
|
||||
/// moderator of the server
|
||||
Moderator,
|
||||
/// a player who's an operator
|
||||
Operator = 0x64,
|
||||
Operator,
|
||||
}
|
||||
|
||||
impl Default for PlayerType {
|
||||
|
@ -47,16 +48,25 @@ impl Default for PlayerType {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for PlayerType {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
if value == Self::Normal as u8 {
|
||||
Ok(Self::Normal)
|
||||
} else if value == Self::Operator as u8 {
|
||||
Ok(Self::Operator)
|
||||
} else {
|
||||
Err(())
|
||||
impl From<&PlayerType> for u8 {
|
||||
fn from(val: &PlayerType) -> Self {
|
||||
match val {
|
||||
PlayerType::Normal => 0,
|
||||
PlayerType::Moderator => 0x64,
|
||||
PlayerType::Operator => 0x64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::{
|
||||
command::Command,
|
||||
level::{block::BLOCK_INFO, BlockUpdate, Level},
|
||||
packet::{client::ClientPacket, server::ServerPacket, PacketWriter, ARRAY_LENGTH},
|
||||
player::{Player, PlayerType},
|
||||
|
@ -72,6 +73,24 @@ async fn handle_stream_inner(
|
|||
let mut read_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 {
|
||||
let ready = stream
|
||||
.ready(Interest::READABLE | Interest::WRITABLE)
|
||||
|
@ -211,10 +230,7 @@ async fn handle_stream_inner(
|
|||
player.packets_to_send.push(message_packet.clone());
|
||||
}
|
||||
}
|
||||
reply_queue.push_back(ServerPacket::Message {
|
||||
player_id: *own_id,
|
||||
message: "Welcome to the server! Enjoyyyyyy".to_string(),
|
||||
});
|
||||
msg!("&dWelcome to the server! Enjoyyyyyy".to_string());
|
||||
reply_queue.push_back(ServerPacket::UpdateUserType {
|
||||
user_type: PlayerType::Operator,
|
||||
});
|
||||
|
@ -241,13 +257,7 @@ async fn handle_stream_inner(
|
|||
|
||||
let new_block_info = BLOCK_INFO.get(&block_type);
|
||||
if new_block_info.is_none() {
|
||||
reply_queue.push_back(ServerPacket::Message {
|
||||
player_id: -1,
|
||||
message: format!(
|
||||
"Unknown block ID: 0x{:0x}",
|
||||
block_type
|
||||
),
|
||||
});
|
||||
msg!(format!("&cUnknown block ID: 0x{:0x}", block_type));
|
||||
continue;
|
||||
}
|
||||
let new_block_info = new_block_info.expect("will never fail");
|
||||
|
@ -266,16 +276,10 @@ async fn handle_stream_inner(
|
|||
.unwrap_or_default();
|
||||
if player_type < new_block_info.place_permissions {
|
||||
cancel = true;
|
||||
reply_queue.push_back(ServerPacket::Message {
|
||||
player_id: -1,
|
||||
message: "Not allowed to place this block.".to_string(),
|
||||
});
|
||||
msg!("&cNot allow to place this block.".to_string());
|
||||
} else if player_type < block_info.break_permissions {
|
||||
cancel = true;
|
||||
reply_queue.push_back(ServerPacket::Message {
|
||||
player_id: -1,
|
||||
message: "Not allowed to break this block.".to_string(),
|
||||
});
|
||||
msg!("&cNot allowed to break this block.".to_string());
|
||||
}
|
||||
|
||||
if cancel {
|
||||
|
@ -305,21 +309,119 @@ async fn handle_stream_inner(
|
|||
yaw,
|
||||
pitch,
|
||||
} => {
|
||||
let packet = ServerPacket::SetPositionOrientation {
|
||||
let mut data = data.write().await;
|
||||
spread_packet!(
|
||||
data,
|
||||
ServerPacket::SetPositionOrientation {
|
||||
player_id: *own_id,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
yaw,
|
||||
pitch,
|
||||
};
|
||||
let mut data = data.write().await;
|
||||
for player in &mut data.players {
|
||||
player.packets_to_send.push(packet.clone());
|
||||
}
|
||||
);
|
||||
}
|
||||
ClientPacket::Message { player_id, message } => {
|
||||
let mut data = data.write().await;
|
||||
|
||||
if let Some(message) = message.strip_prefix(Command::PREFIX) {
|
||||
match Command::parse(message) {
|
||||
Ok(cmd) => {
|
||||
let player = data
|
||||
.players
|
||||
.iter()
|
||||
.find(|p| p.id == *own_id)
|
||||
.expect("missing player");
|
||||
|
||||
if cmd.perms_required() > player.player_type {
|
||||
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}",
|
||||
|
@ -329,9 +431,10 @@ async fn handle_stream_inner(
|
|||
.expect("should never fail")
|
||||
.username
|
||||
);
|
||||
let packet = ServerPacket::Message { player_id, message };
|
||||
for player in &mut data.players {
|
||||
player.packets_to_send.push(packet.clone());
|
||||
spread_packet!(
|
||||
data,
|
||||
ServerPacket::Message { player_id, message }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue