From ede77674d3f9e522447cf85532bbabe1ee1cf0cf Mon Sep 17 00:00:00 2001 From: Alexander Nutz Date: Sat, 27 Sep 2025 20:29:10 +0200 Subject: [PATCH] rewrite sand ticking --- include/globals.h | 11 +++- include/procedures.h | 8 ++- src/procedures.c | 136 ++++++++++++++++++++++++++++++------------- 3 files changed, 109 insertions(+), 46 deletions(-) diff --git a/include/globals.h b/include/globals.h index 71a89c9..40a29cc 100644 --- a/include/globals.h +++ b/include/globals.h @@ -199,11 +199,20 @@ typedef struct { uint8_t block; } BlockChange; +#define UPDATE_BASIC (1 << 0) +// the sand at this position will be moved down immediately when this is processed +#define UPDATE_FALL_SAND (1 << 1) +// the sand below this block will fall soon +#define UPDATE_CHECK_SAND_FALL (1 << 2) + +#define UPDATE_NOW (UPDATE_BASIC | UPDATE_CHECK_SAND_FALL) + typedef struct { + short update_kind; short x; short z; uint8_t y; - uint8_t awaitTicks; + uint8_t await_ticks; } DeferredBlockUpdate; #pragma pack(push, 1) diff --git a/include/procedures.h b/include/procedures.h index 213469d..6e6ee01 100644 --- a/include/procedures.h +++ b/include/procedures.h @@ -32,6 +32,7 @@ uint8_t makeBlockChange (short x, uint8_t y, short z, uint8_t block); uint8_t isInstantlyMined (PlayerData *player, uint8_t block); uint8_t isColumnBlock (uint8_t block); +uint8_t isFallingBlock (uint8_t block); uint8_t isPassableBlock (uint8_t block); uint8_t isPassableSpawnBlock (uint8_t block); uint8_t isReplaceableBlock (uint8_t block); @@ -43,9 +44,10 @@ 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 processBlockUpdate (short x, uint8_t y, short z, uint8_t block); -void deferBlockUpdate (short x, uint8_t y, short z, uint8_t awaitTicks); +void processBlockUpdate (short x, uint8_t y, short z, uint8_t block, short update_kind); +void updateXZNeighbors (short x, uint8_t y, short z, short update_kind); +void updateXYZNeighbors (short x, uint8_t y, short z, short update_kind); +void deferBlockUpdate (short x, uint8_t y, short z, uint8_t await_ticks, short update_kind); void spawnMob (uint8_t type, short x, uint8_t y, short z, uint8_t health); void interactEntity (int entity_id, int interactor_id); diff --git a/src/procedures.c b/src/procedures.c index abc7f58..430c1d8 100644 --- a/src/procedures.c +++ b/src/procedures.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -761,6 +762,20 @@ uint8_t isInstantlyMined (PlayerData *player, uint8_t block) { } +// Checks whether the given block can fall down (like sand, anvils, ...) +uint8_t isFallingBlock (uint8_t block) { + return ( + block == B_snow || + block == B_moss_carpet || + block == B_cactus || + block == B_short_grass || + block == B_dead_bush || + block == B_sand || + block == B_torch || + block == B_oak_sapling + ); +} + // Checks whether the given block has to have something beneath it uint8_t isColumnBlock (uint8_t block) { return ( @@ -1030,6 +1045,7 @@ uint8_t handlePlayerEating (PlayerData *player, uint8_t just_check) { return true; } +/* void handleFluidMovement (short x, uint8_t y, short z, uint8_t fluid, uint8_t block) { // Get fluid level (0-7) @@ -1108,19 +1124,16 @@ void handleFluidMovement (short x, uint8_t y, short z, uint8_t fluid, uint8_t bl makeBlockChange(x, y, z - 1, block + 1); deferBlockUpdate(x, y, z - 1, flow_speed); } - } -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 + 4) fluid = B_lava; - else return; - - handleFluidMovement(x, y, z, fluid, block); - +uint8_t getFluid(uint8_t block) { + if (block >= B_water && block < B_water + 8) + return B_water; + else if (block >= B_lava && block < B_lava + 4) + return B_lava; + return 0; } +*/ #ifdef ENABLE_PICKUP_ANIMATION // Plays the item pickup animation with the given item at the given coordinates @@ -1157,28 +1170,74 @@ void playPickupAnimation (PlayerData *player, uint16_t item, double x, double y, } #endif -void processBlockUpdate (short x, uint8_t y, short z, uint8_t block) { - #ifdef DO_FLUID_FLOW - checkFluidUpdate(x, y, z, block); - #endif +// can not be zero!! +#define SAND_FALL_T 15 +#define SAND_FALL_PROPAGATE_T 2 - if (isColumnBlock(block) && y) { - uint8_t below = getBlockAt(x, y - 1, z); - // TODO: if below block breaks sand - if (isReplaceableBlock(below)) { - // TODO: drop item of below block - makeBlockChange(x, y, z, B_air); - makeBlockChange(x, y - 1, z, B_air); - makeBlockChange(x, y - 1, z, block); - // update this now moved block at the sand fall speed - deferBlockUpdate(x, y - 1, z, 15); - // update the block above next tick - deferBlockUpdate(x, y + 1, z, 0); +void processBlockUpdate (short x, uint8_t y, short z, uint8_t block, short update_kind) { + if (update_kind & UPDATE_BASIC) { + // TODO: water + } + + if (update_kind & UPDATE_CHECK_SAND_FALL) { + // we have a separate UPDATE_CHECK_SAND_FALL, + // to make chains of falling sand fall as one, + // with less delay between each + + if (isFallingBlock(block) && y) { + uint8_t below = getBlockAt(x, y - 1, z); + if (isReplaceableBlock(below)) { + // move the sand down in 15 ticks + deferBlockUpdate(x, y, z, SAND_FALL_T - 1, UPDATE_FALL_SAND); + + if (y != 255 && isFallingBlock(getBlockAt(x, y + 1, z))) { + // also tell the block above that the sand will be gone soon + deferBlockUpdate(x, y + 1, z, SAND_FALL_PROPAGATE_T, UPDATE_CHECK_SAND_FALL); + } + } + } + } + + if (update_kind & UPDATE_FALL_SAND) { + // make sure that is a fallable:tm: block, and we are not in the floor + if (isFallingBlock(block) && y) { + uint8_t below = getBlockAt(x, y - 1, z); + // TODO: what to do if below block breaks sand + if (isReplaceableBlock(below)) { + // TODO: drop item of below block + makeBlockChange(x, y, z, B_air); + makeBlockChange(x, y - 1, z, B_air); + makeBlockChange(x, y - 1, z, block); + // update this block after moved + processBlockUpdate (x, y - 1, z, block, UPDATE_NOW); + // also update the neighbors + updateXZNeighbors(x, y, z, UPDATE_NOW); + updateXZNeighbors(x, y - 1, z, UPDATE_NOW); + if (y != 255) { + processBlockUpdate(x, y + 1, z, getBlockAt(x, y + 1, z), UPDATE_NOW); + } + } } } } -void deferBlockUpdate (short x, uint8_t y, short z, uint8_t awaitTicks) { +void updateXZNeighbors (short x, uint8_t y, short z, short update_kind) { + processBlockUpdate(x - 1, y, z, getBlockAt(x - 1, y, z), update_kind); + processBlockUpdate(x + 1, y, z, getBlockAt(x + 1, y, z), update_kind); + processBlockUpdate(x, y, z - 1, getBlockAt(x, y, z - 1), update_kind); + processBlockUpdate(x, y, z + 1, getBlockAt(x, y, z + 1), update_kind); +} + +void updateXYZNeighbors (short x, uint8_t y, short z, short update_kind) { + processBlockUpdate(x - 1, y, z, getBlockAt(x - 1, y, z), update_kind); + processBlockUpdate(x + 1, y, z, getBlockAt(x + 1, y, z), update_kind); + processBlockUpdate(x, y, z - 1, getBlockAt(x, y, z - 1), update_kind); + processBlockUpdate(x, y, z + 1, getBlockAt(x, y, z + 1), update_kind); + processBlockUpdate(x, y - 1, z, getBlockAt(x, y - 1, z), update_kind); + processBlockUpdate(x, y + 1, z, getBlockAt(x, y + 1, z), update_kind); +} + +void deferBlockUpdate (short x, uint8_t y, short z, uint8_t await_ticks, short update_kind) { if (deferred_block_updates_count == MAX_DEFERRED_BLOCK_UPDATES) { return; } @@ -1187,7 +1246,8 @@ void deferBlockUpdate (short x, uint8_t y, short z, uint8_t awaitTicks) { u->x = x; u->y = y; u->z = z; - u->awaitTicks = awaitTicks + is_processing_deferred_block_updates; + u->await_ticks = await_ticks + is_processing_deferred_block_updates; + u->update_kind = update_kind; } void handlePlayerAction (PlayerData *player, int action, short x, short y, short z) { @@ -1240,11 +1300,7 @@ void handlePlayerAction (PlayerData *player, int action, short x, short y, short } // Update nearby blocks - deferBlockUpdate(x, y + 1, z, 0); - deferBlockUpdate(x - 1, y, z, 0); - deferBlockUpdate(x + 1, y, z, 0); - deferBlockUpdate(x, y, z - 1, 0); - deferBlockUpdate(x, y, z + 1, 0); + updateXYZNeighbors(x, y, z, UPDATE_NOW); } void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t face) { @@ -1389,12 +1445,8 @@ void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t // Clear item id in slot if amount is zero if (*count == 0) *item = 0; // Send updates - processBlockUpdate(x, y, z, block); - deferBlockUpdate(x, y + 1, z, 0); - deferBlockUpdate(x - 1, y, z, 0); - deferBlockUpdate(x + 1, y, z, 0); - deferBlockUpdate(x, y, z - 1, 0); - deferBlockUpdate(x, y, z + 1, 0); + processBlockUpdate(x, y, z, block, UPDATE_NOW); + updateXYZNeighbors(x, y, z, UPDATE_NOW); } // Sync hotbar contents to player @@ -1719,11 +1771,11 @@ void handleServerTick (int64_t time_since_last_tick) { int next_update_idx = 0; for (int i = 0; i < deferred_block_updates_count; i ++) { DeferredBlockUpdate *u = &deferred_block_updates[i]; - if (u->awaitTicks) { - u->awaitTicks --; + if (u->await_ticks) { + u->await_ticks --; deferred_block_updates[next_update_idx ++] = *u; } else { - processBlockUpdate(u->x, u->y, u->z, getBlockAt(u->x, u->y, u->z)); + processBlockUpdate(u->x, u->y, u->z, getBlockAt(u->x, u->y, u->z), u->update_kind); } } deferred_block_updates_count = next_update_idx;