More Accurate Painting/Falling Sand Spawning + Some Fixes
Some checks failed
CI / Test (AMD64, Client) (push) Blocked by required conditions
CI / Test (AMD64, Server) (push) Blocked by required conditions
CI / Test (ARM64, Client) (push) Blocked by required conditions
CI / Test (ARM64, Server) (push) Blocked by required conditions
CI / Test (ARMHF, Client) (push) Blocked by required conditions
CI / Test (ARMHF, Server) (push) Blocked by required conditions
CI / Build Example Mods (push) Blocked by required conditions
CI / Release (push) Blocked by required conditions
CI / Build (AMD64) (push) Successful in 22m34s
CI / Build (ARM64) (push) Successful in 24m56s
CI / Build (ARMHF) (push) Has been cancelled

This commit is contained in:
TheBrokenRail 2025-02-27 14:58:27 -05:00
parent adf92124c3
commit 426a35882f
8 changed files with 94 additions and 22 deletions

View File

@ -35,7 +35,7 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely
* `world.getBlockWithData(x,y,z)` * `world.getBlockWithData(x,y,z)`
* Description: Retrieve the block ID and data value at the specified location. * Description: Retrieve the block ID and data value at the specified location.
* Output: `block_id,data` * 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. * Description: Fill the given region with the specified block.
* `world.getHeight(x,z)` * `world.getHeight(x,z)`
* Description: Get the last (from the top-down) non-solid block's Y-coordinate at the given location. * 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` * Output: List of `x,y,z,face,entity_id`
### RaspberryJuice ### 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. * Description: Retrieve the blocks in the specified region.
* Output: List of <code>block_id<ins>,data</ins></code> * Output: List of <code>block_id<ins>,data</ins></code>
* In compatibility mode, this list is delimited with commas (`,`). * 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)` * `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. * Description: Remove all entities of the specified type[^1][^2] within the given distance of the provided entity.
* Output: See above. * Output: See above.
* `world.spawnEntity(x,y,x,entity_type_id)` * <code>world.spawnEntity(<ins>:</ins>x<ins>:</ins>,<ins>:</ins>y<ins>:</ins>,<ins>:</ins>z<ins>:</ins>,entity_type_id)</code>
* Description: Spawn the specified entity at the given position. * Description: Spawn the specified entity at the given position.
* Output: `entity_id` * Output: `entity_id`
* `world.getEntityTypes()` * `world.getEntityTypes()`

View File

@ -57,7 +57,7 @@ std::map<EntityType, std::pair<std::string, std::string>> &misc_get_entity_type_
std::pair<std::string, std::string> misc_get_entity_type_name(Entity *entity); std::pair<std::string, std::string> misc_get_entity_type_name(Entity *entity);
std::string misc_get_entity_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_encode(const std::string &data);
std::string misc_base64_decode(const std::string &input); std::string misc_base64_decode(const std::string &input);

View File

