Skip to content

Commit

Permalink
Update example plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
vyPal committed Dec 27, 2024
1 parent 888276c commit 8ed5ffc
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 9 deletions.
14 changes: 13 additions & 1 deletion plugins/hello-plugin-source/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,16 @@ pumpkin-protocol = { path = "../../pumpkin-protocol" }
pumpkin-api-macros = { path = "../../pumpkin-api-macros" }
serde = { version = "1.0", features = ["derive"] }
toml = "0.8.19"
async-trait = "0.1.83"
async-trait = "0.1.83"
tokio = { version = "1.42", features = [
"fs",
"io-util",
"macros",
"net",
"rt-multi-thread",
"sync",
"io-std",
"signal",
] }
env_logger = "0.11.6"
log = "0.4.22"
90 changes: 82 additions & 8 deletions plugins/hello-plugin-source/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
use async_trait::async_trait;
use pumpkin::command::args::arg_block::BlockArgumentConsumer;
use pumpkin::command::args::arg_position_block::BlockPosArgumentConsumer;
use pumpkin::command::args::ConsumedArgs;
use pumpkin::command::args::FindArg;
use pumpkin::command::dispatcher::CommandError;
use pumpkin::command::tree::CommandTree;
use pumpkin::command::tree_builder::argument;
use pumpkin::command::tree_builder::literal;
use pumpkin::command::tree_builder::require;
use pumpkin::command::CommandExecutor;
use pumpkin::command::CommandSender;
use pumpkin::entity::player::PermissionLvl;
use pumpkin::plugin::api::types::player::PlayerEvent;
use pumpkin::plugin::*;
use pumpkin::server::Server;
use pumpkin_api_macros::{plugin_event, plugin_impl, plugin_method};
use pumpkin_api_macros::{plugin_event, plugin_impl, plugin_method, with_runtime};
use pumpkin_core::text::color::NamedColor;
use pumpkin_core::text::TextComponent;
use serde::{Deserialize, Serialize};
use std::fs;
use tokio::runtime::Handle;

#[derive(Serialize, Deserialize, Debug)]
struct Config {
Expand All @@ -23,31 +31,97 @@ struct Bans {
players: Vec<String>,
}

const NAMES: [&str; 1] = ["pcmdtest"];
const NAMES: [&str; 1] = ["setblock2"];

const DESCRIPTION: &str = "Testing the ability of plugins to add commands";
const DESCRIPTION: &str = "Place a block.";

struct SayExecutor;
const ARG_BLOCK: &str = "block";
const ARG_BLOCK_POS: &str = "position";

#[derive(Clone, Copy)]
enum Mode {
/// with particles + item drops
Destroy,

/// only replaces air
Keep,

/// default; without particles
Replace,
}

struct SetblockExecutor(Mode);

#[async_trait]
impl CommandExecutor for SayExecutor {
#[with_runtime(global)]
impl CommandExecutor for SetblockExecutor {
async fn execute<'a>(
&self,
sender: &mut CommandSender<'a>,
_server: &Server,
_args: &ConsumedArgs<'a>,
args: &ConsumedArgs<'a>,
) -> Result<(), CommandError> {
sender.send_message(TextComponent::text("Hello, world! This was sent from a plugin as a response to using a command registered by a plugin!")).await;
log::error!("Runtime is {:?}", Handle::current());
let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?;
let block_state_id = block.default_state_id;
let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?;
let mode = self.0;
// TODO: allow console to use the command (seed sender.world)
let world = sender.world().ok_or(CommandError::InvalidRequirement)?;

let success = match mode {
Mode::Destroy => {
world.break_block(pos, None).await;
world.set_block_state(pos, block_state_id).await;
true
}
Mode::Replace => {
world.set_block_state(pos, block_state_id).await;
true
}
Mode::Keep => match world.get_block_state(pos).await {
Ok(old_state) if old_state.air => {
world.set_block_state(pos, block_state_id).await;
true
}
Ok(_) => false,
Err(e) => return Err(CommandError::OtherPumpkin(e.into())),
},
};

sender
.send_message(if success {
TextComponent::text_string(format!("Placed block {} at {pos}", block.name,))
} else {
TextComponent::text_string(format!("Kept block at {pos}"))
.color_named(NamedColor::Red)
})
.await;

Ok(())
}
}

pub fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).execute(&SayExecutor)
CommandTree::new(NAMES, DESCRIPTION).with_child(
require(&|sender| {
sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some()
})
.with_child(
argument(ARG_BLOCK_POS, &BlockPosArgumentConsumer).with_child(
argument(ARG_BLOCK, &BlockArgumentConsumer)
.with_child(literal("replace").execute(&SetblockExecutor(Mode::Replace)))
.with_child(literal("destroy").execute(&SetblockExecutor(Mode::Destroy)))
.with_child(literal("keep").execute(&SetblockExecutor(Mode::Keep)))
.execute(&SetblockExecutor(Mode::Replace)),
),
),
)
}

#[plugin_method]
async fn on_load(&mut self, server: &Context) -> Result<(), String> {
env_logger::init();
server.register_command(init_command_tree()).await;
let data_folder = server.get_data_folder();
if !fs::exists(format!("{}/data.toml", data_folder)).unwrap() {
Expand Down

0 comments on commit 8ed5ffc

Please sign in to comment.