From 9d99100a6b40a5d88f7042afcdd463f2c686b7de Mon Sep 17 00:00:00 2001 From: youke1686 Date: Thu, 1 Jan 2026 15:11:53 +0800 Subject: [PATCH 1/5] add the tool durability --- include/globals.h | 30 ++++++++++++++++++++++++++++++ include/packets.h | 1 + include/tools.h | 5 +++++ 3 files changed, 36 insertions(+) diff --git a/include/globals.h b/include/globals.h index 69757cd4..a54fead5 100644 --- a/include/globals.h +++ b/include/globals.h @@ -4,6 +4,8 @@ #include #include +#include "registries.h" + #ifdef ESP_PLATFORM #define WIFI_SSID "your-ssid" #define WIFI_PASS "your-password" @@ -105,6 +107,31 @@ // Size of the receive buffer for incoming string data #define MAX_RECV_BUF_LEN 256 +// Tools that should have a durability decrease when breaking a block +#define TOOLS { \ + I_wooden_pickaxe, I_wooden_axe, I_wooden_shovel, \ + I_stone_pickaxe, I_stone_axe, I_stone_shovel, \ + I_iron_pickaxe, I_iron_axe, I_iron_shovel, \ + I_golden_pickaxe, I_golden_axe, I_golden_shovel, \ + I_diamond_pickaxe,I_diamond_axe,I_diamond_shovel, \ + I_netherite_pickaxe, I_netherite_axe, I_netherite_shovel, \ + I_shears \ +} + +// The durability of each tool +#define TOOL_DURABILITY { \ + 59, 59, 59, \ + 131, 131, 131, \ + 250, 250, 250, \ + 32, 32, 32, \ + 1561, 1561, 1561, \ + 2031, 2031, 2031, \ + 238 \ +} + +// You must to ensure this after changing the options ahead!!! +#define TOOL_COUNT 19 + // If defined, sends the server brand to clients. Doesn't do much, but will // show up in the top-left of the F3/debug menu, in the Minecraft client. // You can change the brand string in the "brand" variable in src/globals.c @@ -273,4 +300,7 @@ extern int player_data_count; extern MobData mob_data[MAX_MOBS]; +extern uint16_t tools[]; +extern uint16_t tool_durability[]; + #endif diff --git a/include/packets.h b/include/packets.h index ae832ac7..3126089a 100644 --- a/include/packets.h +++ b/include/packets.h @@ -39,6 +39,7 @@ int sc_updateTime (int client_fd, uint64_t ticks); int sc_setCenterChunk (int client_fd, int x, int y); int sc_chunkDataAndUpdateLight (int client_fd, int _x, int _z); int sc_keepAlive (int client_fd); +int sc_setContainerSlotWithComponent (int client_fd, int window_id, uint16_t slot, uint8_t count, uint16_t item, uint8_t component_to_add_amount, uint8_t component_type, uint16_t component_content); int sc_setContainerSlot (int client_fd, int window_id, uint16_t slot, uint8_t count, uint16_t item); int sc_setCursorItem (int client_fd, uint16_t item, uint8_t count); int sc_setHeldItem (int client_fd, uint8_t slot); diff --git a/include/tools.h b/include/tools.h index ff579209..26f071a8 100644 --- a/include/tools.h +++ b/include/tools.h @@ -28,6 +28,7 @@ uint8_t readByte (int client_fd); uint16_t readUint16 (int client_fd); int16_t readInt16 (int client_fd); uint32_t readUint32 (int client_fd); +int32_t readInt32 (int client_fd); uint64_t readUint64 (int client_fd); int64_t readInt64 (int client_fd); float readFloat (int client_fd); @@ -36,10 +37,14 @@ double readDouble (int client_fd); ssize_t readLengthPrefixedData (int client_fd); void readString (int client_fd); void readStringN (int client_fd, uint32_t max_length); +bool readSlotData(int client_fd, uint16_t *item, uint8_t *count); uint32_t fast_rand (); uint64_t splitmix64 (uint64_t state); +bool is_tool(uint16_t item); +uint16_t get_tool_durability(int item); + #ifdef ESP_PLATFORM #include "esp_timer.h" #define get_program_time esp_timer_get_time From 972210e07e50cca617b1cbe12ebf88a405b90cab Mon Sep 17 00:00:00 2001 From: youke1686 Date: Thu, 1 Jan 2026 15:13:54 +0800 Subject: [PATCH 2/5] add the tool durability --- src/globals.c | 3 ++ src/packets.c | 104 +++++++++++++++++++++++++++++++++++++++-------- src/procedures.c | 35 +++++++++------- src/tools.c | 44 ++++++++++++++++++++ 4 files changed, 154 insertions(+), 32 deletions(-) diff --git a/src/globals.c b/src/globals.c index 1b5b462f..1ecec05f 100644 --- a/src/globals.c +++ b/src/globals.c @@ -54,3 +54,6 @@ PlayerData player_data[MAX_PLAYERS]; int player_data_count = 0; MobData mob_data[MAX_MOBS]; + +uint16_t tools[] = TOOLS; +uint16_t tool_durability[] = TOOL_DURABILITY; \ No newline at end of file diff --git a/src/packets.c b/src/packets.c index 4289e651..ccea8f0d 100644 --- a/src/packets.c +++ b/src/packets.c @@ -441,14 +441,15 @@ int sc_keepAlive (int client_fd) { } // S->C Set Container Slot -int sc_setContainerSlot (int client_fd, int window_id, uint16_t slot, uint8_t count, uint16_t item) { +int sc_setContainerSlotWithComponent (int client_fd, int window_id, uint16_t slot, uint8_t count, uint16_t item, uint8_t component_to_add_amount, uint8_t component_type, uint16_t component_content) { writeVarInt(client_fd, 1 + sizeVarInt(window_id) + 1 + 2 + sizeVarInt(count) + - (count > 0 ? sizeVarInt(item) + 2 : 0) + (count > 0 ? sizeVarInt(item) + sizeVarInt(component_to_add_amount) + 1 + + (component_to_add_amount == 1 ? sizeVarInt(component_type) + sizeVarInt(component_content) : 0) : 0)// ← length of "Components to add" array ); writeByte(client_fd, 0x14); @@ -459,14 +460,34 @@ int sc_setContainerSlot (int client_fd, int window_id, uint16_t slot, uint8_t co writeVarInt(client_fd, count); if (count > 0) { writeVarInt(client_fd, item); + writeVarInt(client_fd, component_to_add_amount); writeVarInt(client_fd, 0); - writeVarInt(client_fd, 0); + if (component_to_add_amount == 1) { + writeVarInt(client_fd, component_type); + writeVarInt(client_fd, component_content); + } } return 0; } +// The original Set Container Slot function +int sc_setContainerSlot(int client_fd, int window_id, uint16_t slot, uint8_t count, uint16_t item) { + + if (is_tool(item)){ + if (count > 1) { + return sc_setContainerSlotWithComponent(client_fd, window_id, slot, 1, item, 1, 3, max(((uint32_t)(count - 1) * get_tool_durability(item)) / 256, 1)); + } + else { + return sc_setContainerSlotWithComponent(client_fd, window_id, slot, 1, item, 0, 0, 0); + } + } + else{ + return sc_setContainerSlotWithComponent(client_fd, window_id, slot, count, item, 0, 0, 0); + } +} + // S->C Block Update int sc_blockUpdate (int client_fd, int64_t x, int64_t y, int64_t z, uint8_t block) { writeVarInt(client_fd, 9 + sizeVarInt(block_palette[block])); @@ -617,6 +638,11 @@ int cs_clickContainer (int client_fd) { uint16_t *p_item; uint8_t *p_count; + // Temp vars to prevent durability changes when moving tool in container slots + uint8_t amount = 0; + uint16_t *q_item; + uint8_t *q_count; + #ifdef ALLOW_CHESTS // See the handlePlayerUseItem function for more info on this hack uint8_t *storage_ptr; @@ -644,8 +670,21 @@ int cs_clickContainer (int client_fd) { p_count = &player->inventory_count[slot]; } - if (!readByte(client_fd)) { // no item? + if (!readSlotData(client_fd, &item, &count)) { // no item? if (slot != 255 && apply_changes) { + if (is_tool(*p_item)) { + player->flagval_8 = *p_count; + player->flagval_16 = *p_item; + if (is_tool(*q_item)) { + *q_count = *p_count; + #ifdef ALLOW_CHESTS + if (window_id == 2 && amount > 40) { + broadcastChestUpdate(client_fd, storage_ptr, *q_item, *q_count, amount - 41); + } + #endif + } + } + *p_item = 0; *p_count = 0; #ifdef ALLOW_CHESTS @@ -657,19 +696,49 @@ int cs_clickContainer (int client_fd) { continue; } - item = readVarInt(client_fd); - count = (uint8_t)readVarInt(client_fd); - - // ignore components - readLengthPrefixedData(client_fd); - readLengthPrefixedData(client_fd); if (count > 0 && apply_changes) { + if (mode == 1 && button == 0) { + if (is_tool(item)) { + if (is_tool(player->flagval_16)) { + *p_count = player->flagval_8; *p_item = item; + #ifdef ALLOW_CHESTS + if (window_id == 2 && slot > 40) { + broadcastChestUpdate(client_fd, storage_ptr, item, *p_count, slot - 41); + } + #endif + } else { + q_count = p_count; + q_item = p_item; + #ifdef ALLOW_CHESTS + amount = slot; + #endif + } + } else { *p_count = count; + } + } + else { + if (is_tool(player->flagval_16)) { + if (is_tool(*p_item)) { + amount = *p_count; + *p_count = player->flagval_8; + player->flagval_8 = amount; + } else { + *p_count = player->flagval_8; + } + } else if (is_tool(*p_item)) { + player->flagval_8 = *p_count; + *p_count = count; + } else { + *p_count = count; + } + } + *p_item = item; #ifdef ALLOW_CHESTS if (window_id == 2 && slot > 40) { - broadcastChestUpdate(client_fd, storage_ptr, item, count, slot - 41); + broadcastChestUpdate(client_fd, storage_ptr, item, *p_count, slot - 41); } #endif } @@ -688,16 +757,15 @@ int cs_clickContainer (int client_fd) { } // assign cursor-carried item slot - if (readByte(client_fd)) { - player->flagval_16 = readVarInt(client_fd); - player->flagval_8 = readVarInt(client_fd); - // ignore components - readLengthPrefixedData(client_fd); - readLengthPrefixedData(client_fd); - } else { + if (!readSlotData(client_fd, &player->flagval_16, &amount)) { player->flagval_16 = 0; player->flagval_8 = 0; } + if (!is_tool(player->flagval_16)){ + player->flagval_8 = amount; + } else if (player->flagval_8 == 0) { + player->flagval_8 = 1; + } return 0; diff --git a/src/procedures.c b/src/procedures.c index 9f7af8f0..bed2f0c3 100644 --- a/src/procedures.c +++ b/src/procedures.c @@ -717,27 +717,34 @@ uint16_t getMiningResult (uint16_t held_item, uint8_t block) { } -// Rolls a random number to determine whether the player's tool should break +// Rolls a random number to determine whether the player's tool should has a durability decrease void bumpToolDurability (PlayerData *player) { uint16_t held_item = player->inventory_items[player->hotbar]; // In order to avoid storing durability data, items break randomly with // the probability weighted based on vanilla durability. + if (is_tool(held_item)) { + uint16_t n = get_tool_durability(held_item); + if (255 - player->inventory_count[player->hotbar] < 255 / n + 1) { + player->inventory_items[player->hotbar] = 0; + player->inventory_count[player->hotbar] = 0; + sc_entityEvent(player->client_fd, player->client_fd, 47); + sc_setContainerSlot(player->client_fd, 0, serverSlotToClientSlot(0, player->hotbar), 0, 0); + return; + } + uint32_t r = fast_rand(); - if ( - ((held_item == I_wooden_pickaxe || held_item == I_wooden_axe || held_item == I_wooden_shovel) && r < 72796055) || - ((held_item == I_stone_pickaxe || held_item == I_stone_axe || held_item == I_stone_shovel) && r < 32786009) || - ((held_item == I_iron_pickaxe || held_item == I_iron_axe || held_item == I_iron_shovel) && r < 17179869) || - ((held_item == I_golden_pickaxe || held_item == I_golden_axe || held_item == I_golden_shovel) && r < 134217728) || - ((held_item == I_diamond_pickaxe || held_item == I_diamond_axe || held_item == I_diamond_shovel) && r < 2751420) || - ((held_item == I_netherite_pickaxe || held_item == I_netherite_axe || held_item == I_netherite_shovel) && r < 2114705) || - (held_item == I_shears && r < 18046081) - ) { - player->inventory_items[player->hotbar] = 0; - player->inventory_count[player->hotbar] = 0; - sc_entityEvent(player->client_fd, player->client_fd, 47); - sc_setContainerSlot(player->client_fd, 0, serverSlotToClientSlot(0, player->hotbar), 0, 0); + player->inventory_count[player->hotbar] += 255 / n; + if (r < (255 % n) * (UINT32_MAX / n)) { + player->inventory_count[player->hotbar] += 1; + } + + // the first using of a tool cost at least 1 durability + if (player->inventory_count[player->hotbar] == 1){ + player->inventory_count[player->hotbar] += 1; + } + sc_setContainerSlot(player->client_fd, 0, serverSlotToClientSlot(0, player->hotbar), player->inventory_count[player->hotbar], held_item); } } diff --git a/src/tools.c b/src/tools.c index 96aea8fd..02f73bbc 100644 --- a/src/tools.c +++ b/src/tools.c @@ -193,6 +193,13 @@ uint32_t readUint32 (int client_fd) { ((uint32_t)recv_buffer[2] << 8) | ((uint32_t)recv_buffer[3]); } +int32_t readInt32 (int client_fd) { + recv_count = recv_all(client_fd, recv_buffer, 4, false); + return ((int32_t)recv_buffer[0] << 24) | + ((int32_t)recv_buffer[1] << 16) | + ((int32_t)recv_buffer[2] << 8) | + ((int32_t)recv_buffer[3]); +} uint64_t readUint64 (int client_fd) { recv_count = recv_all(client_fd, recv_buffer, 8, false); return ((uint64_t)recv_buffer[0] << 56) | @@ -268,6 +275,29 @@ void readStringN (int client_fd, uint32_t max_length) { } } +// Reads a Slot Data and return if there is any item +bool readSlotData(int client_fd, uint16_t *item, uint8_t *count) { + if (readByte(client_fd)) { + *item = readVarInt(client_fd); + *count = (uint8_t)readVarInt(client_fd); + + // ignore components + uint8_t component_count = readVarInt(client_fd); + for (uint8_t i = 0; i < component_count; i++) { + readVarInt(client_fd); + readInt32(client_fd); + } + component_count = readVarInt(client_fd); + for (uint8_t i = 0; i < component_count; i++) { + readVarInt(client_fd); + } + + return true; + } + + return false; +} + uint32_t fast_rand () { rng_seed ^= rng_seed << 13; rng_seed ^= rng_seed >> 17; @@ -282,6 +312,20 @@ uint64_t splitmix64 (uint64_t state) { return z ^ (z >> 31); } +bool is_tool(uint16_t item) { + for (uint8_t i = 0; i < TOOL_COUNT; i++) { + if (tools[i] == item) return 1; + } + return 0; +} + +uint16_t get_tool_durability(int item) { + for (uint8_t i = 0; i < TOOL_COUNT; i++) { + if (tools[i] == item) return tool_durability[i]; + } + return 0; +} + #ifndef ESP_PLATFORM // Returns system time in microseconds. // On ESP-IDF, this is available in "esp_timer.h", and returns time *since From 35a9d244cd6bffe8d43838e66a7e70299e8d77b3 Mon Sep 17 00:00:00 2001 From: youke1686 Date: Sat, 10 Jan 2026 06:51:25 +0800 Subject: [PATCH 3/5] add the tool durability --- include/tools.h | 4 ++-- src/tools.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/tools.h b/include/tools.h index 26f071a8..c2fb0511 100644 --- a/include/tools.h +++ b/include/tools.h @@ -37,12 +37,12 @@ double readDouble (int client_fd); ssize_t readLengthPrefixedData (int client_fd); void readString (int client_fd); void readStringN (int client_fd, uint32_t max_length); -bool readSlotData(int client_fd, uint16_t *item, uint8_t *count); +uint8_t readSlotData(int client_fd, uint16_t *item, uint8_t *count); uint32_t fast_rand (); uint64_t splitmix64 (uint64_t state); -bool is_tool(uint16_t item); +uint8_t is_tool(uint16_t item); uint16_t get_tool_durability(int item); #ifdef ESP_PLATFORM diff --git a/src/tools.c b/src/tools.c index 02f73bbc..4229fe12 100644 --- a/src/tools.c +++ b/src/tools.c @@ -276,7 +276,7 @@ void readStringN (int client_fd, uint32_t max_length) { } // Reads a Slot Data and return if there is any item -bool readSlotData(int client_fd, uint16_t *item, uint8_t *count) { +uint8_t readSlotData(int client_fd, uint16_t *item, uint8_t *count) { if (readByte(client_fd)) { *item = readVarInt(client_fd); *count = (uint8_t)readVarInt(client_fd); @@ -312,7 +312,7 @@ uint64_t splitmix64 (uint64_t state) { return z ^ (z >> 31); } -bool is_tool(uint16_t item) { +uint8_t is_tool(uint16_t item) { for (uint8_t i = 0; i < TOOL_COUNT; i++) { if (tools[i] == item) return 1; } From 13327a921cbc9c16fabd4a71a69c47d32fc26925 Mon Sep 17 00:00:00 2001 From: youke1686 Date: Wed, 25 Feb 2026 00:05:01 +0800 Subject: [PATCH 4/5] Attempt to fix possible build failures. but it is difficult to make ensurance. --- src/packets.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packets.c b/src/packets.c index ccea8f0d..e9164c51 100644 --- a/src/packets.c +++ b/src/packets.c @@ -477,7 +477,8 @@ int sc_setContainerSlot(int client_fd, int window_id, uint16_t slot, uint8_t cou if (is_tool(item)){ if (count > 1) { - return sc_setContainerSlotWithComponent(client_fd, window_id, slot, 1, item, 1, 3, max(((uint32_t)(count - 1) * get_tool_durability(item)) / 256, 1)); + uint16_t calc_val = ((uint32_t)(count - 1) * get_tool_durability(item)) / 256; + return sc_setContainerSlotWithComponent(client_fd, window_id, slot, 1, item, 1, 3, (calc_val > 1) ? calc_val : 1); } else { return sc_setContainerSlotWithComponent(client_fd, window_id, slot, 1, item, 0, 0, 0); From 9103dd19d72ac98d34e81d390428d1629ec9dc52 Mon Sep 17 00:00:00 2001 From: youke1686 Date: Sat, 28 Mar 2026 13:04:32 +0800 Subject: [PATCH 5/5] fixed the Null pointer issue Co-authored-by: Sky64Redstone <131387100+sky64redstone@users.noreply.github.com> --- src/packets.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packets.c b/src/packets.c index e9164c51..0e6d53c5 100644 --- a/src/packets.c +++ b/src/packets.c @@ -641,7 +641,7 @@ int cs_clickContainer (int client_fd) { // Temp vars to prevent durability changes when moving tool in container slots uint8_t amount = 0; - uint16_t *q_item; + uint16_t *q_item = NULL; uint8_t *q_count; #ifdef ALLOW_CHESTS @@ -676,7 +676,7 @@ int cs_clickContainer (int client_fd) { if (is_tool(*p_item)) { player->flagval_8 = *p_count; player->flagval_16 = *p_item; - if (is_tool(*q_item)) { + if (q_item && is_tool(*q_item)) { *q_count = *p_count; #ifdef ALLOW_CHESTS if (window_id == 2 && amount > 40) {