From 3fc917efae75bce737800d1cb96e702b6f77ef30 Mon Sep 17 00:00:00 2001
From: ErrorNoInternet <errornointernet@envs.net>
Date: Fri, 21 Feb 2025 23:34:25 -0500
Subject: [PATCH] feat: expose command sender to code from in-game

---
 lib/movement.lua |  4 ++--
 src/commands.rs  | 29 +++++++++++++----------------
 src/http.rs      |  6 +++---
 src/lua/mod.rs   | 21 +++++++++++++++++----
 4 files changed, 35 insertions(+), 25 deletions(-)

diff --git a/lib/movement.lua b/lib/movement.lua
index e885d92..a7d7822 100644
--- a/lib/movement.lua
+++ b/lib/movement.lua
@@ -4,7 +4,7 @@ function look_at_player(name)
 		player.position.y = player.position.y + 1
 		client:look_at(player.position)
 	else
-		client:chat("player not found!")
+		client:chat(string.format("/w %s player not found!", sender))
 	end
 end
 
@@ -13,6 +13,6 @@ function goto_player(name)
 	if player then
 		client:goto(player.position)
 	else
-		client:chat("player not found!")
+		client:chat(string.format("/w %s player not found!", sender))
 	end
 end
diff --git a/src/commands.rs b/src/commands.rs
index f800dd0..ddc8c48 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -2,11 +2,7 @@ use crate::{
     State,
     lua::{eval, exec, reload},
 };
-use azalea::{
-    GameProfileComponent, brigadier::prelude::*, chat::ChatPacket, entity::metadata::Player,
-    prelude::*,
-};
-use bevy_ecs::{entity::Entity, query::With};
+use azalea::{brigadier::prelude::*, chat::ChatPacket, prelude::*};
 use futures::lock::Mutex;
 
 pub type Ctx = CommandContext<Mutex<CommandSource>>;
@@ -36,14 +32,6 @@ impl CommandSource {
             );
         }
     }
-
-    pub fn _entity(&mut self) -> Option<Entity> {
-        let username = self.message.username()?;
-        self.client
-            .entity_by::<With<Player>, &GameProfileComponent>(|profile: &&GameProfileComponent| {
-                profile.name == username
-            })
-    }
 }
 
 pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
@@ -51,7 +39,10 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
         let source = ctx.source.clone();
         tokio::spawn(async move {
             let source = source.lock().await;
-            source.reply(&format!("{:?}", reload(&source.state.lua)));
+            source.reply(&format!(
+                "{:?}",
+                reload(&source.state.lua, source.message.username())
+            ));
         });
         1
     }));
@@ -62,7 +53,10 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
             let code = get_string(ctx, "code").expect("argument should exist");
             tokio::spawn(async move {
                 let source = source.lock().await;
-                source.reply(&format!("{:?}", eval(&source.state.lua, &code).await));
+                source.reply(&format!(
+                    "{:?}",
+                    eval(&source.state.lua, &code, source.message.username()).await
+                ));
             });
             1
         })),
@@ -74,7 +68,10 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
             let code = get_string(ctx, "code").expect("argument should exist");
             tokio::spawn(async move {
                 let source = source.lock().await;
-                source.reply(&format!("{:?}", exec(&source.state.lua, &code).await));
+                source.reply(&format!(
+                    "{:?}",
+                    exec(&source.state.lua, &code, source.message.username()).await
+                ));
             });
             1
         })),
diff --git a/src/http.rs b/src/http.rs
index 3ac8bf5..ce19277 100644
--- a/src/http.rs
+++ b/src/http.rs
@@ -12,7 +12,7 @@ pub async fn serve(
     let path = request.uri().path().to_owned();
 
     Ok(match (request.method(), path.as_str()) {
-        (&Method::POST, "/reload") => match reload(&state.lua) {
+        (&Method::POST, "/reload") => match reload(&state.lua, None) {
             Ok(()) => Response::new(empty()),
             Err(error) => status_code_response(
                 StatusCode::INTERNAL_SERVER_ERROR,
@@ -24,8 +24,8 @@ pub async fn serve(
             let bytes = request.into_body().collect().await?.to_bytes();
             match std::str::from_utf8(&bytes) {
                 Ok(code) => Response::new(full(match path.as_str() {
-                    "/eval" => format!("{:#?}", eval(&state.lua, code).await),
-                    "/exec" => format!("{:#?}", exec(&state.lua, code).await),
+                    "/eval" => format!("{:#?}", eval(&state.lua, code, None).await),
+                    "/exec" => format!("{:#?}", exec(&state.lua, code, None).await),
                     _ => unreachable!(),
                 })),
                 Err(error) => status_code_response(
diff --git a/src/lua/mod.rs b/src/lua/mod.rs
index b526ee7..332eddd 100644
--- a/src/lua/mod.rs
+++ b/src/lua/mod.rs
@@ -12,6 +12,7 @@ use mlua::{Lua, Table};
 #[derive(Debug)]
 #[allow(dead_code)]
 pub enum Error {
+    CreateEnv(mlua::Error),
     EvalChunk(mlua::Error),
     ExecChunk(mlua::Error),
     LoadChunk(mlua::Error),
@@ -32,7 +33,7 @@ pub fn register_functions(lua: &Lua, globals: &Table) -> mlua::Result<()> {
     logging::register_functions(lua, globals)
 }
 
-pub fn reload(lua: &Lua) -> Result<(), Error> {
+pub fn reload(lua: &Lua, sender: Option<String>) -> Result<(), Error> {
     lua.load(
         &std::fs::read_to_string(
             lua.globals()
@@ -41,17 +42,29 @@ pub fn reload(lua: &Lua) -> Result<(), Error> {
         )
         .map_err(Error::ReadFile)?,
     )
+    .set_environment(create_env(lua, sender)?)
     .exec()
     .map_err(Error::LoadChunk)
 }
 
-pub async fn eval(lua: &Lua, code: &str) -> Result<String, Error> {
+pub async fn eval(lua: &Lua, code: &str, sender: Option<String>) -> Result<String, Error> {
     lua.load(code)
+        .set_environment(create_env(lua, sender)?)
         .eval_async::<String>()
         .await
         .map_err(Error::EvalChunk)
 }
 
-pub async fn exec(lua: &Lua, code: &str) -> Result<(), Error> {
-    lua.load(code).exec_async().await.map_err(Error::ExecChunk)
+pub async fn exec(lua: &Lua, code: &str, sender: Option<String>) -> Result<(), Error> {
+    lua.load(code)
+        .set_environment(create_env(lua, sender)?)
+        .exec_async()
+        .await
+        .map_err(Error::ExecChunk)
+}
+
+fn create_env(lua: &Lua, sender: Option<String>) -> Result<Table, Error> {
+    let globals = lua.globals();
+    globals.set("sender", sender).map_err(Error::CreateEnv)?;
+    Ok(globals)
 }