Skip to content

Commit

Permalink
Made everything thread safe
Browse files Browse the repository at this point in the history
- Also improved gamemode command
- We now can use Server in commands
  • Loading branch information
Snowiiii committed Aug 25, 2024
1 parent ddd3f1e commit 83aad86
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 68 deletions.
4 changes: 2 additions & 2 deletions pumpkin-protocol/src/client/play/c_player_info_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ impl<'a> ClientPacket for CPlayerInfoUpdate<'a> {
});
}
PlayerAction::InitializeChat(_) => todo!(),
PlayerAction::UpdateGameMode(_) => todo!(),
PlayerAction::UpdateListed { listed } => p.put_bool(*listed),
PlayerAction::UpdateGameMode(gamemode) => p.put_var_int(gamemode),
PlayerAction::UpdateListed(listed) => p.put_bool(*listed),
PlayerAction::UpdateLatency(_) => todo!(),
PlayerAction::UpdateDisplayName(_) => todo!(),
}
Expand Down
10 changes: 5 additions & 5 deletions pumpkin-protocol/src/client/play/player_action.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::Property;
use crate::{Property, VarInt};

pub enum PlayerAction {
AddPlayer {
name: String,
properties: Vec<Property>,
},
InitializeChat(u8),
UpdateGameMode(u8),
UpdateListed {
listed: bool,
},
/// Gamemode ?
UpdateGameMode(VarInt),
/// Listed ?
UpdateListed(bool),
UpdateLatency(u8),
UpdateDisplayName(u8),
}
5 changes: 3 additions & 2 deletions pumpkin/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{
io::{self, Write},
net::SocketAddr,
rc::Rc,
sync::Arc,
};

use crate::{
Expand Down Expand Up @@ -70,7 +71,7 @@ pub struct Client {
pub connection_state: ConnectionState,
pub encryption: bool,
pub closed: bool,
pub token: Rc<Token>,
pub token: Arc<Token>,
pub connection: TcpStream,
pub address: SocketAddr,
enc: PacketEncoder,
Expand All @@ -81,7 +82,7 @@ pub struct Client {
}

impl Client {
pub fn new(token: Rc<Token>, connection: TcpStream, address: SocketAddr) -> Self {
pub fn new(token: Arc<Token>, connection: TcpStream, address: SocketAddr) -> Self {
Self {
protocol_version: 0,
gameprofile: None,
Expand Down
4 changes: 2 additions & 2 deletions pumpkin/src/client/player_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ impl Player {
server.broadcast_packet(self, &CHeadRot::new(entity_id.into(), yaw as u8));
}

pub fn handle_chat_command(&mut self, _server: &mut Server, command: SChatCommand) {
handle_command(&mut CommandSender::Player(self), &command.command);
pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) {
handle_command(&mut CommandSender::Player(self), server, &command.command);
}

pub fn handle_player_ground(&mut self, _server: &mut Server, ground: SSetPlayerGround) {
Expand Down
20 changes: 10 additions & 10 deletions pumpkin/src/commands/cmd_gamemode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).with_child(
require(&|sender| sender.permission_lvl() >= 2).with_child(
argument(ARG_GAMEMODE, consume_arg_gamemode)
.with_child(
require(&|sender| sender.is_player()).execute(&|sender, args| {
.with_child(require(&|sender| sender.is_player()).execute(
&|sender, server, args| {
let gamemode = parse_arg_gamemode(args)?;

return if let Player(target) = sender {
target.set_gamemode(gamemode);
target.set_gamemode(server, gamemode);
target.send_system_message(TextComponent::text(&format!(
"Game mode was set to {:?}",
gamemode
Expand All @@ -75,22 +75,22 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
} else {
Err(InvalidRequirementError)
};
}),
)
.with_child(
argument(ARG_TARGET, consume_arg_player).execute(&|sender, args| {
},
))
.with_child(argument(ARG_TARGET, consume_arg_player).execute(
&|sender, server, args| {
let gamemode = parse_arg_gamemode(args)?;
let target = parse_arg_player(sender, ARG_TARGET, args)?;

target.set_gamemode(gamemode);
target.set_gamemode(server, gamemode);
target.send_system_message(TextComponent::text(&format!(
"Set own game mode to {:?}",
gamemode
)));

Ok(())
}),
),
},
)),
),
)
}
4 changes: 2 additions & 2 deletions pumpkin/src/commands/cmd_help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn parse_arg_command<'a>(
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION)
.with_child(
argument(ARG_COMMAND, consume_arg_command).execute(&|sender, args| {
argument(ARG_COMMAND, consume_arg_command).execute(&|sender, _, args| {
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);

let tree = parse_arg_command(args, dispatcher)?;
Expand All @@ -50,7 +50,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
Ok(())
}),
)
.execute(&|sender, _args| {
.execute(&|sender, _, _args| {
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);

let mut keys: Vec<&str> = dispatcher.commands.keys().copied().collect();
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/commands/cmd_pumpkin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const NAMES: [&str; 1] = ["pumpkin"];
const DESCRIPTION: &str = "Display information about Pumpkin.";

pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, _| {
CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, _, _| {
let version = env!("CARGO_PKG_VERSION");
let description = env!("CARGO_PKG_DESCRIPTION");

Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/commands/cmd_stop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const DESCRIPTION: &str = "Stop the server.";

pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).with_child(
require(&|sender| sender.permission_lvl() >= 4).execute(&|sender, _args| {
require(&|sender| sender.permission_lvl() >= 4).execute(&|sender, _, _args| {
sender
.send_message(TextComponent::text("Stopping Server").color_named(NamedColor::Red));
std::process::exit(0)
Expand Down
13 changes: 10 additions & 3 deletions pumpkin/src/commands/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::commands::dispatcher::InvalidTreeError::{
};
use crate::commands::tree::{Command, CommandTree, ConsumedArgs, NodeType, RawArgs};
use crate::commands::CommandSender;
use crate::server::Server;
use std::collections::HashMap;

#[derive(Debug)]
Expand All @@ -23,7 +24,12 @@ pub(crate) struct CommandDispatcher<'a> {
/// Stores registered [CommandTree]s and dispatches commands to them.
impl<'a> CommandDispatcher<'a> {
/// Execute a command using its corresponding [CommandTree].
pub(crate) fn dispatch(&'a self, src: &mut CommandSender, cmd: &str) -> Result<(), String> {
pub(crate) fn dispatch(
&'a self,
src: &mut CommandSender,
server: &mut Server,
cmd: &str,
) -> Result<(), String> {
let mut parts = cmd.split_ascii_whitespace();
let key = parts.next().ok_or("Empty Command")?;
let raw_args: Vec<&str> = parts.rev().collect();
Expand All @@ -32,7 +38,7 @@ impl<'a> CommandDispatcher<'a> {

// try paths until fitting path is found
for path in tree.iter_paths() {
match Self::try_is_fitting_path(src, path, tree, raw_args.clone()) {
match Self::try_is_fitting_path(src, server, path, tree, raw_args.clone()) {
Err(InvalidConsumptionError(s)) => {
println!("Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed");
return Err("Internal Error (See logs for details)".into());
Expand Down Expand Up @@ -69,6 +75,7 @@ impl<'a> CommandDispatcher<'a> {

fn try_is_fitting_path(
src: &mut CommandSender,
server: &mut Server,
path: Vec<usize>,
tree: &CommandTree,
mut raw_args: RawArgs,
Expand All @@ -79,7 +86,7 @@ impl<'a> CommandDispatcher<'a> {
match node.node_type {
NodeType::ExecuteLeaf { run } => {
return if raw_args.is_empty() {
run(src, &parsed_args)?;
run(src, server, &parsed_args)?;
Ok(true)
} else {
Ok(false)
Expand Down
5 changes: 3 additions & 2 deletions pumpkin/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::sync::OnceLock;

use crate::commands::dispatcher::CommandDispatcher;
use crate::entity::player::Player;
use crate::server::Server;
mod arg_player;
mod cmd_gamemode;
mod cmd_help;
Expand Down Expand Up @@ -83,10 +84,10 @@ fn dispatcher_init<'a>() -> CommandDispatcher<'a> {
dispatcher
}

pub fn handle_command(sender: &mut CommandSender, cmd: &str) {
pub fn handle_command(sender: &mut CommandSender, server: &mut Server, cmd: &str) {
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);

if let Err(err) = dispatcher.dispatch(sender, cmd) {
if let Err(err) = dispatcher.dispatch(sender, server, cmd) {
sender.send_message(
TextComponent::text(&err).color_named(pumpkin_core::text::color::NamedColor::Red),
)
Expand Down
4 changes: 3 additions & 1 deletion pumpkin/src/commands/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::{HashMap, VecDeque};

use crate::commands::dispatcher::InvalidTreeError;
use crate::commands::CommandSender;
use crate::server::Server;

/// see [crate::commands::tree_builder::argument]
pub(crate) type RawArgs<'a> = Vec<&'a str>;
Expand All @@ -19,7 +20,8 @@ pub(crate) struct Node<'a> {

pub(crate) enum NodeType<'a> {
ExecuteLeaf {
run: &'a (dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError> + Sync),
run: &'a (dyn Fn(&mut CommandSender, &mut Server, &ConsumedArgs) -> Result<(), InvalidTreeError>
+ Sync),
},
#[allow(dead_code)] // todo: remove (so far no commands requiring this are implemented)
Literal { string: &'a str },
Expand Down
7 changes: 5 additions & 2 deletions pumpkin/src/commands/tree_builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::commands::dispatcher::InvalidTreeError;
use crate::commands::tree::{ArgumentConsumer, CommandTree, ConsumedArgs, Node, NodeType};
use crate::commands::CommandSender;
use crate::server::Server;

impl<'a> CommandTree<'a> {
/// Add a child [Node] to the root of this [CommandTree].
Expand Down Expand Up @@ -41,7 +42,8 @@ impl<'a> CommandTree<'a> {
/// Also see [NonLeafNodeBuilder::execute].
pub fn execute(
mut self,
run: &'a (dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError> + Sync),
run: &'a (dyn Fn(&mut CommandSender, &mut Server, &ConsumedArgs) -> Result<(), InvalidTreeError>
+ Sync),
) -> Self {
let node = Node {
node_type: NodeType::ExecuteLeaf { run },
Expand Down Expand Up @@ -117,7 +119,8 @@ impl<'a> NonLeafNodeBuilder<'a> {
/// Also see [CommandTree::execute].
pub fn execute(
mut self,
run: &'a (dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError> + Sync),
run: &'a (dyn Fn(&mut CommandSender, &mut Server, &ConsumedArgs) -> Result<(), InvalidTreeError>
+ Sync),
) -> Self {
self.leaf_nodes.push(LeafNodeBuilder {
node_type: NodeType::ExecuteLeaf { run },
Expand Down
17 changes: 15 additions & 2 deletions pumpkin/src/entity/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use pumpkin_entity::{entity_type::EntityType, Entity, EntityId};
use pumpkin_inventory::player::PlayerInventory;
use pumpkin_protocol::{
bytebuf::{packet_id::Packet, DeserializerError},
client::play::{CGameEvent, CPlayDisconnect, CSyncPlayerPosition, CSystemChatMessage},
client::play::{
CGameEvent, CPlayDisconnect, CPlayerInfoUpdate, CSyncPlayerPosition, CSystemChatMessage,
PlayerAction,
},
server::play::{
SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract,
SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation,
Expand Down Expand Up @@ -139,8 +142,18 @@ impl Player {
self.food_saturation = food_saturation;
}

pub fn set_gamemode(&mut self, gamemode: GameMode) {
pub fn set_gamemode(&mut self, server: &mut Server, gamemode: GameMode) {
self.gamemode = gamemode;
server.broadcast_packet(
self,
&CPlayerInfoUpdate::new(
0x04,
&[pumpkin_protocol::client::play::Player {
uuid: self.client.gameprofile.as_ref().unwrap().id,
actions: vec![PlayerAction::UpdateGameMode((self.gamemode as i32).into())],
}],
),
);
self.client
.send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap()));
}
Expand Down
34 changes: 23 additions & 11 deletions pumpkin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ static ALLOC: dhat::Alloc = dhat::Alloc;

#[cfg(not(target_os = "wasi"))]
fn main() -> io::Result<()> {
use std::{borrow::BorrowMut, sync::{Arc, Mutex}};

use entity::player::Player;
use pumpkin_core::text::{color::NamedColor, TextComponent};

Expand Down Expand Up @@ -95,12 +97,16 @@ fn main() -> io::Result<()> {
let rcon = advanced_configuration.rcon.clone();

let mut clients: HashMap<Token, Client> = HashMap::new();
let mut players: HashMap<Rc<Token>, Rc<RefCell<Player>>> = HashMap::new();
let mut players: HashMap<Arc<Token>, Arc<Mutex<Player>>> = HashMap::new();

let mut server = Server::new((basic_config, advanced_configuration));
let mut server = Arc::new(Mutex::new(Server::new((
basic_config,
advanced_configuration,
))));
log::info!("Started Server took {}ms", time.elapsed().as_millis());
log::info!("You now can connect to the server, Listening on {}", addr);

let server1 = server.clone();
if use_console {
thread::spawn(move || {
let stdin = std::io::stdin();
Expand All @@ -111,15 +117,17 @@ fn main() -> io::Result<()> {
.expect("Failed to read console line");

if !out.is_empty() {
handle_command(&mut commands::CommandSender::Console, &out);
let mut server = server1.lock().unwrap();
handle_command(&mut commands::CommandSender::Console, &mut server, &out);
}
}
});
}
if rcon.enabled {
tokio::spawn(async move {
RCONServer::new(&rcon).await.unwrap();
});
let server = server.clone();
// tokio::spawn(async move {
// RCONServer::new(&rcon, &server).await.unwrap();
// });
}
loop {
if let Err(err) = poll.poll(&mut events, None) {
Expand Down Expand Up @@ -161,16 +169,17 @@ fn main() -> io::Result<()> {
token,
Interest::READABLE.add(Interest::WRITABLE),
)?;
let rc_token = Rc::new(token);
let client = Client::new(Rc::clone(&rc_token), connection, addr);
let rc_token = Arc::new(token);
let client = Client::new(Arc::clone(&rc_token), connection, addr);
clients.insert(token, client);
},

token => {
// Poll Players
let done = if let Some(player) = players.get_mut(&token) {
let mut player = player.borrow_mut();
let mut player = player.lock().unwrap();
player.client.poll(event).await;
let mut server = server.lock().unwrap();
player.process_packets(&mut server);
player.client.closed
} else {
Expand All @@ -179,8 +188,9 @@ fn main() -> io::Result<()> {

if done {
if let Some(player) = players.remove(&token) {
let mut server = server.lock().unwrap();
server.remove_player(&token);
let mut player = player.borrow_mut();
let mut player = player.lock().unwrap();
poll.registry().deregister(&mut player.client.connection)?;
}
}
Expand All @@ -189,6 +199,7 @@ fn main() -> io::Result<()> {
// Maybe received an event for a TCP connection.
let (done, make_player) = if let Some(client) = clients.get_mut(&token) {
client.poll(event).await;
let mut server = server.lock().unwrap();
client.process_packets(&mut server).await;
(client.closed, client.make_player)
} else {
Expand All @@ -201,9 +212,10 @@ fn main() -> io::Result<()> {
poll.registry().deregister(&mut client.connection)?;
} else if make_player {
let token = client.token.clone();
let mut server = server.lock().unwrap();
let player = server.add_player(token.clone(), client);
players.insert(token, player.clone());
let mut player = player.borrow_mut();
let mut player = player.lock().unwrap();
server.spawn_player(&mut player).await;
}
}
Expand Down
Loading

0 comments on commit 83aad86

Please sign in to comment.