From ccc7e5b1908ba67acb2f8b56e11e4207c4f905a6 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 28 Jan 2024 21:48:51 -0500 Subject: [PATCH 1/8] =?UTF-8?q?Fix=20MCPI=20Ignoring=20=E2=99=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libreborn/src/util/cp437.cpp | 2 +- libreborn/src/util/string.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libreborn/src/util/cp437.cpp b/libreborn/src/util/cp437.cpp index 8dd0e81..bb9062f 100644 --- a/libreborn/src/util/cp437.cpp +++ b/libreborn/src/util/cp437.cpp @@ -16,7 +16,7 @@ static std::string to_utf8(const std::u32string &s) { // Minecraft-Flavored CP437 #define CP437_CHARACTERS 256 static const std::string cp437_characters_map[CP437_CHARACTERS] = { - "\0", "☺", "☻", "♥", "♦", "♣", "♠", "•", "◘", "○", "\n", "♂", "♀", "\r", "♫", "☼", + "\0", "☺", "☻", "♥", "♦", "♣", "♠", "•", "◘", "○", "\n", "♂", "♀", "♪", "♫", "☼", "►", "◄", "↕", "‼", "¶", "§", "▬", "↨", "↑", "↓", "→", "←", "∟", "↔", "▲", "▼", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", diff --git a/libreborn/src/util/string.c b/libreborn/src/util/string.c index bf08000..e9e713e 100644 --- a/libreborn/src/util/string.c +++ b/libreborn/src/util/string.c @@ -14,7 +14,7 @@ void sanitize_string(char **str, int max_length, unsigned int allow_newlines) { // Loop Through Message if (!allow_newlines) { for (int i = 0; i < length; i++) { - if ((*str)[i] == '\n' || (*str)[i] == '\r') { + if ((*str)[i] == '\n') { // Replace Newline (*str)[i] = ' '; } From 522cee2d3b8b304b994fadc9f8f133fdca2c23dc Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 30 Jan 2024 01:30:11 -0500 Subject: [PATCH 2/8] Fix overwrite_calls_within Macro --- libreborn/include/libreborn/patch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libreborn/include/libreborn/patch.h b/libreborn/include/libreborn/patch.h index a4587a1..13ef6a0 100644 --- a/libreborn/include/libreborn/patch.h +++ b/libreborn/include/libreborn/patch.h @@ -15,7 +15,7 @@ void _overwrite_calls(const char *file, int line, void *start, void *target); #define overwrite_calls(start, target) _overwrite_calls(__FILE__, __LINE__, start, target); void _overwrite_calls_within(const char *file, int line, void *from, void *to, void *start, void *target); -#define overwrite_calls_within(from, to, start, target) _overwrite_calls(__FILE__, __LINE__, from, to, start, target); +#define overwrite_calls_within(from, to, start, target) _overwrite_calls_within(__FILE__, __LINE__, from, to, start, target); void *extract_from_bl_instruction(unsigned char *from); From 1771919cc1b44b6b249905723e2a27ea84754383 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Wed, 31 Jan 2024 22:13:20 -0500 Subject: [PATCH 3/8] Port Fire/Lava Textures From ReMCPE --- dependencies/symbol-processor/src | 2 +- launcher/src/client/available-feature-flags | 2 +- libreborn/include/libreborn/util.h | 19 ++ mods/CMakeLists.txt | 1 + mods/src/bucket/bucket.cpp | 17 +- mods/src/textures/lava.cpp | 245 ++++++++++++++++++++ mods/src/textures/textures-internal.h | 3 + mods/src/textures/textures.cpp | 5 +- symbols/CMakeLists.txt | 6 +- symbols/src/misc/Common.def | 1 + symbols/src/misc/Mth.def | 5 + symbols/src/misc/Random.def | 11 +- symbols/src/textures/DynamicTexture.def | 12 + symbols/src/{misc => textures}/Texture.def | 0 symbols/src/{misc => textures}/Textures.def | 1 + symbols/src/tile/Tile.def | 2 + 16 files changed, 312 insertions(+), 20 deletions(-) create mode 100644 mods/src/textures/lava.cpp create mode 100644 mods/src/textures/textures-internal.h create mode 100644 symbols/src/misc/Mth.def create mode 100644 symbols/src/textures/DynamicTexture.def rename symbols/src/{misc => textures}/Texture.def (100%) rename symbols/src/{misc => textures}/Textures.def (72%) diff --git a/dependencies/symbol-processor/src b/dependencies/symbol-processor/src index 0b696bd..059e572 160000 --- a/dependencies/symbol-processor/src +++ b/dependencies/symbol-processor/src @@ -1 +1 @@ -Subproject commit 0b696bd55b31416929d0a29d84ad50ab5ba0ceae +Subproject commit 059e572256667f696f2f1ac9f5253859d54f50c4 diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index a8436a3..feba5ff 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -8,7 +8,7 @@ TRUE Fix Sign Placement TRUE Show Block Outlines FALSE Expand Creative Mode Inventory FALSE Remove Creative Mode Restrictions -TRUE Animated Water +TRUE Animated Water & Lava TRUE Remove Invalid Item Background TRUE Disable "gui_blocks" Atlas TRUE Fix Camera Rendering diff --git a/libreborn/include/libreborn/util.h b/libreborn/include/libreborn/util.h index 46e9d8f..b63e86b 100644 --- a/libreborn/include/libreborn/util.h +++ b/libreborn/include/libreborn/util.h @@ -50,6 +50,25 @@ const char *reborn_get_version(); int reborn_is_headless(); int reborn_is_server(); +// Customize VTable +#define CUSTOM_VTABLE(name, parent) \ + static void _setup_##name##_vtable(parent##_vtable *vtable); \ + static parent##_vtable *get_##name##_vtable() { \ + static parent##_vtable *vtable = NULL; \ + /* Allocate VTable */ \ + if (vtable == NULL) { \ + /* Init */ \ + vtable = dup_##parent##_vtable(parent##_vtable_base); \ + ALLOC_CHECK(vtable); \ + /* Setup */ \ + _setup_##name##_vtable(vtable); \ + } \ + /* Return */ \ + return vtable; \ + } \ + /* User-Defined Setup Code */ \ + static void _setup_##name##_vtable(parent##_vtable *vtable) + #ifdef __cplusplus } #endif diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index 2931a92..b367523 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -95,6 +95,7 @@ else() src/screenshot/screenshot.c # textures src/textures/textures.cpp + src/textures/lava.cpp ) endif() diff --git a/mods/src/bucket/bucket.cpp b/mods/src/bucket/bucket.cpp index 60b9638..888a4ee 100644 --- a/mods/src/bucket/bucket.cpp +++ b/mods/src/bucket/bucket.cpp @@ -117,19 +117,10 @@ static int32_t BucketItem_useOn(__attribute__((unused)) Item *item, ItemInstance } // Bucket VTable -static Item_vtable *get_bucket_vtable() { - static Item_vtable *vtable = NULL; - if (vtable == NULL) { - // Init - vtable = dup_Item_vtable(Item_vtable_base); - ALLOC_CHECK(vtable); - - // Modify - vtable->getDescriptionId = BucketItem_getDescriptionId; - vtable->getIcon = BucketItem_getIcon; - vtable->useOn = BucketItem_useOn; - } - return vtable; +CUSTOM_VTABLE(bucket, Item) { + vtable->getDescriptionId = BucketItem_getDescriptionId; + vtable->getIcon = BucketItem_getIcon; + vtable->useOn = BucketItem_useOn; } // Create Items diff --git a/mods/src/textures/lava.cpp b/mods/src/textures/lava.cpp new file mode 100644 index 0000000..edc416d --- /dev/null +++ b/mods/src/textures/lava.cpp @@ -0,0 +1,245 @@ +#include + +#include +#include + +#include "textures-internal.h" + +// Lava texture code was originally decompield by @iProgramMC as part of ReMinecraftPE. +// See: https://github.com/ReMinecraftPE/mcpe + +// Structures +struct LavaTexture { + DynamicTexture super; + int field_14; + int field_18; + float m_data1[256]; + float m_data2[256]; + float m_data3[256]; + float m_data4[256]; +}; +struct LavaSideTexture { + DynamicTexture super; + int field_14; + int field_18; + int field_1C; + float m_data1[256]; + float m_data2[256]; + float m_data3[256]; + float m_data4[256]; +}; +struct FireTexture { + DynamicTexture super; + float m_data1[320]; + float m_data2[320]; + Random m_random; +}; + +// LavaTexture +CUSTOM_VTABLE(lava_texture, DynamicTexture) { + vtable->tick = [](DynamicTexture *super) { + LavaTexture *self = (LavaTexture *) super; + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + float f = 0.0F; + int ax = int(Mth_sin((float(x) * float(M_PI) * 2) / 16.0f) * 1.2f); + int ay = int(Mth_sin((float(y) * float(M_PI) * 2) / 16.0f) * 1.2f); + for (int bx = x - 1; bx <= x + 1; bx++) { + for (int by = y - 1; by <= y + 1; by++) { + int k2 = (bx + ay) & 0xf; + int i3 = (by + ax) & 0xf; + f += self->m_data1[k2 + i3 * 16]; + } + } + self->m_data2[x + y * 16] = f / 10.0f + ((self->m_data3[(x & 0xf) + ((y + 0) & 0xf) * 16] + self->m_data3[((x + 1) & 0xf) + (y & 0xf) * 16] + self->m_data3[((x + 1) & 0xf) + ((y + 1) & 0xf) * 16] + self->m_data3[(x & 0xf) + ((y + 1) & 0xf) * 16]) * 0.25f) * 0.8f; + self->m_data3[x + y * 16] += self->m_data4[x + y * 16] * 0.01f; + if (self->m_data3[x + y * 16] < 0.0f) { + self->m_data3[x + y * 16] = 0.0f; + } + self->m_data4[x + y * 16] -= 0.06f; + if (Mth_random() < 0.005f) { + self->m_data4[x + y * 16] = 1.5f; + } + } + } + std::swap(self->m_data1, self->m_data2); + for (int i = 0; i < 256; i++) { + float x1 = self->m_data1[i] * 2.0f; + if (x1 > 1.0f) { + x1 = 1.0f; + } + if (x1 < 0.0f) { + x1 = 0.0f; + } + self->super.pixels[i * 4 + 0] = int(155.0f + 100.0f * x1); + self->super.pixels[i * 4 + 1] = int(255.0f * x1 * x1); + self->super.pixels[i * 4 + 2] = int(128.0f * x1 * x1 * x1 * x1); + self->super.pixels[i * 4 + 3] = 255; + } + }; +} +static DynamicTexture *create_lava_texture() { + // Construct + LavaTexture *texture = new LavaTexture; + ALLOC_CHECK(texture); + DynamicTexture_constructor(&texture->super, Tile_lava->texture); + // Set VTable + texture->super.vtable = get_lava_texture_vtable(); + // Setup + texture->field_14 = 0; + texture->field_18 = 0; + for (int i = 0; i < 256; i++) { + texture->m_data1[i] = 0.0f; + texture->m_data2[i] = 0.0f; + texture->m_data3[i] = 0.0f; + texture->m_data4[i] = 0.0f; + } + // Return + return (DynamicTexture *) texture; +} + +// LavaSideTexture +CUSTOM_VTABLE(lava_side_texture, DynamicTexture) { + vtable->tick = [](DynamicTexture *super) { + LavaSideTexture *self = (LavaSideTexture *) super; + self->field_1C++; + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + float f = 0.0F; + int ax = int(Mth_sin((float(x) * float(M_PI) * 2) / 16.0f) * 1.2f); + int ay = int(Mth_sin((float(y) * float(M_PI) * 2) / 16.0f) * 1.2f); + for (int bx = x - 1; bx <= x + 1; bx++) { + for (int by = y - 1; by <= y + 1; by++) { + int k2 = (bx + ay) & 0xf; + int i3 = (by + ax) & 0xf; + f += self->m_data1[k2 + i3 * 16]; + } + } + self->m_data2[x + y * 16] = f / 10.0f + ((self->m_data3[(x & 0xf) + ((y + 0) & 0xf) * 16] + self->m_data3[((x + 1) & 0xf) + (y & 0xf) * 16] + self->m_data3[((x + 1) & 0xf) + ((y + 1) & 0xf) * 16] + self->m_data3[(x & 0xf) + ((y + 1) & 0xf) * 16]) * 0.25f) * 0.8f; + self->m_data3[x + y * 16] += self->m_data4[x + y * 16] * 0.01f; + if (self->m_data3[x + y * 16] < 0.0f) { + self->m_data3[x + y * 16] = 0.0f; + } + self->m_data4[x + y * 16] -= 0.06f; + if (Mth_random() < 0.005f) { + self->m_data4[x + y * 16] = 1.5f; + } + } + } + std::swap(self->m_data1, self->m_data2); + for (int i = 0; i < 256; i++) { + float x1 = self->m_data1[(i - 16 * (self->field_1C / 3)) & 0xFF] * 2.0f; + if (x1 > 1.0f) { + x1 = 1.0f; + } + if (x1 < 0.0f) { + x1 = 0.0f; + } + self->super.pixels[i * 4 + 0] = int(155.0f + 100.0f * x1); + self->super.pixels[i * 4 + 1] = int(255.0f * x1 * x1); + self->super.pixels[i * 4 + 2] = int(128.0f * x1 * x1 * x1 * x1); + self->super.pixels[i * 4 + 3] = 255; + } + }; +} +static DynamicTexture *create_lava_side_texture() { + // Construct + LavaSideTexture *texture = new LavaSideTexture; + ALLOC_CHECK(texture); + DynamicTexture_constructor(&texture->super, Tile_lava->texture + 1); + // Set VTable + texture->super.vtable = get_lava_side_texture_vtable(); + // Setup + texture->field_14 = 0; + texture->field_18 = 0; + texture->field_1C = 0; + texture->super.texture_size = 2; + for (int i = 0; i < 256; i++) { + texture->m_data1[i] = 0.0f; + texture->m_data2[i] = 0.0f; + texture->m_data3[i] = 0.0f; + texture->m_data4[i] = 0.0f; + } + // Return + return (DynamicTexture *) texture; +} + +// FireTexture +CUSTOM_VTABLE(fire_texture, DynamicTexture) { + vtable->tick = [](DynamicTexture *super) { + FireTexture *self = (FireTexture *) super; + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 20; j++) { + int l = 18; + float f1 = self->m_data1[i + ((j + 1) % 20) * 16] * l; + for (int i1 = i - 1; i1 <= i + 1; i1++) { + for (int k1 = j; k1 <= j + 1; k1++) { + int i2 = i1; + int k2 = k1; + if (i2 >= 0 && k2 >= 0 && i2 < 16 && k2 < 20) + { + f1 += self->m_data1[i2 + k2 * 16]; + } + l++; + } + } + self->m_data2[i + j * 16] = f1 / 25.2f; + if (j >= 19) { + union { + uint32_t x; + uint8_t b[4]; + } a; + a.x = Random_genrand_int32(&self->m_random); + self->m_data2[i + j * 16] = 0.2f + (((a.b[3] / 256.0f) * 0.1f) + ((((a.b[0] / 256.0f) * (a.b[1] / 256.0f)) * (a.b[2] / 256.0f)) * 4.0f)); + } + } + } + std::swap(self->m_data1, self->m_data2); + for (int i = 0; i < 256; i++) { + float x = self->m_data1[i] * 1.8f; + if (x > 1.0f) { + x = 1.0f; + } + if (x < 0.0f) { + x = 0.0f; + } + self->super.pixels[4 * i + 0] = int(x * 155.0f + 100.0f); + self->super.pixels[4 * i + 1] = int(x * x * 255.0f); + self->super.pixels[4 * i + 2] = int(x * x * x * x * x * x * x * x * x * x * 255.0f); + self->super.pixels[4 * i + 3] = x >= 0.5f ? 255 : 0; + } + }; +} +static DynamicTexture *create_fire_texture(int a2) { + // Construct + FireTexture *texture = new FireTexture; + ALLOC_CHECK(texture); + DynamicTexture_constructor(&texture->super, Tile_fire->texture + (16 * a2)); + // Set VTable + texture->super.vtable = get_fire_texture_vtable(); + // Setup Random + int seed = Common_getTimeMs(); + texture->m_random.seed = seed; + texture->m_random.param_1 = 0x271; + texture->m_random.param_2 = false; + texture->m_random.param_3 = 0; + // Return + return (DynamicTexture *) texture; +} + +// Add Textures +static void Textures_addDynamicTexture_injection(Textures *textures, DynamicTexture *dynamic_texture) { + // Call Original Method + Textures_addDynamicTexture(textures, dynamic_texture); + + // Add Lava + Textures_addDynamicTexture(textures, create_lava_texture()); + Textures_addDynamicTexture(textures, create_lava_side_texture()); + Textures_addDynamicTexture(textures, create_fire_texture(0)); + Textures_addDynamicTexture(textures, create_fire_texture(1)); +} + +// Init +void _init_textures_lava() { + overwrite_call((void *) 0x170b4, (void *) Textures_addDynamicTexture_injection); +} diff --git a/mods/src/textures/textures-internal.h b/mods/src/textures/textures-internal.h new file mode 100644 index 0000000..d195604 --- /dev/null +++ b/mods/src/textures/textures-internal.h @@ -0,0 +1,3 @@ +#pragma once + +__attribute__((visibility("internal"))) void _init_textures_lava(); diff --git a/mods/src/textures/textures.cpp b/mods/src/textures/textures.cpp index 091edd3..297fcd6 100644 --- a/mods/src/textures/textures.cpp +++ b/mods/src/textures/textures.cpp @@ -11,6 +11,7 @@ #include #include #include +#include "textures-internal.h" #include "stb_image.h" @@ -213,8 +214,10 @@ static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) A // Init void init_textures() { // Tick Dynamic Textures (Animated Water) - if (feature_has("Animated Water", server_disabled)) { + if (feature_has("Animated Water & Lava", server_disabled)) { misc_run_on_tick(Minecraft_tick_injection); + // Animated Lava + _init_textures_lava(); } // Scale Animated Textures diff --git a/symbols/CMakeLists.txt b/symbols/CMakeLists.txt index 75341d0..005dec3 100644 --- a/symbols/CMakeLists.txt +++ b/symbols/CMakeLists.txt @@ -131,12 +131,14 @@ set(SRC src/misc/Vec3.def src/misc/HitResult.def src/misc/PerfRenderer.def - src/misc/Texture.def - src/misc/Textures.def + src/textures/Texture.def + src/textures/Textures.def + src/textures/DynamicTexture.def src/misc/SoundEngine.def src/misc/Common.def src/misc/Config.def src/misc/Random.def + src/misc/Mth.def src/input/IMoveInput.def src/input/IBuildInput.def src/input/MouseBuildInput.def diff --git a/symbols/src/misc/Common.def b/symbols/src/misc/Common.def index 076b533..1d75668 100644 --- a/symbols/src/misc/Common.def +++ b/symbols/src/misc/Common.def @@ -5,3 +5,4 @@ static-method void renderCursor(float x, float y, Minecraft *minecraft) = 0x480c static-method void sleepMs(int x) = 0x13cf4; static-method int sdl_key_to_minecraft_key(int sdl_key) = 0x1243c; static-method void anGenBuffers(int count, uint *buffers) = 0x5f28c; +static-method int getTimeMs() = 0x13cd4; diff --git a/symbols/src/misc/Mth.def b/symbols/src/misc/Mth.def new file mode 100644 index 0000000..a3cc129 --- /dev/null +++ b/symbols/src/misc/Mth.def @@ -0,0 +1,5 @@ +static-property Random _random = 0x17a87c; +static-method float random() = 0x777a4; + +static-method float sin(float x) = 0x7775c; +static-method float cos(float x) = 0x77728; diff --git a/symbols/src/misc/Random.def b/symbols/src/misc/Random.def index 5ceca14..1a6fdbc 100644 --- a/symbols/src/misc/Random.def +++ b/symbols/src/misc/Random.def @@ -1,3 +1,10 @@ -method int genrand_int32() = 0x42cf8; +size 0x9d0; -static-property Random random = 0x17a87c; +method float nextFloat() = 0x42cf8; +method uint genrand_int32() = 0x50e14; +method void init_genrand(uint seed) = 0x27d38; + +property uint seed = 0x0; +property int param_1 = 0x9c4; // Set To 0x271 +property bool param_2 = 0x9c8; // Set To False +property float param_3 = 0x9cc; // Set To 0 diff --git a/symbols/src/textures/DynamicTexture.def b/symbols/src/textures/DynamicTexture.def new file mode 100644 index 0000000..152690e --- /dev/null +++ b/symbols/src/textures/DynamicTexture.def @@ -0,0 +1,12 @@ +vtable 0x108120; +vtable-size 0x10; + +size 0x40c; +constructor (int texture) = 0x66154; + +virtual-method void tick() = 0x8; +virtual-method void bindTexture(Textures *textures) = 0xc; + +property int texture_index = 0x4; +property int texture_size = 0x8; +property uchar pixels[1024] = 0xc; diff --git a/symbols/src/misc/Texture.def b/symbols/src/textures/Texture.def similarity index 100% rename from symbols/src/misc/Texture.def rename to symbols/src/textures/Texture.def diff --git a/symbols/src/misc/Textures.def b/symbols/src/textures/Textures.def similarity index 72% rename from symbols/src/misc/Textures.def rename to symbols/src/textures/Textures.def index 82fe458..f906892 100644 --- a/symbols/src/misc/Textures.def +++ b/symbols/src/textures/Textures.def @@ -1,3 +1,4 @@ method void tick(bool param_1) = 0x531c4; method int loadAndBindTexture(std::string *name) = 0x539cc; method int assignTexture(std::string *name, uchar *data) = 0x5354c; +method void addDynamicTexture(DynamicTexture *texture) = 0x534f8; diff --git a/symbols/src/tile/Tile.def b/symbols/src/tile/Tile.def index fe01648..33da5e0 100644 --- a/symbols/src/tile/Tile.def +++ b/symbols/src/tile/Tile.def @@ -15,6 +15,7 @@ virtual-method int getColor(LevelSource *level_source, int x, int y, int z) = 0x virtual-method int getRenderShape() = 0xc; virtual-method Tile *setDescriptionId(std::string *description_id) = 0xe0; +property int texture = 0x4; property int id = 0x8; property int category = 0x3c; @@ -38,6 +39,7 @@ static-property Tile *info_updateGame2 = 0x181c6c; static-property Tile *bedrock = 0x181cc4; static-property Tile *tallgrass = 0x181d0c; static-property Tile *stoneSlab = 0x181b44; +static-property Tile *fire = 0x181de0; // "Carried" Tiles static-property Tile *leaves = 0x18120c; From 6d4ff44092ffa8b7def92105e21891f1cf67b2c5 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Wed, 31 Jan 2024 23:44:04 -0500 Subject: [PATCH 4/8] Java Light Ramp (Also From ReMCPE) --- launcher/src/client/available-feature-flags | 1 + mods/src/misc/misc.c | 17 +++++++++++++++++ symbols/CMakeLists.txt | 2 ++ symbols/src/level/Dimension.def | 5 +++++ symbols/src/level/Level.def | 2 +- symbols/src/level/MultiPlayerLevel.def | 3 +++ symbols/src/level/ServerLevel.def | 2 +- 7 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 symbols/src/level/Dimension.def create mode 100644 symbols/src/level/MultiPlayerLevel.def diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index feba5ff..838422a 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -51,3 +51,4 @@ TRUE Disable Hostile AI In Creative Mode TRUE Load Custom Skins TRUE 3D Chest Model TRUE Replace Block Highlight With Outline +TRUE Use Java Beta 1.3 Light Ramp diff --git a/mods/src/misc/misc.c b/mods/src/misc/misc.c index 0063997..4b37ead 100644 --- a/mods/src/misc/misc.c +++ b/mods/src/misc/misc.c @@ -545,6 +545,18 @@ static int FurnaceTileEntity_getLitProgress_injection(FurnaceTileEntity *furnace return ret; } +// Java Light Ramp +static void Dimension_updateLightRamp_injection(Dimension *self) { + // https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/world/level/Dimension.cpp#L92-L105 + for (int i = 0; i <= 15; i++) { + float f1 = 1.0f - (((float) i) / 15.0f); + self->light_ramp[i] = ((1.0f - f1) / (f1 * 3.0f + 1.0f)) * (1.0f - 0.1f) + 0.1f; + // Default Light Ramp: + // float fVar4 = 1.0 - ((float) i * 0.0625); + // self->light_ramp[i] = ((1.0 - fVar4) / (fVar4 * 3.0 + 1.0)) * 0.95 + 0.15; + } +} + // Init static void nop() { } @@ -731,6 +743,11 @@ void init_misc() { unsigned char mov_r3_ff[4] = {0xff, 0x30, 0xa0, 0xe3}; // "mov r3, #0xff" patch((void *) 0x7178c, mov_r3_ff); + // Java Light Ramp + if (feature_has("Use Java Beta 1.3 Light Ramp", server_disabled)) { + overwrite((void *) Dimension_updateLightRamp_non_virtual, (void *) Dimension_updateLightRamp_injection); + } + // Init C++ And Logging _init_misc_cpp(); _init_misc_logging(); diff --git a/symbols/CMakeLists.txt b/symbols/CMakeLists.txt index 005dec3..d24b5ae 100644 --- a/symbols/CMakeLists.txt +++ b/symbols/CMakeLists.txt @@ -74,6 +74,8 @@ set(SRC src/level/LevelData.def src/level/LevelSettings.def src/level/ServerLevel.def + src/level/Dimension.def + src/level/MultiPlayerLevel.def src/item/ItemRenderer.def src/item/ItemInHandRenderer.def src/item/AuxDataTileItem.def diff --git a/symbols/src/level/Dimension.def b/symbols/src/level/Dimension.def new file mode 100644 index 0000000..ee73739 --- /dev/null +++ b/symbols/src/level/Dimension.def @@ -0,0 +1,5 @@ +vtable 0x110370; + +virtual-method void updateLightRamp() = 0x28; + +property float light_ramp[16] = 0x10; diff --git a/symbols/src/level/Level.def b/symbols/src/level/Level.def index 0411c97..15220bc 100644 --- a/symbols/src/level/Level.def +++ b/symbols/src/level/Level.def @@ -1,6 +1,6 @@ extends LevelSource; -vtable 0x108de0; +vtable 0x10fcf0; method void saveLevelData() = 0xa2e94; method void setTileAndData(int x, int y, int z, int id, int data) = 0xa38b4; diff --git a/symbols/src/level/MultiPlayerLevel.def b/symbols/src/level/MultiPlayerLevel.def new file mode 100644 index 0000000..0b204a8 --- /dev/null +++ b/symbols/src/level/MultiPlayerLevel.def @@ -0,0 +1,3 @@ +extends Level; + +vtable 0x108de0; diff --git a/symbols/src/level/ServerLevel.def b/symbols/src/level/ServerLevel.def index c2c36e3..5416505 100644 --- a/symbols/src/level/ServerLevel.def +++ b/symbols/src/level/ServerLevel.def @@ -2,4 +2,4 @@ extends Level; size 0xb80; -constructor (uchar *storage, uchar *name, LevelSettings *settings, int param_4, uchar *dimension) = 0x7692c; +constructor (uchar *storage, uchar *name, LevelSettings *settings, int param_4, Dimension *dimension) = 0x7692c; From d175f692e0367cee941922f3d117c57293ea1fdc Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Wed, 31 Jan 2024 23:52:29 -0500 Subject: [PATCH 5/8] Put Sending Full Level Behind Flag --- launcher/src/client/available-feature-flags | 1 + mods/src/misc/misc.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index 838422a..de7b9cb 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -52,3 +52,4 @@ TRUE Load Custom Skins TRUE 3D Chest Model TRUE Replace Block Highlight With Outline TRUE Use Java Beta 1.3 Light Ramp +TRUE Send Full Level When Hosting Game diff --git a/mods/src/misc/misc.c b/mods/src/misc/misc.c index 4b37ead..78631c8 100644 --- a/mods/src/misc/misc.c +++ b/mods/src/misc/misc.c @@ -738,10 +738,12 @@ void init_misc() { overwrite_calls((void *) FurnaceTileEntity_getLitProgress, (void *) FurnaceTileEntity_getLitProgress_injection); // Send the full level, not only changed chunks - unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" - patch((void *) 0x717c4, nop_patch); - unsigned char mov_r3_ff[4] = {0xff, 0x30, 0xa0, 0xe3}; // "mov r3, #0xff" - patch((void *) 0x7178c, mov_r3_ff); + if (feature_has("Send Full Level When Hosting Game", server_enabled)) { + unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" + patch((void *) 0x717c4, nop_patch); + unsigned char mov_r3_ff[4] = {0xff, 0x30, 0xa0, 0xe3}; // "mov r3, #0xff" + patch((void *) 0x7178c, mov_r3_ff); + } // Java Light Ramp if (feature_has("Use Java Beta 1.3 Light Ramp", server_disabled)) { From 58713976d4c3174949dc7deced1879fd8eff2a1f Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Thu, 1 Feb 2024 03:12:24 -0500 Subject: [PATCH 6/8] In-Game Chat --- media-layer/core/src/media.c | 2 + media-layer/include/SDL/SDL_keysym.h | 1 + mods/CMakeLists.txt | 5 +- mods/include/mods/chat/chat.h | 1 - mods/include/mods/misc/misc.h | 2 + .../mods/text-input-box/TextInputBox.h | 35 +++ .../mods/text-input-box/TextInputScreen.h | 12 ++ mods/src/chat/chat-internal.h | 7 +- mods/src/chat/chat.cpp | 34 +-- mods/src/chat/ui.c | 83 -------- mods/src/chat/ui.cpp | 98 +++++++++ mods/src/compat/compat.c | 7 +- mods/src/input/misc.c | 3 +- mods/src/misc/misc.c | 3 +- mods/src/sign/sign.cpp | 36 +--- mods/src/text-input-box/README.md | 2 + mods/src/text-input-box/TextInputBox.cpp | 200 ++++++++++++++++++ mods/src/text-input-box/TextInputScreen.cpp | 40 ++++ symbols/CMakeLists.txt | 3 +- symbols/src/game/Minecraft.def | 1 + symbols/src/gui/Font.def | 1 + symbols/src/gui/{components => }/Gui.def | 0 symbols/src/gui/components/GuiComponent.def | 7 +- symbols/src/gui/screens/Screen.def | 11 +- .../Touch_IngameBlockSelectionScreen.def | 3 +- symbols/src/input/Keyboard.def | 1 + 26 files changed, 446 insertions(+), 152 deletions(-) create mode 100644 mods/include/mods/text-input-box/TextInputBox.h create mode 100644 mods/include/mods/text-input-box/TextInputScreen.h delete mode 100644 mods/src/chat/ui.c create mode 100644 mods/src/chat/ui.cpp create mode 100644 mods/src/text-input-box/README.md create mode 100644 mods/src/text-input-box/TextInputBox.cpp create mode 100644 mods/src/text-input-box/TextInputScreen.cpp rename symbols/src/gui/{components => }/Gui.def (100%) create mode 100644 symbols/src/input/Keyboard.def diff --git a/media-layer/core/src/media.c b/media-layer/core/src/media.c index bfe12cf..759c01e 100644 --- a/media-layer/core/src/media.c +++ b/media-layer/core/src/media.c @@ -123,6 +123,8 @@ static SDLKey glfw_key_to_sdl_key(int key) { return SDLK_RETURN; case GLFW_KEY_BACKSPACE: return SDLK_BACKSPACE; + case GLFW_KEY_DELETE: + return SDLK_DELETE; // Fullscreen case GLFW_KEY_F11: return SDLK_F11; diff --git a/media-layer/include/SDL/SDL_keysym.h b/media-layer/include/SDL/SDL_keysym.h index a6397c5..e4ef34e 100644 --- a/media-layer/include/SDL/SDL_keysym.h +++ b/media-layer/include/SDL/SDL_keysym.h @@ -28,6 +28,7 @@ typedef enum { SDLK_s = 115, SDLK_t = 116, SDLK_w = 119, + SDLK_DELETE = 127, SDLK_UP = 273, SDLK_DOWN = 274, SDLK_RIGHT = 275, diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index b367523..a2e620f 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -15,7 +15,7 @@ set(SRC src/version/version.cpp # chat src/chat/chat.cpp - src/chat/ui.c + src/chat/ui.cpp # creative src/creative/creative.cpp # game-mode @@ -96,6 +96,9 @@ else() # textures src/textures/textures.cpp src/textures/lava.cpp + # text-input-box + src/text-input-box/TextInputBox.cpp + src/text-input-box/TextInputScreen.cpp ) endif() diff --git a/mods/include/mods/chat/chat.h b/mods/include/mods/chat/chat.h index e9d4ad8..3ff276c 100644 --- a/mods/include/mods/chat/chat.h +++ b/mods/include/mods/chat/chat.h @@ -15,7 +15,6 @@ extern "C" { #ifndef MCPI_SERVER_MODE void chat_open(); -unsigned int chat_get_counter(); #endif // Override using the HOOK() macro to provide customized chat behavior. diff --git a/mods/include/mods/misc/misc.h b/mods/include/mods/misc/misc.h index 291384b..06b8a78 100644 --- a/mods/include/mods/misc/misc.h +++ b/mods/include/mods/misc/misc.h @@ -28,6 +28,8 @@ void Level_saveLevelData_injection(Level *level); // Use this instead of directly calling Gui::addMessage(), it has proper logging! void misc_add_message(Gui *gui, const char *text); +extern bool is_in_chat; + #ifdef __cplusplus } #endif diff --git a/mods/include/mods/text-input-box/TextInputBox.h b/mods/include/mods/text-input-box/TextInputBox.h new file mode 100644 index 0000000..a75f594 --- /dev/null +++ b/mods/include/mods/text-input-box/TextInputBox.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +struct TextInputBox { + GuiComponent super; + + int m_ID; + int m_xPos; + int m_yPos; + int m_width; + int m_height; + std::string m_placeholder; + std::string m_text; + bool m_bFocused; + bool m_bEnabled; + bool m_bCursorOn; + int m_insertHead; + int m_lastFlashed; + Font *m_pFont; + int m_maxLength; + + void setSize(int x, int y, int width = 200, int height = 12); + void init(Font *pFont); + void setEnabled(bool bEnabled); + void keyPressed(int key); + void charPressed(int chr); + void render(); + void tick(); + void setFocused(bool b); + void onClick(int x, int y); + bool clicked(int x, int y); + + static TextInputBox create(int id, const std::string &placeholder = "", const std::string &text = ""); +}; diff --git a/mods/include/mods/text-input-box/TextInputScreen.h b/mods/include/mods/text-input-box/TextInputScreen.h new file mode 100644 index 0000000..f076d30 --- /dev/null +++ b/mods/include/mods/text-input-box/TextInputScreen.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +struct TextInputScreen { + Screen super; + std::vector m_textInputs; + + static void setup(Screen_vtable *vtable); +}; diff --git a/mods/src/chat/chat-internal.h b/mods/src/chat/chat-internal.h index 436d6b4..293c6a0 100644 --- a/mods/src/chat/chat-internal.h +++ b/mods/src/chat/chat-internal.h @@ -6,9 +6,12 @@ extern "C" { #endif -__attribute__((visibility("internal"))) extern int _chat_enabled; #ifndef MCPI_SERVER_MODE -__attribute__((visibility("internal"))) void _chat_queue_message(char *message); +__attribute__((visibility("internal"))) void _chat_queue_message(const char *message); +#endif + +#ifndef MCPI_HEADLESS_MODE +__attribute__((visibility("internal"))) void _init_chat_ui(); #endif #ifdef __cplusplus diff --git a/mods/src/chat/chat.cpp b/mods/src/chat/chat.cpp index 37bc6a4..1fef0d4 100644 --- a/mods/src/chat/chat.cpp +++ b/mods/src/chat/chat.cpp @@ -5,9 +5,6 @@ #include #include #include -#ifndef MCPI_HEADLESS_MODE -#include -#endif #include #ifndef MCPI_HEADLESS_MODE @@ -22,9 +19,6 @@ #include "chat-internal.h" #include -// Store If Chat is Enabled -int _chat_enabled = 0; - // Message Limitations #define MAX_CHAT_MESSAGE_LENGTH 512 @@ -95,45 +89,27 @@ static void ServerSideNetworkHandler_handle_ChatPacket_injection(ServerSideNetwo #ifndef MCPI_HEADLESS_MODE // Message Queue -static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; static std::vector queue; // Add To Queue -void _chat_queue_message(char *message) { - // Lock - pthread_mutex_lock(&queue_mutex); +void _chat_queue_message(const char *message) { // Add - std::string str; - str.append(message); + std::string str = message; queue.push_back(str); - // Unlock - pthread_mutex_unlock(&queue_mutex); } // Empty Queue unsigned int old_chat_counter = 0; static void send_queued_messages(Minecraft *minecraft) { - // Lock - pthread_mutex_lock(&queue_mutex); - // If Message Was Submitted, No Other Chat Windows Are Open, And The Game Is Not Paused, Then Re-Lock Cursor - unsigned int new_chat_counter = chat_get_counter(); - if (old_chat_counter > new_chat_counter && new_chat_counter == 0) { - // Unlock UI - media_set_interactable(1); - } - old_chat_counter = new_chat_counter; // Loop for (unsigned int i = 0; i < queue.size(); i++) { send_api_chat_command(minecraft, (char *) queue[i].c_str()); } queue.clear(); - // Unlock - pthread_mutex_unlock(&queue_mutex); } #endif // Init void init_chat() { - _chat_enabled = feature_has("Implement Chat", server_enabled); - if (_chat_enabled) { + if (feature_has("Implement Chat", server_enabled)) { // Disable Original ChatPacket Loopback unsigned char disable_chat_packet_loopback_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" patch((void *) 0x6b490, disable_chat_packet_loopback_patch); @@ -141,9 +117,11 @@ void init_chat() { overwrite_call((void *) 0x6b518, (void *) CommandServer_parse_CommandServer_dispatchPacket_injection); // Re-Broadcast ChatPacket patch_address(ServerSideNetworkHandler_handle_ChatPacket_vtable_addr, (void *) ServerSideNetworkHandler_handle_ChatPacket_injection); - // Send Messages On Input Tick #ifndef MCPI_HEADLESS_MODE + // Send Messages On Input Tick input_run_on_tick(send_queued_messages); + // Init UI + _init_chat_ui(); #endif } } diff --git a/mods/src/chat/ui.c b/mods/src/chat/ui.c deleted file mode 100644 index 04c14da..0000000 --- a/mods/src/chat/ui.c +++ /dev/null @@ -1,83 +0,0 @@ -// Config Needs To Load First -#include - -// Chat UI Code Is Useless In Headless Mode -#ifndef MCPI_HEADLESS_MODE - -#include -#include -#include -#include - -#include - -#include "chat-internal.h" -#include - -// Count Chat Windows -static pthread_mutex_t chat_counter_lock = PTHREAD_MUTEX_INITIALIZER; -static volatile unsigned int chat_counter = 0; -unsigned int chat_get_counter() { - return chat_counter; -} - -// Chat Thread -#define DIALOG_TITLE "Chat" -static void *chat_thread(__attribute__((unused)) void *nop) { - // Open - int return_code; - const char *command[] = { - "zenity", - "--title", DIALOG_TITLE, - "--name", MCPI_APP_ID, - "--entry", - "--text", "Enter Chat Message:", - NULL - }; - char *output = run_command(command, &return_code, NULL); - // Handle Message - if (output != NULL) { - // Check Return Code - if (is_exit_status_success(return_code)) { - // Remove Ending Newline - int length = strlen(output); - if (output[length - 1] == '\n') { - output[length - 1] = '\0'; - } - length = strlen(output); - // Don't Allow Empty Strings - if (length > 0) { - // Submit - char *safe_output = to_cp437(output); - _chat_queue_message(safe_output); - free(safe_output); - } - } - // Free Output - free(output); - } - // Update Counter - pthread_mutex_lock(&chat_counter_lock); - chat_counter--; - pthread_mutex_unlock(&chat_counter_lock); - // Return - return NULL; -} - -// Create Chat Thead -void chat_open() { - if (_chat_enabled) { - // Lock UI - media_set_interactable(0); - - // Update Counter - pthread_mutex_lock(&chat_counter_lock); - chat_counter++; - pthread_mutex_unlock(&chat_counter_lock); - // Start Thread - pthread_t thread; - pthread_create(&thread, NULL, chat_thread, NULL); - } -} - -#endif diff --git a/mods/src/chat/ui.cpp b/mods/src/chat/ui.cpp new file mode 100644 index 0000000..5dd304c --- /dev/null +++ b/mods/src/chat/ui.cpp @@ -0,0 +1,98 @@ +// Config Needs To Load First +#include + +// Chat UI Code Is Useless In Headless Mode +#ifndef MCPI_HEADLESS_MODE + +#include "chat-internal.h" +#include +#include +#include + +// Structure +struct ChatScreen { + TextInputScreen super; + TextInputBox chat; +}; +CUSTOM_VTABLE(chat_screen, Screen) { + TextInputScreen::setup(vtable); + // Init + vtable->init = [](Screen *super) { + Screen_init_non_virtual(super); + ChatScreen *self = (ChatScreen *) super; + self->super.m_textInputs.push_back(&self->chat); + self->chat.init(super->font); + self->chat.setFocused(true); + is_in_chat = true; + }; + // Removal + vtable->removed = [](Screen *super) { + Screen_removed_non_virtual(super); + is_in_chat = false; + }; + // Rendering + static Screen_render_t original_render = vtable->render; + vtable->render = [](Screen *super, int x, int y, float param_1) { + // Background + super->vtable->renderBackground(super); + // Render Chat + Gui_renderChatMessages(&super->minecraft->gui, super->height, 20, true, super->font); + // Call Original Method + original_render(super, x, y, param_1); + }; + // Positioning + vtable->setupPositions = [](Screen *super) { + Screen_setupPositions_non_virtual(super); + ChatScreen *self = (ChatScreen *) super; + int height = 20; + int x = 0; + int y = super->height - height; + int width = super->width; + self->chat.setSize(x, y, width, height); + }; + // Key Presses + static Screen_keyPressed_t original_keyPressed = vtable->keyPressed; + vtable->keyPressed = [](Screen *super, int key) { + // Handle Enter + ChatScreen *self = (ChatScreen *) super; + if (key == 0x0d) { + _chat_queue_message(self->chat.m_text.c_str()); + Minecraft_setScreen(super->minecraft, NULL); + } + // Call Original Method + original_keyPressed(super, key); + }; +} +static Screen *create_chat_screen() { + // Construct + ChatScreen *screen = new ChatScreen; + ALLOC_CHECK(screen); + Screen_constructor(&screen->super.super); + + // Set VTable + screen->super.super.vtable = get_chat_screen_vtable(); + + // Setup + screen->chat = TextInputBox::create(0); + + // Return + return (Screen *) screen; +} + +// Open Screen +static bool open_chat_screen = false; +void chat_open() { + open_chat_screen = true; +} + +// Init +void _init_chat_ui() { + misc_run_on_tick([](Minecraft *minecraft) { + if (open_chat_screen && Minecraft_isLevelGenerated(minecraft) && minecraft->screen == NULL) { + Minecraft_setScreen(minecraft, create_chat_screen()); + } + open_chat_screen = false; + }); +} + +#endif diff --git a/mods/src/compat/compat.c b/mods/src/compat/compat.c index 9e4e831..aa8f351 100644 --- a/mods/src/compat/compat.c +++ b/mods/src/compat/compat.c @@ -70,12 +70,7 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) { input_third_person(); handled = 1; } else if (event->key.keysym.sym == SDLK_t) { - // Only When In-Game With No Other Chat Windows Open - if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON && chat_get_counter() == 0) { - // Open Chat - chat_open(); - } - // Mark Handled + chat_open(); handled = 1; } else if (event->key.keysym.sym == SDLK_ESCAPE) { // Treat Escape As Back Button Press (This Fixes Issues With Signs) diff --git a/mods/src/input/misc.c b/mods/src/input/misc.c index d63f310..1dc74f6 100644 --- a/mods/src/input/misc.c +++ b/mods/src/input/misc.c @@ -80,7 +80,8 @@ static void _handle_mouse_grab(Minecraft *minecraft) { // Block UI Interaction When Mouse Is Locked static bool Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection(Minecraft *minecraft) { - if (!enable_misc || SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) { + bool is_in_game = minecraft->screen == NULL || minecraft->screen->vtable == (Screen_vtable *) Touch_IngameBlockSelectionScreen_vtable_base; + if (!enable_misc || (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF && is_in_game)) { // Call Original Method return creative_is_restricted() && Minecraft_isCreativeMode(minecraft); } else { diff --git a/mods/src/misc/misc.c b/mods/src/misc/misc.c index 78631c8..3b5bba4 100644 --- a/mods/src/misc/misc.c +++ b/mods/src/misc/misc.c @@ -65,6 +65,7 @@ static void Gui_renderBubbles_GuiComponent_blit_injection(Gui *component, int32_ // Additional GUI Rendering static int hide_chat_messages = 0; +bool is_in_chat = 0; static int render_selected_item_text = 0; static void Gui_renderChatMessages_injection(Gui *gui, int32_t y_offset, uint32_t max_messages, bool disable_fading, Font *font) { // Handle Classic HUD @@ -76,7 +77,7 @@ static void Gui_renderChatMessages_injection(Gui *gui, int32_t y_offset, uint32_ } // Call Original Method - if (!hide_chat_messages) { + if (!hide_chat_messages && !is_in_chat) { Gui_renderChatMessages(gui, y_offset, max_messages, disable_fading, font); } diff --git a/mods/src/sign/sign.cpp b/mods/src/sign/sign.cpp index ea1e013..11cbac8 100644 --- a/mods/src/sign/sign.cpp +++ b/mods/src/sign/sign.cpp @@ -12,7 +12,13 @@ // Handle Backspace static int32_t sdl_key_to_minecraft_key_injection(int32_t sdl_key) { if (sdl_key == SDLK_BACKSPACE) { - return 8; + return 0x8; + } else if (sdl_key == SDLK_DELETE) { + return 0x2e; + } else if (sdl_key == SDLK_LEFT) { + return 0x25; + } else if (sdl_key == SDLK_RIGHT) { + return 0x27; } else { // Call Original Method return Common_sdl_key_to_minecraft_key(sdl_key); @@ -31,37 +37,17 @@ static void LocalPlayer_openTextEdit_injection(LocalPlayer *local_player, TileEn } // Store Text Input -std::vector input; void sign_key_press(char key) { - input.push_back(key); -} -static void clear_input(__attribute__((unused)) Minecraft *minecraft) { - input.clear(); -} - -// Handle Text Input -static void TextEditScreen_updateEvents_injection(TextEditScreen *screen) { - // Call Original Method - TextEditScreen_updateEvents_non_virtual(screen); - - if (!screen->passthrough_input) { - for (char key : input) { - // Handle Normal Key - screen->vtable->keyboardNewChar(screen, key); - } - } - clear_input(NULL); + Keyboard__inputText.push_back(key); } // Init void init_sign() { if (feature_has("Fix Sign Placement", server_disabled)) { - // Handle Backspace - overwrite_calls((void *) Common_sdl_key_to_minecraft_key, (void *) sdl_key_to_minecraft_key_injection); // Fix Signs patch_address(LocalPlayer_openTextEdit_vtable_addr, (void *) LocalPlayer_openTextEdit_injection); - patch_address(TextEditScreen_updateEvents_vtable_addr, (void *) TextEditScreen_updateEvents_injection); - // Clear Input On Input Tick - input_run_on_tick(clear_input); } + + // Handle Backspace + overwrite_calls((void *) Common_sdl_key_to_minecraft_key, (void *) sdl_key_to_minecraft_key_injection); } diff --git a/mods/src/text-input-box/README.md b/mods/src/text-input-box/README.md new file mode 100644 index 0000000..5132df3 --- /dev/null +++ b/mods/src/text-input-box/README.md @@ -0,0 +1,2 @@ +# ``text-input-box`` Mod +This mod implements a GUI component for text input. This is ported from [ReMinecraftPE](https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/client/gui/components/TextInputBox.cpp). diff --git a/mods/src/text-input-box/TextInputBox.cpp b/mods/src/text-input-box/TextInputBox.cpp new file mode 100644 index 0000000..cf98d3b --- /dev/null +++ b/mods/src/text-input-box/TextInputBox.cpp @@ -0,0 +1,200 @@ +#include + +#include + +TextInputBox TextInputBox::create(int id, const std::string &placeholder, const std::string &text) { + // Construct + TextInputBox self; + GuiComponent_constructor(&self.super); + + // Setup + self.m_ID = id; + self.m_xPos = 0; + self.m_yPos = 0; + self.m_width = 0; + self.m_height = 0; + self.m_placeholder = placeholder; + self.m_text = text; + self.m_bFocused = false; + self.m_bEnabled = true; + self.m_bCursorOn = true; + self.m_insertHead = 0; + self.m_lastFlashed = 0; + self.m_pFont = nullptr; + self.m_maxLength = -1; + + // Return + return self; +} + +void TextInputBox::setSize(int x, int y, int width, int height) { + m_xPos = x; + m_yPos = y; + m_width = width; + m_height = height; +} + +void TextInputBox::init(Font *pFont) { + m_pFont = pFont; +} + +void TextInputBox::setEnabled(bool bEnabled) { + m_bEnabled = bEnabled; +} + +void TextInputBox::keyPressed(int key) { + if (!m_bFocused) { + return; + } + + switch (key) { + case 0x8: { + // Backspace + if (m_text.empty()) { + return; + } + if (m_insertHead <= 0) { + return; + } + if (m_insertHead > int(m_text.size())) { + m_insertHead = int(m_text.size()); + } + m_text.erase(m_text.begin() + m_insertHead - 1, m_text.begin() + m_insertHead); + m_insertHead--; + break; + } + case 0x2e: { + // Delete + if (m_text.empty()) { + return; + } + if (m_insertHead < 0) { + return; + } + if (m_insertHead >= int(m_text.size())) { + return; + } + m_text.erase(m_text.begin() + m_insertHead, m_text.begin() + m_insertHead + 1); + break; + } + case 0x25: { + // Left + m_insertHead--; + if (m_insertHead < 0) { + m_insertHead = 0; + } + break; + } + case 0x27: { + // Right + m_insertHead++; + if (!m_text.empty()) { + if (m_insertHead > int(m_text.size())) { + m_insertHead = int(m_text.size()); + } + } else { + m_insertHead = 0; + } + break; + } + case 0x0d: { + // Enter + m_bFocused = false; + break; + } + } +} + +void TextInputBox::tick() { + if (!m_lastFlashed) { + m_lastFlashed = Common_getTimeMs(); + } + + if (m_bFocused) { + if (Common_getTimeMs() > m_lastFlashed + 500) { + m_lastFlashed += 500; + m_bCursorOn ^= 1; + } + } else { + m_bCursorOn = false; + } +} + +void TextInputBox::setFocused(bool b) { + if (m_bFocused == b) { + return; + } + + m_bFocused = b; + if (b) { + m_lastFlashed = Common_getTimeMs(); + m_bCursorOn = true; + m_insertHead = int(m_text.size()); + } +} + +void TextInputBox::onClick(int x, int y) { + setFocused(clicked(x, y)); +} + +static int PADDING = 5; +void TextInputBox::charPressed(int k) { + if (!m_bFocused) { + return; + } + + // note: the width will increase by the same amount no matter where K is appended + std::string test_str = m_text + char(k); + if (m_maxLength != -1 && int(test_str.length()) > m_maxLength) { + return; + } + int width = Font_width(m_pFont, &test_str); + if (width < (m_width - PADDING)) { + m_text.insert(m_text.begin() + m_insertHead, k); + m_insertHead++; + } +} + +void TextInputBox::render() { + GuiComponent_fill(&super, m_xPos, m_yPos, m_xPos + m_width, m_yPos + m_height, 0xFFAAAAAA); + GuiComponent_fill(&super, m_xPos + 1, m_yPos + 1, m_xPos + m_width - 1, m_yPos + m_height - 1, 0xFF000000); + + int textYPos = (m_height - 8) / 2; + + if (m_text.empty()) { + GuiComponent_drawString(&super, m_pFont, &m_placeholder, m_xPos + PADDING, m_yPos + textYPos, 0x404040); + } else { + GuiComponent_drawString(&super, m_pFont, &m_text, m_xPos + PADDING, m_yPos + textYPos, 0xFFFFFF); + } + + if (m_bCursorOn) { + int xPos = 5; + + std::string substr = m_text.substr(0, m_insertHead); + xPos += Font_width(m_pFont, &substr); + + std::string str = "_"; + GuiComponent_drawString(&super, m_pFont, &str, m_xPos + xPos, m_yPos + textYPos + 2, 0xFFFFFF); + } +} + +bool TextInputBox::clicked(int xPos, int yPos) { + if (!m_bEnabled) { + return false; + } + + if (xPos < m_xPos) { + return false; + } + if (yPos < m_yPos) { + return false; + } + if (xPos >= m_xPos + m_width) { + return false; + } + if (yPos >= m_yPos + m_height) { + return false; + } + + return true; +} diff --git a/mods/src/text-input-box/TextInputScreen.cpp b/mods/src/text-input-box/TextInputScreen.cpp new file mode 100644 index 0000000..2b5a3b4 --- /dev/null +++ b/mods/src/text-input-box/TextInputScreen.cpp @@ -0,0 +1,40 @@ +#include + +#include + +// VTable +void TextInputScreen::setup(Screen_vtable *vtable) { + vtable->keyPressed = [](Screen *super2, int key) { + Screen_keyPressed_non_virtual(super2, key); + TextInputScreen *self = (TextInputScreen *) super2; + for (int i = 0; i < int(self->m_textInputs.size()); i++) { + TextInputBox *textInput = self->m_textInputs[i]; + textInput->keyPressed(key); + } + }; + vtable->keyboardNewChar = [](Screen *super2, char key) { + Screen_keyboardNewChar_non_virtual(super2, key); + TextInputScreen *self = (TextInputScreen *) super2; + for (int i = 0; i < int(self->m_textInputs.size()); i++) { + TextInputBox *textInput = self->m_textInputs[i]; + textInput->charPressed(key); + } + }; + vtable->mouseClicked = [](Screen *super2, int x, int y, int param_1) { + Screen_mouseClicked_non_virtual(super2, x, y, param_1); + TextInputScreen *self = (TextInputScreen *) super2; + for (int i = 0; i < int(self->m_textInputs.size()); i++) { + TextInputBox *textInput = self->m_textInputs[i]; + textInput->onClick(x, y); + } + }; + vtable->render = [](Screen *super2, int x, int y, float param_1) { + Screen_render_non_virtual(super2, x, y, param_1); + TextInputScreen *self = (TextInputScreen *) super2; + for (int i = 0; i < int(self->m_textInputs.size()); i++) { + TextInputBox *textInput = self->m_textInputs[i]; + textInput->tick(); + textInput->render(); + } + }; +} diff --git a/symbols/CMakeLists.txt b/symbols/CMakeLists.txt index d24b5ae..185b743 100644 --- a/symbols/CMakeLists.txt +++ b/symbols/CMakeLists.txt @@ -106,7 +106,7 @@ set(SRC src/gui/components/OptionsPane.def src/gui/components/GuiComponent.def src/gui/components/Button.def - src/gui/components/Gui.def + src/gui/Gui.def src/gui/components/IntRectangle.def src/gui/components/RectangleArea.def src/gui/components/ScrollingPane.def @@ -145,6 +145,7 @@ set(SRC src/input/IBuildInput.def src/input/MouseBuildInput.def src/input/Mouse.def + src/input/Keyboard.def src/recipes/FurnaceRecipes.def src/recipes/Recipes.def src/recipes/Recipes_Type.def diff --git a/symbols/src/game/Minecraft.def b/symbols/src/game/Minecraft.def index b6440d8..637a92f 100644 --- a/symbols/src/game/Minecraft.def +++ b/symbols/src/game/Minecraft.def @@ -40,6 +40,7 @@ property HitResult hit_result = 0xc38; property int progress = 0xc60; property PerfRenderer *perf_renderer = 0xcbc; property CommandServer *command_server = 0xcc0; +property Font *font = 0x16c; // Smooth Lighting static-property bool useAmbientOcclusion = 0x136b90; diff --git a/symbols/src/gui/Font.def b/symbols/src/gui/Font.def index e69de29..2914ff8 100644 --- a/symbols/src/gui/Font.def +++ b/symbols/src/gui/Font.def @@ -0,0 +1 @@ +method int width(std::string *string) = 0x24d4c; diff --git a/symbols/src/gui/components/Gui.def b/symbols/src/gui/Gui.def similarity index 100% rename from symbols/src/gui/components/Gui.def rename to symbols/src/gui/Gui.def diff --git a/symbols/src/gui/components/GuiComponent.def b/symbols/src/gui/components/GuiComponent.def index d245806..77a775b 100644 --- a/symbols/src/gui/components/GuiComponent.def +++ b/symbols/src/gui/components/GuiComponent.def @@ -1,3 +1,8 @@ +size 0x8; + +constructor () = 0x28204; + method void blit(int x_dest, int y_dest, int x_src, int y_src, int width_dest, int height_dest, int width_src, int height_src) = 0x282a4; method void drawCenteredString(Font *font, std::string *text, int x, int y, int color) = 0x2821c; -method void fill(int x1, int y1, int x2, int y2, uint color) = 0x285f0; \ No newline at end of file +method void drawString(Font *font, std::string *text, int x, int y, int color) = 0x28284; +method void fill(int x1, int y1, int x2, int y2, uint color) = 0x285f0; diff --git a/symbols/src/gui/screens/Screen.def b/symbols/src/gui/screens/Screen.def index c02051d..79c3818 100644 --- a/symbols/src/gui/screens/Screen.def +++ b/symbols/src/gui/screens/Screen.def @@ -1,15 +1,23 @@ extends GuiComponent; +size 0x48; +constructor () = 0x29028; + +vtable-size 0x74; vtable 0x1039d8; virtual-method void updateEvents() = 0x14; virtual-method void keyboardNewChar(char key) = 0x70; virtual-method void keyPressed(int key) = 0x6c; -virtual-method void render(int param_1, int param_2, float param_3) = 0x8; +virtual-method void render(int x, int y, float param_1) = 0x8; virtual-method bool handleBackEvent(bool param_1) = 0x24; virtual-method void tick() = 0x28; virtual-method void buttonClicked(Button *button) = 0x60; virtual-method void init() = 0xc; +virtual-method void mouseClicked(int x, int y, int param_1) = 0x64; +virtual-method void removed() = 0x2c; +virtual-method void renderBackground() = 0x30; +virtual-method void setupPositions() = 0x10; property Minecraft *minecraft = 0x14; property std::vector