From 5019aae2df7c32eb7ad4ff6219765cdc7d943637 Mon Sep 17 00:00:00 2001 From: Bigjango13 Date: Tue, 25 Feb 2025 21:29:03 -0500 Subject: [PATCH] Split Up api.cpp + Fixes --- mods/CMakeLists.txt | 1 + mods/src/api/api.cpp | 380 +++++------------------------- mods/src/api/events.cpp | 280 ++++++++++++++++++++++ mods/src/api/internal.h | 22 ++ symbols/src/api/CommandServer.def | 1 + 5 files changed, 365 insertions(+), 319 deletions(-) create mode 100644 mods/src/api/events.cpp create mode 100644 mods/src/api/internal.h diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index dc2ea139..40d0b8d6 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -101,6 +101,7 @@ add_library(mods SHARED src/classic-ui/classic-ui.cpp # api src/api/api.cpp + src/api/events.cpp # shading src/shading/init.cpp src/shading/tesselator.cpp diff --git a/mods/src/api/api.cpp b/mods/src/api/api.cpp index d4cef1fb..62cffe2a 100644 --- a/mods/src/api/api.cpp +++ b/mods/src/api/api.cpp @@ -1,10 +1,7 @@ #include #include #include -#include -#include #include -#include #include #include @@ -16,28 +13,25 @@ #include #include #include -#include #include -// Compatibility Mode -static bool compat_mode = true; +#include "internal.h" -// Argument Separator -static constexpr char arg_separator = ','; -static constexpr char list_separator = '|'; +// Compatibility Mode +bool api_compat_mode = true; // Read String Input static std::string get_input(std::string message) { // Decode - if (!compat_mode) { + if (!api_compat_mode) { message = misc_base64_decode(message); } // Convert To CP-437 return to_cp437(message); } // Output String -static std::string get_output(std::string message, const bool replace_comma = false) { - if (compat_mode) { +std::string api_get_output(std::string message, const bool replace_comma) { + if (api_compat_mode) { // Output In Plaintext For RJ Compatibility std::ranges::replace(message, list_separator, '\\'); if (replace_comma) { @@ -51,7 +45,7 @@ static std::string get_output(std::string message, const bool replace_comma = fa } // Join Strings Into Output -static std::string join_outputs(const std::vector &pieces, const char separator = arg_separator) { +std::string api_join_outputs(const std::vector &pieces, const char separator) { // Join std::string out; for (std::string piece : pieces) { @@ -78,40 +72,40 @@ static std::string join_outputs(const std::vector &pieces, const ch // Get Blocks In Region static std::string get_blocks(CommandServer *server, const Vec3 &start, const Vec3 &end) { // Start Coordinate - int startx = int(start.x); - int starty = int(start.y); - int startz = int(start.z); + int start_x = int(start.x); + int start_y = int(start.y); + int start_z = int(start.z); // End Coordinate - int endx = int(end.x); - int endy = int(end.y); - int endz = int(end.z); + int end_x = int(end.x); + int end_y = int(end.y); + int end_z = int(end.z); // Apply Offset - server->pos_translator.from(startx, starty, startz); - server->pos_translator.from(endx, endy, endz); + server->pos_translator.from(start_x, start_y, start_z); + server->pos_translator.from(end_x, end_y, end_z); // Swap If Needed - if (endx < startx) { - std::swap(startx, endx); + if (end_x < start_x) { + std::swap(start_x, end_x); } - if (endy < starty) { - std::swap(starty, endy); + if (end_y < start_y) { + std::swap(start_y, end_y); } - if (endz < startz) { - std::swap(startz, endz); + if (end_z < start_z) { + std::swap(start_z, end_z); } // Get std::vector ret; - for (int x = startx; x <= endx; x++) { - for (int y = starty; y <= endy; y++) { - for (int z = startz; z <= endz; z++) { + for (int x = start_x; x <= end_x; x++) { + for (int y = start_y; y <= end_y; y++) { + for (int z = start_z; z <= end_z; z++) { ret.push_back(std::to_string(server->minecraft->level->getTile(x, y, z))); } } } // Return - return join_outputs(ret); + return api_join_outputs(ret); } // Set Entity Rotation From XYZ @@ -142,7 +136,7 @@ static Vec3 get_dir(const Entity *entity) { } // Entity Types -std::unordered_map modern_entity_id_mapping = { +static std::unordered_map modern_entity_id_mapping = { {93, EntityType::CHICKEN}, {92, EntityType::COW}, {90, EntityType::PIG}, @@ -160,14 +154,14 @@ std::unordered_map modern_entity_id_mapping = { {7, EntityType::THROWN_EGG}, {9, EntityType::PAINTING} }; -static void convert_to_rj_entity_type(int &type) { +void api_convert_to_rj_entity_type(int &type) { for (const std::pair &pair : modern_entity_id_mapping) { if (static_cast(pair.second) == type) { type = pair.first; } } } -static void convert_to_mcpi_entity_type(int &type) { +void api_convert_to_mcpi_entity_type(int &type) { if (modern_entity_id_mapping.contains(type)) { type = static_cast(modern_entity_id_mapping[type]); } @@ -182,17 +176,17 @@ static std::string get_entity_message(CommandServer *server, Entity *entity) { server->pos_translator.to(x, y, z); // Fix Type ID int type = entity->getEntityTypeId(); - if (compat_mode) { - convert_to_rj_entity_type(type); + if (api_compat_mode) { + api_convert_to_rj_entity_type(type); } // Return - return join_outputs({ + return api_join_outputs({ // ID std::to_string(entity->id), // Type std::to_string(type), // Name - get_output(misc_get_entity_type_name(entity).second, true), + api_get_output(misc_get_entity_type_name(entity).second, true), // X std::to_string(x), // Y @@ -213,156 +207,7 @@ static float distance_between(const Entity *e1, const Entity *e2) { return std::sqrt(dx * dx + dy * dy + dz * dz); } -// Projectile Event -static constexpr int no_entity_id = -1; -struct ProjectileHitEvent { - int x; - int y; - int z; - int owner_id; - int target_id; -}; -static std::string event_to_string(CommandServer *server, const ProjectileHitEvent &e) { - // Offset Position - float nx = float(e.x); - float ny = float(e.y); - float nz = float(e.z); - server->pos_translator.to(nx, ny, nz); - // Get Outputs - std::vector pieces = { - // Position - std::to_string(int(nx)), - std::to_string(int(ny)), - std::to_string(int(nz)) - }; - // Needed For Compatibility - if (compat_mode) { - pieces.push_back("1"); - } - // Owner - Level *level = server->minecraft->level; - std::string owner; - if (compat_mode) { - owner = get_output(misc_get_entity_name(level->getEntity(e.owner_id)), true); - } else { - owner = std::to_string(e.owner_id); - } - pieces.push_back(owner); - // Target - std::string target; - if (e.target_id != no_entity_id) { - if (compat_mode) { - target = get_output(misc_get_entity_name(level->getEntity(e.target_id)), true); - } else { - target = std::to_string(e.target_id); - } - } - pieces.push_back(target); - // Return - return join_outputs(pieces); -} - -// Chat Message Event -struct ChatEvent { - std::string message; - int owner_id; -}; -static std::string event_to_string(__attribute__((unused)) CommandServer *server, const ChatEvent &e) { - return join_outputs({ - std::to_string(e.owner_id), - get_output(e.message, true), - }); -} - -// Block Hit Event -static std::string event_to_string(CommandServer *server, const TileEvent &e) { - // Offset Coordinates - float x = float(e.x); - float y = float(e.y); - float z = float(e.z); - server->pos_translator.to(x, y, z); - // Output - return join_outputs({ - // Position - std::to_string(int(x)), - std::to_string(int(y)), - std::to_string(int(z)), - // Face - std::to_string(e.face), - // Entity ID - std::to_string(e.owner_id) - }); -} - -// Track Event Queues Per Client -struct ExtraClientData { - std::vector projectile_events; - std::vector chat_events; - std::vector block_hit_events; -}; -static std::unordered_map extra_client_data; -static bool CommandServer__updateAccept_setSocketBlocking_injection(const int fd, const bool param_1) { - // Client Was Created - INFO("Client Connected: %i", fd); - extra_client_data[fd] = ExtraClientData(); - return CommandServer::setSocketBlocking(fd, param_1); -} -static bool CommandServer__updateClient_injection(CommandServer__updateClient_t original, CommandServer *self, ConnectedClient &client) { - const bool ret = original(self, client); - if (!ret) { - // Client Disconnected - INFO("Client Disconnected: %i", client.sock); - extra_client_data.erase(client.sock); - } - return ret; -} - -// Clear All Events -static void clear_events(const ConnectedClient &client) { - ExtraClientData &data = extra_client_data[client.sock]; - if (!compat_mode) { - // Match RJ Bug - data.projectile_events.clear(); - } - data.chat_events.clear(); - data.block_hit_events.clear(); -} - -// Clear Events Produced By Given Entity -template -static void clear_events(std::vector &data, const int id) { - std::ranges::remove_if(data, [&id](const T &e) { - return id == e.owner_id; - }); -} -static void clear_events(const ConnectedClient &client, const int id) { - ExtraClientData &data = extra_client_data[client.sock]; - clear_events(data.block_hit_events, id); - clear_events(data.chat_events, id); - clear_events(data.projectile_events, id); -} - -// Get Events In Queue -template -static std::string get_events(CommandServer *server, std::vector &queue, const std::optional id) { - std::vector out; - typename std::vector::iterator it = queue.begin(); - while (it != queue.end()) { - const T &e = *it; - if (id.has_value() && id.value() != e.owner_id) { - // Skip Event - ++it; - continue; - } - // Output - out.push_back(event_to_string(server, e)); - // Erase - it = queue.erase(it); - } - return join_outputs(out, list_separator); -} - -// Map RJ Entity IDs To MCPI IDs +// Parse API Commands static const std::string player_namespace = "player."; #define API_WARN(format, ...) WARN("API: %s: " format, cmd.c_str(), ##__VA_ARGS__) #define ENTITY_NOT_FOUND API_WARN("Entity Not Found: %i", id) @@ -430,12 +275,12 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ ENTITY_NOT_FOUND; return CommandServer::NullString; } else { - return get_output(misc_get_entity_name(entity)) + '\n'; + return api_get_output(misc_get_entity_name(entity), false) + '\n'; } } else if (cmd == "world.getEntities") { next_int(type); - if (compat_mode) { - convert_to_mcpi_entity_type(type); + if (api_compat_mode) { + api_convert_to_mcpi_entity_type(type); } std::vector result; for (Entity *entity : server->minecraft->level->entities) { @@ -444,7 +289,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ result.push_back(get_entity_message(server, entity)); } } - return join_outputs(result, list_separator); + return api_join_outputs(result, list_separator); } else if (cmd == "world.removeEntity") { next_int(id); Entity *entity = server->minecraft->level->getEntity(id); @@ -456,8 +301,8 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ return std::to_string(result) + '\n'; } else if (cmd == "world.removeEntities") { next_int(type); - if (compat_mode) { - convert_to_mcpi_entity_type(type); + if (api_compat_mode) { + api_convert_to_mcpi_entity_type(type); } int removed = 0; for (Entity *entity : server->minecraft->level->entities) { @@ -471,20 +316,20 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ } return std::to_string(removed) + '\n'; } else if (cmd == "events.chat.posts") { - return get_events(server, extra_client_data[client.sock].chat_events, std::nullopt); + return api_get_chat_events(server, client, std::nullopt); } else if (cmd == "entity.events.chat.posts") { next_int(id); - return get_events(server, extra_client_data[client.sock].chat_events, id); + return api_get_chat_events(server, client, id); } else if (cmd == "events.block.hits") { - return get_events(server, extra_client_data[client.sock].block_hit_events, std::nullopt); + return api_get_block_hit_events(server, client, std::nullopt); } else if (cmd == "entity.events.block.hits") { next_int(id); - return get_events(server, extra_client_data[client.sock].block_hit_events, id); + return api_get_block_hit_events(server, client, id); } else if (cmd == "events.projectile.hits") { - return get_events(server, extra_client_data[client.sock].projectile_events, std::nullopt); + return api_get_projectile_events(server, client, std::nullopt); } else if (cmd == "entity.events.projectile.hits") { next_int(id); - return get_events(server, extra_client_data[client.sock].projectile_events, id); + return api_get_projectile_events(server, client, id); } else if (cmd == "entity.setDirection") { next_int(id); next_float(x); @@ -505,7 +350,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ return CommandServer::Fail; } else { Vec3 vec = get_dir(entity); - return join_outputs({std::to_string(vec.x), std::to_string(vec.y), std::to_string(vec.z)}); + return api_join_outputs({std::to_string(vec.x), std::to_string(vec.y), std::to_string(vec.z)}); } } else if (cmd == "entity.setRotation") { next_int(id); @@ -563,7 +408,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ result.push_back(get_entity_message(server, entity)); } } - return join_outputs(result, list_separator); + return api_join_outputs(result, list_separator); } else if (cmd == "entity.removeEntities") { // Parse next_int(id); @@ -603,7 +448,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ #define next_sign_line(i) \ next_string(line_##i, false); \ if (line_##i##_present) { \ - sign->lines[i] = line_##i; \ + sign->lines[i] = get_input(line_##i); \ } \ (void) 0 next_sign_line(0); @@ -626,8 +471,8 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ x -= server->pos_translator.x; y -= server->pos_translator.y; z -= server->pos_translator.z; - if (compat_mode) { - convert_to_mcpi_entity_type(type); + if (api_compat_mode) { + api_convert_to_mcpi_entity_type(type); } // Spawn Entity *entity = misc_make_entity_from_id(server->minecraft->level, type); @@ -639,14 +484,14 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ return std::to_string(entity->id) + '\n'; } else if (cmd == "world.getEntityTypes") { std::vector result; - for (const std::pair> &i: misc_get_entity_type_names()) { + for (const std::pair> &i : misc_get_entity_type_names()) { int id = static_cast(i.first); - if (compat_mode) { - convert_to_rj_entity_type(id); + if (api_compat_mode) { + api_convert_to_rj_entity_type(id); } - result.push_back(join_outputs({std::to_string(id), i.second.second})); + result.push_back(api_join_outputs({std::to_string(id), api_get_output(i.second.second, true)})); } - return join_outputs(result, list_separator); + return api_join_outputs(result, list_separator); } else if (cmd == "entity.setAbsPos") { next_int(id); next_float(x); @@ -666,19 +511,19 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ ENTITY_NOT_FOUND; return CommandServer::Fail; } - return join_outputs({std::to_string(entity->x), std::to_string(entity->y), std::to_string(entity->z)}); + return api_join_outputs({std::to_string(entity->x), std::to_string(entity->y), std::to_string(entity->z)}); } else if (cmd == "entity.events.clear") { next_int(id); - clear_events(client, id); + api_clear_events(client, id); return CommandServer::NullString; } else if (cmd == "events.clear") { - clear_events(client); + api_clear_events(client); return CommandServer::NullString; } else if (cmd == "reborn.disableCompatMode") { - compat_mode = false; + api_compat_mode = false; return std::string(reborn_get_version()) + '\n'; } else if (cmd == "reborn.enableCompatMode") { - compat_mode = true; + api_compat_mode = true; return CommandServer::NullString; } else { // Call Original Method @@ -686,113 +531,10 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ } } -// Push Event To Queue -template -static void push_event(std::vector ExtraClientData::*ptr, const T e) { - for (ExtraClientData &data : extra_client_data | std::views::values) { - (data.*ptr).push_back(e); - } -} - -// Arrow Hits -static void on_projectile_hit(Entity *shooter, const int x, const int y, const int z, const Entity *target) { - if (shooter && shooter->isPlayer()) { - push_event(&ExtraClientData::projectile_events, { - .x = x, - .y = y, - .z = z, - .owner_id = shooter->id, - .target_id = target ? target->id : no_entity_id - }); - } -} -static void on_projectile_hit(Entity *shooter, const Entity *target) { - on_projectile_hit(shooter, int(target->x), int(target->y), int(target->z), target); -} -static Entity *current_shooter = nullptr; -static void Arrow_tick_injection(Arrow_tick_t original, Arrow *self) { - current_shooter = self->level->getEntity(self->getAuxData()); - original(self); -} -static bool Arrow_tick_Entity_hurt_injection(Entity *self, __attribute__((unused)) Entity *cause, int damage) { - on_projectile_hit(current_shooter, self); - // Call Original Method - return self->hurt(cause, damage); -} -static int Arrow_tick_Level_getTile_injection(Level *self, int x, int y, int z) { - on_projectile_hit(current_shooter, x, y, z, nullptr); - // Call Original Method - return self->getTile(x, y, z); -} - -// Throwable Hits -static void Throwable_tick_Throwable_onHit_injection(Throwable *self, HitResult *res) { - if (res != nullptr && res->type != 2) { - Entity *thrower = self->level->getEntity(self->getAuxData()); - if (res->type == 1 && res->entity) { - on_projectile_hit(thrower, res->entity); - } else { - on_projectile_hit(thrower, res->x, res->y, res->z, nullptr); - } - } - // Call Original Method - self->onHit(res); -} - -// Chat Events -static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) { - static bool recursing = false; - if (recursing) { - original(gui, text); - } else { - if (!chat_is_sending()) { - api_add_chat_event(nullptr, text); - } - recursing = true; - original(gui, text); - recursing = false; - } -} -static bool enabled = false; -void api_add_chat_event(const Player *sender, const std::string &message) { - if (!enabled || (!sender && compat_mode)) { - return; - } - push_event(&ExtraClientData::chat_events, {message, sender ? sender->id : no_entity_id}); -} - -// Block Hit Events -static bool GameMode_useItemOn_injection(GameMode_useItemOn_t original, GameMode *self, Player *player, Level *level, ItemInstance *item, const int x, const int y, const int z, const int param_1, const Vec3 ¶m_2) { - // Add Event - if (item && item->id == Item::sword_iron->id) { - push_event(&ExtraClientData::block_hit_events, { - .owner_id = player->id, - .x = x, - .y = y, - .z = z, - .face = param_1 - }); - } - // Call Original Method - return original(self, player, level, item, x, y, z, param_1, param_2); -} -static bool CreatorMode_useItemOn_injection(__attribute__((unused)) CreatorMode_useItemOn_t original, CreatorMode *self, Player *player, Level *level, ItemInstance *item, const int x, const int y, const int z, const int param_1, const Vec3 ¶m_2) { - return GameMode_useItemOn->get(false)((GameMode *) self, player, level, item, x, y, z, param_1, param_2); -} - // Init void init_api() { if (feature_has("Implement RaspberryJuice API", server_enabled)) { - enabled = true; overwrite_calls(CommandServer_parse, CommandServer_parse_injection); - overwrite_calls(Arrow_tick, Arrow_tick_injection); - overwrite_call((void *) 0x8b28c, Entity_hurt, Arrow_tick_Entity_hurt_injection); - overwrite_call((void *) 0x8b388, Level_getTile, Arrow_tick_Level_getTile_injection); - overwrite_call((void *) 0x8c5a4, Throwable_onHit, Throwable_tick_Throwable_onHit_injection); - overwrite_calls(Gui_addMessage, Gui_addMessage_injection); - overwrite_call((void *) 0x6bd78, CommandServer_setSocketBlocking, CommandServer__updateAccept_setSocketBlocking_injection); - overwrite_calls(CommandServer__updateClient, CommandServer__updateClient_injection); - overwrite_calls(GameMode_useItemOn, GameMode_useItemOn_injection); - overwrite_calls(CreatorMode_useItemOn, CreatorMode_useItemOn_injection); + _init_api_events(); } } diff --git a/mods/src/api/events.cpp b/mods/src/api/events.cpp new file mode 100644 index 00000000..ce5c30f8 --- /dev/null +++ b/mods/src/api/events.cpp @@ -0,0 +1,280 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "internal.h" + +// Projectile Event +struct ProjectileHitEvent { + int x; + int y; + int z; + int owner_id; + int target_id; +}; +static std::string event_to_string(CommandServer *server, const ProjectileHitEvent &e) { + // Offset Position + float nx = float(e.x); + float ny = float(e.y); + float nz = float(e.z); + server->pos_translator.to(nx, ny, nz); + // Get Outputs + std::vector pieces = { + // Position + std::to_string(int(nx)), + std::to_string(int(ny)), + std::to_string(int(nz)) + }; + // Needed For Compatibility + if (api_compat_mode) { + pieces.push_back("1"); + } + // Owner + Level *level = server->minecraft->level; + std::string owner; + if (api_compat_mode) { + owner = api_get_output(misc_get_entity_name(level->getEntity(e.owner_id)), true); + } else { + owner = std::to_string(e.owner_id); + } + pieces.push_back(owner); + // Target + std::string target; + if (!api_compat_mode || e.target_id != no_entity_id) { + if (api_compat_mode) { + target = api_get_output(misc_get_entity_name(level->getEntity(e.target_id)), true); + } else { + target = std::to_string(e.target_id); + } + } + pieces.push_back(target); + // Return + return api_join_outputs(pieces); +} + +// Chat Message Event +struct ChatEvent { + std::string message; + int owner_id; +}; +static std::string event_to_string(__attribute__((unused)) CommandServer *server, const ChatEvent &e) { + return api_join_outputs({ + std::to_string(e.owner_id), + api_get_output(e.message, true), + }); +} + +// Block Hit Event +static std::string event_to_string(CommandServer *server, const TileEvent &e) { + // Offset Coordinates + float x = float(e.x); + float y = float(e.y); + float z = float(e.z); + server->pos_translator.to(x, y, z); + // Output + return api_join_outputs({ + // Position + std::to_string(int(x)), + std::to_string(int(y)), + std::to_string(int(z)), + // Face + std::to_string(e.face), + // Entity ID + std::to_string(e.owner_id) + }); +} + +// Track Event Queues Per Client +struct ExtraClientData { + std::vector projectile_events; + std::vector chat_events; + std::vector block_hit_events; +}; +static std::unordered_map extra_client_data; +static bool CommandServer__updateAccept_setSocketBlocking_injection(const int fd, const bool param_1) { + // Client Was Created + extra_client_data[fd] = ExtraClientData(); + return CommandServer::setSocketBlocking(fd, param_1); +} +static bool CommandServer__updateClient_injection(CommandServer__updateClient_t original, CommandServer *self, ConnectedClient &client) { + const bool ret = original(self, client); + if (!ret) { + // Client Disconnected + extra_client_data.erase(client.sock); + } + return ret; +} +static void CommandServer__close_injection(CommandServer__close_t original, CommandServer *self) { + extra_client_data.clear(); + original(self); +} + +// Clear All Events +void api_clear_events(const ConnectedClient &client) { + ExtraClientData &data = extra_client_data[client.sock]; + if (!api_compat_mode) { + // Match RJ Bug + data.projectile_events.clear(); + } + data.chat_events.clear(); + data.block_hit_events.clear(); +} + +// Clear Events Produced By Given Entity +template +static void clear_events(std::vector &data, const int id) { + std::ranges::remove_if(data, [&id](const T &e) { + return id == e.owner_id; + }); +} +void api_clear_events(const ConnectedClient &client, const int id) { + ExtraClientData &data = extra_client_data[client.sock]; + clear_events(data.block_hit_events, id); + clear_events(data.chat_events, id); + clear_events(data.projectile_events, id); +} + +// Get Events In Queue +template +static std::string get_events(CommandServer *server, std::vector &queue, const std::optional id) { + std::vector out; + typename std::vector::iterator it = queue.begin(); + while (it != queue.end()) { + const T &e = *it; + if (id.has_value() && id.value() != e.owner_id) { + // Skip Event + ++it; + continue; + } + // Output + out.push_back(event_to_string(server, e)); + // Erase + it = queue.erase(it); + } + return api_join_outputs(out, list_separator); +} +#define create_get_events(name) \ + std::string api_get_##name##_events(CommandServer *server, const ConnectedClient &client, const std::optional id) { \ + return get_events(server, extra_client_data[client.sock].name##_events, id); \ + } +create_get_events(projectile) +create_get_events(chat) +create_get_events(block_hit) +#undef create_get_events + +// Push Event To Queue +template +static void push_event(std::vector ExtraClientData::*ptr, const T e) { + for (ExtraClientData &data : extra_client_data | std::views::values) { + (data.*ptr).push_back(e); + } +} + +// Arrow Hits +static void on_projectile_hit(Entity *shooter, const int x, const int y, const int z, const Entity *target) { + if (shooter && shooter->isPlayer()) { + push_event(&ExtraClientData::projectile_events, { + .x = x, + .y = y, + .z = z, + .owner_id = shooter->id, + .target_id = target ? target->id : no_entity_id + }); + } +} +static void on_projectile_hit(Entity *shooter, const Entity *target) { + on_projectile_hit(shooter, int(target->x), int(target->y), int(target->z), target); +} +static Entity *current_shooter = nullptr; +static void Arrow_tick_injection(Arrow_tick_t original, Arrow *self) { + current_shooter = self->level->getEntity(self->getAuxData()); + original(self); +} +static bool Arrow_tick_Entity_hurt_injection(Entity *self, __attribute__((unused)) Entity *cause, int damage) { + on_projectile_hit(current_shooter, self); + // Call Original Method + return self->hurt(cause, damage); +} +static int Arrow_tick_Level_getTile_injection(Level *self, int x, int y, int z) { + on_projectile_hit(current_shooter, x, y, z, nullptr); + // Call Original Method + return self->getTile(x, y, z); +} + +// Throwable Hits +static void Throwable_tick_Throwable_onHit_injection(Throwable *self, HitResult *res) { + if (res != nullptr && res->type != 2) { + Entity *thrower = self->level->getEntity(self->getAuxData()); + if (res->type == 1 && res->entity) { + on_projectile_hit(thrower, res->entity); + } else { + on_projectile_hit(thrower, res->x, res->y, res->z, nullptr); + } + } + // Call Original Method + self->onHit(res); +} + +// Chat Events +static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, const std::string &text) { + static bool recursing = false; + if (recursing) { + original(gui, text); + } else { + if (!chat_is_sending()) { + api_add_chat_event(nullptr, text); + } + recursing = true; + original(gui, text); + recursing = false; + } +} +static bool enabled = false; +void api_add_chat_event(const Player *sender, const std::string &message) { + if (!enabled || (!sender && api_compat_mode)) { + return; + } + push_event(&ExtraClientData::chat_events, {message, sender ? sender->id : no_entity_id}); +} + +// Block Hit Events +static bool GameMode_useItemOn_injection(GameMode_useItemOn_t original, GameMode *self, Player *player, Level *level, ItemInstance *item, const int x, const int y, const int z, const int param_1, const Vec3 ¶m_2) { + // Add Event + if (item && item->id == Item::sword_iron->id) { + push_event(&ExtraClientData::block_hit_events, { + .owner_id = player->id, + .x = x, + .y = y, + .z = z, + .face = param_1 + }); + } + // Call Original Method + return original(self, player, level, item, x, y, z, param_1, param_2); +} +static bool CreatorMode_useItemOn_injection(__attribute__((unused)) CreatorMode_useItemOn_t original, CreatorMode *self, Player *player, Level *level, ItemInstance *item, const int x, const int y, const int z, const int param_1, const Vec3 ¶m_2) { + return GameMode_useItemOn->get(false)((GameMode *) self, player, level, item, x, y, z, param_1, param_2); +} + +// Init +void _init_api_events() { + enabled = true; + overwrite_calls(Arrow_tick, Arrow_tick_injection); + overwrite_call((void *) 0x8b28c, Entity_hurt, Arrow_tick_Entity_hurt_injection); + overwrite_call((void *) 0x8b388, Level_getTile, Arrow_tick_Level_getTile_injection); + overwrite_call((void *) 0x8c5a4, Throwable_onHit, Throwable_tick_Throwable_onHit_injection); + overwrite_calls(Gui_addMessage, Gui_addMessage_injection); + overwrite_call((void *) 0x6bd78, CommandServer_setSocketBlocking, CommandServer__updateAccept_setSocketBlocking_injection); + overwrite_calls(CommandServer__updateClient, CommandServer__updateClient_injection); + overwrite_calls(CommandServer__close, CommandServer__close_injection); + overwrite_calls(GameMode_useItemOn, GameMode_useItemOn_injection); + overwrite_calls(CreatorMode_useItemOn, CreatorMode_useItemOn_injection); +} \ No newline at end of file diff --git a/mods/src/api/internal.h b/mods/src/api/internal.h new file mode 100644 index 00000000..1f6f87b7 --- /dev/null +++ b/mods/src/api/internal.h @@ -0,0 +1,22 @@ +#pragma once + +__attribute__((visibility("internal"))) extern bool api_compat_mode; + +static constexpr int no_entity_id = -1; +static constexpr char arg_separator = ','; +static constexpr char list_separator = '|'; + +__attribute__((visibility("internal"))) std::string api_get_output(std::string message, bool replace_comma); +__attribute__((visibility("internal"))) std::string api_join_outputs(const std::vector &pieces, char separator = arg_separator); + +__attribute__((visibility("internal"))) void api_convert_to_rj_entity_type(int &type); +__attribute__((visibility("internal"))) void api_convert_to_mcpi_entity_type(int &type); + +__attribute__((visibility("internal"))) void _init_api_events(); + +__attribute__((visibility("internal"))) void api_clear_events(const ConnectedClient &client); +__attribute__((visibility("internal"))) void api_clear_events(const ConnectedClient &client, int id); + +__attribute__((visibility("internal"))) std::string api_get_projectile_events(CommandServer *server, const ConnectedClient &client, std::optional id); +__attribute__((visibility("internal"))) std::string api_get_chat_events(CommandServer *server, const ConnectedClient &client, std::optional id); +__attribute__((visibility("internal"))) std::string api_get_block_hit_events(CommandServer *server, const ConnectedClient &client, std::optional id); diff --git a/symbols/src/api/CommandServer.def b/symbols/src/api/CommandServer.def index 87a1c4a0..a4321e63 100644 --- a/symbols/src/api/CommandServer.def +++ b/symbols/src/api/CommandServer.def @@ -2,6 +2,7 @@ method std::string parse(ConnectedClient &client, const std::string &command) = method void dispatchPacket(Packet &packet) = 0x6a548; static-method bool setSocketBlocking(int fd, bool param_1) = 0x6a2d4; method bool _updateClient(ConnectedClient &client) = 0x6ba6c; +method void _close() = 0x6a314; property Minecraft *minecraft = 0x18; property OffsetPosTranslator pos_translator = 0x1c;