Add set/getAbsPos API commands and tweak API

This commit is contained in:
Bigjango13 2024-09-24 21:34:10 -07:00
parent 34b13b3257
commit 55304d1c59
8 changed files with 194 additions and 144 deletions

View File

@ -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
TRUE Use Updated Title
TRUE Implement RaspberryJuice API

View File

@ -26,5 +26,8 @@ void misc_run_on_game_key_press(const std::function<bool(Minecraft *, int)> &fun
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(Player *player);
std::map<int, std::string> &misc_get_entity_names();
std::string misc_get_entity_name(Entity *entity);
static constexpr int line_height = 8;
static constexpr int line_height = 8;

View File

@ -1,7 +1,5 @@
#pragma once
#pragma once
#include <symbols/minecraft.h>
#include "server_properties.h"
@ -18,5 +16,4 @@ struct ServerCommand {
extern "C" {
std::vector<ServerCommand> *server_get_commands(Minecraft *minecraft, ServerSideNetworkHandler *server_side_network_handler);
ServerProperties &get_server_properties();
std::string get_player_username(Player *player);
}

View File

@ -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

View File

@ -8,6 +8,7 @@
#include <symbols/minecraft.h>
#include <mods/init/init.h>
#include <mods/misc/misc.h>
#include <mods/server/server.h>
#include <mods/feature/feature.h>
@ -36,34 +37,7 @@ static std::string getBlocks(CommandServer *commandserver, Vec3 start, Vec3 end)
return ret + "\n";
}
static std::map<int, std::string> 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);
}
}

View File

@ -4,10 +4,10 @@
#include <symbols/minecraft.h>
#include <mods/init/init.h>
#include <mods/misc/misc.h>
#include <mods/feature/feature.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;
@ -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";

View File

@ -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<int, std::string> &misc_get_entity_names() {
static std::map<int, std::string> 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<int, std::string> &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

View File

@ -111,14 +111,6 @@ static std::string get_blacklist_file() {
static std::vector<Player *> 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);