diff --git a/docs/API.md b/docs/API.md
index eb02109d..04428002 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -16,7 +16,7 @@ By default, MCPI-Reborn runs in a "compatibility mode." This makes it completely
* Bold text only applies to the compatibility mode.
* Underlined 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 `()` 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 entity_id,entity_type_id,entity_type_name,:x:,:y:,:z:
* `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.
\ No newline at end of file
+[^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.
\ No newline at end of file
diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt
index 40d0b8d6..1bef7671 100644
--- a/mods/CMakeLists.txt
+++ b/mods/CMakeLists.txt
@@ -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
diff --git a/mods/src/api/api.cpp b/mods/src/api/api.cpp
index 814156e8..fc5cf1be 100644
--- a/mods/src/api/api.cpp
+++ b/mods/src/api/api.cpp
@@ -1,13 +1,8 @@
#include
-#include
-#include
-#include
#include
-#include
#include
#include
-#include
#include
#include
@@ -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 &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 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 &pair : modern_entity_id_mapping) {
- if (static_cast(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(modern_entity_id_mapping[type]);
- }
-}
-
// Convert Entity To String
static std::string get_entity_message(CommandServer *server, Entity *entity) {
std::vector 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 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);
diff --git a/mods/src/api/compat.cpp b/mods/src/api/compat.cpp
new file mode 100644
index 00000000..acc59e4a
--- /dev/null
+++ b/mods/src/api/compat.cpp
@@ -0,0 +1,104 @@
+#include
+
+#include
+#include
+
+#include
+
+#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 &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 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 &pair : modern_entity_id_mapping) {
+ if (static_cast(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(modern_entity_id_mapping[type]);
+ }
+}
\ No newline at end of file
diff --git a/mods/src/api/events.cpp b/mods/src/api/events.cpp
index 9b12bcec..50a91687 100644
--- a/mods/src/api/events.cpp
+++ b/mods/src/api/events.cpp
@@ -1,10 +1,7 @@
-#include
#include
#include
-#include
#include
-#include
#include
#include
diff --git a/mods/src/api/internal.h b/mods/src/api/internal.h
index e363f066..4d761989 100644
--- a/mods/src/api/internal.h
+++ b/mods/src/api/internal.h
@@ -1,11 +1,18 @@
#pragma once
+#include
+#include
+#include
+
+#include
+
__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 &pieces, char separator);