Inital RaspberryJuice API port
This commit is contained in:
parent
00d193e732
commit
68a15236ef
@ -99,6 +99,8 @@ set(SRC
|
|||||||
src/multidraw/storage.cpp
|
src/multidraw/storage.cpp
|
||||||
# classic-ui
|
# classic-ui
|
||||||
src/classic-ui/classic-ui.cpp
|
src/classic-ui/classic-ui.cpp
|
||||||
|
# api
|
||||||
|
src/api/api.cpp
|
||||||
)
|
)
|
||||||
# Install Splashes
|
# Install Splashes
|
||||||
install(
|
install(
|
||||||
|
@ -31,4 +31,5 @@ void init_screenshot();
|
|||||||
void init_f3();
|
void init_f3();
|
||||||
void init_multidraw();
|
void init_multidraw();
|
||||||
void init_classic_ui();
|
void init_classic_ui();
|
||||||
|
void init_api();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
|
|
||||||
#include "server_properties.h"
|
#include "server_properties.h"
|
||||||
@ -16,4 +18,5 @@ struct ServerCommand {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
std::vector<ServerCommand> *server_get_commands(Minecraft *minecraft, ServerSideNetworkHandler *server_side_network_handler);
|
std::vector<ServerCommand> *server_get_commands(Minecraft *minecraft, ServerSideNetworkHandler *server_side_network_handler);
|
||||||
ServerProperties &get_server_properties();
|
ServerProperties &get_server_properties();
|
||||||
|
std::string get_player_username(Player *player);
|
||||||
}
|
}
|
88
mods/src/api/README.md
Normal file
88
mods/src/api/README.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# `api` Mod
|
||||||
|
|
||||||
|
This mod implements all of the RaspberryJuice extensions to the MCPI API, for the modding API, see [../misc/api.cpp]().
|
||||||
|
|
||||||
|
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
|
||||||
|
- [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.
|
||||||
|
- [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
|
||||||
|
- [x] `world.getEntities(type: int) -> {id: int, type: int, name: str, x: float, y: float, z: float}[]`
|
||||||
|
- - returns a list of entities
|
||||||
|
- [x] `world.removeEntity(id: int) -> int`
|
||||||
|
- - 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
|
||||||
|
- [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
|
||||||
|
- [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.
|
||||||
|
- [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
|
||||||
|
- [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
|
||||||
|
- [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
|
||||||
|
- [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
|
||||||
|
- [x] `player.setRotation(id: int)`
|
||||||
|
- - Sets the yaw of the player
|
||||||
|
- [x] `entity.setRotation(id: int)`
|
||||||
|
- - Sets the yaw of the entity of `id`
|
||||||
|
- [x] `player.getRotation`
|
||||||
|
- - Gets the yaw of the player
|
||||||
|
- [x] `entity.getRotation`
|
||||||
|
- - Gets the yaw of the entity of `id`
|
||||||
|
- [x] `player.getPitch`
|
||||||
|
- - Gets the pitch of the player
|
||||||
|
- [x] `entity.getPitch`
|
||||||
|
- - Gets the pitch of the entity of `id`
|
||||||
|
- [x] `player.setPitch(id: int)`
|
||||||
|
- - Sets the pitch of the player
|
||||||
|
- [x] `entity.setPitch(id: int)`
|
||||||
|
- - 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.
|
||||||
|
- [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.
|
||||||
|
- [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.
|
||||||
|
- [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.
|
||||||
|
- [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
|
||||||
|
- [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]
|
||||||
|
- [x] `world.getEntityTypes() -> {type: int, name: str}`
|
||||||
|
- - Returns a list of known entity types, if there are modded entities this list may be incorrect
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Egdecases:
|
||||||
|
- `world.removeEntity` will remove a player if given a players id.
|
||||||
|
- `world.removeEntities`/`player.removeEntities`/`entity.removeEntities` will not remove players when given 0 or -1 for type.
|
||||||
|
- `entity.getName` will not get the name of non-player entities with a type id of 0 due to ambiguity.
|
||||||
|
- All Raspberry Juice commands/responses involving the player name are designed around the MCJE username restrictions, not the much looser MCPI restrictions. They may cause problems.
|
||||||
|
|
490
mods/src/api/api.cpp
Normal file
490
mods/src/api/api.cpp
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <streambuf>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
#include <symbols/minecraft.h>
|
||||||
|
|
||||||
|
#include <mods/init/init.h>
|
||||||
|
#include <mods/server/server.h>
|
||||||
|
#include <mods/feature/feature.h>
|
||||||
|
|
||||||
|
static std::string getBlocks(CommandServer *commandserver, Vec3 start, Vec3 end) {
|
||||||
|
int startx = start.x, starty = start.y, startz = start.z;
|
||||||
|
int endx = end.x, endy = end.y, endz = end.z;
|
||||||
|
|
||||||
|
// Offset cords
|
||||||
|
commandserver->pos_translator.from(startx, starty, startz);
|
||||||
|
commandserver->pos_translator.from(endx, endy, endz);
|
||||||
|
|
||||||
|
if (endx < startx) std::swap(startx, endx);
|
||||||
|
if (endy < starty) std::swap(starty, endy);
|
||||||
|
if (endz < startz) std::swap(startz, endz);
|
||||||
|
|
||||||
|
// Get
|
||||||
|
std::string ret = "";
|
||||||
|
for (int x = startx; x <= endx; x++) {
|
||||||
|
for (int y = starty; y <= endy; y++) {
|
||||||
|
for (int z = startz; z <= endz; z++) {
|
||||||
|
ret += std::to_string(commandserver->minecraft->level->getTile(x, y, z)) + ",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret.size() > 1) ret.pop_back();
|
||||||
|
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) {
|
||||||
|
if (entity == NULL) return;
|
||||||
|
|
||||||
|
constexpr double _2PI = 2 * M_PI;
|
||||||
|
if (x == 0 && z == 0) {
|
||||||
|
entity->pitch = y > 0 ? -90 : 90;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity->yaw = fmod((float) atan2(-x, z), (float) _2PI) * (180.0f / M_PI);
|
||||||
|
float xz = sqrt(x * x + z * z);
|
||||||
|
entity->pitch = (float) atan(-y / xz) * (180.0f / M_PI);
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
float z = xz * cos(entity->yaw * (M_PI / 180));
|
||||||
|
return Vec3{x, y, z};
|
||||||
|
}
|
||||||
|
|
||||||
|
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) + "," +
|
||||||
|
// x
|
||||||
|
std::to_string(x) + "," +
|
||||||
|
// y
|
||||||
|
std::to_string(y) + "," +
|
||||||
|
// z
|
||||||
|
std::to_string(z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float edist(Entity *e1, Entity *e2) {
|
||||||
|
if (e1 == NULL || e2 == NULL) return 0;
|
||||||
|
float dx = e2->x - e1->x;
|
||||||
|
float dy = e2->y - e1->y;
|
||||||
|
float dz = e2->z - e1->z;
|
||||||
|
return sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProjectileHitEvent {
|
||||||
|
int x, y, z;
|
||||||
|
std::string owner = "";
|
||||||
|
int targetId = 0;
|
||||||
|
|
||||||
|
std::string toString(CommandServer *commandserver) {
|
||||||
|
float nx = x, ny = y, nz = z;
|
||||||
|
commandserver->pos_translator.to(nx, ny, nz);
|
||||||
|
return std::to_string((int) nx) + ","
|
||||||
|
+ std::to_string((int) ny) + ","
|
||||||
|
+ std::to_string((int) nz) + ",1,"
|
||||||
|
+ owner + "," + std::to_string(targetId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t EVENT_SIZE = 50;
|
||||||
|
static int event_at = 0, event_start = 0;
|
||||||
|
static ProjectileHitEvent hitEvents[EVENT_SIZE];
|
||||||
|
|
||||||
|
void addProjectile(ProjectileHitEvent event) {
|
||||||
|
hitEvents[event_at] = event;
|
||||||
|
event_at++;
|
||||||
|
event_at %= EVENT_SIZE;
|
||||||
|
if (event_at == event_start) {
|
||||||
|
event_start++;
|
||||||
|
event_start %= EVENT_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectileHitEvent popProjectile() {
|
||||||
|
if (event_start == event_at) {
|
||||||
|
ERR("Over popped projectile hit events!");
|
||||||
|
}
|
||||||
|
ProjectileHitEvent ret = hitEvents[event_start];
|
||||||
|
event_start++;
|
||||||
|
event_start %= EVENT_SIZE;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
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";
|
||||||
|
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";
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
return std::to_string(player->id) + "\n";
|
||||||
|
}
|
||||||
|
INFO("Player [%s] not found.", args.c_str());
|
||||||
|
return "Fail\n";
|
||||||
|
} else if (cmd == "entity.getName") {
|
||||||
|
int id;
|
||||||
|
int ret = sscanf(args.c_str(), "%d", &id);
|
||||||
|
if (ret != 1) return "\n";
|
||||||
|
|
||||||
|
Entity *entity = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (entity == NULL) {
|
||||||
|
INFO("Player (or Entity) [%i] not found in entity.getName.", id);
|
||||||
|
}
|
||||||
|
return getEntityName(entity) + "\n";
|
||||||
|
} else if (cmd == "world.getEntities") {
|
||||||
|
int type;
|
||||||
|
int ret = sscanf(args.c_str(), "%d", &type);
|
||||||
|
if (ret != 1) return "\n";
|
||||||
|
|
||||||
|
std::string result = "";
|
||||||
|
for (Entity *entity : commandserver->minecraft->level->entities) {
|
||||||
|
if (type == -1 || entity->getEntityTypeId() == type) {
|
||||||
|
// Get pos
|
||||||
|
// Put it together
|
||||||
|
result += getEntityData(commandserver, entity) + "|";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.size() > 1) result.pop_back();
|
||||||
|
return result + "\n";
|
||||||
|
} else if (cmd == "world.removeEntity") {
|
||||||
|
int id;
|
||||||
|
int ret = sscanf(args.c_str(), "%d", &id);
|
||||||
|
if (ret != 1) return "0\n";
|
||||||
|
|
||||||
|
Entity *entity = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (entity != NULL) {
|
||||||
|
// It may be a good idea to check if the entity is a player before removing
|
||||||
|
// but it doesn't crash, just lock the player in place until you rejoin the world
|
||||||
|
entity->remove();
|
||||||
|
return "1\n";
|
||||||
|
}
|
||||||
|
return "0\n";
|
||||||
|
} else if (cmd == "world.removeEntities") {
|
||||||
|
int type;
|
||||||
|
int ret = sscanf(args.c_str(), "%d", &type);
|
||||||
|
if (ret != 1) return "0\n";
|
||||||
|
|
||||||
|
int removed = 0;
|
||||||
|
for (Entity *entity : commandserver->minecraft->level->entities) {
|
||||||
|
if ((type == -1 || entity->getEntityTypeId() == type) && !entity->isPlayer()) {
|
||||||
|
entity->remove();
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::to_string(removed) + "\n";
|
||||||
|
} else if (cmd == "events.chat.posts") {
|
||||||
|
std::string ret = "";
|
||||||
|
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();
|
||||||
|
return ret + "\n";
|
||||||
|
} else if (cmd == "events.projectile.hits") {
|
||||||
|
std::string result = "";
|
||||||
|
while (event_at != event_start) {
|
||||||
|
result += popProjectile().toString(commandserver) + "|";
|
||||||
|
}
|
||||||
|
if (result.size() > 1) result.pop_back();
|
||||||
|
return result + "\n";
|
||||||
|
} else if (cmd == "player.setDirection") {
|
||||||
|
float x, y, z;
|
||||||
|
int ret = sscanf(args.c_str(), "%f,%f,%f", &x, &y, &z);
|
||||||
|
if (ret != 3) return "";
|
||||||
|
setDir((Entity *) commandserver->minecraft->player, x, y, z);
|
||||||
|
} else if (cmd == "entity.setDirection") {
|
||||||
|
int id;
|
||||||
|
float x, y, z;
|
||||||
|
int ret = sscanf(args.c_str(), "%d,%f,%f,%f", &id, &x, &y, &z);
|
||||||
|
if (ret != 4) return "";
|
||||||
|
Entity *entity = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (entity == NULL) {
|
||||||
|
INFO("Entity [%i] not found.", id);
|
||||||
|
} else {
|
||||||
|
setDir(entity, x, y, z);
|
||||||
|
}
|
||||||
|
} else if (cmd == "player.getDirection") {
|
||||||
|
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") {
|
||||||
|
int id;
|
||||||
|
int ret = sscanf(args.c_str(), "%d", &id);
|
||||||
|
if (ret != 1) return "";
|
||||||
|
Entity *entity = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (entity == NULL) {
|
||||||
|
INFO("Entity [%i] not found.", id);
|
||||||
|
return "Fail\n";
|
||||||
|
} 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") {
|
||||||
|
float yaw;
|
||||||
|
int ret = sscanf(args.c_str(), "%f", &yaw);
|
||||||
|
if (ret != 1) return "";
|
||||||
|
commandserver->minecraft->player->yaw = yaw;
|
||||||
|
} else if (cmd == "entity.setRotation") {
|
||||||
|
int id;
|
||||||
|
float yaw;
|
||||||
|
int ret = sscanf(args.c_str(), "%d,%f", &id, &yaw);
|
||||||
|
if (ret != 2) return "";
|
||||||
|
Entity *entity = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (entity == NULL) {
|
||||||
|
INFO("Entity [%i] not found.", id);
|
||||||
|
} else {
|
||||||
|
entity->yaw = yaw;
|
||||||
|
}
|
||||||
|
} else if (cmd == "player.setPitch") {
|
||||||
|
float pitch;
|
||||||
|
int ret = sscanf(args.c_str(), "%f", &pitch);
|
||||||
|
if (ret != 1) return "";
|
||||||
|
commandserver->minecraft->player->pitch = pitch;
|
||||||
|
} else if (cmd == "entity.setPitch") {
|
||||||
|
int id;
|
||||||
|
float pitch;
|
||||||
|
int ret = sscanf(args.c_str(), "%d,%f", &id, &pitch);
|
||||||
|
if (ret != 2) return "";
|
||||||
|
Entity *entity = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (entity == NULL) {
|
||||||
|
INFO("Entity [%i] not found.", id);
|
||||||
|
} else {
|
||||||
|
entity->pitch = pitch;
|
||||||
|
}
|
||||||
|
} else if (cmd == "player.getRotation") {
|
||||||
|
return std::to_string(commandserver->minecraft->player->yaw) + "\n";
|
||||||
|
} else if (cmd == "entity.getRotation") {
|
||||||
|
int id;
|
||||||
|
int ret = sscanf(args.c_str(), "%d", &id);
|
||||||
|
if (ret != 1) return "";
|
||||||
|
Entity *entity = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (entity == NULL) {
|
||||||
|
INFO("Entity [%i] not found.", id);
|
||||||
|
return "Fail\n";
|
||||||
|
} else {
|
||||||
|
return std::to_string(entity->yaw) + "\n";
|
||||||
|
}
|
||||||
|
} else if (cmd == "player.getPitch") {
|
||||||
|
return std::to_string(commandserver->minecraft->player->pitch) + "\n";
|
||||||
|
} else if (cmd == "entity.getPitch") {
|
||||||
|
int id;
|
||||||
|
int ret = sscanf(args.c_str(), "%d", &id);
|
||||||
|
if (ret != 1) return "";
|
||||||
|
Entity *entity = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (entity == NULL) {
|
||||||
|
INFO("Entity [%i] not found.", id);
|
||||||
|
return "Fail\n";
|
||||||
|
} else {
|
||||||
|
return std::to_string(entity->pitch) + "\n";
|
||||||
|
}
|
||||||
|
} else if (cmd == "player.getEntities" || cmd == "entity.getEntities") {
|
||||||
|
// Parse
|
||||||
|
int dist, type;
|
||||||
|
Entity *src = NULL;
|
||||||
|
if (cmd == "player.getEntities") {
|
||||||
|
int ret = sscanf(args.c_str(), "%d,%d", &dist, &type);
|
||||||
|
if (ret != 2) return "";
|
||||||
|
src = (Entity *) commandserver->minecraft->player;
|
||||||
|
} else {
|
||||||
|
int id = 0;
|
||||||
|
int ret = sscanf(args.c_str(), "%d,%d,%d", &id, &dist, &type);
|
||||||
|
if (ret != 3) return "";
|
||||||
|
src = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (src == NULL) {
|
||||||
|
INFO("Entity [%i] not found.", id);
|
||||||
|
return "Fail\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Run
|
||||||
|
std::string result = "";
|
||||||
|
for (Entity *entity : commandserver->minecraft->level->entities) {
|
||||||
|
if ((type == -1 || entity->getEntityTypeId() == type) && edist(src, entity) < dist) {
|
||||||
|
result += getEntityData(commandserver, entity) + "|";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.size() > 1) result.pop_back();
|
||||||
|
return result + "\n";
|
||||||
|
} else if (cmd == "player.removeEntities" || cmd == "entity.removeEntities") {
|
||||||
|
// Parse
|
||||||
|
int dist, type;
|
||||||
|
Entity *src = NULL;
|
||||||
|
if (cmd == "player.removeEntities") {
|
||||||
|
int ret = sscanf(args.c_str(), "%d,%d", &dist, &type);
|
||||||
|
if (ret != 2) return "";
|
||||||
|
src = (Entity *) commandserver->minecraft->player;
|
||||||
|
} else {
|
||||||
|
int id = 0;
|
||||||
|
int ret = sscanf(args.c_str(), "%d,%d,%d", &id, &dist, &type);
|
||||||
|
if (ret != 3) return "";
|
||||||
|
src = commandserver->minecraft->level->getEntity(id);
|
||||||
|
if (src == NULL) {
|
||||||
|
INFO("Entity [%i] not found.", id);
|
||||||
|
return "Fail\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Run
|
||||||
|
int removed = 0;
|
||||||
|
for (Entity *entity : commandserver->minecraft->level->entities) {
|
||||||
|
if ((type == -1 || entity->getEntityTypeId() == type) && edist(src, entity) < dist && !entity->isPlayer()) {
|
||||||
|
entity->remove();
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::to_string(removed) + "\n";
|
||||||
|
} else if (cmd == "world.setSign") {
|
||||||
|
// Parse
|
||||||
|
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";
|
||||||
|
// Translate
|
||||||
|
commandserver->pos_translator.from(x, y, z);
|
||||||
|
// Set block
|
||||||
|
commandserver->minecraft->level->setTileAndData(x, y, z, id, data);
|
||||||
|
// Set sign data
|
||||||
|
if (ret == 3) return "";
|
||||||
|
SignTileEntity *sign = (SignTileEntity *) commandserver->minecraft->level->getTileEntity(x, y, z);
|
||||||
|
if (sign == NULL || sign->type != 4) return "";
|
||||||
|
if (ret > 5) sign->lines[0] = l1;
|
||||||
|
if (ret > 6) sign->lines[1] = l2;
|
||||||
|
if (ret > 7) sign->lines[2] = l3;
|
||||||
|
if (ret > 8) sign->lines[3] = l4;
|
||||||
|
} else if (cmd == "world.spawnEntity") {
|
||||||
|
// Parse
|
||||||
|
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";
|
||||||
|
// Translate
|
||||||
|
int ix = x, iy = y, iz = z;
|
||||||
|
int ix1 = x, iy1 = y, iz1 = z;
|
||||||
|
commandserver->pos_translator.from(ix, iy, iz);
|
||||||
|
x += ix - ix1;
|
||||||
|
y += iy - iy1;
|
||||||
|
z += iz - iz1;
|
||||||
|
// Spawn
|
||||||
|
Entity *entity = NULL;
|
||||||
|
if (id < 0x40) {
|
||||||
|
entity = (Entity *) MobFactory::CreateMob(id, commandserver->minecraft->level);
|
||||||
|
} else {
|
||||||
|
entity = EntityFactory::CreateEntity(id, commandserver->minecraft->level);
|
||||||
|
}
|
||||||
|
if (entity == NULL) return "Fail\n";
|
||||||
|
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) {
|
||||||
|
result += std::to_string(i.first) + "," + i.second + "|";
|
||||||
|
}
|
||||||
|
if (result.size() > 1) result.pop_back();
|
||||||
|
return result + "\n";
|
||||||
|
} else {
|
||||||
|
// Either invalid or a vanilla command, either way hand it off to the orignal handler
|
||||||
|
return old(commandserver, client, command);
|
||||||
|
}
|
||||||
|
// No return, it's different from empty! Empty is "\n". "" will result in nothing being sent back while "\n" will result in an empty string being sent back
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrow entity hit
|
||||||
|
static Entity *shooter = NULL;
|
||||||
|
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;
|
||||||
|
// 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),
|
||||||
|
.targetId = target->id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrow block hit
|
||||||
|
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),
|
||||||
|
.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);
|
||||||
|
}
|
@ -38,6 +38,7 @@ __attribute__((constructor)) static void init() {
|
|||||||
init_cake();
|
init_cake();
|
||||||
init_home();
|
init_home();
|
||||||
init_override();
|
init_override();
|
||||||
|
init_api();
|
||||||
if (!reborn_is_server()) {
|
if (!reborn_is_server()) {
|
||||||
init_benchmark();
|
init_benchmark();
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ static std::vector<Player *> get_players_in_level(Level *level) {
|
|||||||
return level->players;
|
return level->players;
|
||||||
}
|
}
|
||||||
// Get Player's Username
|
// Get Player's Username
|
||||||
static std::string get_player_username(Player *player) {
|
std::string get_player_username(Player *player) {
|
||||||
const std::string *username = &player->username;
|
const std::string *username = &player->username;
|
||||||
char *safe_username_c = from_cp437(username->c_str());
|
char *safe_username_c = from_cp437(username->c_str());
|
||||||
std::string safe_username = safe_username_c;
|
std::string safe_username = safe_username_c;
|
||||||
|
@ -131,6 +131,7 @@ set(SRC
|
|||||||
src/gui/components/GuiComponent.def
|
src/gui/components/GuiComponent.def
|
||||||
src/gui/components/Button.def
|
src/gui/components/Button.def
|
||||||
src/gui/Gui.def
|
src/gui/Gui.def
|
||||||
|
src/gui/GuiMessage.def
|
||||||
src/gui/components/IntRectangle.def
|
src/gui/components/IntRectangle.def
|
||||||
src/gui/components/RectangleArea.def
|
src/gui/components/RectangleArea.def
|
||||||
src/gui/components/ScrollingPane.def
|
src/gui/components/ScrollingPane.def
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
extends Entity;
|
extends Entity;
|
||||||
|
|
||||||
|
vtable 0x10db20;
|
||||||
|
|
||||||
property bool is_critical = 0xd8;
|
property bool is_critical = 0xd8;
|
||||||
|
property int hit_x = 0xdc;
|
||||||
|
property int hit_y = 0xe0;
|
||||||
|
property int hit_z = 0xe4;
|
||||||
|
property bool grounded = 0xf0;
|
||||||
|
property int flight_time = 0xf8;
|
||||||
|
@ -16,6 +16,7 @@ method void handleKeyPressed(int key) = 0x25a08;
|
|||||||
method void renderHearts() = 0x2641c;
|
method void renderHearts() = 0x2641c;
|
||||||
method void renderDebugInfo() = 0x26958;
|
method void renderDebugInfo() = 0x26958;
|
||||||
|
|
||||||
|
property std::vector<GuiMessage> messages = 0x18;
|
||||||
property Minecraft *minecraft = 0x9f4;
|
property Minecraft *minecraft = 0x9f4;
|
||||||
property float selected_item_text_timer = 0x9fc;
|
property float selected_item_text_timer = 0x9fc;
|
||||||
property int flashing_slot = 0xa2c;
|
property int flashing_slot = 0xa2c;
|
||||||
|
6
symbols/src/gui/GuiMessage.def
Normal file
6
symbols/src/gui/GuiMessage.def
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
size 0x8;
|
||||||
|
|
||||||
|
property std::string message = 0x0;
|
||||||
|
property int time = 0x4;
|
||||||
|
|
||||||
|
mark-as-simple;
|
@ -1 +1,3 @@
|
|||||||
extends TileEntity;
|
extends TileEntity;
|
||||||
|
|
||||||
|
property std::string lines[4] = 0x30;
|
Loading…
Reference in New Issue
Block a user