package dev.zontreck.fire.events; import dev.zontreck.fire.FireMod; import dev.zontreck.fire.config.server.FireServerConfig; import dev.zontreck.fire.data.BlockSnapshot; import dev.zontreck.libzontreck.util.ServerUtilities; import dev.zontreck.libzontreck.vectors.WorldPosition; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.Container; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.ChestBlock; import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.level.BlockEvent; import net.minecraftforge.event.server.ServerStoppingEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import java.time.Instant; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; public class EventHandler { static { MinecraftForge.EVENT_BUS.register(EventHandler.class); } private static final AtomicLong ALIVE_TICKS = new AtomicLong(0); @SubscribeEvent public void onBlockBreak(BlockEvent.NeighborNotifyEvent event) { if (ServerUtilities.isClient()) return; if(!FireMod.ENABLED) { return; } ServerLevel world = (ServerLevel) event.getLevel(); if(FireServerConfig.restore.blacklistedDimensions.contains(WorldPosition.getDim(world))) { //FireMod.LOGGER.info("Blacklisted dimension, ignoring"); return; // Ignore it. } BlockPos pos = event.getPos(); BlockState blockState = event.getState(); boolean bBurning=false; if(blockState.isBurning(world, pos)) { bBurning=true; } if(!bBurning)return; for(Direction dir : event.getNotifiedSides()) { switch(dir) { case UP -> { pos = event.getPos().above(); break; } case DOWN -> { pos = event.getPos().below(); break; } case EAST -> { pos = event.getPos().east(); break; } case WEST -> { pos = event.getPos().west(); break; } case NORTH -> { pos = event.getPos().north(); break; } case SOUTH -> { pos = event.getPos().south(); break; } } blockState = world.getBlockState(pos); if (blockState.is(Blocks.FIRE) || blockState.isAir() || !blockState.isFlammable(world, pos, dir)) { continue; // DO NOT CACHE FIRE OR AIR BLOCKS, OR NON-FLAMMABLE BLOCKS } // Do not cache containers and their inventories. This could be used as a duplication exploit in that case BlockEntity entity = world.getBlockEntity(pos); if(entity instanceof BaseContainerBlockEntity && FireServerConfig.restore.restoreContainers) { if(blockState.isFlammable(world, pos, dir)) { // We're caching it, remove it from the world immediately to prevent a dupe. It will be restored afterwards world.setBlock(pos, Blocks.AIR.defaultBlockState(), ChestBlock.UPDATE_ALL); } } //FireMod.LOGGER.info("Fire detected"); FireMod.blockRestoreData.add(new BlockSnapshot(world, pos)); } FireMod.blockRestoreData.commit(); } @SubscribeEvent public void onServerTick(TickEvent.LevelTickEvent event) { if(ServerUtilities.isClient()) return; if(!FireMod.ENABLED) { return; } if (event.phase == TickEvent.Phase.END) { if(ALIVE_TICKS.getAndIncrement() % 10 == 0) { if(restoreBurnedBlocks()) FireMod.blockRestoreData.commit(); } } } @SubscribeEvent public void onServerStopping(ServerStoppingEvent event) { if(ServerUtilities.isClient()) return; FireMod.ENABLED=false; FireMod.blockRestoreData.commit(); } public boolean restoreBurnedBlocks() { long currentTime = Instant.now().getEpochSecond(); Lock lock = FireMod.blockRestoreData.acquireWriteLock(); lock.lock(); try { // Restore one block per tick, after it hasnt burned for long enough Iterator> it = FireMod.blockRestoreData.snapshots.entrySet().iterator(); while(it.hasNext()) { var entry = it.next(); if((currentTime - entry.getValue().burnTime) >= FireServerConfig.restore.delayForRestore + (FireMod.blockRestoreData.snapshots.size())) { it.remove(); entry.getValue().restore(); //FireMod.LOGGER.info("Restoring burned block"); return true; } } }finally { lock.unlock(); } return false; } }