Final Touches On The API

This commit is contained in:
TheBrokenRail 2025-02-27 04:32:50 -05:00
parent be820dd362
commit 71e660575f
6 changed files with 137 additions and 127 deletions

View File

@ -16,7 +16,7 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely
* <b>Bold</b> text only applies to the compatibility mode.
* <ins>Underlined</ins> text only applies when it is disabled.
* Text enclosed in curly braces (for instance `{text}`) is meant to be [Base64-URL](https://base64.guru/standards/base64url)-encoded when the compatibility mode is disabled.
* In compatibility mode, entity type IDs are automatically translated to/from their [MC Java equivalents](https://mcreator.net/wiki/entity-ids#toc-index-2).
* In the compatibility mode, entity type IDs are automatically translated to/from their [MC Java equivalents](https://mcreator.net/wiki/entity-ids#toc-index-2).
## Commands
* Commands are formatted like `<command>(<args>)` and may return a response. The response `Fail` indicates an error.
@ -88,30 +88,30 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely
* Description: Retrieve the entity ID of the specified player.
* Output: `entity_id`
* `world.getEntities(entity_type_id)`
* Description: Retrieve all entities of the specified type[^1].
* Description: Retrieve all entities of the specified type[^1][^2].
* Output: List <code>entity_id,entity_type_id<b>,entity_type_name</b>,:x:,:y:,:z:</code>
* `entity.getEntities(entity_id,distance,entity_type_id)`
* Description: Retrieve all entities of the specified type[^1] within the given distance of the provided entity.
* Description: Retrieve all entities of the specified type[^1][^2] within the given distance of the provided entity.
* Output: See above.
* `world.removeEntity(entity_id)`
* Description: Remove the specified entity.
* Description: Remove the specified entity[^1].
* Output: `number_of_entities_removed`
* `world.removeEntities(entity_type_id)`
* Description: Remove all entities of the specified type[^1].
* Description: Remove all entities of the specified type[^1][^2].
* Output: See above.
* `entity.removeEntities(entity_id,distance,entity_type_id)`
* Description: Remove all entities of the specified type[^1] within the given distance of the provided entity.
* Description: Remove all entities of the specified type[^1][^2] within the given distance of the provided entity.
* Output: See above.
* `world.spawnEntity(x,y,x,entity_type_id)`
* Description: Spawn the specified entity at the given position.
* Output: `entity_id`
* `world.getEntityTypes()`
* Description: Retrieve all known entity types.
* Description: Retrieve all spawnable entity types.
* Output: List of `entity_type_id,entity_type_name`
* `world.setSign(x,y,z,id,data[,{line_1}][,{line_2}][,{line_3}][,{line_4}])`
* Description: Set the specified block at the given location. If the block is a sign, then also set its contents.
* `entity.getName(entity_id)`
* Description: Retrieve the name of the specified entity.
* Description: Retrieve the name of the specified entity. For players, this will be their username.
* Output: `{entity_name}`
* `entity.setDirection(entity_id,:x:,:y:,:z:)`
* Description: Set the specified entity's rotation using a unit vector.
@ -128,10 +128,10 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely
* `entity.getPitch(entity_id)`
* Description: Retrieve the specified entity's pitch.
* Output: `:pitch:`
* `entity.setAbsPos(entity_id,:x:,:y:,:z:)`[^2]
* Description: Move the specified entity to the given absolute[^3] position.
* `entity.getAbsPos(entity_id)`[^2]
* Description: Retrieve the given entity's absolute[^3] position.
* `entity.setAbsPos(entity_id,:x:,:y:,:z:)`[^3]
* Description: Move the specified entity to the given absolute[^4] position.
* `entity.getAbsPos(entity_id)`[^3]
* Description: Retrieve the given entity's absolute[^4] position.
* Output: `:x:,:y:,:z:`
* `entity.events.block.hits(entity_id)`
* Description: Retrieve all queued block hit events produced by the specified entity.
@ -163,9 +163,10 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely
* Description: Retrieve the text of the given sign.
* Output: List of `{line}`
* `entity.getType(entity_id)`
* Description: Check the type of the given entity.
* Description: Check the type of the given entity. For special entities like players, this will be `0`.
* Output: `entity_type_id`
[^1]: If the ID is `-1`, it will match all entities.
[^2]: RaspberryJuice only implements the `player.*` versions of these commands.
[^3]: The API normally applies an offset to all coordinates, these commands use the raw data.
[^1]: These commands will never match players.
[^2]: If the ID is `-1`, it will match all entities.
[^3]: RaspberryJuice only implements the `player.*` versions of these commands.
[^4]: The API normally applies an offset to all coordinates, these commands use the raw data.

View File

@ -102,6 +102,7 @@ add_library(mods SHARED
# api
src/api/api.cpp
src/api/events.cpp
src/api/compat.cpp
# shading
src/shading/init.cpp
src/shading/tesselator.cpp

View File

@ -1,13 +1,8 @@
#include <cmath>
#include <string>
#include <algorithm>
#include <optional>
#include <sstream>
#include <libreborn/util/string.h>
#include <libreborn/patch.h>
#include <libreborn/config.h>
#include <symbols/minecraft.h>
#include <mods/api/api.h>
#include <mods/init/init.h>
@ -16,62 +11,6 @@
#include "internal.h"
// Compatibility Mode
bool api_compat_mode = true;
// Read String Input
static std::string get_input(std::string message) {
// Decode
if (!api_compat_mode) {
message = misc_base64_decode(message);
}
// Convert To CP-437
return to_cp437(message);
}
// Output String
std::string api_get_output(std::string message, const bool replace_comma) {
// Convert To Unicode
message = from_cp437(message);
// Escape Characters
if (api_compat_mode) {
// Output In Plaintext For RJ Compatibility
std::ranges::replace(message, list_separator, '\\');
if (replace_comma) {
std::ranges::replace(message, arg_separator, '.');
}
} else {
// Encode
message = misc_base64_encode(message);
}
// Return
return message;
}
// Join Strings Into Output
std::string api_join_outputs(const std::vector<std::string> &pieces, const char separator) {
// Join
std::string out;
for (std::string piece : pieces) {
// Check
if (piece.find(separator) != std::string::npos) {
// This Should Be Escapes
IMPOSSIBLE();
}
// Remove Trailing Newline
if (!piece.empty() && piece.back() == '\n') {
piece.pop_back();
}
// Add
out += piece + separator;
}
// Remove Hanging Comma
if (!out.empty()) {
out.pop_back();
}
// Return
return out + '\n';
}
// Get Blocks In Region
static std::string get_blocks(CommandServer *server, const Vec3 &start, const Vec3 &end) {
// Start Coordinate
@ -180,46 +119,6 @@ static Vec3 get_dir(const Entity *entity) {
return Vec3{x, y, z};
}
// Entity Types
static std::unordered_map<int, EntityType> modern_entity_id_mapping = {
{93, EntityType::CHICKEN},
{92, EntityType::COW},
{90, EntityType::PIG},
{91, EntityType::SHEEP},
{54, EntityType::ZOMBIE},
{50, EntityType::CREEPER},
{51, EntityType::SKELETON},
{52, EntityType::SPIDER},
{57, EntityType::ZOMBIE_PIGMAN},
{1, EntityType::DROPPED_ITEM},
{20, EntityType::PRIMED_TNT},
{21, EntityType::FALLING_SAND},
{10, EntityType::ARROW},
{11, EntityType::THROWN_SNOWBALL},
{7, EntityType::THROWN_EGG},
{9, EntityType::PAINTING}
};
void api_convert_to_outside_entity_type(int &type) {
if (!api_compat_mode) {
return;
}
// Convert To RJ-Compatible Entity Type
for (const std::pair<const int, EntityType> &pair : modern_entity_id_mapping) {
if (static_cast<int>(pair.second) == type) {
type = pair.first;
}
}
}
void api_convert_to_mcpi_entity_type(int &type) {
if (!api_compat_mode) {
return;
}
// Convert To Native Entity Type
if (modern_entity_id_mapping.contains(type)) {
type = static_cast<int>(modern_entity_id_mapping[type]);
}
}
// Convert Entity To String
static std::string get_entity_message(CommandServer *server, Entity *entity) {
std::vector<std::string> pieces;
@ -252,7 +151,7 @@ static float distance_between(const Entity *e1, const Entity *e2) {
const float dx = e2->x - e1->x;
const float dy = e2->y - e1->y;
const float dz = e2->z - e1->z;
return std::sqrt(dx * dx + dy * dy + dz * dz);
return std::sqrt((dx * dx) + (dy * dy) + (dz * dz));
}
// Get Sign Tile Entity
@ -291,6 +190,7 @@ static const std::string player_namespace = "player.";
#define next_int(out) next_number(out, int, std::stoi)
#define next_float(out) next_number(out, float, std::stof)
std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServer *server, ConnectedClient &client, const std::string &command) {
// Parse Command
size_t arg_start = command.find('(');
if (arg_start == std::string::npos) {
return CommandServer::Fail;
@ -324,7 +224,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
// Parse
next_string(input, true);
// Search
std::string username = get_input(input);
std::string username = api_get_input(input);
for (Player *player : server->minecraft->level->players) {
if (misc_get_player_username_utf(player) == username) {
// Found
@ -402,7 +302,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
// Run
std::vector<std::string> result;
for (Entity *entity : server->minecraft->level->entities) {
if (is_entity_selected(entity, type) && distance_between(src, entity) < dist) {
if (is_entity_selected(entity, type) && distance_between(src, entity) <= dist) {
result.push_back(get_entity_message(server, entity));
}
}
@ -419,7 +319,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
// Run
int removed = 0;
for (Entity *entity : server->minecraft->level->entities) {
if (is_entity_selected(entity, type) && distance_between(src, entity) < dist) {
if (is_entity_selected(entity, type) && distance_between(src, entity) <= dist) {
entity->remove();
removed++;
}
@ -554,7 +454,7 @@ std::string CommandServer_parse_injection(CommandServer_parse_t old, CommandServ
if (sign != nullptr) {
#define next_sign_line(i) \
next_string(line_##i, false); \
sign->lines[i] = get_input(line_##i); \
sign->lines[i] = api_get_input(line_##i); \
(void) 0
next_sign_line(0);
next_sign_line(1);

104
mods/src/api/compat.cpp Normal file
View File

@ -0,0 +1,104 @@
#include <algorithm>
#include <libreborn/util/string.h>
#include <libreborn/log.h>
#include <mods/misc/misc.h>
#include "internal.h"
// Compatibility Mode
bool api_compat_mode = true;
// Read String Input
std::string api_get_input(std::string message) {
// Decode
if (!api_compat_mode) {
message = misc_base64_decode(message);
}
// Convert To CP-437
return to_cp437(message);
}
// Output String
std::string api_get_output(std::string message, const bool replace_comma) {
// Convert To Unicode
message = from_cp437(message);
// Escape Characters
if (api_compat_mode) {
// Output In Plaintext For RJ Compatibility
std::ranges::replace(message, list_separator, '\\');
if (replace_comma) {
std::ranges::replace(message, arg_separator, '.');
}
} else {
// Encode
message = misc_base64_encode(message);
}
// Return
return message;
}
// Join Strings Into Output
std::string api_join_outputs(const std::vector<std::string> &pieces, const char separator) {
// Join
std::string out;
for (std::string piece : pieces) {
// Check
if (piece.find(separator) != std::string::npos) {
// This Should Be Escapes
IMPOSSIBLE();
}
// Remove Trailing Newline
if (!piece.empty() && piece.back() == '\n') {
piece.pop_back();
}
// Add
out += piece + separator;
}
// Remove Hanging Comma
if (!out.empty()) {
out.pop_back();
}
// Return
return out + '\n';
}
// Entity Types
static std::unordered_map<int, EntityType> modern_entity_id_mapping = {
{93, EntityType::CHICKEN},
{92, EntityType::COW},
{90, EntityType::PIG},
{91, EntityType::SHEEP},
{54, EntityType::ZOMBIE},
{50, EntityType::CREEPER},
{51, EntityType::SKELETON},
{52, EntityType::SPIDER},
{57, EntityType::ZOMBIE_PIGMAN},
{1, EntityType::DROPPED_ITEM},
{20, EntityType::PRIMED_TNT},
{21, EntityType::FALLING_SAND},
{10, EntityType::ARROW},
{11, EntityType::THROWN_SNOWBALL},
{7, EntityType::THROWN_EGG},
{9, EntityType::PAINTING}
};
void api_convert_to_outside_entity_type(int &type) {
if (!api_compat_mode) {
return;
}
// Convert To RJ-Compatible Entity Type
for (const std::pair<const int, EntityType> &pair : modern_entity_id_mapping) {
if (static_cast<int>(pair.second) == type) {
type = pair.first;
}
}
}
void api_convert_to_mcpi_entity_type(int &type) {
if (!api_compat_mode) {
return;
}
// Convert To Native Entity Type
if (modern_entity_id_mapping.contains(type)) {
type = static_cast<int>(modern_entity_id_mapping[type]);
}
}

View File

@ -1,10 +1,7 @@
#include <vector>
#include <algorithm>
#include <ranges>
#include <optional>
#include <libreborn/patch.h>
#include <symbols/minecraft.h>
#include <mods/misc/misc.h>
#include <mods/chat/chat.h>

View File

@ -1,11 +1,18 @@
#pragma once
#include <string>
#include <vector>
#include <optional>
#include <symbols/minecraft.h>
__attribute__((visibility("internal"))) extern bool api_compat_mode;
static constexpr int no_entity_id = -1;
static constexpr char arg_separator = ',';
static constexpr char list_separator = '|';
static constexpr char arg_separator = ','; // Used For Arguments And Tuples
static constexpr char list_separator = '|'; // Used For Lists
__attribute__((visibility("internal"))) std::string api_get_input(std::string message);
__attribute__((visibility("internal"))) std::string api_get_output(std::string message, bool replace_comma);
__attribute__((visibility("internal"))) std::string api_join_outputs(const std::vector<std::string> &pieces, char separator);