Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: commands and basic chat #143

Draft
wants to merge 36 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b233a52
feat: commands
radstevee Dec 23, 2024
11a4d90
refactor(commands): rename module, style(commands): reformat
radstevee Dec 23, 2024
fab00e9
feat(commands): quoted string parser, parser errors
radstevee Dec 23, 2024
ede9f8b
initial shitty macrso
radstevee Dec 24, 2024
f878610
still broken macros
radstevee Dec 25, 2024
b99e7f3
feat: working command macros
radstevee Dec 26, 2024
ce5c410
style: reformat
radstevee Dec 26, 2024
4d2dc2a
feat(net): commands and system messages, refactor commands a bit, fea…
radstevee Dec 26, 2024
3724dde
fix(net): deadlock when command accesses streamwriter, fix(storage): …
radstevee Dec 27, 2024
62f0c48
Merge branch 'master' into feat/commands
radstevee Dec 27, 2024
f88ec8f
fix(storage): remove non existant mod
radstevee Dec 27, 2024
dfa71d8
make clippy happy
radstevee Dec 27, 2024
0815cca
style(adapters/anvil): make clippy happy
radstevee Dec 27, 2024
fb625f8
refactor(commands): move test dependencies to dev-dependencies section
radstevee Dec 27, 2024
f1ddf72
feat(commands): command nodes and graph
radstevee Dec 27, 2024
522a978
style(commands): make clippy happy
radstevee Dec 28, 2024
37c5fe6
refactor(commands): move command handling to events in bin
radstevee Dec 28, 2024
0e6e536
feat(commands): working command node sending
radstevee Dec 29, 2024
b7840dd
fix(commands): CommandInput::remaining_input doing wrong things, carg…
radstevee Dec 29, 2024
49c38b5
fix: packet_handlers::commands being in muh global gitignore
radstevee Dec 29, 2024
f7971f1
style(commands): make clippy happy
radstevee Dec 29, 2024
5080189
feat(commands): vanilla arg types and props, fix some graph issues
radstevee Dec 29, 2024
4870143
fix(commands): fix graph test being inverted
radstevee Dec 29, 2024
5b737da
refactor(commands/int_parser): return i32 instead of u32
radstevee Dec 29, 2024
b86d99a
feat(macros/arg): parse parser as expr instead of ident allowing for …
radstevee Dec 29, 2024
1771a50
cargo fmt
radstevee Dec 29, 2024
231b8fb
feat: basic unsigned chat
radstevee Dec 29, 2024
0575ff3
Merge branch 'master' into feat/commands
radstevee Dec 30, 2024
30664f6
feat: entity utils
radstevee Dec 30, 2024
5c9c4cd
refactor(entity_utils): make SendMessageExt use state instead of univ…
radstevee Dec 30, 2024
d1156a2
Merge branch 'master' into feat/commands
radstevee Dec 30, 2024
40d3904
refactor(entity_utils): make SendMessageExt use &GlobalState
radstevee Dec 30, 2024
4f1db52
feat(commands): completion API
radstevee Dec 30, 2024
ebb5c52
fix(entity_utils): rust edition
radstevee Dec 30, 2024
2552af3
Merge branch 'master' into feat/commands
radstevee Jan 3, 2025
335c1e1
fix my fucked merge
radstevee Jan 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ members = [
"src/lib/adapters/anvil",
"src/lib/adapters/nbt",
"src/lib/adapters/nbt",
"src/lib/commands",
"src/lib/default_commands",
"src/lib/core",
"src/lib/core/state",
"src/lib/derive_macros",
Expand All @@ -34,6 +36,7 @@ members = [
"src/lib/utils/logging",
"src/lib/utils/profiling",
"src/lib/world",
"src/lib/entity_utils",
]

#================== Lints ==================#
Expand Down Expand Up @@ -87,6 +90,8 @@ codegen-units = 1
ferrumc-anvil = { path = "src/lib/adapters/anvil" }
ferrumc-config = { path = "src/lib/utils/config" }
ferrumc-core = { path = "src/lib/core" }
ferrumc-default-commands = { path = "src/lib/default_commands" }
ferrumc-commands = { path = "src/lib/commands" }
ferrumc-ecs = { path = "src/lib/ecs" }
ferrumc-events = { path = "src/lib/events"}
ferrumc-general-purpose = { path = "src/lib/utils/general_purpose" }
Expand All @@ -103,7 +108,7 @@ ferrumc-storage = { path = "src/lib/storage" }
ferrumc-text = { path = "src/lib/text" }
ferrumc-utils = { path = "src/lib/utils" }
ferrumc-world = { path = "src/lib/world" }

ferrumc-entity-utils = { path = "src/lib/entity_utils" }


# Asynchronous
Expand Down Expand Up @@ -156,7 +161,7 @@ whirlwind = "0.1.1"
# Macros
lazy_static = "1.5.0"
quote = "1.0.37"
syn = "2.0.77"
syn = { version = "2.0.77", features = ["full"] }
proc-macro2 = "1.0.86"
proc-macro-crate = "3.2.0"
paste = "1.0.15"
Expand Down Expand Up @@ -187,6 +192,7 @@ colored = "2.1.0"
# Misc
deepsize = "0.2.0"
page_size = "0.6.0"
enum-ordinalize = "4.3.0"
regex = "1.11.1"

# I/O
Expand All @@ -195,5 +201,3 @@ memmap2 = "0.9.5"

# Benchmarking
criterion = { version = "0.5.1", features = ["html_reports"] }


3 changes: 3 additions & 0 deletions src/bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ ferrumc-macros = { workspace = true }
ferrumc-nbt = { workspace = true }
ferrumc-general-purpose = { workspace = true }
ferrumc-state = { workspace = true }
ferrumc-commands = { workspace = true }
ferrumc-default-commands = { workspace = true }
ferrumc-text = { workspace = true }

parking_lot = { workspace = true, features = ["deadlock_detection"] }
tracing = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions src/bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ async fn entry() -> Result<()> {
let global_state = Arc::new(state);
create_whitelist().await;

// Needed for some reason because ctor doesn't really want to do ctor things otherwise.
ferrumc_default_commands::init();

let all_system_handles = tokio::spawn(definition::start_all_systems(global_state.clone()));

//Start the systems and wait until all of them are done
Expand Down
24 changes: 24 additions & 0 deletions src/bin/src/packet_handlers/chat_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use ferrumc_core::identity::player_identity::PlayerIdentity;
use ferrumc_macros::event_handler;
use ferrumc_net::{
packets::{
incoming::chat_message::ChatMessageEvent, outgoing::system_message::SystemMessagePacket,
},
utils::broadcast::{BroadcastOptions, BroadcastToAll},
NetResult,
};
use ferrumc_state::GlobalState;
use ferrumc_text::TextComponentBuilder;

#[event_handler]
async fn chat_message(event: ChatMessageEvent, state: GlobalState) -> NetResult<ChatMessageEvent> {
let identity = state.universe.get::<PlayerIdentity>(event.player_conn_id)?;
let message =
TextComponentBuilder::new(format!("<{}> {}", identity.username, event.message)).build();
let packet = SystemMessagePacket::new(message, false);
state
.broadcast(&packet, BroadcastOptions::default().all())
.await?;

Ok(event)
}
82 changes: 82 additions & 0 deletions src/bin/src/packet_handlers/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::sync::{Arc, Mutex};

use ferrumc_commands::{ctx::CommandContext, infrastructure::find_command, input::CommandInput};
use ferrumc_macros::event_handler;
use ferrumc_net::{
connection::StreamWriter,
errors::NetError,
packets::{
incoming::command::CommandDispatchEvent, outgoing::system_message::SystemMessagePacket,
},
};
use ferrumc_net_codec::encode::NetEncodeOpts;
use ferrumc_state::GlobalState;
use ferrumc_text::{NamedColor, TextComponentBuilder};

#[event_handler]
async fn handle_command_dispatch(
event: CommandDispatchEvent,
state: GlobalState,
) -> Result<CommandDispatchEvent, NetError> {
let mut writer = state.universe.get_mut::<StreamWriter>(event.conn_id)?;

let command = find_command(event.command.as_str());
if command.is_none() {
writer
.send_packet(
&SystemMessagePacket::new(
TextComponentBuilder::new("Unknown command")
.color(NamedColor::Red)
.build(),
false,
),
&NetEncodeOpts::WithLength,
)
.await?;
return Ok(event);
}

let command = command.unwrap();

let input = &event
.command
.strip_prefix(command.name)
.unwrap_or(&event.command)
.trim_start();
let input = CommandInput::of(input.to_string());
let ctx = CommandContext::new(input.clone(), command.clone(), state.clone(), event.conn_id);
if let Err(err) = command.validate(&ctx, &Arc::new(Mutex::new(input))) {
writer
.send_packet(
&SystemMessagePacket::new(
TextComponentBuilder::new("Invalid arguments: ")
.extra(err)
.color(NamedColor::Red)
.build(),
false,
),
&NetEncodeOpts::WithLength,
)
.await?;
return Ok(event);
}

drop(writer); // Avoid deadlocks if the executor accesses the stream writer
if let Err(err) = command.execute(ctx).await {
let mut writer = state.universe.get_mut::<StreamWriter>(event.conn_id)?;
writer
.send_packet(
&SystemMessagePacket::new(
TextComponentBuilder::new("Failed executing command: ")
.extra(err)
.color(NamedColor::Red)
.build(),
false,
),
&NetEncodeOpts::WithLength,
)
.await?;
};

Ok(event)
}
9 changes: 9 additions & 0 deletions src/bin/src/packet_handlers/login_process.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use ferrumc_commands::graph::CommandsPacket;
use ferrumc_config::statics::{get_global_config, get_whitelist};
use ferrumc_core::chunks::chunk_receiver::ChunkReceiver;
use ferrumc_core::identity::player_identity::PlayerIdentity;
Expand Down Expand Up @@ -202,6 +203,14 @@ async fn handle_ack_finish_configuration(
&NetEncodeOpts::WithLength,
)
.await?;

trace!(
"Sending command graph: {:#?}",
ferrumc_commands::infrastructure::get_graph()
);
writer
.send_packet(&CommandsPacket::create(), &NetEncodeOpts::WithLength)
.await?;

send_keep_alive(entity_id, &state, &mut writer).await?;

Expand Down
2 changes: 2 additions & 0 deletions src/bin/src/packet_handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod chat_message;
mod commands;
mod animations;
mod handshake;
mod login_process;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/adapters/anvil/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl LoadedAnvilFile {
let location = (u32::from(self.table[i * 4]) << 24)
| (u32::from(self.table[i * 4 + 1]) << 16)
| (u32::from(self.table[i * 4 + 2]) << 8)
| u32::from(self.table[i * 4 + 3]);
| (u32::from(self.table[i * 4 + 3]));
if location != 0 {
locations.push(location);
}
Expand Down
24 changes: 24 additions & 0 deletions src/lib/commands/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "ferrumc-commands"
version = "0.1.0"
edition = "2021"

[dependencies]
thiserror = { workspace = true }
tracing = { workspace = true }
dashmap = { workspace = true }
tokio = { workspace = true }
ferrumc-text = { workspace = true }
ferrumc-state = { workspace = true }
enum-ordinalize = { workspace = true }
ferrumc-macros = { workspace = true }

ferrumc-net-codec = { workspace = true }
ferrumc-net = { workspace = true }
ferrumc-config = { workspace = true }
flate2 = { workspace = true }

[dev-dependencies] # Needed for the ServerState mock... :concern:
ferrumc-ecs = { workspace = true }
ferrumc-world = { workspace = true }
ctor = { workspace = true }
19 changes: 19 additions & 0 deletions src/lib/commands/src/arg/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use parser::ArgumentParser;

pub mod parser;

pub struct CommandArgument {
pub name: String,
pub required: bool,
pub parser: Box<dyn ArgumentParser>,
}

impl CommandArgument {
pub fn new(name: String, required: bool, parser: Box<dyn ArgumentParser>) -> Self {
CommandArgument {
name,
required,
parser,
}
}
}
68 changes: 68 additions & 0 deletions src/lib/commands/src/arg/parser/int.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::sync::{Arc, Mutex};

use crate::{ctx::CommandContext, input::CommandInput, ParserResult};

use super::{
utils::error,
vanilla::{
int::IntParserFlags, MinecraftArgument, MinecraftArgumentProperties, MinecraftArgumentType,
},
ArgumentParser,
};

pub struct IntParser;

impl ArgumentParser for IntParser {
fn parse(&self, _ctx: Arc<CommandContext>, input: Arc<Mutex<CommandInput>>) -> ParserResult {
let token = input.lock().unwrap().read_string();

match token.parse::<i32>() {
Ok(int) => Ok(Box::new(int)),
Err(err) => Err(error(err)),
}
}

fn new() -> Self
where
Self: Sized,
{
IntParser
}

fn vanilla(&self) -> MinecraftArgument {
MinecraftArgument {
argument_type: MinecraftArgumentType::Int,
props: MinecraftArgumentProperties::Int(IntParserFlags::default()),
}
}

fn completions(
&self,
_ctx: Arc<CommandContext>,
input: Arc<Mutex<CommandInput>>,
) -> Vec<String> {
let input = input.lock().unwrap();

let mut numbers = Vec::new();
let token = input.peek_string();

let input_num = if token == "-" {
"-0".to_string()
} else if token.is_empty() {
"0".to_string()
} else {
token
};

if input_num.parse::<i32>().is_err() {
return numbers;
}

for n in 0..=9 {
let n = n.to_string();
numbers.push(input_num.clone() + &n);
}

numbers
}
}
19 changes: 19 additions & 0 deletions src/lib/commands/src/arg/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::sync::{Arc, Mutex};

use crate::{ctx::CommandContext, input::CommandInput, ParserResult};

pub mod int;
pub mod string;
pub mod utils;
pub mod vanilla;

pub trait ArgumentParser: Send + Sync {
fn parse(&self, ctx: Arc<CommandContext>, input: Arc<Mutex<CommandInput>>) -> ParserResult;
fn completions(&self, ctx: Arc<CommandContext>, input: Arc<Mutex<CommandInput>>)
-> Vec<String>;

fn new() -> Self
where
Self: Sized;
fn vanilla(&self) -> vanilla::MinecraftArgument;
}
Loading
Loading