WIP RJ API

This commit is contained in:
Bigjango13 2025-02-25 07:13:46 -05:00 committed by TheBrokenRail
parent 240d07e1ec
commit 49f654916d
23 changed files with 1112 additions and 88 deletions

@ -1 +1 @@
Subproject commit 01a64f321d5f98a59d1432f659b8a9e11d3d60c6
Subproject commit 3db1515ecf3a5ab8f9b3b642dc564ca84ab3182c

View File

@ -61,6 +61,7 @@
* `Batch Font Rendering` (Enabled By Default)
* `Fix Furnace Screen Visual Bug` (Enabled By Default)
* `Fix Held Item Poking Through Screen Overlay` (Enabled By Default)
* `Implement RaspberryJuice API` (Enabled By Default)
* Existing Functionality (All Enabled By Default)
* `Fix Screen Rendering When Hiding HUD`
* `Sanitize Usernames`

View File

@ -1,11 +1,13 @@
# Credits
| Project | Reason |
| --- | --- |
| [mhsjlw/mcpilauncher](https://github.com/mhsjlw/mcpilauncher/blob/master/trampoline/trampoline.c) | Information On Getting Minecraft: Pi Eiditon To Run On Desktop Linux |
| [mhsjlw/mcpilauncher](https://web.archive.org/web/20220727030722/https://github.com/mhsjlw/mcpilauncher/blob/master/trampoline/trampoline.c) | Information On Getting Minecraft: Pi Edition To Run On Desktop Linux |
| [Phirel's Survival Patch](https://www.minecraftforum.net/forums/minecraft-editions/minecraft-pi-edition/1960005-survival-mode-patch) | Information On Survival Mode Support |
| [zhuowei/MinecraftPEModWiki](https://github.com/zhuowei/MinecraftPEModWiki/wiki/How-some-unlocks-are-made) | Information On Smooth Lighting Support |
| [zhuowei/RaspberryJuice](https://github.com/zhuowei/RaspberryJuice) | Design Of RaspberryJuice Extended API |
| [Ghidra](https://ghidra-sre.org) | Used For Decompiling Minecraft: Pi Edition |
| [RetDec](https://retdec.com) | Used For Decompiling Minecraft: Pi Edition |
| [minecraft-linux/mcpelauncher-core](https://github.com/minecraft-linux/mcpelauncher-core/blob/6b5e17b5685a612143297ae4595bdd12327284f3/src/patch_utils.cpp#L42) | Original Function Overwrite Code |
| [Hooking C Functions at Runtime - Thomas Finch](http://thomasfinch.me/blog/2015/07/24/Hooking-C-Functions-At-Runtime.html) | Original Patching Code |
| [ReMinecraftPE](https://github.com/ReMinecraftPE/mcpe) | A Lot Of Decompiled Code |
| [Bigjango](https://github.com/Bigjango13) | A Ton Of Programming Contributions |

View File

@ -1,7 +1,7 @@
OPTION(debug, "debug", 'd', "Enable Debug Logging")
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
OPTION(disable_logger, "disable-logger", -1, "Disable Logger (And Crash Report Dialog)")
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialog")
OPTION(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache")
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit")
OPTION(print_available_feature_flags, "print-available-feature-flags", -6, "Print Available Client-Mode Feature Flags")

View File

@ -150,3 +150,4 @@ CATEGORY Miscellaneous
TRUE Add Camera Functionality
TRUE Update Default Options
TRUE Remove Chest Placement Restrictions
TRUE Implement RaspberryJuice API

View File

@ -34,6 +34,7 @@ add_library(mods SHARED
src/misc/ui.cpp
src/misc/tinting.cpp
src/misc/home.cpp
src/misc/base64.cpp
# extend
src/extend/Screen.cpp
src/extend/DynamicTexture.cpp
@ -98,6 +99,8 @@ add_library(mods SHARED
src/multidraw/storage.cpp
# classic-ui
src/classic-ui/classic-ui.cpp
# api
src/api/api.cpp
# shading
src/shading/init.cpp
src/shading/tesselator.cpp

View File

@ -0,0 +1,8 @@
#pragma once
#include <string>
#include <symbols/minecraft.h>
extern "C" {
void api_add_chat_event(const Player *sender, const std::string &message);
}

View File

@ -1,7 +1,6 @@
#pragma once
#include <string>
#include <symbols/minecraft.h>
extern "C" {
@ -9,6 +8,7 @@ extern "C" {
std::string chat_send_api_command(const Minecraft *minecraft, const std::string &str);
// Override using the HOOK() macro to provide customized chat behavior.
void chat_send_message_to_clients(ServerSideNetworkHandler *server_side_network_handler, const char *username, const char *message);
void chat_send_message_to_clients(ServerSideNetworkHandler *server_side_network_handler, const Player *sender, const char *message);
void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet);
bool chat_is_sending();
}

View File

@ -28,5 +28,6 @@ void init_screenshot();
void init_f3();
void init_multidraw();
void init_classic_ui();
void init_api();
void init_shading();
}

View File

@ -28,4 +28,38 @@ void misc_run_on_key_press(const std::function<bool(Minecraft *, int)> &func);
void misc_run_on_creative_inventory_setup(const std::function<void(FillingContainer *)> &function);
void misc_run_on_swap_buffers(const std::function<void()> &function);
std::string misc_get_player_username_utf(const Player *player);
enum class EntityType {
UNKNOWN = 0,
// Animals
CHICKEN = 10,
COW = 11,
PIG = 12,
SHEEP = 13,
// Hostiles
ZOMBIE = 32,
CREEPER = 33,
SKELETON = 34,
SPIDER = 35,
ZOMBIE_PIGMAN = 36,
// Special #1, Miscellaneous
DROPPED_ITEM = 64,
PRIMED_TNT = 65,
FALLING_SAND = 66,
PAINTING = 83,
// Special #2, Projectiles
ARROW = 80,
THROWN_SNOWBALL = 81,
THROWN_EGG = 82
};
std::map<EntityType, std::pair<std::string, std::string>> &misc_get_entity_type_names();
std::pair<std::string, std::string> misc_get_entity_type_name(Entity *entity);
std::string misc_get_entity_name(Entity *entity);
Entity *misc_make_entity_from_id(Level *level, int id);
std::string misc_base64_encode(const std::string &data);
std::string misc_base64_decode(const std::string &input);
static constexpr int line_height = 8;

823
mods/src/api/api.cpp Normal file
View File

@ -0,0 +1,823 @@
#include <cmath>
#include <string>
#include <fstream>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <optional>
#include <ranges>
#include <libreborn/log.h>
#include <libreborn/util/string.h>
#include <libreborn/patch.h>
#include <libreborn/config.h>
#include <symbols/minecraft.h>
#include <mods/api/api.h>
#include <mods/init/init.h>
#include <mods/misc/misc.h>
#include <mods/chat/chat.h>
#include <mods/feature/feature.h>
// Compatibility Mode
static bool compat_mode = true;
// Argument Separator
static constexpr char arg_separator = ',';
static constexpr char list_separator = '|';
// Read String Input
static std::string get_input(std::string message) {
// Decode
if (!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) {
// Output In Plaintext For RJ Compatibility
std::ranges::replace(message, list_separator, '\\');
if (replace_comma) {
std::ranges::replace(message, arg_separator, '.');
}
} else {
message = misc_base64_encode(message);
}
// Convert To Unicode
return from_cp437(message);
}
// Join Strings Into Output
static std::string join_outputs(const std::vector<std::string> &pieces, const char separator = arg_separator) {
// Join
std::string out;
for (std::string piece : pieces) {
// Check
if (piece.find(separator) != std::string::npos) {
// This Should Be Escapes
IMPOSSIBLE();
}
// Remove Trailing Newline
if (!piece.empty() && piece.back() == '\n') {
piece.pop_back();
}
// Add
out += piece + separator;
}
// Remove Hanging Comma
if (!out.empty()) {
out.pop_back();
}
// Return
return out + '\n';
}
// 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);
// End Coordinate
int endx = int(end.x);
int endy = int(end.y);
int endz = int(end.z);
// Apply Offset
server->pos_translator.from(startx, starty, startz);
server->pos_translator.from(endx, endy, endz);
// Swap If Needed
if (endx < startx) {
std::swap(startx, endx);
}
if (endy < starty) {
std::swap(starty, endy);
}
if (endz < startz) {
std::swap(startz, endz);
}
// Get
std::vector<std::string> ret;
for (int x = startx; x <= endx; x++) {
for (int y = starty; y <= endy; y++) {
for (int z = startz; z <= endz; z++) {
ret.push_back(std::to_string(server->minecraft->level->getTile(x, y, z)));
}
}
}
// Return
return join_outputs(ret);
}
// Set Entity Rotation From XYZ
static void set_dir(Entity *entity, const float x, const float y, const float z) {
// Check Rotation
if (entity == nullptr) {
return;
}
// Calculate
if (x == 0 && z == 0) {
entity->pitch = y > 0 ? -90 : 90;
return;
}
constexpr float _2PI = 2 * M_PI;
constexpr float factor = float(180.0f / M_PI);
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;
}
// Convert Entity Rotation To XYZ
static Vec3 get_dir(const Entity *entity) {
constexpr float factor = float(M_PI / 180);
const float y = -std::sin(entity->pitch * factor);
const float xz = std::cos(entity->pitch * factor);
const float x = -xz * std::sin(entity->yaw * factor);
const float z = xz * std::cos(entity->yaw * factor);
return Vec3{x, y, z};
}
// Entity Types
std::unordered_map<int, EntityType> modern_entity_id_mapping = {
{93, EntityType::CHICKEN},
{92, EntityType::COW},
{90, EntityType::PIG},
{91, EntityType::SHEEP},
{54, EntityType::ZOMBIE},
{50, EntityType::CREEPER},
{51, EntityType::SKELETON},
{52, EntityType::SPIDER},
{57, EntityType::ZOMBIE_PIGMAN},
{1, EntityType::DROPPED_ITEM},
{20, EntityType::PRIMED_TNT},
{21, EntityType::FALLING_SAND},
{10, EntityType::ARROW},
{11, EntityType::THROWN_SNOWBALL},
{7, EntityType::THROWN_EGG},
{9, EntityType::PAINTING}
};
static void convert_to_rj_entity_type(int &type) {
for (const std::pair<const int, EntityType> &pair : modern_entity_id_mapping) {
if (static_cast<int>(pair.second) == type) {
type = pair.first;
}
}
}
static void convert_to_mcpi_entity_type(int &type) {
if (modern_entity_id_mapping.contains(type)) {
type = static_cast<int>(modern_entity_id_mapping[type]);
}
}
// Convert Entity To String
static std::string get_entity_message(CommandServer *server, Entity *entity) {
// Offset Position
float x = entity->x;
float y = entity->y - entity->height_offset;
float z = entity->z;
server->pos_translator.to(x, y, z);
// Fix Type ID
int type = entity->getEntityTypeId();
if (compat_mode) {
convert_to_rj_entity_type(type);
}
// Return
return join_outputs({
// ID
std::to_string(entity->id),
// Type
std::to_string(type),
// Name
get_output(misc_get_entity_type_name(entity).second, true),
// X
std::to_string(x),
// Y
std::to_string(y),
// X
std::to_string(z)
});
}
// Calculate Distance Between Entities
static float distance_between(const Entity *e1, const Entity *e2) {
if (e1 == nullptr || e2 == nullptr) {
return 0;
}
const float dx = e2->x - e1->x;
const float dy = e2->y - e1->y;
const float dz = e2->z - e1->z;
return std::sqrt(dx * dx + dy * dy + dz * dz);
}
// Projectile Event
struct ProjectileHitEvent {
int x;
int y;
int z;
std::string owner = "";
int owner_id = 0;
int target_id = 0;
};
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
if (compat_mode) {
pieces.push_back(get_output(e.owner, true));
} else {
pieces.push_back(std::to_string(e.owner_id));
pieces.push_back(std::to_string(e.target_id));
}
// Return
return join_outputs(pieces);
}
// Chat Message Event
struct ChatEvent {
std::string message = "";
int owner_id = 0;
};
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
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;
}
// 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 constexpr int no_entity_id = -1;
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)
std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServer *server, ConnectedClient &client, const std::string &command) {
size_t arg_start = command.find("(");
if (arg_start == std::string::npos) {
return CommandServer::Fail;
}
std::string cmd = command.substr(0, arg_start);
// rfind() So ) In Input Does Not Break
size_t cmd_end = command.rfind(")");
if (cmd_end == std::string::npos) {
return CommandServer::Fail;
}
std::string args = command.substr(arg_start + 1, cmd_end - arg_start - 1);
// Redirect Player Namespace To The Entity One
if (server->minecraft->player != nullptr) {
if (cmd.starts_with(player_namespace) && cmd != "player.setting") {
cmd = "entity." + cmd.substr(player_namespace.size());
args = std::to_string(server->minecraft->player->id) + arg_separator + args;
}
}
// And Now The Big If-Else Chain
if (cmd == "world.getBlocks") {
int x0, y0, z0, x1, y1, z1;
if (sscanf(args.c_str(), "%d,%d,%d,%d,%d,%d", &x0, &y0, &z0, &x1, &y1, &z1) != 6) {
return CommandServer::Fail;
}
// Get The Blocks
return get_blocks(server, Vec3{(float) x0, (float) y0, (float) z0}, Vec3{(float) x1, (float) y1, (float) z1});
} else if (cmd == "world.getPlayerId") {
std::string username = get_input(args);
for (Player *player : server->minecraft->level->players) {
if (misc_get_player_username_utf(player) == username) {
return std::to_string(player->id) + "\n";
}
}
API_WARN("Player Not Found: %s", args.c_str());
return CommandServer::Fail;
} else if (cmd == "entity.getName") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
return CommandServer::NullString;
} else {
return get_output(misc_get_entity_name(entity)) + '\n';
}
} else if (cmd == "world.getEntities") {
int type;
if (sscanf(args.c_str(), "%d", &type) != 1) {
return CommandServer::Fail;
}
if (compat_mode) {
convert_to_mcpi_entity_type(type);
}
std::vector<std::string> result;
for (Entity *entity : server->minecraft->level->entities) {
int i = entity->getEntityTypeId();
if (i > 0 && (type == no_entity_id || i == type)) {
result.push_back(get_entity_message(server, entity));
}
}
return join_outputs(result, list_separator);
} else if (cmd == "world.removeEntity") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
int result = 0;
if (entity != nullptr && !entity->isPlayer()) {
entity->remove();
result++;
}
return std::to_string(result) + '\n';
} else if (cmd == "world.removeEntities") {
int type;
if (sscanf(args.c_str(), "%d", &type) != 1) {
return CommandServer::Fail;
}
if (compat_mode) {
convert_to_mcpi_entity_type(type);
}
int removed = 0;
for (Entity *entity : server->minecraft->level->entities) {
int i = entity->getEntityTypeId();
if (i > 0) {
if (type == no_entity_id || i == type) {
entity->remove();
removed++;
}
}
}
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);
} else if (cmd == "entity.events.chat.posts") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
return get_events(server, extra_client_data[client.sock].chat_events, id);
} else if (cmd == "events.block.hits") {
return get_events(server, extra_client_data[client.sock].block_hit_events, std::nullopt);
} else if (cmd == "entity.events.block.hits") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
return get_events(server, extra_client_data[client.sock].block_hit_events, id);
} else if (cmd == "events.projectile.hits") {
return get_events(server, extra_client_data[client.sock].projectile_events, std::nullopt);
} else if (cmd == "entity.events.projectile.hits") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
return get_events(server, extra_client_data[client.sock].projectile_events, id);
} else if (cmd == "entity.setDirection") {
int id;
float x, y, z;
if (sscanf(args.c_str(), "%d,%f,%f,%f", &id, &x, &y, &z) != 4) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
} else {
set_dir(entity, x, y, z);
}
return CommandServer::NullString;
} else if (cmd == "entity.getDirection") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
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)});
}
} else if (cmd == "entity.setRotation") {
int id;
float yaw;
if (sscanf(args.c_str(), "%d,%f", &id, &yaw) != 2) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
} else {
entity->yaw = yaw;
}
return CommandServer::NullString;
} else if (cmd == "entity.setPitch") {
int id;
float pitch;
if (sscanf(args.c_str(), "%d,%f", &id, &pitch) != 2) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
} else {
entity->pitch = pitch;
}
return CommandServer::NullString;
} else if (cmd == "entity.getRotation") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
return CommandServer::Fail;
} else {
return std::to_string(entity->yaw) + '\n';
}
} else if (cmd == "entity.getPitch") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
return CommandServer::Fail;
} else {
return std::to_string(entity->pitch) + '\n';
}
} else if (cmd == "entity.getEntities") {
// Parse
int dist, type;
int id = 0;
if (sscanf(args.c_str(), "%d,%d,%d", &id, &dist, &type) != 3) {
return CommandServer::Fail;
}
Entity *src = server->minecraft->level->getEntity(id);
if (src == nullptr) {
ENTITY_NOT_FOUND;
return CommandServer::Fail;
}
// Run
std::vector<std::string> result;
for (Entity *entity : server->minecraft->level->entities) {
int i = entity->getEntityTypeId();
if (i > 0 && (type == no_entity_id || i == type) && distance_between(src, entity) < dist) {
result.push_back(get_entity_message(server, entity));
}
}
return join_outputs(result, list_separator);
} else if (cmd == "entity.removeEntities") {
// Parse
int dist, type;
int id = 0;
if (sscanf(args.c_str(), "%d,%d,%d", &id, &dist, &type) != 3) {
return CommandServer::Fail;
}
Entity *src = server->minecraft->level->getEntity(id);
if (src == nullptr) {
ENTITY_NOT_FOUND;
return CommandServer::Fail;
}
// Run
int removed = 0;
for (Entity *entity : server->minecraft->level->entities) {
int i = entity->getEntityTypeId();
if (i > 0 && (type == no_entity_id || i == type) && distance_between(src, entity) < dist) {
entity->remove();
removed++;
}
}
return std::to_string(removed) + '\n';
} else if (cmd == "world.setSign") {
// Parse
int x, y, z, id, data;
char l1[100], l2[100], l3[100], l4[100];
int ret = sscanf(args.c_str(), "%d,%d,%d,%d,%d,%99[^,],%99[^,],%99[^,],%99s", &x, &y, &z, &id, &data, l1, l2, l3, l4);
constexpr int minimal_arg_count = 5;
constexpr int line_count = 4;
if (ret < minimal_arg_count || ret > (minimal_arg_count + line_count)) {
return CommandServer::Fail;
}
// Translate
server->pos_translator.from(x, y, z);
// Set Block
server->minecraft->level->setTileAndData(x, y, z, id, data);
// Set Sign Data
if (ret == minimal_arg_count) {
return CommandServer::NullString;
}
SignTileEntity *sign = (SignTileEntity *) server->minecraft->level->getTileEntity(x, y, z);
if (sign == nullptr || sign->type != 4) {
return CommandServer::NullString;
}
char *lines[line_count] = {l1, l2, l3, l4};
for (int i = 0; i < line_count; i++) {
if (ret > (i + minimal_arg_count)) {
sign->lines[i] = get_input(lines[i]);
}
}
// Send Update Packet
sign->setChanged();
Packet *packet = sign->getUpdatePacket();
server->minecraft->rak_net_instance->send(*packet);
return CommandServer::NullString;
} else if (cmd == "world.spawnEntity") {
// Parse
float x, y, z;
int id;
if (sscanf(args.c_str(), "%f,%f,%f,%d", &x, &y, &z, &id) != 4) {
return CommandServer::Fail;
}
// Translate
x -= server->pos_translator.x;
y -= server->pos_translator.y;
z -= server->pos_translator.z;
if (compat_mode) {
convert_to_mcpi_entity_type(id);
}
// Spawn
Entity *entity = misc_make_entity_from_id(server->minecraft->level, id);
if (entity == nullptr) {
return CommandServer::Fail;
}
entity->moveTo(x, y, z, 0, 0);
server->minecraft->level->addEntity(entity);
return std::to_string(entity->id) + '\n';
} else if (cmd == "world.getEntityTypes") {
std::vector<std::string> result;
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);
if (compat_mode) {
convert_to_rj_entity_type(id);
}
result.push_back(join_outputs({std::to_string(id), i.second.second}));
}
return join_outputs(result, list_separator);
} else if (cmd == "entity.setAbsPos") {
int id;
float x, y, z;
if (sscanf(args.c_str(), "%d,%f,%f,%f", &id, &x, &y, &z) != 4) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
return CommandServer::Fail;
}
entity->moveTo(x, y, z, entity->yaw, entity->pitch);
return CommandServer::NullString;
} else if (cmd == "entity.getAbsPos") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
Entity *entity = server->minecraft->level->getEntity(id);
if (entity == nullptr) {
ENTITY_NOT_FOUND;
return CommandServer::Fail;
}
return join_outputs({std::to_string(entity->x), std::to_string(entity->y), std::to_string(entity->z)});
} else if (cmd == "entity.events.clear") {
int id;
if (sscanf(args.c_str(), "%d", &id) != 1) {
return CommandServer::Fail;
}
clear_events(client, id);
return CommandServer::NullString;
} else if (cmd == "events.clear") {
clear_events(client);
return CommandServer::NullString;
} else if (cmd == "reborn.disableCompatMode") {
compat_mode = false;
return std::string(reborn_get_version()) + '\n';
} else if (cmd == "reborn.enableCompatMode") {
compat_mode = true;
return CommandServer::NullString;
} else {
// Call Original Method
return old(server, client, command);
}
}
// 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 Entity Hit
static Entity *shooter = nullptr;
static HitResult *Arrow_tick_HitResult_constructor_injection(HitResult *self, Entity *target) {
// Original
self->constructor(target);
// Add event
if (shooter && shooter->isPlayer()) {
push_event(&ExtraClientData::projectile_events, {
.x = (int) target->x,
.y = (int) target->y,
.z = (int) target->z,
.owner = ((Player *) shooter)->username,
.owner_id = shooter->id,
.target_id = target->id
});
}
return self;
}
// Arrow block hit
static void Arrow_tick_injection(Arrow_tick_t old, Arrow *self) {
const int oldFlightTime = self->flight_time;
shooter = self->level->getEntity(self->getAuxData());
old(self);
if (!self->pending_removal && self->grounded && oldFlightTime != self->flight_time) {
if (shooter && shooter->isPlayer()) {
// Hit! Get the data
push_event(&ExtraClientData::projectile_events, {
.x = self->hit_x,
.y = self->hit_y,
.z = self->hit_z,
.owner = ((Player *) shooter)->username,
.owner_id = shooter->id,
.target_id = 0
});
}
}
}
// Throwable Hits
static void Throwable_tick_Throwable_onHit_injection(Throwable *self, HitResult *res) {
if (res == nullptr || res->type == 2) {
return self->onHit(res);
}
Entity *thrower = self->level->getEntity(self->getAuxData());
if (thrower == nullptr || !thrower->isPlayer()) {
return self->onHit(res);
}
ProjectileHitEvent event;
if (res->type == 1 && res->entity) {
// Entity
event = {
.x = (int) res->exact.x,
.y = (int) res->exact.y,
.z = (int) res->exact.z,
.owner = ((Player *) thrower)->username,
.owner_id = thrower->id,
.target_id = res->entity->id
};
} else {
// Tile
event = {
.x = res->x,
.y = res->y,
.z = res->z,
.owner = ((Player *) thrower)->username,
.owner_id = thrower->id,
.target_id = 0
};
}
push_event(&ExtraClientData::projectile_events, event);
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, ChatEvent{message, sender ? sender->id : no_entity_id});
}
// 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 *) 0x8b1e8, HitResult_constructor, Arrow_tick_HitResult_constructor_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);
}
}

