From 55304d1c59d65425efd14e2036b6b5621b176590 Mon Sep 17 00:00:00 2001 From: Bigjango13 Date: Tue, 24 Sep 2024 21:34:10 -0700 Subject: [PATCH] Add set/getAbsPos API commands and tweak API --- launcher/src/client/available-feature-flags | 3 +- mods/include/mods/misc/misc.h | 5 +- mods/include/mods/server/server.h | 3 - mods/src/api/README.md | 90 +++++----- mods/src/api/api.cpp | 184 +++++++++++--------- mods/src/death/death.cpp | 4 +- mods/src/misc/api.cpp | 39 +++++ mods/src/server/server.cpp | 10 +- 8 files changed, 194 insertions(+), 144 deletions(-) diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index 2fb56a46..97c281f3 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -105,4 +105,5 @@ TRUE Fix Camera Functionality TRUE Property Scale Animated Textures TRUE Allow High-Resolution Title TRUE Improved Classic Title Positioning -TRUE Use Updated Title \ No newline at end of file +TRUE Use Updated Title +TRUE Implement RaspberryJuice API \ No newline at end of file diff --git a/mods/include/mods/misc/misc.h b/mods/include/mods/misc/misc.h index 57ffebca..62754077 100644 --- a/mods/include/mods/misc/misc.h +++ b/mods/include/mods/misc/misc.h @@ -26,5 +26,8 @@ void misc_run_on_game_key_press(const std::function &fun void misc_run_on_key_press(const std::function &func); void misc_run_on_creative_inventory_setup(const std::function &function); void misc_run_on_swap_buffers(const std::function &function); +std::string misc_get_player_username(Player *player); +std::map &misc_get_entity_names(); +std::string misc_get_entity_name(Entity *entity); -static constexpr int line_height = 8; \ No newline at end of file +static constexpr int line_height = 8; diff --git a/mods/include/mods/server/server.h b/mods/include/mods/server/server.h index f36bc342..7a6db404 100644 --- a/mods/include/mods/server/server.h +++ b/mods/include/mods/server/server.h @@ -1,7 +1,5 @@ #pragma once -#pragma once - #include #include "server_properties.h" @@ -18,5 +16,4 @@ struct ServerCommand { extern "C" { std::vector *server_get_commands(Minecraft *minecraft, ServerSideNetworkHandler *server_side_network_handler); ServerProperties &get_server_properties(); -std::string get_player_username(Player *player); } \ No newline at end of file diff --git a/mods/src/api/README.md b/mods/src/api/README.md index 25e410dd..173cab02 100644 --- a/mods/src/api/README.md +++ b/mods/src/api/README.md @@ -4,79 +4,83 @@ This mod implements all of the RaspberryJuice extensions to the MCPI API, for th This includes: - [x] `world.getBlocks(x0: int, y0: int, z0: int, x1: int, y1: int, z1: int) -> int[]` -- - Gets all the block ids between x0, y0, z0 and x1, y1, z1 + - Gets all the block ids between x0, y0, z0 and x1, y1, z1 - [x] `world.getPlayerId(name: str) -> int` -- - Gets the id of the first player who has the same name as `name`. "Fail" is returned on failure. + - Gets the id of the first player who has the same name as `name`. "Fail" is returned on failure. - [x] `entity.getName(id: int) -> str` -- - Gets the name of the entity of `id`, as MCPI does not have name tags, it will just be the entity type for non-players + - Gets the name of the entity of `id`, as MCPI does not have name tags, it will just be the entity type for non-players - [x] `world.getEntities(type: int) -> {id: int, type: int, name: str, x: float, y: float, z: float}[]` -- - returns a list of entities + - Returns a list of entities - [x] `world.removeEntity(id: int) -> int` -- - removes a single entity and returns 1 if successful + - Removes a single entity and returns 1 if successful - [x] `world.removeEntities(type: int) -> int` -- - removes all entities of a type, and returns how many were removed + - Removes all entities of a type, and returns how many were removed - [x] `events.chat.posts() -> {"0", message}[]` -- - gets a list of chat messages, see the limitations section down below to see why it has a zero -- - The messages are not cleared + - Gets a list of chat messages, see the limitations section down below to see why it has a zero + - The messages are not cleared - [x] `events.projectile.hits() -> {x: int, y: int, z: int, "1", owner: string, target: int}` -- - Returns a list of projectile hit events, the list is cleared each time it is read by this call -- - When target id is not 0, it means the projectile hit an entity and `x, y, z` is the entity's position -- - When target id is 0, it means the projectile hit a block and `x, y, z` is the block's position -- - Unlike RaspberryJuice, the last argument is the hit entity id, not the entity's name. This shouldn't break anything, and indeed should be more useful. + - Returns a list of projectile hit events, the list is cleared each time it is read by this call + - When target id is not 0, it means the projectile hit an entity and (x, y, z) is the entity's position + - When target id is 0, it means the projectile hit a block and (x, y, z) is the block's position + - Unlike RaspberryJuice, the last argument is the hit entity id, not the entity's name. This shouldn't break anything, and indeed should be more useful. - [x] `player.setDirection(x: float, y: float, z: float)` -- - sets rotation as if the player was at 0, 0, 0 and looking towards x, y, z + - sets rotation as if the player was at 0, 0, 0 and looking towards (x, y, z) - [x] `entity.setDirection(id: int, x: float, y: float, z: float)` -- - sets rotation as if the entity of `id` was at 0, 0, 0 and looking towards x, y, z + - Sets rotation as if the entity of `id` was at 0, 0, 0 and looking towards (x, y, z) - [x] `player.getDirection() -> {x: float, y: float, z: float}` -- - gets the location the player would be looking at if they were at 0, 0, 0 + - Gets the location the player would be looking at if they were at 0, 0, 0 - [x] `entity.getDirection() -> {x: float, y: float, z: float}` -- - gets the location the entity of `id` would be looking at if they were at 0, 0, 0 + - Gets the location the entity of `id` would be looking at if they were at 0, 0, 0 - [x] `player.setRotation(id: int)` -- - Sets the yaw of the player + - Sets the yaw of the player - [x] `entity.setRotation(id: int)` -- - Sets the yaw of the entity of `id` + - Sets the yaw of the entity of `id` - [x] `player.getRotation` -- - Gets the yaw of the player + - Gets the yaw of the player - [x] `entity.getRotation` -- - Gets the yaw of the entity of `id` + - Gets the yaw of the entity of `id` - [x] `player.getPitch` -- - Gets the pitch of the player + - Gets the pitch of the player - [x] `entity.getPitch` -- - Gets the pitch of the entity of `id` + - Gets the pitch of the entity of `id` - [x] `player.setPitch(id: int)` -- - Sets the pitch of the player + - Sets the pitch of the player - [x] `entity.setPitch(id: int)` -- - Sets the pitch of the entity of `id` + - Sets the pitch of the entity of `id` - [x] `player.getEntities(dist: int, type: int) -> {id: int, type: int, name: str, x: float, y: float, z: float}[]` -- - Gets all entities within `dist` of the player if the entity is of `type` (if `type` is -1, all entity types are included). -- - This can include the player. + - Gets all entities within `dist` of the player if the entity is of `type` (if `type` is -1, all entity types are included). + - This can include the player. - [x] `entity.getEntities(id: int, dist: int, type: int) -> {id: int, type: int, name: str, x: float, y: float, z: float}[]` -- - Gets all entities within `dist` of the entity of `id` if the entity is of `type` (if `type` is -1, all entity types are included). -- - This can include the entity. + - Gets all entities within `dist` of the entity of `id` if the entity is of `type` (if `type` is -1, all entity types are included). + - This can include the entity. - [x] `player.removeEntities(dist: int, type: int) -> int` -- - Removes all entities within `dist` of the player if the entity is of `type` (if `type` is -1, all entity types are included). -- - This will not include any players. -- - It returns the number of entities removed. + - Removes all entities within `dist` of the player if the entity is of `type` (if `type` is -1, all entity types are included). + - This will not include any players. + - It returns the number of entities removed. - [x] `entity.removeEntities(id: int, dist: int, type: int) -> int` -- - Removes all entities within `dist` of the entity of `id` if the entity is of `type` (if `type` is -1, all entity types are included). -- - This will not include any players, but may include the entity calling it if the entity is not a player. -- - It returns the number of entities removed. + - Removes all entities within `dist` of the entity of `id` if the entity is of `type` (if `type` is -1, all entity types are included). + - This will not include any players, but may include the entity calling it if the entity is not a player. + - It returns the number of entities removed. - [x] `world.setSign(x: int, y: int, z: int, id: int, data: int, [l1: str], [l2: str], [l3: str], [l4: str])` -- - Sets a block of `id:data` at the specified point, if the block is a sign, it will attempt to set lines 1 through 4 of the sign to the given text -- - For the API, the lines must be below 100 characters, however when loading signs MCPI will cap it at 16 characters (this can be disabled by patching out the call at `0xd1e2c`). -- - The lines are optional -- - The wiki has a list of blocks: https://mcpirevival.miraheze.org/wiki/Minecraft:_Pi_Edition_Complete_Block_List, sign is 63 and wall sign is 68 + - Sets a block of `id:data` at the specified point, if the block is a sign, it will attempt to set lines 1 through 4 of the sign to the given text + - For the API, the lines must be below 100 characters, however when loading signs MCPI will cap it at 16 characters (this can be disabled by patching out the call at `0xd1e2c`). + - The lines are optional + - The wiki has a list of blocks: https://mcpirevival.miraheze.org/wiki/Minecraft:_Pi_Edition_Complete_Block_List, sign is 63 and wall sign is 68 - [x] `world.spawnEntity(x: int, y: int, z: int, type: int) -> int` -- - Spawns an entity of `type` at the given position -- - Entities with a type of 0 cannot be spawned -- - The list of entity types can be found by running the command below, or on the wiki: https://mcpirevival.miraheze.org/wiki/Minecraft:_Pi_Edition_Complete_Entity_List + - Spawns an entity of `type` at the given position + - Entities with a type of 0 cannot be spawned + - The list of entity types can be found by running the command below, or on the wiki: https://mcpirevival.miraheze.org/wiki/Minecraft:_Pi_Edition_Complete_Entity_List - [x] `world.getEntityTypes() -> {type: int, name: str}` -- - Returns a list of known entity types, if there are modded entities this list may be incorrect + - Returns a list of known entity types, if there are modded entities this list may be incorrect +- [x] `player.getAbsPos() -> {x: int, y: int, z: int}` + - Gets the absolute position of the player, aka, without passing it through `OffsetPosTranslator` +- [x] `player.setAbsPos(x: int, y: int, z: int)` + - Sets the absolute position of the player, aka, without passing it through `OffsetPosTranslator` + It does not implement (due to MCPI limitations): - Per-entity events - Per-player events -- `player.getAbsPos`/`player.setAbsPos` as MCPI already does this - `events.chat.posts`'s id field is always set to -1 - `events.chat.posts` only returns the last 30 messages, and does *not* clear them after each API call diff --git a/mods/src/api/api.cpp b/mods/src/api/api.cpp index 80241193..8d62a70e 100644 --- a/mods/src/api/api.cpp +++ b/mods/src/api/api.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -36,34 +37,7 @@ static std::string getBlocks(CommandServer *commandserver, Vec3 start, Vec3 end) return ret + "\n"; } -static std::map names = { - // Unsavables - {0, "Unknown"}, - // Animals - {10, "Chicken"}, {11, "Cow"}, {12, "Pig"}, {13, "Sheep"}, - // Hostiles - {32, "Zombie"}, {33, "Creeper"}, {34, "Skeleton"}, {35, "Spider"}, {36, "PigZombie"}, - // Special 2, projectiles - {80, "Arrow"}, {81, "Snowball"}, {82, "ThrownEgg"}, - // Special 1, misc - {64, "ItemEntity"}, {65, "PrimedTnt"}, {66, "FallingTile"}, {83, "Painting"} -}; -void setEntityName(int type, std::string name) { - names[type] = name; -} -std::string getEntityName(Entity *entity) { - if (entity == NULL) { - return ""; - } else if (entity->isPlayer()) { - return get_player_username((Player *) entity); - } else { - int type = entity->getEntityTypeId(); - if (names.find(type) != names.end()) return names[type]; - return std::to_string(entity->id); - } -} - -void setDir(Entity *entity, float x, float y, float z) { +static void setDir(Entity *entity, float x, float y, float z) { if (entity == NULL) return; constexpr double _2PI = 2 * M_PI; @@ -76,7 +50,7 @@ void setDir(Entity *entity, float x, float y, float z) { float xz = sqrt(x * x + z * z); entity->pitch = (float) atan(-y / xz) * (180.0f / M_PI); } -Vec3 getDir(Entity *entity) { +static Vec3 getDir(Entity *entity) { float y = -sin(entity->pitch * (M_PI / 180)); float xz = cos(entity->pitch * (M_PI / 180)); float x = -xz * sin(entity->yaw * (M_PI / 180)); @@ -84,14 +58,14 @@ Vec3 getDir(Entity *entity) { return Vec3{x, y, z}; } -std::string getEntityData(CommandServer *commandserver, Entity *entity) { +static std::string getEntityData(CommandServer *commandserver, Entity *entity) { float x = entity->x, y = entity->y - entity->height_offset, z = entity->z; commandserver->pos_translator.to(x, y, z); return std::to_string(entity->id) + "," + // type std::to_string(entity->getEntityTypeId()) + "," + // name - getEntityName(entity) + "," + + misc_get_entity_name(entity) + "," + // x std::to_string(x) + "," + // y @@ -100,7 +74,7 @@ std::string getEntityData(CommandServer *commandserver, Entity *entity) { std::to_string(z); } -float edist(Entity *e1, Entity *e2) { +static float distance_between(Entity *e1, Entity *e2) { if (e1 == NULL || e2 == NULL) return 0; float dx = e2->x - e1->x; float dy = e2->y - e1->y; @@ -127,7 +101,7 @@ constexpr size_t EVENT_SIZE = 50; static int event_at = 0, event_start = 0; static ProjectileHitEvent hitEvents[EVENT_SIZE]; -void addProjectile(ProjectileHitEvent event) { +static void addProjectile(ProjectileHitEvent event) { hitEvents[event_at] = event; event_at++; event_at %= EVENT_SIZE; @@ -137,7 +111,7 @@ void addProjectile(ProjectileHitEvent event) { } } -ProjectileHitEvent popProjectile() { +static ProjectileHitEvent popProjectile() { if (event_start == event_at) { ERR("Over popped projectile hit events!"); } @@ -147,31 +121,32 @@ ProjectileHitEvent popProjectile() { return ret; } +static const std::string fail = "Fail\n"; std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServer *commandserver, ConnectedClient &client, const std::string &command) { size_t arg_start = command.find("("); - if (arg_start == std::string::npos) return "Fail\n"; + if (arg_start == std::string::npos) return fail; std::string cmd = command.substr(0, arg_start); // rfind so ) in input doesn't break size_t cmd_end = command.rfind(")"); - if (cmd_end == std::string::npos) return "Fail\n"; + if (cmd_end == std::string::npos) return fail; std::string args = command.substr(arg_start + 1, cmd_end - arg_start - 1); // And now the big if-else chain if (cmd == "world.getBlocks") { int x0, y0, z0, x1, y1, z1; int ret = sscanf(args.c_str(), "%d,%d,%d,%d,%d,%d", &x0, &y0, &z0, &x1, &y1, &z1); - if (ret != 6) return "Fail\n"; + if (ret != 6) return fail; // Get the blocks return getBlocks(commandserver, Vec3{(float) x0, (float) y0, (float) z0}, Vec3{(float) x1, (float) y1, (float) z1}); } else if (cmd == "world.getPlayerId") { for (Player *player : commandserver->minecraft->level->players) { - if (get_player_username(player) == args) + if (misc_get_player_username(player) == args) return std::to_string(player->id) + "\n"; } - INFO("Player [%s] not found.", args.c_str()); - return "Fail\n"; + WARN("Player [%s] not found.", args.c_str()); + return fail; } else if (cmd == "entity.getName") { int id; int ret = sscanf(args.c_str(), "%d", &id); @@ -179,9 +154,9 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ Entity *entity = commandserver->minecraft->level->getEntity(id); if (entity == NULL) { - INFO("Player (or Entity) [%i] not found in entity.getName.", id); + WARN("Player (or Entity) [%i] not found in entity.getName.", id); } - return getEntityName(entity) + "\n"; + return misc_get_entity_name(entity) + "\n"; } else if (cmd == "world.getEntities") { int type; int ret = sscanf(args.c_str(), "%d", &type); @@ -228,7 +203,6 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ for (GuiMessage gm : commandserver->minecraft->gui.messages) { std::string message = gm.message; std::replace(message.begin(), message.end(), '|', '\\'); - INFO("%s %i", message.c_str(), gm.time); ret += "0," + message + "|"; } if (ret.size() > 1) ret.pop_back(); @@ -240,7 +214,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ } if (result.size() > 1) result.pop_back(); return result + "\n"; - } else if (cmd == "player.setDirection") { + } else if (cmd == "player.setDirection" && commandserver->minecraft->player) { float x, y, z; int ret = sscanf(args.c_str(), "%f,%f,%f", &x, &y, &z); if (ret != 3) return ""; @@ -252,11 +226,11 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ if (ret != 4) return ""; Entity *entity = commandserver->minecraft->level->getEntity(id); if (entity == NULL) { - INFO("Entity [%i] not found.", id); + WARN("Entity [%i] not found.", id); } else { setDir(entity, x, y, z); } - } else if (cmd == "player.getDirection") { + } else if (cmd == "player.getDirection" && commandserver->minecraft->player) { Vec3 vec = getDir((Entity *) commandserver->minecraft->player); return std::to_string(vec.x) + "," + std::to_string(vec.y) + "," + std::to_string(vec.z) + "\n"; } else if (cmd == "entity.getDirection") { @@ -265,13 +239,13 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ if (ret != 1) return ""; Entity *entity = commandserver->minecraft->level->getEntity(id); if (entity == NULL) { - INFO("Entity [%i] not found.", id); - return "Fail\n"; + WARN("Entity [%i] not found.", id); + return fail; } else { Vec3 vec = getDir(entity); return std::to_string(vec.x) + "," + std::to_string(vec.y) + "," + std::to_string(vec.z) + "\n"; } - } else if (cmd == "player.setRotation") { + } else if (cmd == "player.setRotation" && commandserver->minecraft->player) { float yaw; int ret = sscanf(args.c_str(), "%f", &yaw); if (ret != 1) return ""; @@ -283,11 +257,11 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ if (ret != 2) return ""; Entity *entity = commandserver->minecraft->level->getEntity(id); if (entity == NULL) { - INFO("Entity [%i] not found.", id); + WARN("Entity [%i] not found.", id); } else { entity->yaw = yaw; } - } else if (cmd == "player.setPitch") { + } else if (cmd == "player.setPitch" && commandserver->minecraft->player) { float pitch; int ret = sscanf(args.c_str(), "%f", &pitch); if (ret != 1) return ""; @@ -299,11 +273,11 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ if (ret != 2) return ""; Entity *entity = commandserver->minecraft->level->getEntity(id); if (entity == NULL) { - INFO("Entity [%i] not found.", id); + WARN("Entity [%i] not found.", id); } else { entity->pitch = pitch; } - } else if (cmd == "player.getRotation") { + } else if (cmd == "player.getRotation" && commandserver->minecraft->player) { return std::to_string(commandserver->minecraft->player->yaw) + "\n"; } else if (cmd == "entity.getRotation") { int id; @@ -311,12 +285,12 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ if (ret != 1) return ""; Entity *entity = commandserver->minecraft->level->getEntity(id); if (entity == NULL) { - INFO("Entity [%i] not found.", id); - return "Fail\n"; + WARN("Entity [%i] not found.", id); + return fail; } else { return std::to_string(entity->yaw) + "\n"; } - } else if (cmd == "player.getPitch") { + } else if (cmd == "player.getPitch" && commandserver->minecraft->player) { return std::to_string(commandserver->minecraft->player->pitch) + "\n"; } else if (cmd == "entity.getPitch") { int id; @@ -324,8 +298,8 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ if (ret != 1) return ""; Entity *entity = commandserver->minecraft->level->getEntity(id); if (entity == NULL) { - INFO("Entity [%i] not found.", id); - return "Fail\n"; + WARN("Entity [%i] not found.", id); + return fail; } else { return std::to_string(entity->pitch) + "\n"; } @@ -335,7 +309,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ Entity *src = NULL; if (cmd == "player.getEntities") { int ret = sscanf(args.c_str(), "%d,%d", &dist, &type); - if (ret != 2) return ""; + if (ret != 2 || commandserver->minecraft->player == NULL) return ""; src = (Entity *) commandserver->minecraft->player; } else { int id = 0; @@ -343,14 +317,14 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ if (ret != 3) return ""; src = commandserver->minecraft->level->getEntity(id); if (src == NULL) { - INFO("Entity [%i] not found.", id); - return "Fail\n"; + WARN("Entity [%i] not found.", id); + return fail; } } // Run std::string result = ""; for (Entity *entity : commandserver->minecraft->level->entities) { - if ((type == -1 || entity->getEntityTypeId() == type) && edist(src, entity) < dist) { + if ((type == -1 || entity->getEntityTypeId() == type) && distance_between(src, entity) < dist) { result += getEntityData(commandserver, entity) + "|"; } } @@ -362,7 +336,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ Entity *src = NULL; if (cmd == "player.removeEntities") { int ret = sscanf(args.c_str(), "%d,%d", &dist, &type); - if (ret != 2) return ""; + if (ret != 2 || commandserver->minecraft->player == NULL) return ""; src = (Entity *) commandserver->minecraft->player; } else { int id = 0; @@ -370,14 +344,14 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ if (ret != 3) return ""; src = commandserver->minecraft->level->getEntity(id); if (src == NULL) { - INFO("Entity [%i] not found.", id); - return "Fail\n"; + WARN("Entity [%i] not found.", id); + return fail; } } // Run int removed = 0; for (Entity *entity : commandserver->minecraft->level->entities) { - if ((type == -1 || entity->getEntityTypeId() == type) && edist(src, entity) < dist && !entity->isPlayer()) { + if ((type == -1 || entity->getEntityTypeId() == type) && distance_between(src, entity) < dist && !entity->isPlayer()) { entity->remove(); removed++; } @@ -388,7 +362,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ 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); - if (ret < 5) return "Fail\n"; + if (ret < 5) return fail; // Translate commandserver->pos_translator.from(x, y, z); // Set block @@ -406,7 +380,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ float x, y, z; int id; int ret = sscanf(args.c_str(), "%f,%f,%f,%d", &x, &y, &z, &id); - if (ret != 4) return "Fail\n"; + if (ret != 4) return fail; // Translate int ix = x, iy = y, iz = z; int ix1 = x, iy1 = y, iz1 = z; @@ -421,17 +395,26 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ } else { entity = EntityFactory::CreateEntity(id, commandserver->minecraft->level); } - if (entity == NULL) return "Fail\n"; + if (entity == NULL) return fail; entity->moveTo(x, y, z, 0, 0); commandserver->minecraft->level->addEntity(entity); return std::to_string(entity->id) + "\n"; } else if (cmd == "world.getEntityTypes") { std::string result = ""; - for (auto &i : names) { + for (auto &i : misc_get_entity_names()) { result += std::to_string(i.first) + "," + i.second + "|"; } if (result.size() > 1) result.pop_back(); return result + "\n"; + } else if (cmd == "player.setAbsPos" && commandserver->minecraft->player) { + float x, y, z; + int ret = sscanf(args.c_str(), "%f,%f,%f", &x, &y, &z); + if (ret != 3) return fail; + commandserver->minecraft->player->moveTo(x, y, z, commandserver->minecraft->player->yaw, commandserver->minecraft->player->pitch); + } else if (cmd == "player.getAbsPos" && commandserver->minecraft->player) { + return std::to_string(commandserver->minecraft->player->x) + "," + + std::to_string(commandserver->minecraft->player->y) + "," + + std::to_string(commandserver->minecraft->player->z) + "\n"; } else { // Either invalid or a vanilla command, either way hand it off to the orignal handler return old(commandserver, client, command); @@ -442,21 +425,21 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ // Arrow entity hit static Entity *shooter = NULL; -HitResult *Arrow_tick_HitResult_constructor_injection(HitResult *self, Entity *target) { +static HitResult *Arrow_tick_HitResult_constructor_injection(HitResult *self, Entity *target) { // Orignal self->type = 1; self->entity = target; self->unknown = false; - self->x = target->x; - self->y = target->y; - self->z = target->z; + self->exact.x = target->x; + self->exact.y = target->y; + self->exact.z = target->z; // Add event if (shooter && shooter->isPlayer()) { addProjectile(ProjectileHitEvent{ - .x = (int) self->x, - .y = (int) self->y, - .z = (int) self->z, - .owner = get_player_username((Player *) shooter), + .x = (int) target->x, + .y = (int) target->y, + .z = (int) target->z, + .owner = misc_get_player_username((Player *) shooter), .targetId = target->id }); } @@ -464,27 +447,58 @@ HitResult *Arrow_tick_HitResult_constructor_injection(HitResult *self, Entity *t } // Arrow block hit -void Arrow_tick_injection(Arrow_tick_t old, Arrow *self) { +static void Arrow_tick_injection(Arrow_tick_t old, Arrow *self) { int oldFlightTime = self->flight_time; shooter = self->level->getEntity(self->getAuxData()); old(self); if (self && !self->pending_removal && self->grounded && oldFlightTime != self->flight_time) { if (shooter && shooter->isPlayer()) { // Hit! Get the data - INFO("B_ADDED"); addProjectile(ProjectileHitEvent{ .x = self->hit_x, .y = self->hit_y, .z = self->hit_z, - .owner = get_player_username((Player *) shooter), + .owner = misc_get_player_username((Player *) shooter), .targetId = 0 }); } } } -void init_api() { - overwrite_calls(CommandServer_parse, CommandServer_parse_injection); - overwrite_calls(Arrow_tick, Arrow_tick_injection); - overwrite_call((void *) 0x8b1e8, (void *) Arrow_tick_HitResult_constructor_injection); +// Throwable hits +static void Throwable_tick_Throwable_onHit_injection(Throwable *self, HitResult *res) { + if (res == NULL || res->type == 2) return self->onHit(res); + Entity *thrower = self->level->getEntity(self->getAuxData()); + if (thrower == NULL || !thrower->isPlayer()) return self->onHit(res); + ProjectileHitEvent event; + if (res->type == 1 && res->entity) { + // Entity + event = ProjectileHitEvent { + .x = (int) res->exact.x, + .y = (int) res->exact.y, + .z = (int) res->exact.z, + .owner = misc_get_player_username((Player *) thrower), + .targetId = res->entity->id + }; + } else { + // Tile + event = ProjectileHitEvent { + .x = res->x, + .y = res->y, + .z = res->z, + .owner = misc_get_player_username((Player *) thrower), + .targetId = 0 + }; + } + addProjectile(event); + self->onHit(res); +} + +void init_api() { + if (feature_has("Implement RaspberryJuice API", server_enabled)) { + overwrite_calls(CommandServer_parse, CommandServer_parse_injection); + overwrite_calls(Arrow_tick, Arrow_tick_injection); + overwrite_call((void *) 0x8b1e8, (void *) Arrow_tick_HitResult_constructor_injection); + overwrite_call((void *) 0x8c5a4, (void *) Throwable_tick_Throwable_onHit_injection); + } } diff --git a/mods/src/death/death.cpp b/mods/src/death/death.cpp index 2c804783..e533612d 100644 --- a/mods/src/death/death.cpp +++ b/mods/src/death/death.cpp @@ -4,10 +4,10 @@ #include #include +#include #include // 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; @@ -29,7 +29,7 @@ std::string get_death_message(Player *player, Entity *cause, const bool was_shot } else if (32 <= type_id && type_id <= 36) { // Normal monster message += "a "; - message += monster_names[type_id - 32]; + message += misc_get_entity_names()[type_id]; } else { // Unknown creature message += "a mysterious beast"; diff --git a/mods/src/misc/api.cpp b/mods/src/misc/api.cpp index 48ee22fc..10f3b869 100644 --- a/mods/src/misc/api.cpp +++ b/mods/src/misc/api.cpp @@ -129,6 +129,45 @@ void misc_render_background(int color, const Minecraft *minecraft, const int x, t->draw(); } +// Get Player's Username +std::string misc_get_player_username(Player *player) { + const std::string *username = &player->username; + char *safe_username_c = from_cp437(username->c_str()); + std::string safe_username = safe_username_c; + free(safe_username_c); + return safe_username; +} + +std::map &misc_get_entity_names() { + static std::map entity_names = { + // Unsavables + {0, "Unknown"}, + // Animals + {10, "Chicken"}, {11, "Cow"}, {12, "Pig"}, {13, "Sheep"}, + // Hostiles + {32, "Zombie"}, {33, "Creeper"}, {34, "Skeleton"}, {35, "Spider"}, {36, "Zombie Pigman"}, + // Special 1, misc + {64, "ItemEntity"}, {65, "PrimedTnt"}, {66, "FallingTile"}, {83, "Painting"}, + // Special 2, projectiles + {80, "Arrow"}, {81, "Snowball"}, {82, "ThrownEgg"} + }; + return entity_names; +}; + + +std::string misc_get_entity_name(Entity *entity) { + if (entity == NULL) { + return ""; + } else if (entity->isPlayer()) { + return misc_get_player_username((Player *) entity); + } else { + int type = entity->getEntityTypeId(); + std::map &names = misc_get_entity_names(); + if (names.find(type) != names.end()) return names[type]; + return std::to_string(entity->id); + } +} + // Init void _init_misc_api() { // Handle Custom Creative Inventory Setup Behavior diff --git a/mods/src/server/server.cpp b/mods/src/server/server.cpp index e7b498cf..3e8751ad 100644 --- a/mods/src/server/server.cpp +++ b/mods/src/server/server.cpp @@ -111,14 +111,6 @@ static std::string get_blacklist_file() { static std::vector get_players_in_level(Level *level) { return level->players; } -// Get Player's Username -std::string get_player_username(Player *player) { - const std::string *username = &player->username; - char *safe_username_c = from_cp437(username->c_str()); - std::string safe_username = safe_username_c; - free(safe_username_c); - return safe_username; -} // Get Level From Minecraft static Level *get_level(const Minecraft *minecraft) { return minecraft->level; @@ -133,7 +125,7 @@ static void find_players(Minecraft *minecraft, const std::string &target_usernam for (std::size_t i = 0; i < players.size(); i++) { // Iterate Players Player *player = players[i]; - std::string username = get_player_username(player); + std::string username = misc_get_player_username(player); if (all_players || username == target_username) { // Run Callback callback(minecraft, username, player);