From e91e5be732477d6dbae43f84d832198993a85605 Mon Sep 17 00:00:00 2001 From: sylvessa <225480449+sylvessa@users.noreply.github.com> Date: Sun, 19 Apr 2026 08:41:54 -0500 Subject: [PATCH] BlockFadeEvent --- .../Event/Block/BlockFadeEvent.cs | 41 +++++++++++++++++++ .../FourKitHost.Events.cs | 19 +++++++++ .../docs/usage-of-all-events.md | 36 ++++++++++++++++ Minecraft.Server/FourKitBridge.cpp | 10 +++++ Minecraft.Server/FourKitBridge.h | 1 + Minecraft.Server/cmake/sources/Common.cmake | 5 +++ Minecraft.World/FarmTile.cpp | 8 ++++ Minecraft.World/GrassTile.cpp | 4 ++ Minecraft.World/IceTile.cpp | 12 ++++++ Minecraft.World/MycelTile.cpp | 8 ++++ Minecraft.World/RedStoneOreTile.cpp | 8 ++++ Minecraft.World/SnowTile.cpp | 11 ++++- 12 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 Minecraft.Server.FourKit/Event/Block/BlockFadeEvent.cs diff --git a/Minecraft.Server.FourKit/Event/Block/BlockFadeEvent.cs b/Minecraft.Server.FourKit/Event/Block/BlockFadeEvent.cs new file mode 100644 index 000000000..edb45ae5c --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Block/BlockFadeEvent.cs @@ -0,0 +1,41 @@ +namespace Minecraft.Server.FourKit.Event.Block; + +using Minecraft.Server.FourKit.Block; + +/// +/// Called when a block fades, melts or disappears based on world conditions. +/// +/// Examples: +/// +/// Snow melting due to being near a light source. +/// Ice melting due to being near a light source. +/// +/// +/// If a Block Fade event is cancelled, the block will not fade, melt or disappear. +/// +public class BlockFadeEvent : BlockEvent, Cancellable +{ + private readonly BlockState _newState; + private bool _cancel; + + internal BlockFadeEvent(Block block, BlockState newState) : base(block) + { + _newState = newState; + _cancel = false; + } + + /// + /// Gets the state of the block that will be fading, melting or disappearing. + /// + /// The block state of the block that will be fading, melting or disappearing. + public BlockState getNewState() => _newState; + + /// + public bool isCancelled() => _cancel; + + /// + public void setCancelled(bool cancel) + { + _cancel = cancel; + } +} diff --git a/Minecraft.Server.FourKit/FourKitHost.Events.cs b/Minecraft.Server.FourKit/FourKitHost.Events.cs index f6c6ef253..baa9543c2 100644 --- a/Minecraft.Server.FourKit/FourKitHost.Events.cs +++ b/Minecraft.Server.FourKit/FourKitHost.Events.cs @@ -1187,6 +1187,25 @@ public static partial class FourKitHost } } + [UnmanagedCallersOnly] + public static int FireBlockFade(int dimId, int x, int y, int z, int newTileId, int newTileData) + { + try + { + var world = FourKit.getWorld(dimId); + var block = new Block.Block(world, x, y, z); + var newState = new Block.BlockState(world, x, y, z, newTileId, newTileData); + var evt = new Event.Block.BlockFadeEvent(block, newState); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FireBlockFade error: {ex}"); + return 0; + } + } + [UnmanagedCallersOnly] public static int FirePistonExtend(int dimId, int x, int y, int z, int direction, int length) { diff --git a/Minecraft.Server.FourKit/docs/usage-of-all-events.md b/Minecraft.Server.FourKit/docs/usage-of-all-events.md index 234ede222..6004e850b 100644 --- a/Minecraft.Server.FourKit/docs/usage-of-all-events.md +++ b/Minecraft.Server.FourKit/docs/usage-of-all-events.md @@ -896,6 +896,42 @@ public void onSpread(BlockSpreadEvent e) --- +@subsection blockfadeevent BlockFadeEvent + +\ref Minecraft.Server.FourKit.Event.Block.BlockFadeEvent "BlockFadeEvent" is fired when a block fades, melts or disappears based on world conditions. Examples include snow melting near a light source and ice melting near a light source. If cancelled, the block will not fade, melt or disappear. + +```csharp +[EventHandler] +public void onFade(BlockFadeEvent e) +{ + // prevent ice from melting + if (e.getBlock().getType() == Material.ICE) + { + e.setCancelled(true); + } +} +``` + +```csharp +[EventHandler] +public void onFade(BlockFadeEvent e) +{ + // log what a block will become when it fades + Console.WriteLine($"{e.getBlock().getType()} at {e.getBlock().getX()},{e.getBlock().getY()},{e.getBlock().getZ()} is fading into {e.getNewState().getType()}"); +} +``` + +| Method | Description | +|--------|-------------| +| `getBlock()` | The `Block` that is fading, melting or disappearing. | +| `getNewState()` | The `BlockState` representing what the block will become after fading. | +| `isCancelled()` | Whether the fade is cancelled. | +| `setCancelled(bool)` | Cancel or allow the fade. | + +> **Cancellable:** Yes + +--- + @subsection blockburnevent BlockBurnEvent \ref Minecraft.Server.FourKit.Event.Block.BlockBurnEvent "BlockBurnEvent" is fired when a block is destroyed as a result of being burnt by fire. diff --git a/Minecraft.Server/FourKitBridge.cpp b/Minecraft.Server/FourKitBridge.cpp index e7f64cd19..f1a166d53 100644 --- a/Minecraft.Server/FourKitBridge.cpp +++ b/Minecraft.Server/FourKitBridge.cpp @@ -99,6 +99,7 @@ typedef int(__stdcall *fn_fire_block_grow)(int dimId, int x, int y, int z, int n typedef int(__stdcall *fn_fire_block_form)(int dimId, int x, int y, int z, int newTileId, int newTileData); typedef int(__stdcall *fn_fire_block_burn)(int dimId, int x, int y, int z); typedef int(__stdcall *fn_fire_block_spread)(int dimId, int x, int y, int z, int srcX, int srcY, int srcZ, int newTileId, int newTileData); +typedef int(__stdcall *fn_fire_block_fade)(int dimId, int x, int y, int z, int newTileId, int newTileData); typedef int(__stdcall *fn_fire_piston_extend)(int dimId, int x, int y, int z, int direction, int length); typedef int(__stdcall *fn_fire_piston_retract)(int dimId, int x, int y, int z, int direction); typedef int(__stdcall *fn_fire_command_preprocess)(int entityId, const char *cmdUtf8, int cmdByteLen, char *outBuf, int outBufSize, int *outLen); @@ -161,6 +162,7 @@ static fn_fire_block_grow s_managedFireBlockGrow = nullptr; static fn_fire_block_form s_managedFireBlockForm = nullptr; static fn_fire_block_burn s_managedFireBlockBurn = nullptr; static fn_fire_block_spread s_managedFireBlockSpread = nullptr; +static fn_fire_block_fade s_managedFireBlockFade = nullptr; static fn_fire_piston_extend s_managedFirePistonExtend = nullptr; static fn_fire_piston_retract s_managedFirePistonRetract = nullptr; static fn_fire_command_preprocess s_managedFireCommandPreprocess = nullptr; @@ -244,6 +246,7 @@ void Initialize() {L"FireBlockForm", (void **)&s_managedFireBlockForm}, {L"FireBlockBurn", (void **)&s_managedFireBlockBurn}, {L"FireBlockSpread", (void **)&s_managedFireBlockSpread}, + {L"FireBlockFade", (void **)&s_managedFireBlockFade}, {L"FirePistonExtend", (void **)&s_managedFirePistonExtend}, {L"FirePistonRetract", (void **)&s_managedFirePistonRetract}, {L"FireCommandPreprocess", (void **)&s_managedFireCommandPreprocess}, @@ -1064,4 +1067,11 @@ bool FireChunkUnload(int dimId, int chunkX, int chunkZ) return false; return s_managedFireChunkUnload(dimId, chunkX, chunkZ) != 0; } + +bool FireBlockFade(int dimId, int x, int y, int z, int newTileId, int newTileData) +{ + if (!s_initialized || !s_managedFireBlockFade) + return false; + return s_managedFireBlockFade(dimId, x, y, z, newTileId, newTileData) != 0; +} } // namespace FourKitBridge diff --git a/Minecraft.Server/FourKitBridge.h b/Minecraft.Server/FourKitBridge.h index eafb3c827..754807700 100644 --- a/Minecraft.Server/FourKitBridge.h +++ b/Minecraft.Server/FourKitBridge.h @@ -88,6 +88,7 @@ namespace FourKitBridge bool FireBlockForm(int dimId, int x, int y, int z, int newTileId, int newTileData); bool FireBlockBurn(int dimId, int x, int y, int z); bool FireBlockSpread(int dimId, int x, int y, int z, int srcX, int srcY, int srcZ, int newTileId, int newTileData); + bool FireBlockFade(int dimId, int x, int y, int z, int newTileId, int newTileData); bool FirePistonExtend(int dimId, int x, int y, int z, int direction, int length); bool FirePistonRetract(int dimId, int x, int y, int z, int direction); bool FireCommandPreprocess(int entityId, const std::wstring &commandLine, std::wstring &outCommand); diff --git a/Minecraft.Server/cmake/sources/Common.cmake b/Minecraft.Server/cmake/sources/Common.cmake index 47917e606..6f4be05f9 100644 --- a/Minecraft.Server/cmake/sources/Common.cmake +++ b/Minecraft.Server/cmake/sources/Common.cmake @@ -519,8 +519,13 @@ set(_MINECRAFT_SERVER_COMMON_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/CropTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/FireTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/GrassTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/MycelTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/FarmTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/RedstoneOreTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/PistonBaseTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/ReedTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/SnowTile.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/IceTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/StemTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/NetherWartTile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/LiquidTileDynamic.cpp" diff --git a/Minecraft.World/FarmTile.cpp b/Minecraft.World/FarmTile.cpp index 39bf685f5..f15893dab 100644 --- a/Minecraft.World/FarmTile.cpp +++ b/Minecraft.World/FarmTile.cpp @@ -4,6 +4,10 @@ #include "net.minecraft.h" #include "net.minecraft.world.h" #include "FarmTile.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../Minecraft.Server/FourKitBridge.h" +#include "Dimension.h" +#endif FarmTile::FarmTile(int id) : Tile(id, Material::dirt,isSolidRender()) @@ -70,6 +74,10 @@ void FarmTile::tick(Level *level, int x, int y, int z, Random *random) { if (!isUnderCrops(level, x, y, z)) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockFade(level->dimension->id, x, y, z, Tile::dirt_Id, 0)) + return; +#endif level->setTileAndUpdate(x, y, z, Tile::dirt_Id); } } diff --git a/Minecraft.World/GrassTile.cpp b/Minecraft.World/GrassTile.cpp index afa5ed26a..08d5ce8e4 100644 --- a/Minecraft.World/GrassTile.cpp +++ b/Minecraft.World/GrassTile.cpp @@ -98,6 +98,10 @@ void GrassTile::tick(Level *level, int x, int y, int z, Random *random) if (level->getRawBrightness(x, y + 1, z) < MIN_BRIGHTNESS && Tile::lightBlock[level->getTile(x, y + 1, z)] > 2) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockFade(level->dimension->id, x, y, z, Tile::dirt_Id, 0)) + return; +#endif level->setTileAndUpdate(x, y, z, Tile::dirt_Id); } else diff --git a/Minecraft.World/IceTile.cpp b/Minecraft.World/IceTile.cpp index 60e162916..0cf11f09c 100644 --- a/Minecraft.World/IceTile.cpp +++ b/Minecraft.World/IceTile.cpp @@ -5,6 +5,10 @@ #include "net.minecraft.world.food.h" #include "net.minecraft.stats.h" #include "IceTile.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../Minecraft.Server/FourKitBridge.h" +#include "Dimension.h" +#endif IceTile::IceTile(int id) : HalfTransparentTile(id, L"ice", Material::ice, false) { @@ -64,9 +68,17 @@ void IceTile::tick(Level *level, int x, int y, int z, Random *random) { if (level->dimension->ultraWarm) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockFade(level->dimension->id, x, y, z, 0, 0)) + return; +#endif level->removeTile(x, y, z); return; } +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockFade(level->dimension->id, x, y, z, Tile::calmWater_Id, 0)) + return; +#endif this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); level->setTileAndUpdate(x, y, z, Tile::calmWater_Id); } diff --git a/Minecraft.World/MycelTile.cpp b/Minecraft.World/MycelTile.cpp index 76b742e34..fb6d73ae6 100644 --- a/Minecraft.World/MycelTile.cpp +++ b/Minecraft.World/MycelTile.cpp @@ -3,6 +3,10 @@ #include "net.minecraft.world.level.h" #include "net.minecraft.h" #include "net.minecraft.world.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../Minecraft.Server/FourKitBridge.h" +#include "Dimension.h" +#endif MycelTile::MycelTile(int id) : Tile(id, Material::grass) { @@ -40,6 +44,10 @@ void MycelTile::tick(Level *level, int x, int y, int z, Random *random) if (level->getRawBrightness(x, y + 1, z) < MIN_BRIGHTNESS && Tile::lightBlock[level->getTile(x, y + 1, z)] > 2) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockFade(level->dimension->id, x, y, z, Tile::dirt_Id, 0)) + return; +#endif level->setTileAndUpdate(x, y, z, Tile::dirt_Id); } else diff --git a/Minecraft.World/RedStoneOreTile.cpp b/Minecraft.World/RedStoneOreTile.cpp index 407da2cc3..9711f260c 100644 --- a/Minecraft.World/RedStoneOreTile.cpp +++ b/Minecraft.World/RedStoneOreTile.cpp @@ -2,6 +2,10 @@ #include "net.minecraft.world.level.h" #include "RedStoneOreTile.h" #include "net.minecraft.world.item.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../Minecraft.Server/FourKitBridge.h" +#include "Dimension.h" +#endif RedStoneOreTile::RedStoneOreTile(int id, bool lit) : Tile(id, Material::stone) { @@ -56,6 +60,10 @@ void RedStoneOreTile::tick(Level *level, int x, int y, int z, Random* random) { if (id == Tile::redStoneOre_lit_Id) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockFade(level->dimension->id, x, y, z, Tile::redStoneOre_Id, 0)) + return; +#endif level->setTileAndUpdate(x, y, z, Tile::redStoneOre_Id); } } diff --git a/Minecraft.World/SnowTile.cpp b/Minecraft.World/SnowTile.cpp index e73f5ceb3..2a86ea3ab 100644 --- a/Minecraft.World/SnowTile.cpp +++ b/Minecraft.World/SnowTile.cpp @@ -4,6 +4,11 @@ #include "net.minecraft.world.item.h" #include "SnowTile.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../Minecraft.Server/FourKitBridge.h" +#include "Dimension.h" +#endif + SnowTile::SnowTile(int id) : Tile(id, Material::snow) { setTicking(true); @@ -23,7 +28,11 @@ int SnowTile::getResourceCount(Random *random) void SnowTile::tick(Level *level, int x, int y, int z, Random *random) { if (level->getBrightness(LightLayer::Block, x, y, z) > 11) - { + { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (FourKitBridge::FireBlockFade(level->dimension->id, x, y, z, 0, 0)) + return; +#endif this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); level->removeTile(x, y, z); }