From 2ab196e5f05c0accc716fd8810ce1a9c64a6c7c9 Mon Sep 17 00:00:00 2001 From: p2r3 Date: Wed, 13 Aug 2025 20:57:58 +0300 Subject: [PATCH] implement block mining conditions --- build_registries.js | 21 ++++++++++++++------- main.c | 8 ++++++++ src/globals.c | 1 + src/globals.h | 1 + src/packets.c | 9 +-------- src/tools.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ src/tools.h | 4 ++++ 7 files changed, 75 insertions(+), 15 deletions(-) diff --git a/build_registries.js b/build_registries.js index 1098401..7559c1a 100644 --- a/build_registries.js +++ b/build_registries.js @@ -1,6 +1,16 @@ const fs = require("fs/promises"); const path = require("path"); +// Overrides for block-to-item conversion +const blockToItemOverrides = { + "grass_block": "dirt", + "stone": "cobblestone" +}; + +const biomes = [ + "plains" +]; + // Extract item and block data from registry dump async function extractItemsAndBlocks () { @@ -48,12 +58,13 @@ async function extractItemsAndBlocks () { const exceptions = [ "air", "water", "lava" ]; // While we're at it, map block IDs to item IDs - const mapping = []; + const mapping = [], mappingWithOverrides = []; for (const block in blocks) { if (!(block in items) && !exceptions.includes(block)) continue; palette[block] = blocks[block]; mapping.push(items[block] || 0); + mappingWithOverrides.push(items[blockToItemOverrides[block]] || items[block] || 0); if (mapping.length === 256) break; } @@ -64,7 +75,7 @@ async function extractItemsAndBlocks () { blockRegistry[block.replace("minecraft:", "")] = blockRegistrySource[block].protocol_id; } - return { blocks, items, palette, mapping, blockRegistry }; + return { blocks, items, palette, mapping, mappingWithOverrides, blockRegistry }; } @@ -206,10 +217,6 @@ const requiredRegistries = [ "damage_type" ]; -const biomes = [ - "plains" -]; - async function convert () { const inputPath = __dirname + "/notchian/generated/data/minecraft"; @@ -276,7 +283,7 @@ ${toCArray(tagBuffer)} // Block palette uint16_t block_palette[] = { ${Object.values(itemsAndBlocks.palette).join(", ")} }; // Block-to-item mapping -uint16_t B_to_I[] = { ${itemsAndBlocks.mapping.join(", ")} }; +uint16_t B_to_I[] = { ${itemsAndBlocks.mappingWithOverrides.join(", ")} }; // Item-to-block mapping uint8_t I_to_B (uint16_t item) { switch (item) { diff --git a/main.c b/main.c index 2d6ebd2..4964d89 100644 --- a/main.c +++ b/main.c @@ -282,6 +282,14 @@ int main () { sc_keepAlive(client_fd); sc_updateTime(client_fd, world_time += 200); clock_gettime(CLOCK_REALTIME, &keepalive_last); + /** + * If the RNG seed ever hits 0, it'll never generate anything + * else. This is because the fast_rand function uses a simple + * XORshift. This isn't a common concern, so we only check for + * this periodically. If it does become zero, we reset it to + * the world seed as a good-enough fallback. + */ + if (rng_seed == 0) rng_seed = world_seed; } } diff --git a/src/globals.c b/src/globals.c index b515cdc..dfce411 100644 --- a/src/globals.c +++ b/src/globals.c @@ -9,6 +9,7 @@ ssize_t recv_count; uint8_t recv_buffer[256] = {0}; uint32_t world_seed = 0xA103DE6C; +uint32_t rng_seed = 0xE2B9419; BlockChange block_changes[20000]; int block_changes_count = 0; diff --git a/src/globals.h b/src/globals.h index bd4c343..66a0dbc 100644 --- a/src/globals.h +++ b/src/globals.h @@ -22,6 +22,7 @@ extern ssize_t recv_count; extern uint8_t recv_buffer[256]; extern uint32_t world_seed; +extern uint32_t rng_seed; #pragma pack(push, 1) diff --git a/src/packets.c b/src/packets.c index f924698..a20667c 100644 --- a/src/packets.c +++ b/src/packets.c @@ -386,14 +386,7 @@ int cs_playerAction (int client_fd) { // block was mined in survival uint8_t block = getBlockAt(x, y, z); - uint16_t item, tmp; - - if (block == B_oak_leaves) { - if (sequence % 200 < 2) item = I_apple; - else if (sequence % 50 < 2) item = I_stick; - else if (sequence % 40 < 2) item = I_oak_sapling; - else item = 0; - } else item = B_to_I[block]; + uint16_t tmp, item = getMiningResult(client_fd, block); makeBlockChange(x, y, z, 0); diff --git a/src/tools.c b/src/tools.c index 8969bc3..fcb2713 100644 --- a/src/tools.c +++ b/src/tools.c @@ -5,6 +5,7 @@ #include #include "globals.h" +#include "registries.h" #include "varnum.h" #include "packets.h" #include "tools.h" @@ -102,6 +103,13 @@ void readString (int client_fd) { recv_buffer[recv_count] = '\0'; } +uint32_t fast_rand () { + rng_seed ^= rng_seed << 13; + rng_seed ^= rng_seed >> 17; + rng_seed ^= rng_seed << 5; + return rng_seed; +} + int client_states[MAX_PLAYERS * 2]; void setClientState (int client_fd, int new_state) { @@ -275,3 +283,41 @@ void makeBlockChange (short x, short y, short z, uint8_t block) { block_changes_count ++; } + +// Returns the result of mining a block, taking into account the block type and tools +// Probability numbers obtained with this formula: N = floor(P * 32 ^ 2) +uint16_t getMiningResult (int client_fd, uint8_t block) { + + switch (block) { + + case B_oak_leaves: + uint32_t r = fast_rand(); + printf("fast_rand: %u, in distribution: %.3f\n", r, (float)r / (4294967295.0f)); + if (r < 21474836) return I_apple; // 0.5% + if (r < 85899345) return I_stick; // 2% + if (r < 214748364) return I_oak_sapling; // 5% + return 0; + break; + + case B_stone: + case B_cobblestone: + // Check if player is holding a pickaxe + PlayerData *player; + if (getPlayerData(client_fd, &player)) return 0; + uint16_t held_item = player->inventory_items[player->hotbar]; + if ( + held_item != I_wooden_pickaxe && + held_item != I_stone_pickaxe && + held_item != I_iron_pickaxe && + held_item != I_golden_pickaxe && + held_item != I_diamond_pickaxe && + held_item != I_netherite_pickaxe + ) return 0; + break; + + default: break; + } + + return B_to_I[block]; + +} diff --git a/src/tools.h b/src/tools.h index a60a5c3..db1046e 100644 --- a/src/tools.h +++ b/src/tools.h @@ -25,6 +25,8 @@ double readDouble (int client_fd); void readString (int client_fd); +uint32_t fast_rand (); + extern int client_states[MAX_PLAYERS * 2]; void setClientState (int client_fd, int new_state); @@ -42,4 +44,6 @@ uint8_t clientSlotToServerSlot (int window_id, uint8_t slot); uint8_t getBlockChange (short x, short y, short z); void makeBlockChange (short x, short y, short z, uint8_t block); +uint16_t getMiningResult (int client_fd, uint8_t block); + #endif