From 426a35882fe234a6ac7e9fbd5186a35e9b01d2a7 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Thu, 27 Feb 2025 14:58:27 -0500 Subject: [PATCH] More Accurate Painting/Falling Sand Spawning + Some Fixes --- docs/API.md | 6 +- mods/include/mods/misc/misc.h | 2 +- mods/src/api/api.cpp | 11 ++- mods/src/api/compat.cpp | 6 +- mods/src/chat/chat.cpp | 15 +++- mods/src/misc/api.cpp | 68 +++++++++++++++---- symbols/src/entity/painting/HangingEntity.def | 5 +- symbols/src/entity/painting/Painting.def | 3 + 8 files changed, 94 insertions(+), 22 deletions(-) diff --git a/docs/API.md b/docs/API.md index 04428002..41f075a8 100644 --- a/docs/API.md +++ b/docs/API.md @@ -35,7 +35,7 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely * `world.getBlockWithData(x,y,z)` * Description: Retrieve the block ID and data value at the specified location. * Output: `block_id,data` -* `world.setBlocks(x0,y0,z0,x1,y1,x1,block_id[,data])` +* `world.setBlocks(x0,y0,z0,x1,y1,z1,block_id[,data])` * Description: Fill the given region with the specified block. * `world.getHeight(x,z)` * Description: Get the last (from the top-down) non-solid block's Y-coordinate at the given location. @@ -80,7 +80,7 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely * Output: List of `x,y,z,face,entity_id` ### RaspberryJuice -* `world.getBlocks(x0,y0,z0,x1,y1,x1)` +* `world.getBlocks(x0,y0,z0,x1,y1,z1)` * Description: Retrieve the blocks in the specified region. * Output: List of block_id,data * In compatibility mode, this list is delimited with commas (`,`). @@ -102,7 +102,7 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely * `entity.removeEntities(entity_id,distance,entity_type_id)` * Description: Remove all entities of the specified type[^1][^2] within the given distance of the provided entity. * Output: See above. -* `world.spawnEntity(x,y,x,entity_type_id)` +* world.spawnEntity(:x:,:y:,:z:,entity_type_id) * Description: Spawn the specified entity at the given position. * Output: `entity_id` * `world.getEntityTypes()` diff --git a/mods/include/mods/misc/misc.h b/mods/include/mods/misc/misc.h index ae5bf22e..5d6a2e3a 100644 --- a/mods/include/mods/misc/misc.h +++ b/mods/include/mods/misc/misc.h @@ -57,7 +57,7 @@ std::map> &misc_get_entity_type_ std::pair 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); +Entity *misc_make_entity_from_id(Level *level, int id, float x, float y, float z); std::string misc_base64_encode(const std::string &data); std::string misc_base64_decode(const std::string &input); diff --git a/mods/src/api/api.cpp b/mods/src/api/api.cpp index b383de63..2ac12cd6 100644 --- a/mods/src/api/api.cpp +++ b/mods/src/api/api.cpp @@ -299,6 +299,7 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original, if (src == nullptr) { return CommandServer::Fail; } + api_convert_to_mcpi_entity_type(type); // Run std::vector result; for (Entity *entity : server->minecraft->level->entities) { @@ -316,6 +317,7 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original, if (src == nullptr) { return CommandServer::Fail; } + api_convert_to_mcpi_entity_type(type); // Run int removed = 0; for (Entity *entity : server->minecraft->level->entities) { @@ -333,14 +335,17 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original, next_int(type); // Translate server->pos_translator.from_float(x, y, z); + if (api_compat_mode) { + x = float(int(x)); + y = float(int(y)); + z = float(int(z)); + } api_convert_to_mcpi_entity_type(type); // Spawn - Entity *entity = misc_make_entity_from_id(server->minecraft->level, type); + Entity *entity = misc_make_entity_from_id(server->minecraft->level, type, x, y, z); 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") { // Get All Valid Entity Types diff --git a/mods/src/api/compat.cpp b/mods/src/api/compat.cpp index a3b0d44a..9499015f 100644 --- a/mods/src/api/compat.cpp +++ b/mods/src/api/compat.cpp @@ -82,6 +82,7 @@ static std::unordered_map modern_entity_id_mapping = { {7, EntityType::THROWN_EGG}, {9, EntityType::PAINTING} }; +static constexpr int unknown_entity_type_id = static_cast(EntityType::UNKNOWN); void api_convert_to_outside_entity_type(int &type) { if (!api_compat_mode) { return; @@ -90,9 +91,10 @@ void api_convert_to_outside_entity_type(int &type) { for (const std::pair &pair : modern_entity_id_mapping) { if (static_cast(pair.second) == type) { type = pair.first; - break; + return; } } + type = unknown_entity_type_id;; } void api_convert_to_mcpi_entity_type(int &type) { if (!api_compat_mode) { @@ -101,5 +103,7 @@ void api_convert_to_mcpi_entity_type(int &type) { // Convert To Native Entity Type if (modern_entity_id_mapping.contains(type)) { type = static_cast(modern_entity_id_mapping[type]); + } else { + type = unknown_entity_type_id;; } } \ No newline at end of file diff --git a/mods/src/chat/chat.cpp b/mods/src/chat/chat.cpp index eeea963f..2fee1e25 100644 --- a/mods/src/chat/chat.cpp +++ b/mods/src/chat/chat.cpp @@ -30,6 +30,14 @@ static void send_api_chat_command(const Minecraft *minecraft, const char *str) { chat_send_api_command(minecraft, command); } +// Track "Real" API Clients +static bool is_real_api_client; +static std::string CommandServer_parse_injection(CommandServer_parse_t original, CommandServer *self, ConnectedClient &client, const std::string &input) { + is_real_api_client = client.sock >= 0; + // Call Original Method + return original(self, client, input); +} + // Send Message To Players std::string _chat_get_prefix(const char *username) { return std::string("<") + username + "> "; @@ -57,7 +65,11 @@ 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, (Player *) minecraft->player, message); + if (is_real_api_client) { + server_side_network_handler->displayGameMessage(packet->message); + } else { + chat_send_message_to_clients(server_side_network_handler, (Player *) minecraft->player, message); + } } else { // Client rak_net_instance->send(*(Packet *) packet); @@ -103,6 +115,7 @@ void init_chat() { unsigned char disable_chat_packet_loopback_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" patch((void *) 0x6b490, disable_chat_packet_loopback_patch); // Manually Send (And Loopback) ChatPacket + overwrite_calls(CommandServer_parse, CommandServer_parse_injection); overwrite_call((void *) 0x6b518, CommandServer_dispatchPacket, CommandServer_parse_CommandServer_dispatchPacket_injection); // Re-Broadcast ChatPacket patch_vtable(ServerSideNetworkHandler_handle_ChatPacket, ServerSideNetworkHandler_handle_ChatPacket_injection); diff --git a/mods/src/misc/api.cpp b/mods/src/misc/api.cpp index ef4b410b..a250cfbb 100644 --- a/mods/src/misc/api.cpp +++ b/mods/src/misc/api.cpp @@ -205,24 +205,61 @@ std::map> &misc_get_entity_type_ } // Spawn Entities -Entity *misc_make_entity_from_id(Level *level, const int id) { +static bool painting_direction_is_valid; +static void Painting_setRandomMotive_HangingEntity_setDir_injection(HangingEntity *self, const int direction) { + Painting *painting = (Painting *) self; + painting_direction_is_valid = painting->motive != nullptr; + if (!painting_direction_is_valid) { + painting->motive = *Painting::default_motive; + } + // Call Original Method + self->setDir(direction); +} +Entity *misc_make_entity_from_id(Level *level, const int id, const float x, const float y, const float z) { + // Create + Entity *entity; if (id < static_cast(EntityType::DROPPED_ITEM)) { - // Spwn Mob - return (Entity *) MobFactory::CreateMob(id, level); + // Mob + entity = (Entity *) MobFactory::CreateMob(id, level); } else { - // Spawn Entity - Entity *entity = EntityFactory::CreateEntity(id, level); + // Entity + entity = EntityFactory::CreateEntity(id, level); + } + // Setup + if (entity) { + // Position + entity->moveTo(x, y, z, 0, 0); + // Adjust switch (id) { case static_cast(EntityType::PAINTING): { - // Fix Crash - ((Painting *) entity)->motive = Motive::DefaultImage; + // Find Valid Direction + Painting *painting = (Painting *) entity; + const std::vector>> directions = { + {1, {-1, 0}}, // West + {0, {0, 1}}, // South + {3, {1, 0}}, // East + {2, {0, -1}} // North + }; + for (const std::pair> &info : directions) { + // Select Motive + int direction = info.first; + int new_x = int(x) - info.second.first; + int new_y = int(y); + int new_z = int(z) - info.second.second; + painting->setPosition(new_x, new_y, new_z); + painting->setRandomMotive(direction); + // Check + if (painting_direction_is_valid) { + break; + } + } break; } case static_cast(EntityType::FALLING_SAND): { - // Sensible Default - FallingTile *sand = (FallingTile *) entity; - sand->tile_id = Tile::sand->id; - sand->time = 1; + // Use Current Tile + FallingTile *tile = (FallingTile *) entity; + tile->tile_id = level->getTile(int(x), int(y), int(z)); + tile->data = level->getData(int(x), int(y), int(z)); break; } case static_cast(EntityType::DROPPED_ITEM): { @@ -231,8 +268,11 @@ Entity *misc_make_entity_from_id(Level *level, const int id) { break; } } - return entity; + // Add To World + level->addEntity(entity); } + // Return + return entity; } // Username In Unicode @@ -244,4 +284,8 @@ std::string misc_get_player_username_utf(const Player *player) { void _init_misc_api() { // Handle Custom Creative Inventory Setup Behavior overwrite_call((void *) 0x8e0fc, FillingContainer_addItem, Inventory_setupDefault_FillingContainer_addItem_call_injection); + // Easy Way To Check Painting Orientation + unsigned char set_null_as_motive_patch[4] = {0x00, 0x30, 0xa0, 0x03}; // "moveq r3, #0x0" + patch((void *) 0x834e0, set_null_as_motive_patch); + overwrite_call((void *) 0x8367c, HangingEntity_setDir, Painting_setRandomMotive_HangingEntity_setDir_injection); } diff --git a/symbols/src/entity/painting/HangingEntity.def b/symbols/src/entity/painting/HangingEntity.def index d280437c..f282fd75 100644 --- a/symbols/src/entity/painting/HangingEntity.def +++ b/symbols/src/entity/painting/HangingEntity.def @@ -1,3 +1,6 @@ extends Entity; -vtable 0x10aba8; \ No newline at end of file +vtable 0x10aba8; + +method void setPosition(int x, int y, int z) = 0x7e888; +method void setDir(int direction) = 0x7e950; \ No newline at end of file diff --git a/symbols/src/entity/painting/Painting.def b/symbols/src/entity/painting/Painting.def index be0aa4a1..935a6cce 100644 --- a/symbols/src/entity/painting/Painting.def +++ b/symbols/src/entity/painting/Painting.def @@ -2,4 +2,7 @@ extends HangingEntity; vtable 0x10b0b0; +method void setRandomMotive(int direction) = 0x83420; + +static-property Motive **default_motive = 0x836d0; property Motive *motive = 0xe4; \ No newline at end of file