@ -299,6 +299,7 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original,
if (src == nullptr) { if (src == nullptr) {
return CommandServer::Fail; return CommandServer::Fail;
} }
api_convert_to_mcpi_entity_type(type);
// Run // Run
std::vector<std::string> result; std::vector<std::string> result;
for (Entity *entity : server->minecraft->level->entities) { for (Entity *entity : server->minecraft->level->entities) {
@ -316,6 +317,7 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original,
if (src == nullptr) { if (src == nullptr) {
return CommandServer::Fail; return CommandServer::Fail;
} }
api_convert_to_mcpi_entity_type(type);
// Run // Run
int removed = 0; int removed = 0;
for (Entity *entity : server->minecraft->level->entities) { for (Entity *entity : server->minecraft->level->entities) {
@ -333,14 +335,17 @@ static std::string CommandServer_parse_injection(CommandServer_parse_t original,
next_int(type); next_int(type);
// Translate // Translate
server->pos_translator.from_float(x, y, z); 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); 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, x, y, z);
if (entity == nullptr) { if (entity == nullptr) {
return CommandServer::Fail; return CommandServer::Fail;
} }
entity->moveTo(x, y, z, 0, 0);
server->minecraft->level->addEntity(entity);
return std::to_string(entity->id) + '\n'; return std::to_string(entity->id) + '\n';
} else if (cmd == "world.getEntityTypes") { } else if (cmd == "world.getEntityTypes") {
// Get All Valid Entity Types // Get All Valid Entity Types

View File

@ -82,6 +82,7 @@ static std::unordered_map<int, EntityType> modern_entity_id_mapping = {
{7, EntityType::THROWN_EGG}, {7, EntityType::THROWN_EGG},
{9, EntityType::PAINTING} {9, EntityType::PAINTING}
}; };
static constexpr int unknown_entity_type_id = static_cast<int>(EntityType::UNKNOWN);
void api_convert_to_outside_entity_type(int &type) { void api_convert_to_outside_entity_type(int &type) {
if (!api_compat_mode) { if (!api_compat_mode) {
return; return;
@ -90,9 +91,10 @@ void api_convert_to_outside_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;
break; return;
} }
} }
type = unknown_entity_type_id;;
} }
void api_convert_to_mcpi_entity_type(int &type) { void api_convert_to_mcpi_entity_type(int &type) {
if (!api_compat_mode) { if (!api_compat_mode) {
@ -101,5 +103,7 @@ void api_convert_to_mcpi_entity_type(int &type) {
// Convert To Native Entity Type // Convert To Native Entity 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]);
} else {
type = unknown_entity_type_id;;
} }
} }

View File

@ -30,6 +30,14 @@ static void send_api_chat_command(const Minecraft *minecraft, const char *str) {
chat_send_api_command(minecraft, command); 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 // Send Message To Players
std::string _chat_get_prefix(const char *username) { std::string _chat_get_prefix(const char *username) {
return std::string("<") + username + "> "; return std::string("<") + username + "> ";
@ -57,7 +65,11 @@ void chat_handle_packet_send(const Minecraft *minecraft, ChatPacket *packet) {
// Hosting Multiplayer // Hosting Multiplayer
const char *message = packet->message.c_str(); const char *message = packet->message.c_str();
ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) minecraft->network_handler; 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 { } else {
// Client // Client
rak_net_instance->send(*(Packet *) packet); 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" unsigned char disable_chat_packet_loopback_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
patch((void *) 0x6b490, disable_chat_packet_loopback_patch); patch((void *) 0x6b490, disable_chat_packet_loopback_patch);
// Manually Send (And Loopback) ChatPacket // Manually Send (And Loopback) ChatPacket
overwrite_calls(CommandServer_parse, CommandServer_parse_injection);
overwrite_call((void *) 0x6b518, CommandServer_dispatchPacket, CommandServer_parse_CommandServer_dispatchPacket_injection); overwrite_call((void *) 0x6b518, CommandServer_dispatchPacket, CommandServer_parse_CommandServer_dispatchPacket_injection);
// Re-Broadcast ChatPacket // Re-Broadcast ChatPacket
patch_vtable(ServerSideNetworkHandler_handle_ChatPacket, ServerSideNetworkHandler_handle_ChatPacket_injection); patch_vtable(ServerSideNetworkHandler_handle_ChatPacket, ServerSideNetworkHandler_handle_ChatPacket_injection);

View File

@ -205,24 +205,61 @@ std::map<EntityType, std::pair<std::string, std::string>> &misc_get_entity_type_
} }
// Spawn Entities // 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<int>(EntityType::DROPPED_ITEM)) { if (id < static_cast<int>(EntityType::DROPPED_ITEM)) {
// Spwn Mob // Mob
return (Entity *) MobFactory::CreateMob(id, level); entity = (Entity *) MobFactory::CreateMob(id, level);
} else { } else {
// Spawn Entity // Entity
Entity *entity = EntityFactory::CreateEntity(id, level); entity = EntityFactory::CreateEntity(id, level);
}
// Setup
if (entity) {
// Position
entity->moveTo(x, y, z, 0, 0);
// Adjust
switch (id) { switch (id) {
case static_cast<int>(EntityType::PAINTING): { case static_cast<int>(EntityType::PAINTING): {
// Fix Crash // Find Valid Direction
((Painting *) entity)->motive = Motive::DefaultImage; Painting *painting = (Painting *) entity;
const std::vector<std::pair<int, std::pair<int, int>>> directions = {
{1, {-1, 0}}, // West
{0, {0, 1}}, // South
{3, {1, 0}}, // East
{2, {0, -1}} // North
};
for (const std::pair<int, std::pair<int, int>> &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; break;
} }
case static_cast<int>(EntityType::FALLING_SAND): { case static_cast<int>(EntityType::FALLING_SAND): {
// Sensible Default // Use Current Tile
FallingTile *sand = (FallingTile *) entity; FallingTile *tile = (FallingTile *) entity;
sand->tile_id = Tile::sand->id; tile->tile_id = level->getTile(int(x), int(y), int(z));
sand->time = 1; tile->data = level->getData(int(x), int(y), int(z));
break; break;
} }
case static_cast<int>(EntityType::DROPPED_ITEM): { case static_cast<int>(EntityType::DROPPED_ITEM): {
@ -231,8 +268,11 @@ Entity *misc_make_entity_from_id(Level *level, const int id) {
break; break;
} }
} }
return entity; // Add To World
level->addEntity(entity);
} }
// Return
return entity;
} }
// Username In Unicode // Username In Unicode
@ -244,4 +284,8 @@ std::string misc_get_player_username_utf(const Player *player) {
void _init_misc_api() { void _init_misc_api() {
// Handle Custom Creative Inventory Setup Behavior // Handle Custom Creative Inventory Setup Behavior
overwrite_call((void *) 0x8e0fc, FillingContainer_addItem, Inventory_setupDefault_FillingContainer_addItem_call_injection); 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);
} }

View File

@ -1,3 +1,6 @@
extends Entity; extends Entity;
vtable 0x10aba8; vtable 0x10aba8;
method void setPosition(int x, int y, int z) = 0x7e888;
method void setDir(int direction) = 0x7e950;

View File

@ -2,4 +2,7 @@ extends HangingEntity;
vtable 0x10b0b0; vtable 0x10b0b0;
method void setRandomMotive(int direction) = 0x83420;
static-property Motive **default_motive = 0x836d0;
property Motive *motive = 0xe4; property Motive *motive = 0xe4;