More API Improvements
This commit is contained in:
parent
54ef10e539
commit
7a237a9ee6
@ -74,7 +74,7 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely
|
||||
* Description: Move the camera to the given position. The XZ-coordinates are automatically offset by `0.5`.
|
||||
* `events.clear()`
|
||||
* Description: Clear all queued events.
|
||||
* Note: On RaspberryJuice, this *does not* clear projectile events. This behavior is maintained only in compatibility mode.
|
||||
* Note: On RaspberryJuice, this *does not* clear projectile events. This behavior is maintained only in the compatibility mode.
|
||||
* `events.block.hits()`
|
||||
* Description: Retrieve all queued block hit events.
|
||||
* Output: List of `x,y,z,face,entity_id`
|
||||
|
@ -67,6 +67,7 @@
|
||||
* `Fix Torch Placement` (Enabled By Default)
|
||||
* `Fix Eggs Spawning Abnormally Healthy Chickens` (Enabled By Default)
|
||||
* `Correctly Close API Sockets` (Enabled By Default)
|
||||
* `Optimized API Sockets` (Enabled By Default)
|
||||
* Existing Functionality (All Enabled By Default)
|
||||
* `Fix Screen Rendering When Hiding HUD`
|
||||
* `Sanitize Usernames`
|
||||
|
@ -141,6 +141,7 @@ CATEGORY Bug Fixes
|
||||
TRUE Fix HUD When Spectating Other Players
|
||||
TRUE Fix Crash When Spectated Entity Is Removed
|
||||
TRUE Correctly Close API Sockets
|
||||
TRUE Fix Moving Players With The API In Multiplayer
|
||||
TRUE Fix Reloading Textures On Resize
|
||||
TRUE Fix options.txt Loading/Saving
|
||||
TRUE Fix Hanging When No Valid Spawn Point Exists
|
||||
@ -150,10 +151,12 @@ CATEGORY Logging
|
||||
TRUE Log Game Status
|
||||
TRUE Log RakNet Startup Errors
|
||||
CATEGORY Miscellaneous
|
||||
CATEGORY API
|
||||
TRUE Implement RaspberryJuice API
|
||||
TRUE Optimized API Sockets
|
||||
TRUE Fullscreen Support
|
||||
TRUE Always Save Chest Tile Entities
|
||||
TRUE Screenshot Support
|
||||
TRUE Add Camera Functionality
|
||||
TRUE Update Default Options
|
||||
TRUE Remove Chest Placement Restrictions
|
||||
TRUE Implement RaspberryJuice API
|
||||
TRUE Remove Chest Placement Restrictions
|
@ -103,6 +103,8 @@ add_library(mods SHARED
|
||||
src/api/api.cpp
|
||||
src/api/events.cpp
|
||||
src/api/compat.cpp
|
||||
src/api/misc.cpp
|
||||
src/api/socket.cpp
|
||||
# shading
|
||||
src/shading/init.cpp
|
||||
src/shading/tesselator.cpp
|
||||
|
@ -42,6 +42,8 @@ static std::string get_blocks(CommandServer *server, const Vec3 &start, const Ve
|
||||
|
||||
// Get
|
||||
std::vector<std::string> ret;
|
||||
const size_t expected_size = (end_x - start_x + 1) * (end_y - start_y + 1) * (end_z - start_z + 1);
|
||||
ret.reserve(expected_size);
|
||||
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++) {
|
||||
@ -54,46 +56,13 @@ static std::string get_blocks(CommandServer *server, const Vec3 &start, const Ve
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret.size() != expected_size) {
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
// Return
|
||||
return api_join_outputs(ret, api_compat_mode ? arg_separator : list_separator);
|
||||
}
|
||||
|
||||
// Properly Teleport Players
|
||||
static void update_player_position(const Entity *entity) {
|
||||
if (entity->vtable == (Entity_vtable *) ServerPlayer_vtable::base) {
|
||||
const ServerPlayer *player = (ServerPlayer *) entity;
|
||||
MovePlayerPacket *packet = MovePlayerPacket::allocate();
|
||||
((Packet *) packet)->constructor();
|
||||
packet->vtable = MovePlayerPacket_vtable::base;
|
||||
packet->x = player->x;
|
||||
packet->y = player->y - player->height_offset;
|
||||
packet->z = player->z;
|
||||
packet->yaw = player->yaw;
|
||||
packet->pitch = player->pitch;
|
||||
packet->entity_id = player->id;
|
||||
player->minecraft->rak_net_instance->send(*(Packet *) packet);
|
||||
packet->destructor_deleting();
|
||||
}
|
||||
}
|
||||
static void Entity_moveTo_injection(Entity *self, const float x, const float y, const float z, const float yaw, const float pitch) {
|
||||
self->moveTo(x, y, z, yaw, pitch);
|
||||
update_player_position(self);
|
||||
}
|
||||
static void ClientSideNetworkHandler_handle_MovePlayerPacket_injection(ClientSideNetworkHandler_handle_MovePlayerPacket_t original, ClientSideNetworkHandler *self, const RakNet_RakNetGUID &rak_net_guid, MovePlayerPacket *packet) {
|
||||
if (self->level) {
|
||||
Entity *entity = self->level->getEntity(packet->entity_id);
|
||||
if (entity) {
|
||||
if (entity == (Entity *) self->minecraft->player) {
|
||||
// Just Teleport
|
||||
entity->moveTo(packet->x, packet->y, packet->z, packet->yaw, packet->pitch);
|
||||
} else {
|
||||
// Call Original Method
|
||||
original(self, rak_net_guid, packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set Entity Rotation From XYZ
|
||||
static void set_dir(Entity *entity, const float x, const float y, const float z) {
|
||||
// Check Rotation
|
||||
@ -110,7 +79,7 @@ static void set_dir(Entity *entity, const float x, const float y, const float z)
|
||||
entity->yaw = std::fmod(std::atan2(-x, z), _2PI) * factor;
|
||||
const float xz = std::sqrt(x * x + z * z);
|
||||
entity->pitch = std::atan(-y / xz) * factor;
|
||||
update_player_position(entity);
|
||||
api_update_entity_position(entity);
|
||||
}
|
||||
// Convert Entity Rotation To XYZ
|
||||
static Vec3 get_dir(const Entity *entity) {
|
||||
@ -194,7 +163,7 @@ static bool is_entity_selected(Entity *entity, const int target_type) {
|
||||
#define next_float(out) next_number(out, float, std::stof)
|
||||
// Parse API Commands
|
||||
#define package_str(name) (#name ".")
|
||||
static bool _package(std::string &cmd, const std::string &package) {
|
||||
static bool _package(std::string_view &cmd, const std::string &package) {
|
||||
if (cmd.starts_with(package)) {
|
||||
cmd = cmd.substr(package.size());
|
||||
return true;
|
||||
@ -214,33 +183,38 @@ static bool _package(std::string &cmd, const std::string &package) {
|
||||
force_semicolon()
|
||||
static std::string CommandServer_parse_injection(CommandServer_parse_t original, CommandServer *server, ConnectedClient &client, const std::string &command) {
|
||||
// Parse Command
|
||||
size_t arg_start = command.find('(');
|
||||
std::string_view command_view = command;
|
||||
size_t arg_start = command_view.find('(');
|
||||
if (arg_start == std::string::npos) {
|
||||
return CommandServer::Fail;
|
||||
}
|
||||
std::string cmd = command.substr(0, arg_start);
|
||||
size_t cmd_end = command.rfind(')');
|
||||
std::string_view cmd = command_view.substr(0, arg_start);
|
||||
size_t cmd_end = command_view.rfind(')');
|
||||
if (cmd_end == std::string::npos) {
|
||||
return CommandServer::Fail;
|
||||
}
|
||||
std::string args_str = command.substr(arg_start + 1, cmd_end - arg_start - 1);
|
||||
std::string_view args_str = command_view.substr(arg_start + 1, cmd_end - arg_start - 1);
|
||||
|
||||
// Redirect Player Package To Entity
|
||||
std::string _new_cmd;
|
||||
std::string _new_args_str;
|
||||
package(player) {
|
||||
// The One Exception
|
||||
passthrough(setting);
|
||||
// Redirect All Other Commands
|
||||
LocalPlayer *player = server->minecraft->player;
|
||||
if (player) {
|
||||
cmd = package_str(entity) + cmd;
|
||||
args_str = std::to_string(player->id) + arg_separator + args_str;
|
||||
_new_cmd = package_str(entity) + std::string(cmd);
|
||||
cmd = _new_cmd;
|
||||
_new_args_str = std::to_string(player->id) + arg_separator + std::string(args_str);
|
||||
args_str = _new_args_str;
|
||||
} else {
|
||||
return CommandServer::Fail;
|
||||
}
|
||||
}
|
||||
|
||||
// Read Arguments
|
||||
std::stringstream args(args_str);
|
||||
std::stringstream args(args_str.data());
|
||||
|
||||
// Manipulate The Level
|
||||
package(world) {
|
||||
@ -459,7 +433,7 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original,
|
||||
}
|
||||
|
||||
// Get/Remove Nearby Entities
|
||||
package(getEntities) {
|
||||
command(getEntities) {
|
||||
// Parse
|
||||
_get_entity(); // Matching RJ Behavior, Even Though It is Dumb
|
||||
next_int(dist);
|
||||
@ -515,7 +489,7 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original,
|
||||
next_float(yaw);
|
||||
// Set
|
||||
entity->yaw = yaw;
|
||||
update_player_position(entity);
|
||||
api_update_entity_position(entity);
|
||||
return CommandServer::NullString;
|
||||
}
|
||||
command(getRotation) {
|
||||
@ -530,7 +504,7 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original,
|
||||
next_float(pitch);
|
||||
// Set
|
||||
entity->pitch = pitch;
|
||||
update_player_position(entity);
|
||||
api_update_entity_position(entity);
|
||||
return CommandServer::NullString;
|
||||
}
|
||||
command(getPitch) {
|
||||
@ -548,7 +522,8 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original,
|
||||
next_float(y);
|
||||
next_float(z);
|
||||
// Set
|
||||
Entity_moveTo_injection(entity, x, y, z, entity->yaw, entity->pitch);
|
||||
entity->moveTo(x, y, z, entity->yaw, entity->pitch);
|
||||
api_update_entity_position(entity);
|
||||
return CommandServer::NullString;
|
||||
}
|
||||
command(getAbsPos) {
|
||||
@ -630,74 +605,13 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original,
|
||||
return CommandServer::Fail;
|
||||
}
|
||||
|
||||
// Fix HUD Spectating Other Players
|
||||
template <typename... Args>
|
||||
static void ItemInHandRenderer_render_injection(const std::function<void(ItemInHandRenderer *, Args...)> &original, ItemInHandRenderer *self, Args... args) {
|
||||
// "Fix" Current Player
|
||||
LocalPlayer *&player = self->minecraft->player;
|
||||
LocalPlayer *old_player = player;
|
||||
Mob *camera = self->minecraft->camera;
|
||||
if (camera && camera->isPlayer()) {
|
||||
player = (LocalPlayer *) camera;
|
||||
}
|
||||
// Call Original Method
|
||||
original(self, std::forward<Args>(args)...);
|
||||
// Revert "Fix"
|
||||
player = old_player;
|
||||
}
|
||||
|
||||
// Fix Crash When Camera Entity Is Removed
|
||||
static void LevelRenderer_entityRemoved_injection(LevelRenderer *self, Entity *entity) {
|
||||
// Call Original Method
|
||||
LevelListener_entityRemoved->get(false)((LevelListener *) self, entity);
|
||||
// Fix Camera
|
||||
Minecraft *minecraft = self->minecraft;
|
||||
if ((Entity *) minecraft->camera == entity) {
|
||||
minecraft->camera = (Mob *) minecraft->player;
|
||||
}
|
||||
}
|
||||
|
||||
// Close Sockets
|
||||
static void CommandServer__close_injection(CommandServer__close_t original, CommandServer *self) {
|
||||
// Close
|
||||
for (const ConnectedClient &client : self->clients) {
|
||||
close(client.sock);
|
||||
}
|
||||
self->clients.clear();
|
||||
// Call Original Method
|
||||
original(self);
|
||||
}
|
||||
static void Minecraft_leaveGame_injection(Minecraft_leaveGame_t original, Minecraft *self, const bool save_remote_level) {
|
||||
// Destroy Server
|
||||
CommandServer *&server = self->command_server;
|
||||
if (server) {
|
||||
server->destructor(0);
|
||||
server = nullptr;
|
||||
}
|
||||
// Call Original Method
|
||||
original(self, save_remote_level);
|
||||
}
|
||||
|
||||
// Init
|
||||
void init_api() {
|
||||
if (feature_has("Implement RaspberryJuice API", server_enabled)) {
|
||||
overwrite_calls(CommandServer_parse, CommandServer_parse_injection);
|
||||
_init_api_events();
|
||||
// Fix Teleporting Players
|
||||
overwrite_calls(ClientSideNetworkHandler_handle_MovePlayerPacket, ClientSideNetworkHandler_handle_MovePlayerPacket_injection);
|
||||
overwrite_call((void *) 0x6b6e8, Entity_moveTo, Entity_moveTo_injection);
|
||||
}
|
||||
// Bug Fixes
|
||||
if (feature_has("Fix HUD When Spectating Other Players", server_enabled)) {
|
||||
overwrite_calls(ItemInHandRenderer_render, ItemInHandRenderer_render_injection<float>);
|
||||
overwrite_calls(ItemInHandRenderer_renderScreenEffect, ItemInHandRenderer_render_injection<float>);
|
||||
overwrite_calls(ItemInHandRenderer_tick, ItemInHandRenderer_render_injection<>);
|
||||
}
|
||||
if (feature_has("Fix Crash When Spectated Entity Is Removed", server_enabled)) {
|
||||
patch_vtable(LevelRenderer_entityRemoved, LevelRenderer_entityRemoved_injection);
|
||||
}
|
||||
if (feature_has("Correctly Close API Sockets", server_enabled)) {
|
||||
overwrite_calls(CommandServer__close, CommandServer__close_injection);
|
||||
overwrite_calls(Minecraft_leaveGame, Minecraft_leaveGame_injection);
|
||||
}
|
||||
// Miscellaneous Changes
|
||||
_init_api_socket();
|
||||
_init_api_misc();
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
#include <libreborn/patch.h>
|
||||
@ -101,22 +100,15 @@ static bool CommandServer__updateAccept_setSocketBlocking_injection(const int 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
|
||||
extra_client_data.erase(client.sock);
|
||||
}
|
||||
return ret;
|
||||
void api_free_event_data(const int sock) {
|
||||
extra_client_data.erase(sock);
|
||||
}
|
||||
static void CommandServer__close_injection(CommandServer__close_t original, CommandServer *self) {
|
||||
// Server Shutdown
|
||||
void api_free_all_event_data() {
|
||||
extra_client_data.clear();
|
||||
original(self);
|
||||
}
|
||||
|
||||
// Clear All Events
|
||||
static void clear_events(const ConnectedClient &client) {
|
||||
static void clear_all_events(const ConnectedClient &client) {
|
||||
ExtraClientData &data = extra_client_data.at(client.sock);
|
||||
if (!api_compat_mode) {
|
||||
// Match RJ Bug
|
||||
@ -129,9 +121,9 @@ static void clear_events(const ConnectedClient &client) {
|
||||
// Clear Events Produced By Given Entity
|
||||
template <typename T>
|
||||
static void _clear_events(std::vector<T> &data, const int id) {
|
||||
data.erase(std::remove_if(data.begin(), data.end(), [&id](const T &e) {
|
||||
std::erase_if(data, [&id](const T &e) {
|
||||
return id == e.owner_id;
|
||||
}), data.end());
|
||||
});
|
||||
}
|
||||
static void clear_events(const ConnectedClient &client, const int id) {
|
||||
ExtraClientData &data = extra_client_data.at(client.sock);
|
||||
@ -287,31 +279,29 @@ void _init_api_events() {
|
||||
overwrite_calls(Gui_addMessage, Gui_addMessage_injection);
|
||||
// Track Connected Clients
|
||||
overwrite_call((void *) 0x6bd78, CommandServer_setSocketBlocking, CommandServer__updateAccept_setSocketBlocking_injection);
|
||||
overwrite_calls(CommandServer__updateClient, CommandServer__updateClient_injection);
|
||||
overwrite_calls(CommandServer__close, CommandServer__close_injection);
|
||||
// Track Block Hits
|
||||
overwrite_calls(GameMode_useItemOn, GameMode_useItemOn_injection);
|
||||
overwrite_calls(CreatorMode_useItemOn, CreatorMode_useItemOn_injection);
|
||||
}
|
||||
|
||||
// Handle Commands
|
||||
std::string api_handle_event_command(CommandServer *server, const ConnectedClient &client, const std::string &cmd, std::optional<int> id) {
|
||||
std::string api_handle_event_command(CommandServer *server, const ConnectedClient &client, const std::string_view &cmd, const std::optional<int> id) {
|
||||
if (cmd == "clear") {
|
||||
// Clear Events
|
||||
if (id.has_value()) {
|
||||
clear_events(client, id.value());
|
||||
} else {
|
||||
clear_events(client);
|
||||
clear_all_events(client);
|
||||
}
|
||||
return CommandServer::NullString;
|
||||
} else if (cmd == "chat.posts") {
|
||||
// Chat Events
|
||||
return get_chat_events(server, client, id);
|
||||
} else if (cmd == "block.hits") {
|
||||
// Chat Events
|
||||
// Block Hit Events
|
||||
return get_block_hit_events(server, client, id);
|
||||
} else if (cmd == "projectile.hits") {
|
||||
// Chat Events
|
||||
// Projectile Events
|
||||
return get_projectile_events(server, client, id);
|
||||
} else {
|
||||
// Invalid Command
|
||||
|
@ -19,8 +19,14 @@ __attribute__((visibility("internal"))) std::string api_join_outputs(const std::
|
||||
__attribute__((visibility("internal"))) void api_convert_to_outside_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_update_entity_position(const Entity *entity);
|
||||
|
||||
__attribute__((visibility("internal"))) std::string api_handle_event_command(CommandServer *server, const ConnectedClient &client, const std::string &cmd, std::optional<int> id);
|
||||
__attribute__((visibility("internal"))) void _init_api_events();
|
||||
__attribute__((visibility("internal"))) void _init_api_misc();
|
||||
__attribute__((visibility("internal"))) void _init_api_socket();
|
||||
|
||||
__attribute__((visibility("internal"))) std::string api_handle_event_command(CommandServer *server, const ConnectedClient &client, const std::string_view &cmd, std::optional<int> id);
|
||||
__attribute__((visibility("internal"))) void api_free_event_data(int sock);
|
||||
__attribute__((visibility("internal"))) void api_free_all_event_data();
|
||||
|
||||
__attribute__((visibility("internal"))) extern bool api_suppress_chat_events;
|
||||
|
118
mods/src/api/misc.cpp
Normal file
118
mods/src/api/misc.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include <libreborn/patch.h>
|
||||
|
||||
#include <mods/feature/feature.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
// Fix HUD Spectating Other Players
|
||||
template <typename... Args>
|
||||
static void ItemInHandRenderer_render_injection(const std::function<void(ItemInHandRenderer *, Args...)> &original, ItemInHandRenderer *self, Args... args) {
|
||||
// "Fix" Current Player
|
||||
LocalPlayer *&player = self->minecraft->player;
|
||||
LocalPlayer *old_player = player;
|
||||
Mob *camera = self->minecraft->camera;
|
||||
if (camera && camera->isPlayer()) {
|
||||
player = (LocalPlayer *) camera;
|
||||
}
|
||||
// Call Original Method
|
||||
original(self, std::forward<Args>(args)...);
|
||||
// Revert "Fix"
|
||||
player = old_player;
|
||||
}
|
||||
|
||||
// Fix Crash When Camera Entity Is Removed
|
||||
static void LevelRenderer_entityRemoved_injection(LevelRenderer *self, Entity *entity) {
|
||||
// Call Original Method
|
||||
LevelListener_entityRemoved->get(false)((LevelListener *) self, entity);
|
||||
// Fix Camera
|
||||
Minecraft *minecraft = self->minecraft;
|
||||
if ((Entity *) minecraft->camera == entity) {
|
||||
minecraft->camera = (Mob *) minecraft->player;
|
||||
}
|
||||
}
|
||||
|
||||
// Close Sockets
|
||||
static void CommandServer__close_injection_1(CommandServer__close_t original, CommandServer *self) {
|
||||
// Close
|
||||
for (const ConnectedClient &client : self->clients) {
|
||||
close(client.sock);
|
||||
}
|
||||
self->clients.clear();
|
||||
// Call Original Method
|
||||
original(self);
|
||||
}
|
||||
static void Minecraft_leaveGame_injection(Minecraft_leaveGame_t original, Minecraft *self, const bool save_remote_level) {
|
||||
// Destroy Server
|
||||
CommandServer *&server = self->command_server;
|
||||
if (server) {
|
||||
server->destructor(0);
|
||||
::operator delete(server);
|
||||
server = nullptr;
|
||||
}
|
||||
// Call Original Method
|
||||
original(self, save_remote_level);
|
||||
}
|
||||
static bool CommandServer__updateClient_injection_1(CommandServer__updateClient_t original, CommandServer *self, ConnectedClient &client) {
|
||||
// Call Original Method
|
||||
const bool ret = original(self, client);
|
||||
// Close Socket If Needed
|
||||
if (!ret) {
|
||||
close(client.sock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Properly Teleport Players
|
||||
void api_update_entity_position(const Entity *entity) {
|
||||
MovePlayerPacket *packet = MovePlayerPacket::allocate(); // Despite The Name, This Supports All Entities
|
||||
((Packet *) packet)->constructor();
|
||||
packet->vtable = MovePlayerPacket_vtable::base;
|
||||
packet->x = entity->x;
|
||||
packet->y = entity->y - entity->height_offset;
|
||||
packet->z = entity->z;
|
||||
packet->yaw = entity->yaw;
|
||||
packet->pitch = entity->pitch;
|
||||
packet->entity_id = entity->id;
|
||||
entity->level->rak_net_instance->send(*(Packet *) packet);
|
||||
packet->destructor_deleting();
|
||||
}
|
||||
static void CommandServer_parse_Entity_moveTo_injection(Entity *self, const float x, const float y, const float z, const float yaw, const float pitch) {
|
||||
self->moveTo(x, y, z, yaw, pitch);
|
||||
api_update_entity_position(self);
|
||||
}
|
||||
static void ClientSideNetworkHandler_handle_MovePlayerPacket_injection(ClientSideNetworkHandler_handle_MovePlayerPacket_t original, ClientSideNetworkHandler *self, const RakNet_RakNetGUID &rak_net_guid, MovePlayerPacket *packet) {
|
||||
if (self->level) {
|
||||
Entity *entity = self->level->getEntity(packet->entity_id);
|
||||
if (entity) {
|
||||
if (entity == (Entity *) self->minecraft->player) {
|
||||
// Just Teleport
|
||||
entity->moveTo(packet->x, packet->y, packet->z, packet->yaw, packet->pitch);
|
||||
} else {
|
||||
// Call Original Method
|
||||
original(self, rak_net_guid, packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init
|
||||
void _init_api_misc() {
|
||||
// Bug Fixes
|
||||
if (feature_has("Fix HUD When Spectating Other Players", server_enabled)) {
|
||||
overwrite_calls(ItemInHandRenderer_render, ItemInHandRenderer_render_injection<float>);
|
||||
overwrite_calls(ItemInHandRenderer_renderScreenEffect, ItemInHandRenderer_render_injection<float>);
|
||||
overwrite_calls(ItemInHandRenderer_tick, ItemInHandRenderer_render_injection<>);
|
||||
}
|
||||
if (feature_has("Fix Crash When Spectated Entity Is Removed", server_enabled)) {
|
||||
patch_vtable(LevelRenderer_entityRemoved, LevelRenderer_entityRemoved_injection);
|
||||
}
|
||||
if (feature_has("Correctly Close API Sockets", server_enabled)) {
|
||||
overwrite_calls(CommandServer__close, CommandServer__close_injection_1);
|
||||
overwrite_calls(Minecraft_leaveGame, Minecraft_leaveGame_injection);
|
||||
overwrite_calls(CommandServer__updateClient, CommandServer__updateClient_injection_1);
|
||||
}
|
||||
if (feature_has("Fix Moving Players With The API In Multiplayer", server_enabled)) {
|
||||
overwrite_calls(ClientSideNetworkHandler_handle_MovePlayerPacket, ClientSideNetworkHandler_handle_MovePlayerPacket_injection);
|
||||
overwrite_call((void *) 0x6b6e8, Entity_moveTo, CommandServer_parse_Entity_moveTo_injection);
|
||||
}
|
||||
}
|
131
mods/src/api/socket.cpp
Normal file
131
mods/src/api/socket.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
#include <sys/socket.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <libreborn/patch.h>
|
||||
#include <libreborn/log.h>
|
||||
|
||||
#include <mods/feature/feature.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
// Queue Data For Writing
|
||||
static std::unordered_map<int, std::string> to_write;
|
||||
static void write_queued_data(const int sock) {
|
||||
// Check
|
||||
if (!to_write.contains(sock)) {
|
||||
return;
|
||||
}
|
||||
// Write Data
|
||||
std::string &data = to_write.at(sock);
|
||||
size_t pos = 0;
|
||||
while (pos < data.size()) {
|
||||
const size_t remaining = data.size() - pos;
|
||||
ssize_t bytes_sent = send(sock, data.data() + pos, remaining, 0);
|
||||
if (bytes_sent == -1) {
|
||||
bytes_sent = 0;
|
||||
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
// Unable To Send Right Now, Try Again Later
|
||||
break;
|
||||
} else {
|
||||
// Error, Give Up
|
||||
pos = 0;
|
||||
data.clear();
|
||||
}
|
||||
}
|
||||
// Advance
|
||||
pos += bytes_sent;
|
||||
}
|
||||
// Erase Sent Data
|
||||
data.erase(0, pos);
|
||||
// Remove Finished Sockets
|
||||
std::erase_if(to_write, [](const std::pair<const int, std::string> &pair) {
|
||||
return pair.second.empty();
|
||||
});
|
||||
}
|
||||
static void handle_line(CommandServer *self, ConnectedClient &client, const std::string &line) {
|
||||
// Run
|
||||
const std::string ret = self->parse(client, line);
|
||||
// Return
|
||||
if (ret != CommandServer::NullString) {
|
||||
if (ret.back() != '\n') {
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
// Queue For Sending
|
||||
to_write[client.sock] += ret;
|
||||
}
|
||||
}
|
||||
// Remove Speed Limit
|
||||
static constexpr int max_read_per_tick_per_client = 16384; // 16 KiB
|
||||
static bool CommandServer__updateClient_injection_2(__attribute__((unused)) CommandServer__updateClient_t original, CommandServer *self, ConnectedClient &client) {
|
||||
// Read Lines
|
||||
size_t total_read = 0;
|
||||
constexpr size_t buffer_size = 2048;
|
||||
char buffer[buffer_size];
|
||||
while (total_read < max_read_per_tick_per_client) {
|
||||
const ssize_t bytes_received = recv(client.sock, buffer, buffer_size, 0);
|
||||
if (bytes_received == -1) {
|
||||
if (errno == EINTR) {
|
||||
// Signal Detected, Try Again
|
||||
continue;
|
||||
} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
// End Of Available Data (For Now)
|
||||
break;
|
||||
} else {
|
||||
// Error
|
||||
return false;
|
||||
}
|
||||
} else if (bytes_received == 0) {
|
||||
// Connection Closed
|
||||
return false;
|
||||
}
|
||||
total_read += bytes_received;
|
||||
// Append Received Data To Client Buffer
|
||||
client.str.append(buffer, bytes_received);
|
||||
}
|
||||
|
||||
// Process Lines
|
||||
size_t start = 0;
|
||||
size_t end;
|
||||
while ((end = client.str.find('\n', start)) != std::string::npos) {
|
||||
end++; // Include Newline
|
||||
const std::string line = client.str.substr(start, end - start);
|
||||
handle_line(self, client, line);
|
||||
start = end;
|
||||
}
|
||||
client.str.erase(0, start);
|
||||
|
||||
// Write Queued Data
|
||||
write_queued_data(client.sock);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear Extra Data On Socket Close
|
||||
static bool CommandServer__updateClient_injection_3(CommandServer__updateClient_t original, CommandServer *self, ConnectedClient &client) {
|
||||
const bool ret = original(self, client);
|
||||
if (!ret) {
|
||||
// Client Disconnected
|
||||
api_free_event_data(client.sock);
|
||||
to_write.erase(client.sock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static void CommandServer__close_injection_2(CommandServer__close_t original, CommandServer *self) {
|
||||
// Clear All Extra Data
|
||||
api_free_all_event_data();
|
||||
to_write.clear();
|
||||
// Call Original Method
|
||||
original(self);
|
||||
}
|
||||
|
||||
// Init
|
||||
void _init_api_socket() {
|
||||
// Optimization
|
||||
if (feature_has("Optimized API Sockets", server_enabled)) {
|
||||
overwrite_calls(CommandServer__updateClient, CommandServer__updateClient_injection_2);
|
||||
}
|
||||
// Clear Extra Data On Socket Close
|
||||
overwrite_calls(CommandServer__updateClient, CommandServer__updateClient_injection_3);
|
||||
overwrite_calls(CommandServer__close, CommandServer__close_injection_2);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user