diff --git a/build_registries.js b/build_registries.js index 53ac7e7..2114614 100644 --- a/build_registries.js +++ b/build_registries.js @@ -39,6 +39,13 @@ const blockBlacklist = [ const blockWhitelist = [ "air", "water", + "water_1", + "water_2", + "water_3", + "water_4", + "water_5", + "water_6", + "water_7", "lava", "snowy_grass_block", "mud", @@ -105,6 +112,12 @@ async function extractItemsAndBlocks () { const snowyState = entry[1].states.find(c => c.properties.snowy); blocks["snowy_" + entry[0].replace("minecraft:", "")] = snowyState.id; } + // Include levels for fluids + if ("fluid" in entry[1].definition) { + for (let i = 1; i <= 7; i ++) { + blocks[entry[0].replace("minecraft:", "") + "_" + i] = defaultState.id + i; + } + } } for (const item in itemSource) { @@ -332,7 +345,8 @@ async function convert () { const tagBuffer = serializeTags({ "fluid": { - "water": [ 2 ] // source water block + // Water and lava, both flowing and still states + "water": [ 1, 2, 3, 4 ] }, "block": { "mineable/pickaxe": [ diff --git a/include/procedures.h b/include/procedures.h index 66fec3a..9bf34a1 100644 --- a/include/procedures.h +++ b/include/procedures.h @@ -37,6 +37,8 @@ void bumpToolDurability (PlayerData *player); void handlePlayerAction (PlayerData *player, int action, short x, short y, short z); void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t face); +void checkFluidUpdate (short x, uint8_t y, short z, uint8_t block); + void spawnMob (uint8_t type, short x, uint8_t y, short z, uint8_t health); void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t damage); void handleServerTick (int64_t time_since_last_tick); diff --git a/src/globals.c b/src/globals.c index 112b26e..583d74a 100644 --- a/src/globals.c +++ b/src/globals.c @@ -29,7 +29,7 @@ uint8_t recv_buffer[256] = {0}; uint32_t world_seed = 0xA103DE6C; uint32_t rng_seed = 0xE2B9419; -uint16_t world_time = 13000; +uint16_t world_time = 0; uint16_t client_count; diff --git a/src/procedures.c b/src/procedures.c index 731bc72..24e9827 100644 --- a/src/procedures.c +++ b/src/procedures.c @@ -488,12 +488,19 @@ uint8_t isPassableBlock (uint8_t block) { uint8_t isReplaceableBlock (uint8_t block) { return ( block == B_air || - block == B_water || + (block >= B_water && block < B_water + 8) || block == B_short_grass || block == B_snow ); } +uint8_t isReplaceableFluid (uint8_t block, uint8_t level, uint8_t fluid) { + if (block >= fluid && block - fluid < 8) { + return block - fluid > level; + } + return isReplaceableBlock(block); +} + // Checks whether the given item can be used in a composter // Returns the probability (out of 2^32) to return bone meal uint32_t isCompostItem (uint16_t item) { @@ -627,7 +634,84 @@ uint8_t handlePlayerEating (PlayerData *player, uint8_t just_check) { return true; } -void handlePlayerAction(PlayerData *player, int action, short x, short y, short z) { +void handleFluidMovement (short x, uint8_t y, short z, uint8_t fluid, uint8_t block) { + + // Get fluid level (0-7) + // The terminology here is a bit different from vanilla: + // a higher fluid "level" means the fluid has traveled farther + uint8_t level = block - fluid; + + // Query blocks adjacent to this water stream + uint8_t adjacent[4] = { + getBlockAt(x + 1, y, z), + getBlockAt(x - 1, y, z), + getBlockAt(x, y, z + 1), + getBlockAt(x, y, z - 1) + }; + + // Handle maintaining connections to a water source + if (level != 0) { + // Check if this fluid is connected to a block exactly one level lower + uint8_t connected = false; + for (int i = 0; i < 4; i ++) { + if (adjacent[i] == block - 1) { + connected = true; + break; + } + } + // If not connected, clear this block and recalculate surrounding flow + if (!connected) { + makeBlockChange(x, y, z, B_air); + checkFluidUpdate(x + 1, y, z, adjacent[0]); + checkFluidUpdate(x - 1, y, z, adjacent[1]); + checkFluidUpdate(x, y, z + 1, adjacent[2]); + checkFluidUpdate(x, y, z - 1, adjacent[3]); + return; + } + } + + // Check if water should flow down, prioritize that over lateral flow + uint8_t block_below = getBlockAt(x, y - 1, z); + if (isReplaceableBlock(block_below)) { + makeBlockChange(x, y - 1, z, fluid); + return handleFluidMovement(x, y - 1, z, fluid, fluid); + } + + // Stop flowing laterally at the maximum level + if (level == 7) return; + + // Handle lateral water flow, increasing level by 1 + if (isReplaceableFluid(adjacent[0], level, fluid)) { + makeBlockChange(x + 1, y, z, block + 1); + handleFluidMovement(x + 1, y, z, fluid, block + 1); + } + if (isReplaceableFluid(adjacent[1], level, fluid)) { + makeBlockChange(x - 1, y, z, block + 1); + handleFluidMovement(x - 1, y, z, fluid, block + 1); + } + if (isReplaceableFluid(adjacent[2], level, fluid)) { + makeBlockChange(x, y, z + 1, block + 1); + handleFluidMovement(x, y, z + 1, fluid, block + 1); + } + if (isReplaceableFluid(adjacent[3], level, fluid)) { + makeBlockChange(x, y, z - 1, block + 1); + handleFluidMovement(x, y, z - 1, fluid, block + 1); + } + +} + +void checkFluidUpdate (short x, uint8_t y, short z, uint8_t block) { + + uint8_t fluid; + if (block >= B_water && block < B_water + 8) fluid = B_water; + else if (block >= B_lava && block < B_lava + 8) fluid = B_lava; + else return; + + handleFluidMovement(x, y, z, fluid, block); + +} + +void handlePlayerAction (PlayerData *player, int action, short x, short y, short z) { // Re-sync slot when player drops an item if (action == 3 || action == 4) { @@ -669,11 +753,17 @@ void handlePlayerAction(PlayerData *player, int action, short x, short y, short if (item) givePlayerItem(player, item, 1); bumpToolDurability(player); - // Check if any blocks above this should break - uint8_t y_offset = 1; - uint8_t block_above = getBlockAt(x, y + y_offset, z); + // Update nearby fluids + uint8_t block_above = getBlockAt(x, y + 1, z); + checkFluidUpdate(x, y + 1, z, block_above); + checkFluidUpdate(x - 1, y, z, getBlockAt(x - 1, y, z)); + checkFluidUpdate(x + 1, y, z, getBlockAt(x + 1, y, z)); + checkFluidUpdate(x, y, z - 1, getBlockAt(x, y, z - 1)); + checkFluidUpdate(x, y, z + 1, getBlockAt(x, y, z + 1)); - // Iterate upward over all blocks in the column + // Check if any blocks above this should break, and if so, + // iterate upward over all blocks in the column and break them + uint8_t y_offset = 1; while (isColumnBlock(block_above)) { // Destroy the next block makeBlockChange(x, y + y_offset, z, 0); @@ -781,6 +871,12 @@ void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t if (*count == 0) *item = 0; // Apply server-side block change makeBlockChange(x, y, z, block); + // Calculate fluid flow + checkFluidUpdate(x, y + 1, z, getBlockAt(x, y + 1, z)); + checkFluidUpdate(x - 1, y, z, getBlockAt(x - 1, y, z)); + checkFluidUpdate(x + 1, y, z, getBlockAt(x + 1, y, z)); + checkFluidUpdate(x, y, z - 1, getBlockAt(x, y, z - 1)); + checkFluidUpdate(x, y, z + 1, getBlockAt(x, y, z + 1)); } // Sync hotbar contents to player