View File

@ -7,6 +7,7 @@
#include <mods/feature/feature.h>
#include "chat-internal.h"
#include <mods/chat/chat.h>
#include <mods/api/api.h>
// Send UTF-8 API Command
std::string chat_send_api_command(const Minecraft *minecraft, const std::string &str) {
@ -33,10 +34,18 @@ static void send_api_chat_command(const Minecraft *minecraft, const char *str) {
std::string _chat_get_prefix(const char *username) {
return std::string("<") + username + "> ";
}
void chat_send_message_to_clients(ServerSideNetworkHandler *server_side_network_handler, const char *username, const char *message) {
static bool is_sending = false;
bool chat_is_sending() {
return is_sending;
}
void chat_send_message_to_clients(ServerSideNetworkHandler *server_side_network_handler, const Player *sender, const char *message) {
is_sending = true;
api_add_chat_event(sender, message);
const char *username = ((Player *) sender)->username.c_str();
std::string full_message = _chat_get_prefix(username) + message;
sanitize_string(full_message, MAX_CHAT_MESSAGE_LENGTH, false);
server_side_network_handler->displayGameMessage(full_message);
is_sending = false;
}
// Handle Chat packet Send
void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) {
@ -48,7 +57,7 @@ void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) {
// Hosting Multiplayer
const char *message = packet->message.c_str();
ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) minecraft->network_handler;
chat_send_message_to_clients(server_side_network_handler, Strings::default_username, (char *) message);
chat_send_message_to_clients(server_side_network_handler, (Player *) minecraft->player, message);
} else {
// Client
rak_net_instance->send(*(Packet *) packet);
@ -67,9 +76,8 @@ static void CommandServer_parse_CommandServer_dispatchPacket_injection(CommandSe
static void ServerSideNetworkHandler_handle_ChatPacket_injection(ServerSideNetworkHandler *server_side_network_handler, const RakNet_RakNetGUID &rak_net_guid, ChatPacket *chat_packet) {
const Player *player = server_side_network_handler->getPlayer(rak_net_guid);
if (player != nullptr) {
const char *username = player->username.c_str();
const char *message = chat_packet->message.c_str();
chat_send_message_to_clients(server_side_network_handler, username, message);
chat_send_message_to_clients(server_side_network_handler, player, message);
}
}

View File

@ -6,38 +6,38 @@
#include <mods/init/init.h>
#include <mods/feature/feature.h>
#include <mods/misc/misc.h>
// Death Messages
static const char *monster_names[] = {"Zombie", "Creeper", "Skeleton", "Spider", "Zombie Pigman"};
std::string get_death_message(Player *player, Entity *cause, const bool was_shot = false) {
// Prepare Death Message
std::string message = player->username;
if (cause) {
// Entity cause
// Entity Cause
const int type_id = cause->getEntityTypeId();
int aux = cause->getAuxData();
const int aux = cause->getAuxData();
const bool is_player = cause->isPlayer();
if (cause->getCreatureBaseType() != 0 || is_player) {
// Killed by a creature
// Killed By A Creature
if (was_shot) {
message += " was shot by ";
} else {
message += " was killed by ";
}
if (is_player) {
// Killed by a player
// Killed By A Player
message += ((Player *) cause)->username;
} else if (32 <= type_id && type_id <= 36) {
// Normal monster
message += "a ";
message += monster_names[type_id - 32];
} else {
// Unknown creature
message += "a mysterious beast";
// Normal Creature
std::string type = misc_get_entity_type_name(cause).first;
if (type.empty()) {
type = "mysterious beast";
}
message += "a " + type;
}
return message;
} else if (aux) {
// Killed by a throwable with owner
// Killed By A Throwable With Owner
Level *level = player->level;
Entity *shooter = level->getEntity(aux);
return get_death_message(player, shooter, true);
@ -49,19 +49,20 @@ std::string get_death_message(Player *player, Entity *cause, const bool was_shot
return message + " admired too much art";
}
}
// Miscellaneous Causes
if (was_shot) {
// Throwable with invalid owner
// Throwable With Invalid Owner
return message + " was shot under mysterious circumstances";
} else if (cause) {
// Unknown entity
// Unknown Entity
return message + " was killed";
} else {
// Anything else
// Anything Else
return message + " has died";
}
}
// Track If A Mob Is Being Hurt
static bool is_hurt = false;
static bool Mob_hurt_injection(Mob_hurt_t original, Mob *mob, Entity *source, const int dmg) {
is_hurt = true;
@ -91,7 +92,7 @@ static void Player_die_injection(const std::function<void(ParentSelf *, Entity *
template <typename OriginalSelf, typename Self>
static void Player_actuallyHurt_injection(const std::function<void(OriginalSelf *, int)> &original, Self *player, int32_t damage) {
// Store Old Health
int32_t old_health = player->health;
const int32_t old_health = player->health;
// Call Original Method
original((OriginalSelf *) player, damage);

View File

@ -134,7 +134,7 @@ static std::vector<std::string> get_debug_info_right(const Minecraft *minecraft)
z = entity->z;
type = "Entity";
const int type_id = entity->getEntityTypeId();
type_info.push_back(format_type(type_id, "")); // TODO: Specify name when RJ PR is merged
type_info.push_back(format_type(type_id, misc_get_entity_type_name(entity).first));
type_info.push_back("ID: " + std::to_string(entity->id));
if (entity->isMob()) {
Mob *mob = (Mob *) entity;

View File

@ -38,6 +38,7 @@ __attribute__((constructor)) static void init() {
init_bucket();
init_cake();
init_override();
init_api();
if (!reborn_is_server()) {
init_benchmark();
}

View File

@ -2,6 +2,7 @@
#include <libreborn/patch.h>
#include <libreborn/util/util.h>
#include <libreborn/util/string.h>
#include <symbols/minecraft.h>
#include <GLES/gl.h>
@ -128,10 +129,10 @@ void misc_render_background(int color, const Minecraft *minecraft, const int x,
Tesselator *t = &Tesselator::instance;
t->begin(GL_QUADS);
t->color(color, color, color, 255);
float x1 = x;
float x2 = x + width;
float y1 = y;
float y2 = y + height;
float x1 = float(x);
float x2 = x1 + float(width);
float y1 = float(y);
float y2 = y1 + float(height);
t->vertexUV(x1, y2, 0.0f, x1 / 32.0f, y2 / 32.0f);
t->vertexUV(x2, y2, 0.0f, x2 / 32.0f, y2 / 32.0f);
t->vertexUV(x2, y1, 0.0f, x2 / 32.0f, y1 / 32.0f);
@ -139,6 +140,84 @@ void misc_render_background(int color, const Minecraft *minecraft, const int x,
t->draw();
}
// Entity Names
static std::pair<std::string, std::string> format_entity_name(const std::string &friendly_name) {
std::string api_name = friendly_name;
for (char &c : api_name) {
c = c == ' ' ? '_' : std::toupper(c);
}
return {friendly_name, api_name};
}
std::pair<std::string, std::string> misc_get_entity_type_name(Entity *entity) {
if (entity) {
if (entity->isPlayer()) {
// Player
return format_entity_name("Player");
} else {
const EntityType type = static_cast<EntityType>(entity->getEntityTypeId());
if (misc_get_entity_type_names().contains(type)) {
// Normal Entity
return misc_get_entity_type_names()[type];
} else if (type == EntityType::UNKNOWN) {
// Special Entity
static std::unordered_map<void *, std::string> vtable_to_name = {
{(void *) Particle_vtable::base, "Particle"},
{(void *) TripodCamera_vtable::base, "Tripod Camera"},
{(void *) CameraEntity_vtable::base, "API Camera"}
};
void *vtable = entity->vtable;
if (vtable_to_name.contains(vtable)) {
return format_entity_name(vtable_to_name[vtable]);
}
}
}
}
// Invalid
return format_entity_name("Unknown");
}
std::string misc_get_entity_name(Entity *entity) {
if (entity && entity->isPlayer()) {
return ((Player *) entity)->username;
} else {
return misc_get_entity_type_name(entity).first;
}
}
std::map<EntityType, std::pair<std::string, std::string>> &misc_get_entity_type_names() {
static std::map<EntityType, std::pair<std::string, std::string>> names = {
{EntityType::CHICKEN, format_entity_name("Chicken")},
{EntityType::COW, format_entity_name("Cow")},
{EntityType::PIG, format_entity_name("Pig")},
{EntityType::SHEEP, format_entity_name("Sheep")},
{EntityType::ZOMBIE, format_entity_name("Zombie")},
{EntityType::CREEPER, format_entity_name("Creeper")},
{EntityType::SKELETON, format_entity_name("Skeleton")},
{EntityType::SPIDER, format_entity_name("Spider")},
{EntityType::ZOMBIE_PIGMAN, {"Zombie Pigman", "PIG_ZOMBIE"}},
{EntityType::DROPPED_ITEM, format_entity_name("Dropped Item")},
{EntityType::PRIMED_TNT, format_entity_name("Primed TNT")},
{EntityType::FALLING_SAND, format_entity_name("Falling Block")},
{EntityType::PAINTING, format_entity_name("Painting")},
{EntityType::ARROW, format_entity_name("Arrow")},
{EntityType::THROWN_SNOWBALL, format_entity_name("Snowball")},
{EntityType::THROWN_EGG, format_entity_name("Egg")}
};
return names;
}
// Spawn Entities
Entity *misc_make_entity_from_id(Level *level, const int id) {
if (id < 0x40) {
return (Entity *) MobFactory::CreateMob(id, level);
} else {
return EntityFactory::CreateEntity(id, level);
}
}
// Username In Unicode
std::string misc_get_player_username_utf(const Player *player) {
return from_cp437(player->username);
}
// Init
void _init_misc_api() {
// Handle Custom Creative Inventory Setup Behavior

96
mods/src/misc/base64.cpp Normal file
View File

@ -0,0 +1,96 @@
#include <cstdint>
#include <string>
// Base64 Encoder (https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594)
std::string misc_base64_encode(const std::string &data) {
static constexpr char encoding_table[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
const size_t in_len = data.size();
const size_t out_len = 4 * ((in_len + 2) / 3);
std::string ret(out_len, '\0');
size_t i;
char *p = const_cast<char *>(ret.c_str());
for (i = 0; i < in_len - 2; i += 3) {
*p++ = encoding_table[(data[i] >> 2) & 0x3f];
*p++ = encoding_table[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xf0) >> 4)];
*p++ = encoding_table[((data[i + 1] & 0xf) << 2) | ((int) (data[i + 2] & 0xc0) >> 6)];
*p++ = encoding_table[data[i + 2] & 0x3f];
}
if (i < in_len) {
*p++ = encoding_table[(data[i] >> 2) & 0x3f];
if (i == (in_len - 1)) {
*p++ = encoding_table[((data[i] & 0x3) << 4)];
*p++ = '=';
}
else {
*p++ = encoding_table[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xf0) >> 4)];
*p++ = encoding_table[((data[i + 1] & 0xf) << 2)];
}
*p++ = '=';
}
return ret;
}
// Base64 Decoder
std::string misc_base64_decode(const std::string &input) {
static constexpr unsigned char decoding_table[] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64
};
const size_t in_len = input.size();
if (in_len % 4 != 0) {
return "";
}
std::string out = "";
size_t out_len = in_len / 4 * 3;
if (in_len >= 1 && input[in_len - 1] == '=') out_len--;
if (in_len >= 2 && input[in_len - 2] == '=') out_len--;
out.resize(out_len);
for (size_t i = 0, j = 0; i < in_len;) {
const uint32_t a = input[i] == '=' ? 0 & i++ : decoding_table[static_cast<int>(input[i++])];
const uint32_t b = input[i] == '=' ? 0 & i++ : decoding_table[static_cast<int>(input[i++])];
const uint32_t c = input[i] == '=' ? 0 & i++ : decoding_table[static_cast<int>(input[i++])];
const uint32_t d = input[i] == '=' ? 0 & i++ : decoding_table[static_cast<int>(input[i++])];
const uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6);
if (j < out_len) {
out[j++] = (triple >> 2 * 8) & 0xFF;
}
if (j < out_len) {
out[j++] = (triple >> 1 * 8) & 0xFF;
}
if (j < out_len) {
out[j++] = (triple >> 0 * 8) & 0xFF;
}
}
return out;
}

View File

@ -116,12 +116,6 @@ static std::string get_blacklist_file() {
static std::vector<Player *> get_players_in_level(Level *level) {
return level->players;
}
// Get Player's Username
static std::string get_player_username(const Player *player) {
const std::string *username = &player->username;
std::string safe_username = from_cp437(*username);
return safe_username;
}
// Get Level From Minecraft
static Level *get_level(const Minecraft *minecraft) {
return minecraft->level;
@ -135,7 +129,7 @@ static void find_players(Minecraft *minecraft, const std::string &target_usernam
bool found_player = false;
for (Player *player : players) {
// Iterate Players
std::string username = get_player_username(player);
std::string username = misc_get_player_username_utf(player);
if (all_players || username == target_username) {
// Run Callback
callback(minecraft, username, player);

View File

@ -5,50 +5,13 @@
#include <mods/init/init.h>
#include <mods/feature/feature.h>
#include <mods/misc/misc.h>
#include "skin-internal.h"
// Base64 Encode (https://gist.github.com/tomykaira/f0fd86b6c73063283afe550bc5d77594)
static std::string base64_encode(const std::string &data) {
static constexpr char encoding_table[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
const size_t in_len = data.size();
const size_t out_len = 4 * ((in_len + 2) / 3);
std::string ret(out_len, '\0');
size_t i;
char *p = const_cast<char *>(ret.c_str());
for (i = 0; i < in_len - 2; i += 3) {
*p++ = encoding_table[(data[i] >> 2) & 0x3f];
*p++ = encoding_table[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xf0) >> 4)];
*p++ = encoding_table[((data[i + 1] & 0xf) << 2) | ((int) (data[i + 2] & 0xc0) >> 6)];
*p++ = encoding_table[data[i + 2] & 0x3f];
}
if (i < in_len) {
*p++ = encoding_table[(data[i] >> 2) & 0x3f];
if (i == (in_len - 1)) {
*p++ = encoding_table[((data[i] & 0x3) << 4)];
*p++ = '=';
}
else {
*p++ = encoding_table[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xf0) >> 4)];
*p++ = encoding_table[((data[i + 1] & 0xf) << 2)];
}
*p++ = '=';
}
return ret;
}
// Change Texture For Player Entities
static std::string get_skin_texture_path(const std::string &username) {
return '$' + misc_base64_encode(username);
}
static void Player_username_assign_injection(std::string *target, const std::string &username) {
// Call Original Method
*target = username;
@ -59,7 +22,7 @@ static void Player_username_assign_injection(std::string *target, const std::str
std::string *texture = &player->texture;
// Set Texture
*texture = '$' + base64_encode(username);
*texture = get_skin_texture_path(username);
}
static void Player_username_assign_injection_2(std::string *target, const char *username) {
const std::string username_str = username;
@ -67,12 +30,11 @@ static void Player_username_assign_injection_2(std::string *target, const char *
}
// Change Texture For HUD
static uint32_t Textures_loadAndBindTexture_injection(Textures *textures, __attribute__((unused)) std::string const& name) {
static uint32_t ItemInHandRenderer_render_Textures_loadAndBindTexture_injection(Textures *textures, __attribute__((unused)) std::string const& name) {
// Change Texture
static std::string new_texture;
if (new_texture.length() == 0) {
const std::string username = base64_encode(Strings::default_username);
new_texture = '$' + username;
new_texture = get_skin_texture_path(Strings::default_username);
}
// Call Original Method
@ -91,7 +53,7 @@ void init_skin() {
overwrite_call_manual((void *) 0x7639c, (void *) Player_username_assign_injection_2);
// HUD
overwrite_call((void *) 0x4c6d0, Textures_loadAndBindTexture, Textures_loadAndBindTexture_injection);
overwrite_call((void *) 0x4c6d0, Textures_loadAndBindTexture, ItemInHandRenderer_render_Textures_loadAndBindTexture_injection);
// Loader
_init_skin_loader();

View File

@ -63,3 +63,4 @@ Raspberry Pi!
It's alive!
Now with cake!
The bug attractor!
I promise, this is the last time!

View File

@ -1,5 +1,11 @@
method std::string parse(ConnectedClient &client, const std::string &command) = 0x6aa8c;
method void dispatchPacket(Packet &packet) = 0x6a548;
static-method bool setSocketBlocking(int fd, bool param_1) = 0x6a2d4;
method bool _updateClient(ConnectedClient &client) = 0x6ba6c;
property Minecraft *minecraft = 0x18;
property OffsetPosTranslator pos_translator = 0x1c;
static-property std::string Fail = 0x137dbc;
static-property std::string Ok = 0x138a08;
static-property std::string NullString = 0x137db8;

View File

@ -1,6 +1,6 @@
size 0x14;
property int entityId = 0x0;
property int owner_id = 0x0;
property int x = 0x4;
property int y = 0x8;
property int z = 0xc;

View File

@ -10,6 +10,9 @@ virtual-method bool shouldSave() = 0x8;
virtual-method void load(CompoundTag *tag) = 0xc;
virtual-method bool save(CompoundTag *tag) = 0x10;
virtual-method void tick() = 0x14;
virtual-method Packet *getUpdatePacket() = 0x20;
method void setChanged() = 0xd23a4;
property Level *level = 0x4;
property int x = 0x8;