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