Split Up api.cpp + Fixes

This commit is contained in:
Bigjango13 2025-02-25 21:29:03 -05:00 committed by TheBrokenRail
parent b75d923c83
commit 5019aae2df
5 changed files with 365 additions and 319 deletions

View File

@ -101,6 +101,7 @@ add_library(mods SHARED
src/classic-ui/classic-ui.cpp src/classic-ui/classic-ui.cpp
# api # api
src/api/api.cpp src/api/api.cpp
src/api/events.cpp
# shading # shading
src/shading/init.cpp src/shading/init.cpp
src/shading/tesselator.cpp src/shading/tesselator.cpp

View File

@ -1,10 +1,7 @@
#include <cmath> #include <cmath>
#include <string> #include <string>
#include <algorithm> #include <algorithm>
#include <vector>
#include <unordered_map>
#include <optional> #include <optional>
#include <ranges>
#include <sstream> #include <sstream>
#include <libreborn/log.h> #include <libreborn/log.h>
@ -16,28 +13,25 @@
#include <mods/api/api.h> #include <mods/api/api.h>
#include <mods/init/init.h> #include <mods/init/init.h>
#include <mods/misc/misc.h> #include <mods/misc/misc.h>
#include <mods/chat/chat.h>
#include <mods/feature/feature.h> #include <mods/feature/feature.h>
// Compatibility Mode #include "internal.h"
static bool compat_mode = true;
// Argument Separator // Compatibility Mode
static constexpr char arg_separator = ','; bool api_compat_mode = true;
static constexpr char list_separator = '|';
// Read String Input // Read String Input
static std::string get_input(std::string message) { static std::string get_input(std::string message) {
// Decode // Decode
if (!compat_mode) { if (!api_compat_mode) {
message = misc_base64_decode(message); message = misc_base64_decode(message);
} }
// Convert To CP-437 // Convert To CP-437
return to_cp437(message); return to_cp437(message);
} }
// Output String // Output String
static std::string get_output(std::string message, const bool replace_comma = false) { std::string api_get_output(std::string message, const bool replace_comma) {
if (compat_mode) { if (api_compat_mode) {
// Output In Plaintext For RJ Compatibility // Output In Plaintext For RJ Compatibility
std::ranges::replace(message, list_separator, '\\'); std::ranges::replace(message, list_separator, '\\');
if (replace_comma) { if (replace_comma) {
@ -51,7 +45,7 @@ static std::string get_output(std::string message, const bool replace_comma = fa
} }
// Join Strings Into Output // Join Strings Into Output
static std::string join_outputs(const std::vector<std::string> &pieces, const char separator = arg_separator) { std::string api_join_outputs(const std::vector<std::string> &pieces, const char separator) {
// Join // Join
std::string out; std::string out;
for (std::string piece : pieces) { for (std::string piece : pieces) {
@ -78,40 +72,40 @@ static std::string join_outputs(const std::vector<std::string> &pieces, const ch
// Get Blocks In Region // Get Blocks In Region
static std::string get_blocks(CommandServer *server, const Vec3 &start, const Vec3 &end) { static std::string get_blocks(CommandServer *server, const Vec3 &start, const Vec3 &end) {
// Start Coordinate // Start Coordinate
int startx = int(start.x); int start_x = int(start.x);
int starty = int(start.y); int start_y = int(start.y);
int startz = int(start.z); int start_z = int(start.z);
// End Coordinate // End Coordinate
int endx = int(end.x); int end_x = int(end.x);
int endy = int(end.y); int end_y = int(end.y);
int endz = int(end.z); int end_z = int(end.z);
// Apply Offset // Apply Offset
server->pos_translator.from(startx, starty, startz); server->pos_translator.from(start_x, start_y, start_z);
server->pos_translator.from(endx, endy, endz); server->pos_translator.from(end_x, end_y, end_z);
// Swap If Needed // Swap If Needed
if (endx < startx) { if (end_x < start_x) {
std::swap(startx, endx); std::swap(start_x, end_x);
} }
if (endy < starty) { if (end_y < start_y) {
std::swap(starty, endy); std::swap(start_y, end_y);
} }
if (endz < startz) { if (end_z < start_z) {
std::swap(startz, endz); std::swap(start_z, end_z);
} }
// Get // Get
std::vector<std::string> ret; std::vector<std::string> ret;
for (int x = startx; x <= endx; x++) { for (int x = start_x; x <= end_x; x++) {
for (int y = starty; y <= endy; y++) { for (int y = start_y; y <= end_y; y++) {
for (int z = startz; z <= endz; z++) { for (int z = start_z; z <= end_z; z++) {
ret.push_back(std::to_string(server->minecraft->level->getTile(x, y, z))); ret.push_back(std::to_string(server->minecraft->level->getTile(x, y, z)));
} }
} }
} }
// Return // Return
return join_outputs(ret); return api_join_outputs(ret);
} }
// Set Entity Rotation From XYZ // Set Entity Rotation From XYZ
@ -142,7 +136,7 @@ static Vec3 get_dir(const Entity *entity) {
} }
// Entity Types // Entity Types
std::unordered_map<int, EntityType> modern_entity_id_mapping = { static std::unordered_map<int, EntityType> modern_entity_id_mapping = {
{93, EntityType::CHICKEN}, {93, EntityType::CHICKEN},
{92, EntityType::COW}, {92, EntityType::COW},
{90, EntityType::PIG}, {90, EntityType::PIG},
@ -160,14 +154,14 @@ std::unordered_map<int, EntityType> modern_entity_id_mapping = {
{7, EntityType::THROWN_EGG}, {7, EntityType::THROWN_EGG},
{9, EntityType::PAINTING} {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<const int, EntityType> &pair : modern_entity_id_mapping) { for (const std::pair<const int, EntityType> &pair : modern_entity_id_mapping) {
if (static_cast<int>(pair.second) == type) { if (static_cast<int>(pair.second) == type) {
type = pair.first; 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)) { if (modern_entity_id_mapping.contains(type)) {
type = static_cast<int>(modern_entity_id_mapping[type]); type = static_cast<int>(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); server->pos_translator.to(x, y, z);
// Fix Type ID // Fix Type ID
int type = entity->getEntityTypeId(); int type = entity->getEntityTypeId();
if (compat_mode) { if (api_compat_mode) {
convert_to_rj_entity_type(type); api_convert_to_rj_entity_type(type);
} }
// Return // Return
return join_outputs({ return api_join_outputs({
// ID // ID
std::to_string(entity->id), std::to_string(entity->id),
// Type // Type
std::to_string(type), std::to_string(type),
// Name // Name
get_output(misc_get_entity_type_name(entity).second, true), api_get_output(misc_get_entity_type_name(entity).second, true),
// X // X
std::to_string(x), std::to_string(x),
// Y // Y
@ -213,156 +207,7 @@ static float distance_between(const Entity *e1, const Entity *e2) {
return std::sqrt(dx * dx + dy * dy + dz * dz); return std::sqrt(dx * dx + dy * dy + dz * dz);
} }
// Projectile Event // Parse API Commands
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<ProjectileHitEvent> projectile_events;
std::vector<ChatEvent> chat_events;
std::vector<TileEvent> block_hit_events;
};
static std::unordered_map<int, ExtraClientData> 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 <typename T>
static void clear_events(std::vector<T> &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 <typename T>
static std::string get_events(CommandServer *server, std::vector<T> &queue, const std::optional<int> id) {
std::vector<std::string> out;
typename std::vector<T>::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
static const std::string player_namespace = "player."; static const std::string player_namespace = "player.";
#define API_WARN(format, ...) WARN("API: %s: " format, cmd.c_str(), ##__VA_ARGS__) #define API_WARN(format, ...) WARN("API: %s: " format, cmd.c_str(), ##__VA_ARGS__)
#define ENTITY_NOT_FOUND API_WARN("Entity Not Found: %i", id) #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; ENTITY_NOT_FOUND;
return CommandServer::NullString; return CommandServer::NullString;
} else { } 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") { } else if (cmd == "world.getEntities") {
next_int(type); next_int(type);
if (compat_mode) { if (api_compat_mode) {
convert_to_mcpi_entity_type(type); api_convert_to_mcpi_entity_type(type);
} }
std::vector<std::string> result; std::vector<std::string> result;
for (Entity *entity : server->minecraft->level->entities) { 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)); 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") { } else if (cmd == "world.removeEntity") {
next_int(id); next_int(id);
Entity *entity = server->minecraft->level->getEntity(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'; return std::to_string(result) + '\n';
} else if (cmd == "world.removeEntities") { } else if (cmd == "world.removeEntities") {
next_int(type); next_int(type);
if (compat_mode) { if (api_compat_mode) {
convert_to_mcpi_entity_type(type); api_convert_to_mcpi_entity_type(type);
} }
int removed = 0; int removed = 0;
for (Entity *entity : server->minecraft->level->entities) { 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'; return std::to_string(removed) + '\n';
} else if (cmd == "events.chat.posts") { } 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") { } else if (cmd == "entity.events.chat.posts") {
next_int(id); 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") { } 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") { } else if (cmd == "entity.events.block.hits") {
next_int(id); 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") { } 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") { } else if (cmd == "entity.events.projectile.hits") {
next_int(id); 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") { } else if (cmd == "entity.setDirection") {
next_int(id); next_int(id);
next_float(x); next_float(x);
@ -505,7 +350,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
return CommandServer::Fail; return CommandServer::Fail;
} else { } else {
Vec3 vec = get_dir(entity); 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") { } else if (cmd == "entity.setRotation") {
next_int(id); 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)); 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") { } else if (cmd == "entity.removeEntities") {
// Parse // Parse
next_int(id); next_int(id);
@ -603,7 +448,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
#define next_sign_line(i) \ #define next_sign_line(i) \
next_string(line_##i, false); \ next_string(line_##i, false); \
if (line_##i##_present) { \ if (line_##i##_present) { \
sign->lines[i] = line_##i; \ sign->lines[i] = get_input(line_##i); \
} \ } \
(void) 0 (void) 0
next_sign_line(0); next_sign_line(0);
@ -626,8 +471,8 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
x -= server->pos_translator.x; x -= server->pos_translator.x;
y -= server->pos_translator.y; y -= server->pos_translator.y;
z -= server->pos_translator.z; z -= server->pos_translator.z;
if (compat_mode) { if (api_compat_mode) {
convert_to_mcpi_entity_type(type); api_convert_to_mcpi_entity_type(type);
} }
// Spawn // Spawn
Entity *entity = misc_make_entity_from_id(server->minecraft->level, type); 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'; return std::to_string(entity->id) + '\n';
} else if (cmd == "world.getEntityTypes") { } else if (cmd == "world.getEntityTypes") {
std::vector<std::string> result; std::vector<std::string> result;
for (const std::pair<const EntityType, std::pair<std::string, std::string>> &i: misc_get_entity_type_names()) { for (const std::pair<const EntityType, std::pair<std::string, std::string>> &i : misc_get_entity_type_names()) {
int id = static_cast<int>(i.first); int id = static_cast<int>(i.first);
if (compat_mode) { if (api_compat_mode) {
convert_to_rj_entity_type(id); 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") { } else if (cmd == "entity.setAbsPos") {
next_int(id); next_int(id);
next_float(x); next_float(x);
@ -666,19 +511,19 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
ENTITY_NOT_FOUND; ENTITY_NOT_FOUND;
return CommandServer::Fail; 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") { } else if (cmd == "entity.events.clear") {
next_int(id); next_int(id);
clear_events(client, id); api_clear_events(client, id);
return CommandServer::NullString; return CommandServer::NullString;
} else if (cmd == "events.clear") { } else if (cmd == "events.clear") {
clear_events(client); api_clear_events(client);
return CommandServer::NullString; return CommandServer::NullString;
} else if (cmd == "reborn.disableCompatMode") { } else if (cmd == "reborn.disableCompatMode") {
compat_mode = false; api_compat_mode = false;
return std::string(reborn_get_version()) + '\n'; return std::string(reborn_get_version()) + '\n';
} else if (cmd == "reborn.enableCompatMode") { } else if (cmd == "reborn.enableCompatMode") {
compat_mode = true; api_compat_mode = true;
return CommandServer::NullString; return CommandServer::NullString;
} else { } else {
// Call Original Method // Call Original Method
@ -686,113 +531,10 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
} }
} }
// Push Event To Queue
template <typename T>
static void push_event(std::vector<T> 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 &param_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 &param_2) {
return GameMode_useItemOn->get(false)((GameMode *) self, player, level, item, x, y, z, param_1, param_2);
}
// Init // Init
void init_api() { void init_api() {
if (feature_has("Implement RaspberryJuice API", server_enabled)) { if (feature_has("Implement RaspberryJuice API", server_enabled)) {
enabled = true;
overwrite_calls(CommandServer_parse, CommandServer_parse_injection); overwrite_calls(CommandServer_parse, CommandServer_parse_injection);
overwrite_calls(Arrow_tick, Arrow_tick_injection); _init_api_events();
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);
} }
} }

280
mods/src/api/events.cpp Normal file
View File

@ -0,0 +1,280 @@
#include <vector>
#include <algorithm>
#include <ranges>
#include <optional>
#include <libreborn/patch.h>
#include <symbols/minecraft.h>
#include <mods/misc/misc.h>
#include <mods/chat/chat.h>
#include <mods/api/api.h>
#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<ProjectileHitEvent> projectile_events;
std::vector<ChatEvent> chat_events;
std::vector<TileEvent> block_hit_events;
};
static std::unordered_map<int, ExtraClientData> 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 <typename T>
static void clear_events(std::vector<T> &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 <typename T>
static std::string get_events(CommandServer *server, std::vector<T> &queue, const std::optional<int> id) {
std::vector<std::string> out;
typename std::vector<T>::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<int> 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 <typename T>
static void push_event(std::vector<T> 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 &param_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 &param_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);
}

22
mods/src/api/internal.h Normal file
View File

@ -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<std::string> &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<int> id);
__attribute__((visibility("internal"))) std::string api_get_chat_events(CommandServer *server, const ConnectedClient &client, std::optional<int> id);
__attribute__((visibility("internal"))) std::string api_get_block_hit_events(CommandServer *server, const ConnectedClient &client, std::optional<int> id);

View File

@ -2,6 +2,7 @@ method std::string parse(ConnectedClient &client, const std::string &command) =
method void dispatchPacket(Packet &packet) = 0x6a548; method void dispatchPacket(Packet &packet) = 0x6a548;
static-method bool setSocketBlocking(int fd, bool param_1) = 0x6a2d4; static-method bool setSocketBlocking(int fd, bool param_1) = 0x6a2d4;
method bool _updateClient(ConnectedClient &client) = 0x6ba6c; method bool _updateClient(ConnectedClient &client) = 0x6ba6c;
method void _close() = 0x6a314;
property Minecraft *minecraft = 0x18; property Minecraft *minecraft = 0x18;
property OffsetPosTranslator pos_translator = 0x1c; property OffsetPosTranslator pos_translator = 0x1c;