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

Commands #487

Draft
wants to merge 61 commits into
base: main
Choose a base branch
from
Draft

Commands #487

wants to merge 61 commits into from

Conversation

Iaiao
Copy link
Contributor

@Iaiao Iaiao commented Sep 26, 2021

Commands

Status

  • Ready
  • Development
  • Hold

Description

This PR adds commands. I've started working on this with lieutenant, but I did't like regex because it's hard to write regexps for something like @e[advancements={"adv":{criteria:"true"}}], it required some refactoring to make compatible structure for Declare Commands packet, CommandCtx: Send + Sync wouldn't allow LifetimelessMut<Game> and has no concept of tab completion, so I decided to make my own dispatcher instead of rewriting 95% of the existing code.

Things to do:

  • Vanilla commands
    • /help (/?)
    • /advancement
    • /attribute
    • /ban
    • /ban-ip
    • /banlist
    • /bossbar
    • /clear
    • /clone
    • /data
    • /datapack
    • /debug
    • /defaultgamemode
    • /deop
    • /difficulty
    • /effect
    • /enchant
    • /execute
    • /experience (/xp)
    • /fill
    • /forceload
    • /function
    • /gamemode
    • /gamerule
    • /give
    • /item
    • /kick
    • /kill
    • /list
    • /locate
    • /locatebiome
    • /loot
    • /me
    • /op
    • /pardon
    • /pardon-ip
    • /particle
    • /perf
    • /playsound
    • /recipe
    • /reload
    • /save-all
    • /save-off
    • /save-on
    • /say
    • /schedule
    • /scoreboard
    • /seed
    • /setblock
    • /setidletimeout
    • /setworldspawn
    • /spawnpoint
    • /spectate
    • /spreadplayers
    • /stop
    • /stopsound
    • /summon
    • /tag
    • /team
    • /teammsg (/tm)
    • /teleport (/tp)
    • /tell (/msg, /w)
    • /tellraw
    • /time
    • /title
    • /trigger
    • /weather
    • /whitelist
    • /worldborder
  • Tab completions
    • Tooltips
    • Replace position (start and end of replacement)
  • Arguments
    • Integer (i32, i64)
    • Float (f32, f64)
    • Ranges
    • String
    • Entity selectors
      • Selector parsing (serde)
      • Finding matching entities
      • "tag" selector (serde)
      • Selection by name and uuid
    • Custom enum argument macro
    • Boolean
    • Block position
    • Vec3
    • Vec2
    • Block State
    • Block Predicate
    • Itemstack
    • Item Predicate
    • Chat color
    • Text Component
    • Message (string with selectors)
    • Nbt
    • Nbt path
    • Objective
    • Objective criteria
    • Operation
    • Particle
    • Rotation
    • Angle
    • Scoreboard slot
    • Swizzle
    • Team
    • Item Slot (name)
    • Resource Location (identifier)
    • Mob effect
    • Function
    • Entity anchor
    • Item enchantment
    • Entity summon
    • Dimension
    • Uuid
    • Nbt tag (partial, used in data modify command)
    • Nbt compound tag (full)
    • Time
  • Console
  • Plugin support (I've done the rust native API and it works, and tried to make it work with wasm32-wasi, but I'm new to FFI and wasm, so I'll definitely need some help with this)
    • A fully functional example plugin (bungeecord-servers)
  • Permissions support
  • Declare Commands packet (maybe do something better than LengthInferredVecU8?)

Things to discuss

  • The new command crate's name
  • Command macro design
  • Should the /help command be auto-generated?
  • How can we organize vanilla command implementations? All in one file, one file per command or group them somehow

Related issues

Checklist

  • Ran cargo fmt, cargo clippy --all-targets, cargo build --release and cargo test and fixed any generated errors!
  • Removed unnecessary commented out code
  • Used specific traces (if you trace actions please specify the cause i.e. the player)

Note: if you locally don't get any errors, but GitHub Actions fails (especially at clippy) you might want to check your rust toolchain version. You can then feel free to fix these warnings/errors in your PR.

Copy link
Contributor

@ambeeeeee ambeeeeee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not going to solve borrow checking issues with an unsafe magic pointer.

I would recommend cherry picking all your commits and reapplying over the current main, as this pr includes code that was unmerged.

Comment on lines 31 to 868
// // Some(uuid) => uuid,
// // None => return Err(PardonError::NotPlayer.into()),
// //};
//
// //{
// // let bi_lock = ctx.game.resources.get::<WrappedBanInfo>();
// // let mut ban_info = bi_lock.write().unwrap();
// // ban_info
// // .uuid_bans
// // .remove(&uuid.to_hyphenated_ref().to_string());
// //}
//
// if let Ok(mut chat) = ctx.ecs.get_mut::<ChatBox>(ctx.sender) {
// let kick_confirm =
// Text::translate_with("commands.pardon.success", vec![Text::from(name.0)]);
// chat.send_system(kick_confirm);
// }
//
// Ok(None)
// }
//
// #[derive(Debug, Error)]
// pub enum PardonIpError {
// #[error("Invalid IP Address")]
// NotIp,
// }
//
// #[command(usage = "pardon-ip <ip>")]
// pub fn pardonip(ctx: &mut CommandCtx, ip: String) -> anyhow::Result<Option<String>> {
// // Try to parse ip
// let _addr = IpAddr::from_str(&ip).map_err(|_| PardonIpError::NotIp)?;
//
// //{
// // let mut bi_lock = ctx.game.resources.get_mut::<WrappedBanInfo>();
// // let mut ban_info = bi_lock.write().unwrap();
// // ban_info.ip_bans.remove(&addr);
// //}
//
// if let Ok(mut chat) = ctx.ecs.get_mut::<ChatBox>(ctx.sender) {
// let kick_confirm = Text::translate_with("commands.pardon.success", vec![Text::from(ip)]);
// chat.send_system(kick_confirm);
// }
//
// Ok(None)
// }
//
// #[command(usage = "time query <info>")]
// pub fn time_query(
// ctx: &mut CommandCtx,
// info: TimeQueryInformation,
// ) -> anyhow::Result<Option<String>> {
// let time = match info {
// TimeQueryInformation::DayTime => ctx.world.time.time(),
// TimeQueryInformation::GameTime => ctx.world.time.world_age(),
// TimeQueryInformation::Day => ctx.world.time.days(),
// };
//
// if let Ok(mut chat) = ctx.ecs.get_mut::<ChatBox>(ctx.sender) {
// let message =
// Text::translate_with("commands.time.query", vec![Text::from(time.to_string())]);
// chat.send_system(message);
// }
//
// Ok(None)
// }
//
// #[command(usage = "time add <time>")]
// pub fn time_add(ctx: &mut CommandCtx, time: TimeArgument) -> anyhow::Result<Option<String>> {
// let time = ctx.world.time.time() + time.0;
// set_time(ctx, time);
// Ok(None)
// }
//
// #[command(usage = "time set <time>")]
// pub fn time_set_0(ctx: &mut CommandCtx, time: TimeArgument) -> anyhow::Result<Option<String>> {
// set_time(ctx, time.0);
// Ok(None)
// }
//
// #[command(usage = "time set <time_spec>")]
// pub fn time_set_1(ctx: &mut CommandCtx, time_spec: TimeSpec) -> anyhow::Result<Option<String>> {
// set_time(
// ctx,
// match time_spec {
// TimeSpec::Day => 1_000,
// TimeSpec::Noon => 6_000,
// TimeSpec::Night => 13_000,
// TimeSpec::Midnight => 18_000,
// },
// );
// Ok(None)
// }
//
// fn set_time(ctx: &mut CommandCtx, time: u64) {
// ctx.ecs.insert_event(TimeUpdateEvent {
// old: ctx.world.time.time(),
// new: time,
// });
// ctx.world.time.set_time(time);
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to look into a different way to organize commands instead of a monolithic implementation file.

Comment on lines 15 to 44
/// Dumb workaround for a certain lifetime issue.
///
/// `CommandCtx` stores references to `Game`, and it
/// is used as the `C` parameter for `CommandDispatcher`,
/// This combination of lifetimes and storage in structs
/// prevents a lifetime-based `CommandCtx` from being stored
/// in `CommandState` without adding a lifetime parameter to `CommandState`.
///
/// Since `CommandCtx` is never actually _stored_ in `CommandState` (it's
/// only passed into a function), we can (hopefully) soundly erase
/// the lifetime parameters. FIXME: if someone has a better solution,
/// a PR is welcome :)
pub struct LifetimelessMut<T>(*mut T);

impl<T> Deref for LifetimelessMut<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
unsafe { &mut *self.0 }
}
}

impl<T> DerefMut for LifetimelessMut<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0 }
}
}

unsafe impl<T> Send for LifetimelessMut<T> where T: Send {}
unsafe impl<T> Sync for LifetimelessMut<T> where T: Sync {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q_Q Can we talk about this please

Copy link
Contributor

@ambeeeeee ambeeeeee Sep 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's basically 0 chance this will ever be merged as-is

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants