2026-05-04 12:09:37 +02:00
/ *
* Decompiled with CFR 0 . 152 .
* /
package com.vinlanx.explosionoverhaul ;
import com.google.gson.Gson ;
import com.google.gson.GsonBuilder ;
import com.google.gson.reflect.TypeToken ;
import com.mojang.serialization.Codec ;
import com.mojang.serialization.DataResult ;
import com.mojang.serialization.DynamicOps ;
import com.vinlanx.explosionoverhaul.Config ;
import com.vinlanx.explosionoverhaul.ExplosionOverhaul ;
import com.vinlanx.explosionoverhaul.PacketHandler ;
import com.vinlanx.explosionoverhaul.ScanLoadPromptPacket ;
import com.vinlanx.explosionoverhaul.ScanProgressPacket ;
import com.vinlanx.explosionoverhaul.ScanPromptPacket ;
import java.io.BufferedReader ;
import java.io.BufferedWriter ;
import java.io.ByteArrayInputStream ;
import java.io.DataInput ;
import java.io.DataInputStream ;
import java.io.IOException ;
import java.io.InputStream ;
import java.io.RandomAccessFile ;
import java.io.Reader ;
import java.lang.reflect.Type ;
import java.nio.file.Files ;
import java.nio.file.LinkOption ;
import java.nio.file.OpenOption ;
import java.nio.file.Path ;
import java.nio.file.Paths ;
import java.nio.file.attribute.FileAttribute ;
import java.util.ArrayList ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.IdentityHashMap ;
import java.util.List ;
import java.util.Locale ;
import java.util.Map ;
import java.util.Set ;
import java.util.concurrent.CompletableFuture ;
import java.util.concurrent.ConcurrentHashMap ;
import java.util.concurrent.ConcurrentLinkedQueue ;
import java.util.concurrent.CopyOnWriteArrayList ;
import java.util.concurrent.ExecutorService ;
import java.util.concurrent.Executors ;
import java.util.concurrent.TimeUnit ;
import java.util.concurrent.atomic.AtomicInteger ;
import java.util.concurrent.atomic.AtomicReference ;
import java.util.concurrent.atomic.LongAdder ;
import java.util.function.Supplier ;
import java.util.stream.Collectors ;
import java.util.stream.Stream ;
import java.util.zip.GZIPInputStream ;
import java.util.zip.InflaterInputStream ;
import net.minecraft.core.BlockPos ;
import net.minecraft.core.IdMap ;
import net.minecraft.core.registries.Registries ;
import net.minecraft.nbt.CompoundTag ;
import net.minecraft.nbt.ListTag ;
import net.minecraft.nbt.NbtIo ;
import net.minecraft.nbt.NbtOps ;
import net.minecraft.resources.ResourceKey ;
import net.minecraft.resources.ResourceLocation ;
import net.minecraft.server.MinecraftServer ;
import net.minecraft.server.level.ServerLevel ;
import net.minecraft.server.level.ServerPlayer ;
import net.minecraft.world.entity.player.Player ;
import net.minecraft.world.level.ChunkPos ;
import net.minecraft.world.level.Level ;
import net.minecraft.world.level.LevelAccessor ;
import net.minecraft.world.level.block.Block ;
import net.minecraft.world.level.block.Blocks ;
import net.minecraft.world.level.block.state.BlockState ;
import net.minecraft.world.level.chunk.PalettedContainer ;
import net.minecraft.world.level.storage.LevelResource ;
import net.neoforged.neoforge.event.TickEvent ;
import net.neoforged.neoforge.event.entity.player.PlayerEvent ;
import net.neoforged.neoforge.event.level.BlockEvent ;
import net.neoforged.neoforge.event.level.ChunkEvent ;
import net.neoforged.neoforge.event.server.ServerStartedEvent ;
import net.neoforged.neoforge.event.server.ServerStoppedEvent ;
import net.neoforged.bus.api.SubscribeEvent ;
import net.neoforged.fml.common.Mod ;
import net.neoforged.neoforge.network.PacketDistributor ;
import net.neoforged.neoforge.registries.ForgeRegistries ;
import net.neoforged.neoforge.server.ServerLifecycleHooks ;
@Mod.EventBusSubscriber
public class BlockIndexManager {
private static volatile MinecraftServer currentServer ;
private static final Map < ResourceKey < Level > , ConcurrentHashMap < Long , Set < Long > > > indexByDimension ;
private static final ConcurrentLinkedQueue < ScanTask > chunkScanQueue ;
private static final Map < ResourceKey < Level > , Set < Long > > pendingChunkKeys ;
public static volatile boolean ENABLED ;
private static final boolean SCAN_ONLY_ON_START = true ;
private static volatile boolean INITIAL_SCAN_ENQUEUED ;
private static volatile int totalChunksToScan ;
private static volatile int chunksScanned ;
private static volatile boolean scanningComplete ;
private static volatile boolean abortCurrentScan ;
private static volatile int lampsFound ;
private static volatile int dripstonesFound ;
private static volatile int glassBlocksFound ;
private static volatile ResourceKey < Level > lastScannedDimension ;
private static volatile boolean waitingForUserInput ;
private static volatile ServerLevel pendingScanLevel ;
private static volatile ResourceKey < Level > currentScannedWorld ;
private static volatile boolean isSingleplayer ;
private static volatile boolean serverScanPromptShown ;
private static volatile boolean serverScanDecisionMade ;
private static volatile boolean isRescanMode ;
private static volatile ServerPlayer rescanRequestingPlayer ;
private static volatile boolean userDeclinedScan ;
private static volatile boolean waitingForLoadDecision ;
private static volatile boolean hasSaveFile ;
private static volatile String currentWorldId ;
private static final List < String > DEFAULT_REINFORCED_GLASS_BLACKLIST ;
private static final Path MOD_CONFIG_DIRECTORY ;
private static final Path GLASS_BLACKLIST_FILE ;
private static final CopyOnWriteArrayList < String > GLASS_BLACKLIST ;
private static final AtomicReference < Set < String > > GLASS_BLACKLIST_LOOKUP ;
private static ConcurrentHashMap < Long , Set < Long > > getOrCreateDimIndex ( ServerLevel level ) {
ResourceKey dim = level . m_46472_ ( ) ;
return indexByDimension . computeIfAbsent ( ( ResourceKey < Level > ) dim , k - > new ConcurrentHashMap ( ) ) ;
}
private static Set < Long > getOrCreatePendingSet ( ServerLevel level ) {
ResourceKey dim = level . m_46472_ ( ) ;
return pendingChunkKeys . computeIfAbsent ( ( ResourceKey < Level > ) dim , k - > Collections . newSetFromMap ( new ConcurrentHashMap ( ) ) ) ;
}
private static long chunkKey ( ChunkPos cp ) {
return ( long ) cp . f_45578_ & 0xFFFFFFFFL | ( ( long ) cp . f_45579_ & 0xFFFFFFFFL ) < < 32 ;
}
private static long packPos ( BlockPos pos ) {
return pos . m_121878_ ( ) ;
}
private static BlockPos unpackPos ( long packed ) {
return BlockPos . m_122022_ ( ( long ) packed ) ;
}
public static int getTotalChunksToScan ( ) {
return totalChunksToScan ;
}
public static int getChunksScanned ( ) {
return chunksScanned ;
}
public static float getScanProgress ( ) {
if ( totalChunksToScan = = 0 ) {
return 1 . 0f ;
}
return Math . min ( 1 . 0f , ( float ) chunksScanned / ( float ) totalChunksToScan ) ;
}
public static boolean isScanningComplete ( ) {
return scanningComplete ;
}
public static int getLampsFound ( ) {
return lampsFound ;
}
public static int getDripstonesFound ( ) {
return dripstonesFound ;
}
public static int getGlassBlocksFound ( ) {
return glassBlocksFound ;
}
private static void clearScanDataSilent ( ) {
indexByDimension . clear ( ) ;
chunkScanQueue . clear ( ) ;
pendingChunkKeys . clear ( ) ;
totalChunksToScan = 0 ;
chunksScanned = 0 ;
scanningComplete = false ;
currentScannedWorld = null ;
lastScannedDimension = null ;
lampsFound = 0 ;
dripstonesFound = 0 ;
glassBlocksFound = 0 ;
}
private static void clearScanData ( ) {
BlockIndexManager . clearScanDataSilent ( ) ;
BlockIndexManager . sendProgressUpdate ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: cleared all scan data for world switch " ) ;
}
private static void clearAndSaveData ( ) {
if ( ! indexByDimension . isEmpty ( ) & & scanningComplete ) {
try {
MinecraftServer server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
BlockIndexManager . saveIndexData ( server ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: saved data before clearing " ) ;
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to save data before clearing: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
BlockIndexManager . clearScanData ( ) ;
}
private static void startSingleplayerScan ( ServerLevel level ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: starting singleplayer scan for ALL dimensions " ) ;
BlockIndexManager . clearScanDataSilent ( ) ;
MinecraftServer server = level . m_7654_ ( ) ;
BlockIndexManager . scanExistingChunksOnServer ( server ) ;
}
private static void startServerScan ( MinecraftServer server ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: starting server-wide scan for all dimensions " ) ;
BlockIndexManager . clearScanDataSilent ( ) ;
BlockIndexManager . scanExistingChunksOnServer ( server ) ;
}
private static void scanSingleWorld ( ServerLevel level ) {
try {
chunksScanned = 0 ;
scanningComplete = false ;
lastScannedDimension = level . m_46472_ ( ) ;
lampsFound = 0 ;
dripstonesFound = 0 ;
glassBlocksFound = 0 ;
String dimensionName = level . m_46472_ ( ) . m_135782_ ( ) . toString ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: scanning existing chunks in world: {} " , ( Object ) dimensionName ) ;
Set < ChunkPos > existingChunks = BlockIndexManager . getAllExistingChunks ( level ) ;
totalChunksToScan = existingChunks . size ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: found {} chunks to scan " , ( Object ) totalChunksToScan ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: using safe server-thread scanning for live chunks " ) ;
BlockIndexManager . scanWithSingleThread ( level , existingChunks ) ;
}
catch ( Exception ex ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: failed to start scan: {} " , ( Object ) ex . toString ( ) ) ;
}
}
private static void scanWithSingleThread ( ServerLevel level , Set < ChunkPos > existingChunks ) {
for ( ChunkPos cp : existingChunks ) {
chunkScanQueue . add ( new ScanTask ( ( ResourceKey < Level > ) level . m_46472_ ( ) , cp ) ) ;
}
BlockIndexManager . sendProgressUpdate ( ) ;
}
private static Set < ChunkPos > getAllExistingChunks ( ServerLevel level ) {
Set < ChunkPos > existingChunks ;
block18 : {
existingChunks = Collections . newSetFromMap ( new ConcurrentHashMap ( ) ) ;
try {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: scanning for all existing chunks... " ) ;
MinecraftServer server = level . m_7654_ ( ) ;
String dimPath = level . m_46472_ ( ) . m_135782_ ( ) . m_135815_ ( ) ;
Path worldPath = server . m_129843_ ( LevelResource . f_78182_ ) ;
Path regionPath = " overworld " . equals ( dimPath ) ? worldPath . resolve ( " region " ) : ( " the_nether " . equals ( dimPath ) ? worldPath . resolve ( " DIM-1 " ) . resolve ( " region " ) : ( " the_end " . equals ( dimPath ) ? worldPath . resolve ( " DIM1 " ) . resolve ( " region " ) : worldPath . resolve ( " dimensions " ) . resolve ( level . m_46472_ ( ) . m_135782_ ( ) . m_135827_ ( ) ) . resolve ( level . m_46472_ ( ) . m_135782_ ( ) . m_135815_ ( ) ) . resolve ( " region " ) ) ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: scanning region files in: {} " , ( Object ) regionPath ) ;
if ( Files . exists ( regionPath , new LinkOption [ 0 ] ) ) {
try ( Stream < Path > regionFiles = Files . list ( regionPath ) ; ) {
List mcaFiles = regionFiles . filter ( path - > path . toString ( ) . endsWith ( " .mca " ) ) . collect ( Collectors . toList ( ) ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: found {} .mca files in region directory " , ( Object ) mcaFiles . size ( ) ) ;
mcaFiles . parallelStream ( ) . forEach ( regionFile - > {
block13 : {
try {
int chunksInThisRegion ;
int regionZ ;
int regionX ;
block14 : {
String [ ] parts ;
String fileName = regionFile . getFileName ( ) . toString ( ) ;
if ( ! fileName . startsWith ( " r. " ) | | ! fileName . endsWith ( " .mca " ) | | ( parts = fileName . substring ( 2 , fileName . length ( ) - 4 ) . split ( " \\ . " ) ) . length ! = 2 ) break block13 ;
regionX = Integer . parseInt ( parts [ 0 ] ) ;
regionZ = Integer . parseInt ( parts [ 1 ] ) ;
chunksInThisRegion = 0 ;
try ( InputStream is = Files . newInputStream ( regionFile , new OpenOption [ 0 ] ) ; ) {
byte [ ] header = new byte [ 4096 ] ;
int bytesRead = is . read ( header ) ;
if ( bytesRead < 4096 ) break block14 ;
for ( int i = 0 ; i < 1024 ; + + i ) {
int offset = i * 4 ;
int chunkOffset = ( header [ offset ] & 0xFF ) < < 16 | ( header [ offset + 1 ] & 0xFF ) < < 8 | header [ offset + 2 ] & 0xFF ;
if ( chunkOffset < = 0 ) continue ;
int cx = i % 32 ;
int cz = i / 32 ;
int chunkX = regionX * 32 + cx ;
int chunkZ = regionZ * 32 + cz ;
ChunkPos cp = new ChunkPos ( chunkX , chunkZ ) ;
existingChunks . add ( cp ) ;
+ + chunksInThisRegion ;
}
}
catch ( Exception readEx ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: failed to read region file {}: {} " , ( Object ) fileName , ( Object ) readEx . getMessage ( ) ) ;
for ( int cx = 0 ; cx < 32 ; + + cx ) {
for ( int cz = 0 ; cz < 32 ; + + cz ) {
int chunkX = regionX * 32 + cx ;
int chunkZ = regionZ * 32 + cz ;
ChunkPos cp = new ChunkPos ( chunkX , chunkZ ) ;
existingChunks . add ( cp ) ;
+ + chunksInThisRegion ;
}
}
}
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: region {},{} - found {} existing chunks (read from .mca header) " , new Object [ ] { regionX , regionZ , chunksInThisRegion } ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: failed to parse region file {}: {} " , ( Object ) regionFile . getFileName ( ) , ( Object ) e . getMessage ( ) ) ;
}
}
} ) ;
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: scanned region files and found {} existing chunks " , ( Object ) existingChunks . size ( ) ) ;
break block18 ;
}
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: region directory not found: {} " , ( Object ) regionPath ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . error ( " BlockIndexManager: failed to scan world storage: {} " , ( Object ) e . getMessage ( ) ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: using fallback - estimate chunks from .mca files " ) ;
try {
String dimPath = level . m_46472_ ( ) . m_135782_ ( ) . m_135815_ ( ) ;
MinecraftServer server = level . m_7654_ ( ) ;
Path worldPath = server . m_129843_ ( LevelResource . f_78182_ ) ;
Path regionPath = " overworld " . equals ( dimPath ) ? worldPath . resolve ( " region " ) : ( " the_nether " . equals ( dimPath ) ? worldPath . resolve ( " DIM-1 " ) . resolve ( " region " ) : ( " the_end " . equals ( dimPath ) ? worldPath . resolve ( " DIM1 " ) . resolve ( " region " ) : worldPath . resolve ( " dimensions " ) . resolve ( level . m_46472_ ( ) . m_135782_ ( ) . m_135827_ ( ) ) . resolve ( level . m_46472_ ( ) . m_135782_ ( ) . m_135815_ ( ) ) . resolve ( " region " ) ) ) ;
if ( ! Files . exists ( regionPath , new LinkOption [ 0 ] ) ) break block18 ;
try ( Stream < Path > regionFiles = Files . list ( regionPath ) ; ) {
regionFiles . filter ( path - > path . toString ( ) . endsWith ( " .mca " ) ) . forEach ( regionFile - > {
String [ ] parts ;
String fileName = regionFile . getFileName ( ) . toString ( ) ;
if ( fileName . startsWith ( " r. " ) & & fileName . endsWith ( " .mca " ) & & ( parts = fileName . substring ( 2 , fileName . length ( ) - 4 ) . split ( " \\ . " ) ) . length = = 2 ) {
int regionX = Integer . parseInt ( parts [ 0 ] ) ;
int regionZ = Integer . parseInt ( parts [ 1 ] ) ;
for ( int cx = 0 ; cx < 32 ; + + cx ) {
for ( int cz = 0 ; cz < 32 ; + + cz ) {
int chunkX = regionX * 32 + cx ;
int chunkZ = regionZ * 32 + cz ;
ChunkPos cp = new ChunkPos ( chunkX , chunkZ ) ;
existingChunks . add ( cp ) ;
}
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: fallback added ~1024 chunks from region {},{} " , ( Object ) regionX , ( Object ) regionZ ) ;
}
} ) ;
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: fallback found {} estimated chunks " , ( Object ) existingChunks . size ( ) ) ;
}
catch ( Exception fallbackEx ) {
ExplosionOverhaul . LOGGER . error ( " BlockIndexManager: fallback also failed: {} " , ( Object ) fallbackEx . getMessage ( ) ) ;
}
}
}
return existingChunks ;
}
private static void saveIndexDataAfterScan ( MinecraftServer server ) {
try {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Starting auto-save after scan completion... " ) ;
BlockIndexManager . saveIndexData ( server ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to auto-save scan data: {} " , ( Object ) e . getMessage ( ) ) ;
e . printStackTrace ( ) ;
}
}
private static void sendProgressUpdate ( ) {
try {
ExplosionOverhaul . LOGGER . debug ( " sendProgressUpdate: {}/{} chunks, complete={}, L:{} D:{} G:{} " , new Object [ ] { chunksScanned , totalChunksToScan , scanningComplete , lampsFound , dripstonesFound , glassBlocksFound } ) ;
if ( scanningComplete ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: sending final progress update to OP players - {}/{} chunks, lamps={}, dripstones={}, glass={} " , new Object [ ] { chunksScanned , totalChunksToScan , lampsFound , dripstonesFound , glassBlocksFound } ) ;
}
ScanProgressPacket packet = new ScanProgressPacket ( totalChunksToScan , chunksScanned , scanningComplete , lampsFound , dripstonesFound , glassBlocksFound ) ;
MinecraftServer server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
for ( ServerPlayer player : server . m_6846_ ( ) . m_11314_ ( ) ) {
if ( ! BlockIndexManager . isPlayerAuthorized ( player ) ) continue ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player ) , ( Object ) packet ) ;
}
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send scan progress update: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
private static void sendMultiThreadProgressUpdate ( int currentProcessed , int currentLamps , int currentDripstones , int currentGlass ) {
try {
ScanProgressPacket packet = new ScanProgressPacket ( totalChunksToScan , currentProcessed , false , currentLamps , currentDripstones , currentGlass ) ;
MinecraftServer server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
for ( ServerPlayer player : server . m_6846_ ( ) . m_11314_ ( ) ) {
if ( ! BlockIndexManager . isPlayerAuthorized ( player ) ) continue ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player ) , ( Object ) packet ) ;
}
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send multi-thread progress update: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
private static void sendScanPromptPacket ( ServerPlayer player ) {
try {
ScanPromptPacket packet = new ScanPromptPacket ( true ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player ) , ( Object ) packet ) ;
ExplosionOverhaul . LOGGER . info ( " Sent scan prompt to authorized player: {} " , ( Object ) player . m_7755_ ( ) . getString ( ) ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send scan prompt: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
public static void startManualScan ( ) {
if ( BlockIndexManager . isChunkScanningDisabled ( ) ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: manual scan cancelled - chunk scanning is disabled in config " ) ;
BlockIndexManager . cancelManualScan ( ) ;
return ;
}
if ( waitingForUserInput & & pendingScanLevel ! = null ) {
waitingForUserInput = false ;
serverScanDecisionMade = true ;
userDeclinedScan = false ;
if ( isRescanMode ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: clearing data for rescan as user confirmed " ) ;
BlockIndexManager . clearScanDataSilent ( ) ;
try {
MinecraftServer server = pendingScanLevel . m_7654_ ( ) ;
Path saveFile = BlockIndexManager . getSaveFilePath ( server ) ;
if ( Files . exists ( saveFile , new LinkOption [ 0 ] ) ) {
Files . delete ( saveFile ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: deleted old save file for rescan " ) ;
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to delete old save file: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
if ( isSingleplayer | | isRescanMode ) {
BlockIndexManager . startSingleplayerScan ( pendingScanLevel ) ;
} else {
BlockIndexManager . startServerScan ( pendingScanLevel . m_7654_ ( ) ) ;
}
ServerPlayer requestingPlayer = rescanRequestingPlayer ;
pendingScanLevel = null ;
isRescanMode = false ;
rescanRequestingPlayer = null ;
try {
if ( requestingPlayer ! = null & & requestingPlayer . m_6084_ ( ) ) {
ScanPromptPacket packet = new ScanPromptPacket ( false ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > requestingPlayer ) , ( Object ) packet ) ;
} else {
MinecraftServer server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
for ( ServerPlayer player : server . m_6846_ ( ) . m_11314_ ( ) ) {
if ( ! BlockIndexManager . isPlayerAuthorized ( player ) ) continue ;
ScanPromptPacket packet = new ScanPromptPacket ( false ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player ) , ( Object ) packet ) ;
}
}
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to hide scan prompt: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
}
/ *
* Unable to fully structure code
* /
public static void loadExistingData ( ) {
if ( BlockIndexManager . waitingForLoadDecision & & BlockIndexManager . pendingScanLevel ! = null ) {
BlockIndexManager . waitingForLoadDecision = false ;
BlockIndexManager . serverScanDecisionMade = true ;
server = BlockIndexManager . pendingScanLevel . m_7654_ ( ) ;
loaded = BlockIndexManager . loadIndexData ( server ) ;
if ( loaded ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Successfully loaded existing scan data " ) ;
BlockIndexManager . sendProgressUpdate ( ) ;
try {
currentServer = ServerLifecycleHooks . getCurrentServer ( ) ;
2026-05-04 12:24:39 +02:00
if ( currentServer ! = null ) {
for ( ServerPlayer serverPlayer : currentServer . m_6846_ ( ) . m_11314_ ( ) ) {
if ( ! BlockIndexManager . isPlayerAuthorized ( serverPlayer ) ) continue ;
packet = new ScanLoadPromptPacket ( false ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > serverPlayer ) , ( Object ) packet ) ;
ExplosionOverhaul . LOGGER . debug ( " Hidden load prompt for authorized player: {} " , ( Object ) serverPlayer . m_7755_ ( ) . getString ( ) ) ;
}
2026-05-04 12:09:37 +02:00
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to hide load prompt: {} " , ( Object ) e . getMessage ( ) ) ;
}
} else {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: Failed to load existing data, falling back to new scan " ) ;
BlockIndexManager . startNewScan ( ) ;
}
BlockIndexManager . pendingScanLevel = null ;
}
}
public static void startNewScan ( ) {
if ( waitingForLoadDecision & & pendingScanLevel ! = null ) {
waitingForLoadDecision = false ;
serverScanDecisionMade = true ;
userDeclinedScan = false ;
try {
MinecraftServer server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
for ( ServerPlayer serverPlayer : server . m_6846_ ( ) . m_11314_ ( ) ) {
if ( ! BlockIndexManager . isPlayerAuthorized ( serverPlayer ) ) continue ;
ScanLoadPromptPacket hideLoadPacket = new ScanLoadPromptPacket ( false ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > serverPlayer ) , ( Object ) hideLoadPacket ) ;
ScanPromptPacket showScanPacket = new ScanPromptPacket ( true ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > serverPlayer ) , ( Object ) showScanPacket ) ;
ExplosionOverhaul . LOGGER . debug ( " Switched prompts for authorized player: {} " , ( Object ) serverPlayer . m_7755_ ( ) . getString ( ) ) ;
}
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to switch prompts: {} " , ( Object ) e . getMessage ( ) ) ;
}
waitingForUserInput = true ;
serverScanDecisionMade = false ;
}
}
public static void cancelManualScan ( ) {
if ( waitingForUserInput ) {
waitingForUserInput = false ;
serverScanDecisionMade = true ;
userDeclinedScan = true ;
pendingScanLevel = null ;
ServerPlayer requestingPlayer = rescanRequestingPlayer ;
isRescanMode = false ;
rescanRequestingPlayer = null ;
chunkScanQueue . clear ( ) ;
totalChunksToScan = 0 ;
chunksScanned = 0 ;
scanningComplete = false ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: manual scan cancelled by user - cleared scan queue " ) ;
try {
if ( requestingPlayer ! = null & & requestingPlayer . m_6084_ ( ) ) {
ScanPromptPacket packet = new ScanPromptPacket ( false ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > requestingPlayer ) , ( Object ) packet ) ;
} else {
MinecraftServer server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
for ( ServerPlayer player : server . m_6846_ ( ) . m_11314_ ( ) ) {
if ( ! BlockIndexManager . isPlayerAuthorized ( player ) ) continue ;
ScanPromptPacket packet = new ScanPromptPacket ( false ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player ) , ( Object ) packet ) ;
}
}
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to hide scan prompt: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
}
public static void resetScanState ( ) {
waitingForUserInput = false ;
waitingForLoadDecision = false ;
pendingScanLevel = null ;
serverScanPromptShown = false ;
serverScanDecisionMade = false ;
isRescanMode = false ;
rescanRequestingPlayer = null ;
userDeclinedScan = false ;
hasSaveFile = false ;
INITIAL_SCAN_ENQUEUED = false ;
BlockIndexManager . clearAndSaveData ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: scan state reset by command " ) ;
}
public static void forceServerScan ( MinecraftServer server ) {
BlockIndexManager . resetScanState ( ) ;
BlockIndexManager . startServerScan ( server ) ;
}
public static void forceSingleplayerScan ( ServerLevel level ) {
BlockIndexManager . resetScanState ( ) ;
BlockIndexManager . startSingleplayerScan ( level ) ;
}
public static void showRescanPrompt ( ServerPlayer player ) {
isRescanMode = true ;
waitingForUserInput = true ;
pendingScanLevel = player . m_284548_ ( ) ;
rescanRequestingPlayer = player ;
serverScanPromptShown = true ;
serverScanDecisionMade = false ;
try {
ScanPromptPacket packet = new ScanPromptPacket ( true ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player ) , ( Object ) packet ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: showing rescan prompt to player: {} " , ( Object ) player . m_7755_ ( ) . getString ( ) ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send rescan prompt: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
private static void sendScanPromptPacket ( boolean show ) {
try {
MinecraftServer server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
for ( ServerPlayer player : server . m_6846_ ( ) . m_11314_ ( ) ) {
if ( ! BlockIndexManager . isPlayerAuthorized ( player ) ) continue ;
ScanPromptPacket packet = new ScanPromptPacket ( show ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player ) , ( Object ) packet ) ;
if ( ! show ) continue ;
ExplosionOverhaul . LOGGER . debug ( " Sent scan prompt to authorized player: {} " , ( Object ) player . m_7755_ ( ) . getString ( ) ) ;
}
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send scan prompt packet: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
private static boolean isGlassBlock ( BlockState state ) {
try {
String blockName = ForgeRegistries . BLOCKS . getKey ( ( Object ) state . m_60734_ ( ) ) . toString ( ) . toLowerCase ( Locale . ROOT ) ;
if ( BlockIndexManager . isGlassProtected ( blockName ) ) {
return false ;
}
return blockName . contains ( " glass " ) ;
}
catch ( Exception e ) {
return state . m_60713_ ( Blocks . f_50058_ ) | | state . m_60713_ ( Blocks . f_152498_ ) ;
}
}
private static boolean isLampName ( String name ) {
return " minecraft:redstone_lamp " . equals ( name ) ;
}
private static boolean isDripstoneName ( String name ) {
return " minecraft:pointed_dripstone " . equals ( name ) ;
}
private static boolean isGlassName ( String name ) {
if ( name = = null ) {
return false ;
}
String normalized = name . toLowerCase ( Locale . ROOT ) ;
return normalized . contains ( " glass " ) & & ! BlockIndexManager . isGlassProtected ( normalized ) ;
}
public static boolean isReinforcedGlass ( BlockState state ) {
try {
String blockName = ForgeRegistries . BLOCKS . getKey ( ( Object ) state . m_60734_ ( ) ) . toString ( ) . toLowerCase ( Locale . ROOT ) ;
return BlockIndexManager . isGlassProtected ( blockName ) ;
}
catch ( Exception e ) {
return false ;
}
}
public static List < String > getReinforcedGlassBlacklist ( ) {
return new ArrayList < String > ( GLASS_BLACKLIST ) ;
}
public static List < String > getDefaultReinforcedGlassBlacklist ( ) {
return new ArrayList < String > ( DEFAULT_REINFORCED_GLASS_BLACKLIST ) ;
}
public static void setReinforcedGlassBlacklistFromList ( List < String > list ) {
BlockIndexManager . applyGlassBlacklistList ( list ) ;
BlockIndexManager . saveGlassBlacklistToJson ( ) ;
}
private static void ensureModConfigDirectory ( ) {
try {
Files . createDirectories ( MOD_CONFIG_DIRECTORY , new FileAttribute [ 0 ] ) ;
}
catch ( IOException e ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: failed to create config directory for glass blacklist " , ( Throwable ) e ) ;
}
}
private static void loadGlassBlacklistFromJson ( ) {
BlockIndexManager . ensureModConfigDirectory ( ) ;
Gson gson = new GsonBuilder ( ) . setPrettyPrinting ( ) . create ( ) ;
if ( ! Files . exists ( GLASS_BLACKLIST_FILE , new LinkOption [ 0 ] ) ) {
BlockIndexManager . applyGlassBlacklistList ( DEFAULT_REINFORCED_GLASS_BLACKLIST ) ;
BlockIndexManager . saveGlassBlacklistToJson ( ) ;
return ;
}
try ( BufferedReader reader = Files . newBufferedReader ( GLASS_BLACKLIST_FILE ) ; ) {
List list = ( List ) gson . fromJson ( ( Reader ) reader , new TypeToken < List < String > > ( ) { } . getType ( ) ) ;
if ( list = = null ) {
BlockIndexManager . applyGlassBlacklistList ( DEFAULT_REINFORCED_GLASS_BLACKLIST ) ;
return ;
}
BlockIndexManager . applyGlassBlacklistList ( list ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: failed to load GlassBlacklist.json, falling back to defaults " , ( Throwable ) e ) ;
BlockIndexManager . applyGlassBlacklistList ( DEFAULT_REINFORCED_GLASS_BLACKLIST ) ;
}
}
private static void saveGlassBlacklistToJson ( ) {
BlockIndexManager . ensureModConfigDirectory ( ) ;
Gson gson = new GsonBuilder ( ) . setPrettyPrinting ( ) . create ( ) ;
try ( BufferedWriter writer = Files . newBufferedWriter ( GLASS_BLACKLIST_FILE , new OpenOption [ 0 ] ) ; ) {
gson . toJson ( new ArrayList < String > ( GLASS_BLACKLIST ) , ( Appendable ) writer ) ;
}
catch ( IOException e ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: failed to save GlassBlacklist.json " , ( Throwable ) e ) ;
}
}
private static void applyGlassBlacklistList ( List < String > list ) {
List < String > normalized = BlockIndexManager . normalizeBlacklist ( list ) ;
GLASS_BLACKLIST . clear ( ) ;
GLASS_BLACKLIST . addAll ( normalized ) ;
GLASS_BLACKLIST_LOOKUP . set ( new HashSet < String > ( normalized ) ) ;
}
private static List < String > normalizeBlacklist ( List < String > entries ) {
if ( entries = = null ) {
return new ArrayList < String > ( ) ;
}
ArrayList < String > result = new ArrayList < String > ( ) ;
HashSet < String > seen = new HashSet < String > ( ) ;
for ( String raw : entries ) {
String candidate ;
if ( raw = = null | | ( candidate = raw . trim ( ) . toLowerCase ( Locale . ROOT ) ) . isEmpty ( ) | | ! seen . add ( candidate ) ) continue ;
result . add ( candidate ) ;
}
return result ;
}
private static boolean isGlassProtected ( String name ) {
return GLASS_BLACKLIST_LOOKUP . get ( ) . contains ( name ) ;
}
private static String generateWorldId ( MinecraftServer server ) {
try {
Path worldPath = server . m_129843_ ( LevelResource . f_78182_ ) ;
String worldName = server . m_129910_ ( ) . m_5462_ ( ) ;
String folderName = worldPath . getFileName ( ) . toString ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: World path: {} " , ( Object ) worldPath ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: World name from level.dat: '{}' " , ( Object ) worldName ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Folder name: '{}' " , ( Object ) folderName ) ;
if ( folderName = = null | | folderName . trim ( ) . isEmpty ( ) ) {
folderName = " world " ;
}
if ( worldName = = null | | worldName . trim ( ) . isEmpty ( ) ) {
worldName = " Unknown " ;
}
String combined = folderName + " _ " + worldName ;
String result = combined . replaceAll ( " [^a-zA-Z0-9_-] " , " _ " ) . toLowerCase ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Generated world ID: '{}' " , ( Object ) result ) ;
return result ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to generate world ID, using fallback: {} " , ( Object ) e . getMessage ( ) ) ;
String fallback = " unknown_world_ " + System . currentTimeMillis ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Using fallback ID: '{}' " , ( Object ) fallback ) ;
return fallback ;
}
}
private static Path getSaveFilePath ( MinecraftServer server ) {
String worldId = BlockIndexManager . generateWorldId ( server ) ;
Path configDir = server . m_129843_ ( LevelResource . f_78182_ ) . getParent ( ) . resolve ( " config " ) . resolve ( " explosionoverhaul " ) ;
try {
Files . createDirectories ( configDir , new FileAttribute [ 0 ] ) ;
ExplosionOverhaul . LOGGER . debug ( " BlockIndexManager: Using config directory: {} " , ( Object ) configDir ) ;
}
catch ( IOException e ) {
ExplosionOverhaul . LOGGER . error ( " Failed to create config directory: {} " , ( Object ) e . getMessage ( ) ) ;
}
Path saveFile = configDir . resolve ( " block_index_ " + worldId + " .json " ) ;
ExplosionOverhaul . LOGGER . debug ( " BlockIndexManager: Save file path: {} " , ( Object ) saveFile ) ;
return saveFile ;
}
public static void saveIndexData ( MinecraftServer server ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Attempting to save data... " ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: IndexByDimension size: {} " , ( Object ) indexByDimension . size ( ) ) ;
try {
Path saveFile = BlockIndexManager . getSaveFilePath ( server ) ;
if ( ! scanningComplete ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: Scan incomplete - deleting save file instead of saving corrupted data " ) ;
if ( Files . exists ( saveFile , new LinkOption [ 0 ] ) ) {
Files . delete ( saveFile ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Deleted incomplete save file: {} " , ( Object ) saveFile ) ;
}
return ;
}
if ( indexByDimension . isEmpty ( ) ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: No data to save - index is empty! " ) ;
return ;
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Saving to file: {} " , ( Object ) saveFile ) ;
HashMap saveData = new HashMap ( ) ;
int totalBlocks = 0 ;
for ( Map . Entry < ResourceKey < Level > , ConcurrentHashMap < Long , Set < Long > > > dimEntry : indexByDimension . entrySet ( ) ) {
String dimKey = dimEntry . getKey ( ) . m_135782_ ( ) . toString ( ) ;
HashMap < String , Set < Long > > chunkMap = new HashMap < String , Set < Long > > ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Processing dimension: {} " , ( Object ) dimKey ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Chunks in dimension: {} " , ( Object ) dimEntry . getValue ( ) . size ( ) ) ;
for ( Map . Entry < Long , Set < Long > > chunkEntry : dimEntry . getValue ( ) . entrySet ( ) ) {
Set < Long > blocks = chunkEntry . getValue ( ) ;
if ( blocks . isEmpty ( ) ) continue ;
chunkMap . put ( chunkEntry . getKey ( ) . toString ( ) , blocks ) ;
totalBlocks + = blocks . size ( ) ;
}
if ( chunkMap . isEmpty ( ) ) continue ;
saveData . put ( dimKey , chunkMap ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Added {} chunks with blocks to save data for dimension {} " , ( Object ) chunkMap . size ( ) , ( Object ) dimKey ) ;
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Total blocks to save: {} " , ( Object ) totalBlocks ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Dimensions to save: {} " , ( Object ) saveData . size ( ) ) ;
Gson gson = new GsonBuilder ( ) . setPrettyPrinting ( ) . create ( ) ;
String json = gson . toJson ( saveData ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: JSON length: {} " , ( Object ) json . length ( ) ) ;
if ( json . length ( ) < 20 ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: JSON too small ({} bytes) - skipping save to prevent corruption " , ( Object ) json . length ( ) ) ;
return ;
}
Files . write ( saveFile , json . getBytes ( ) , new OpenOption [ 0 ] ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Successfully saved index data to {} " , ( Object ) saveFile ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . error ( " BlockIndexManager: Failed to save index data: {} " , ( Object ) e . getMessage ( ) ) ;
e . printStackTrace ( ) ;
}
}
public static boolean loadIndexData ( MinecraftServer server ) {
try {
Type saveDataType ;
Path saveFile = BlockIndexManager . getSaveFilePath ( server ) ;
if ( ! Files . exists ( saveFile , new LinkOption [ 0 ] ) ) {
ExplosionOverhaul . LOGGER . debug ( " BlockIndexManager: No save file found at {} " , ( Object ) saveFile ) ;
return false ;
}
Gson gson = new Gson ( ) ;
String json = new String ( Files . readAllBytes ( saveFile ) ) ;
Map saveData = ( Map ) gson . fromJson ( json , saveDataType = new TypeToken < Map < String , Map < String , Set < Long > > > > ( ) { } . getType ( ) ) ;
if ( saveData = = null | | saveData . isEmpty ( ) ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: Save file is empty or corrupted " ) ;
return false ;
}
indexByDimension . clear ( ) ;
for ( Map . Entry dimEntry : saveData . entrySet ( ) ) {
try {
ResourceKey dimKey = ResourceKey . m_135785_ ( ( ResourceKey ) Registries . f_256858_ , ( ResourceLocation ) ResourceLocation . parse ( ( String ) ( ( String ) dimEntry . getKey ( ) ) ) ) ;
ConcurrentHashMap chunkMap = new ConcurrentHashMap ( ) ;
for ( Map . Entry chunkEntry : ( ( Map ) dimEntry . getValue ( ) ) . entrySet ( ) ) {
Long chunkKey = Long . parseLong ( ( String ) chunkEntry . getKey ( ) ) ;
Set blockSet = Collections . newSetFromMap ( new ConcurrentHashMap ( ) ) ;
blockSet . addAll ( ( Collection ) chunkEntry . getValue ( ) ) ;
chunkMap . put ( chunkKey , blockSet ) ;
}
indexByDimension . put ( ( ResourceKey < Level > ) dimKey , chunkMap ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: Failed to load dimension {}: {} " , dimEntry . getKey ( ) , ( Object ) e . getMessage ( ) ) ;
}
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Successfully loaded index data from {} " , ( Object ) saveFile ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Loaded {} dimensions with indexed blocks " , ( Object ) indexByDimension . size ( ) ) ;
scanningComplete = true ;
totalChunksToScan = 0 ;
chunksScanned = 0 ;
lampsFound = 0 ;
dripstonesFound = 0 ;
glassBlocksFound = 0 ;
return true ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . error ( " BlockIndexManager: Failed to load index data: {} " , ( Object ) e . getMessage ( ) ) ;
e . printStackTrace ( ) ;
return false ;
}
}
public static boolean hasSaveFile ( MinecraftServer server ) {
Path saveFile = BlockIndexManager . getSaveFilePath ( server ) ;
return Files . exists ( saveFile , new LinkOption [ 0 ] ) & & Files . isRegularFile ( saveFile , new LinkOption [ 0 ] ) ;
}
private static int getChunksToScanPerTick ( ) {
int cpuPercent = ( Integer ) Config . COMMON . scan . cpuUsagePercent . get ( ) ;
int baseChunks = 500 ;
return Math . max ( 1 , baseChunks * cpuPercent / 100 ) ;
}
private static boolean isChunkScanningDisabled ( ) {
return ( Boolean ) Config . COMMON . scan . enableBlockIndexing . get ( ) = = false ;
}
private static boolean isScanning ( ) {
return ! chunkScanQueue . isEmpty ( ) & & totalChunksToScan > 0 & & ! scanningComplete ;
}
public static boolean isRescanMode ( ) {
return isRescanMode ;
}
public static void register ( ServerLevel level , BlockPos pos , BlockType type ) {
ConcurrentHashMap < Long , Set < Long > > dimIndex = BlockIndexManager . getOrCreateDimIndex ( level ) ;
ChunkPos cp = new ChunkPos ( pos ) ;
long ck = BlockIndexManager . chunkKey ( cp ) ;
dimIndex . computeIfAbsent ( ck , k - > Collections . newSetFromMap ( new ConcurrentHashMap ( ) ) ) . add ( BlockIndexManager . packPos ( pos ) ) ;
}
public static void unregister ( ServerLevel level , BlockPos pos , BlockType type ) {
ChunkPos cp ;
long ck ;
ConcurrentHashMap < Long , Set < Long > > dimIndex = BlockIndexManager . getOrCreateDimIndex ( level ) ;
Set < Long > set = dimIndex . get ( ck = BlockIndexManager . chunkKey ( cp = new ChunkPos ( pos ) ) ) ;
if ( set ! = null ) {
set . remove ( BlockIndexManager . packPos ( pos ) ) ;
if ( set . isEmpty ( ) ) {
dimIndex . remove ( ck ) ;
}
}
}
public static void scanChunk ( ServerLevel level , ChunkPos cp ) {
long ck ;
ConcurrentHashMap < Long , Set < Long > > dimIndex = BlockIndexManager . getOrCreateDimIndex ( level ) ;
if ( dimIndex . containsKey ( ck = BlockIndexManager . chunkKey ( cp ) ) ) {
return ;
}
int baseX = cp . m_45604_ ( ) ;
int baseZ = cp . m_45605_ ( ) ;
Set set = Collections . newSetFromMap ( new ConcurrentHashMap ( ) ) ;
int chunkLamps = 0 ;
int chunkDripstones = 0 ;
int chunkGlass = 0 ;
for ( int lx = 0 ; lx < 16 ; + + lx ) {
for ( int lz = 0 ; lz < 16 ; + + lz ) {
for ( int y = level . m_141937_ ( ) ; y < level . m_151558_ ( ) ; + + y ) {
BlockPos pos = new BlockPos ( baseX + lx , y , baseZ + lz ) ;
if ( ! level . m_46749_ ( pos ) ) continue ;
BlockState state = level . m_8055_ ( pos ) ;
if ( state . m_60713_ ( Blocks . f_50261_ ) ) {
set . add ( BlockIndexManager . packPos ( pos ) ) ;
+ + chunkLamps ;
continue ;
}
if ( state . m_60713_ ( Blocks . f_152588_ ) ) {
set . add ( BlockIndexManager . packPos ( pos ) ) ;
+ + chunkDripstones ;
continue ;
}
if ( ! BlockIndexManager . isGlassBlock ( state ) ) continue ;
set . add ( BlockIndexManager . packPos ( pos ) ) ;
+ + chunkGlass ;
}
}
}
lampsFound + = chunkLamps ;
dripstonesFound + = chunkDripstones ;
glassBlocksFound + = chunkGlass ;
if ( ! set . isEmpty ( ) ) {
dimIndex . put ( ck , set ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Found {} blocks in chunk {} {}: {} " , new Object [ ] { set . size ( ) , cp . f_45578_ , cp . f_45579_ , set . size ( ) > 10 ? set . size ( ) + " blocks " : set } ) ;
}
if ( + + chunksScanned > = totalChunksToScan & & totalChunksToScan > 0 ) {
MinecraftServer server ;
scanningComplete = true ;
int totalBlocksFound = 0 ;
for ( Map . Entry < ResourceKey < Level > , ConcurrentHashMap < Long , Set < Long > > > dimEntry : indexByDimension . entrySet ( ) ) {
for ( Map . Entry < Long , Set < Long > > chunkEntry : dimEntry . getValue ( ) . entrySet ( ) ) {
totalBlocksFound + = chunkEntry . getValue ( ) . size ( ) ;
}
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: chunk scanning completed! Scanned {}/{} chunks " , ( Object ) chunksScanned , ( Object ) totalChunksToScan ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Found {} total blocks across {} dimensions " , ( Object ) totalBlocksFound , ( Object ) indexByDimension . size ( ) ) ;
try {
server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: Starting auto-save after scan completion... " ) ;
BlockIndexManager . saveIndexData ( server ) ;
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to auto-save scan data: {} " , ( Object ) e . getMessage ( ) ) ;
e . printStackTrace ( ) ;
}
try {
server = ServerLifecycleHooks . getCurrentServer ( ) ;
if ( server ! = null ) {
server . execute ( ( ) - > BlockIndexManager . sendProgressUpdate ( ) ) ;
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to enqueue final progress update: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
if ( chunksScanned % 10 = = 0 | | scanningComplete ) {
BlockIndexManager . sendProgressUpdate ( ) ;
}
}
public static void removeChunk ( ServerLevel level , ChunkPos cp ) {
ConcurrentHashMap < Long , Set < Long > > dimIndex = BlockIndexManager . getOrCreateDimIndex ( level ) ;
long ck = BlockIndexManager . chunkKey ( cp ) ;
dimIndex . remove ( ck ) ;
Set < Long > pending = BlockIndexManager . getOrCreatePendingSet ( level ) ;
pending . remove ( ck ) ;
}
public static List < BlockPos > getNearby ( ServerLevel level , BlockPos center , int radius , BlockType type ) {
ArrayList < BlockPos > result = new ArrayList < BlockPos > ( ) ;
int minX = center . m_123341_ ( ) - radius ;
int maxX = center . m_123341_ ( ) + radius ;
int minZ = center . m_123343_ ( ) - radius ;
int maxZ = center . m_123343_ ( ) + radius ;
int minChunkX = minX > > 4 ;
int maxChunkX = maxX > > 4 ;
int minChunkZ = minZ > > 4 ;
int maxChunkZ = maxZ > > 4 ;
ConcurrentHashMap < Long , Set < Long > > dimIndex = BlockIndexManager . getOrCreateDimIndex ( level ) ;
long r2 = ( long ) radius * ( long ) radius ;
for ( int cx = minChunkX ; cx < = maxChunkX ; + + cx ) {
for ( int cz = minChunkZ ; cz < = maxChunkZ ; + + cz ) {
long ck = BlockIndexManager . chunkKey ( new ChunkPos ( cx , cz ) ) ;
Set < Long > set = dimIndex . get ( ck ) ;
if ( set = = null | | set . isEmpty ( ) ) continue ;
for ( long packed : set ) {
long dz ;
long dy ;
BlockPos pos = BlockIndexManager . unpackPos ( packed ) ;
long dx = pos . m_123341_ ( ) - center . m_123341_ ( ) ;
if ( dx * dx + ( dy = ( long ) ( pos . m_123342_ ( ) - center . m_123342_ ( ) ) ) * dy + ( dz = ( long ) ( pos . m_123343_ ( ) - center . m_123343_ ( ) ) ) * dz > r2 ) continue ;
BlockState state = level . m_8055_ ( pos ) ;
if ( type = = BlockType . LAMP & & state . m_60713_ ( Blocks . f_50261_ ) ) {
result . add ( pos ) ;
}
if ( type = = BlockType . DRIPSTONE & & state . m_60713_ ( Blocks . f_152588_ ) ) {
result . add ( pos ) ;
}
if ( type ! = BlockType . GLASS | | ! BlockIndexManager . isGlassBlock ( state ) ) continue ;
result . add ( pos ) ;
}
}
}
return result ;
}
@SubscribeEvent
public static void onChunkLoad ( ChunkEvent . Load event ) {
if ( ! ENABLED ) {
return ;
}
if ( BlockIndexManager . isChunkScanningDisabled ( ) ) {
return ;
}
LevelAccessor levelAccessor = event . getLevel ( ) ;
if ( ! ( levelAccessor instanceof ServerLevel ) ) {
return ;
}
ServerLevel level = ( ServerLevel ) levelAccessor ;
if ( userDeclinedScan ) {
return ;
}
if ( ! isSingleplayer & & ! serverScanDecisionMade ) {
return ;
}
if ( INITIAL_SCAN_ENQUEUED ) {
return ;
}
ResourceKey chunkDimension = level . m_46472_ ( ) ;
if ( lastScannedDimension ! = null & & ! lastScannedDimension . equals ( ( Object ) chunkDimension ) ) {
return ;
}
if ( scanningComplete ) {
return ;
}
long ck = BlockIndexManager . chunkKey ( event . getChunk ( ) . m_7697_ ( ) ) ;
Set < Long > pending = BlockIndexManager . getOrCreatePendingSet ( level ) ;
if ( pending . add ( ck ) ) {
chunkScanQueue . add ( new ScanTask ( ( ResourceKey < Level > ) level . m_46472_ ( ) , event . getChunk ( ) . m_7697_ ( ) ) ) ;
}
}
@SubscribeEvent
public static void onChunkUnload ( ChunkEvent . Unload event ) {
if ( ! ENABLED ) {
return ;
}
LevelAccessor levelAccessor = event . getLevel ( ) ;
if ( ! ( levelAccessor instanceof ServerLevel ) ) {
return ;
}
ServerLevel level = ( ServerLevel ) levelAccessor ;
BlockIndexManager . removeChunk ( level , event . getChunk ( ) . m_7697_ ( ) ) ;
chunkScanQueue . removeIf ( t - > t . dim ( ) . equals ( ( Object ) level . m_46472_ ( ) ) & & t . chunkPos ( ) . equals ( ( Object ) event . getChunk ( ) . m_7697_ ( ) ) ) ;
}
@SubscribeEvent
public static void onBlockPlaced ( BlockEvent . EntityPlaceEvent event ) {
if ( ! ENABLED ) {
return ;
}
LevelAccessor levelAccessor = event . getLevel ( ) ;
if ( ! ( levelAccessor instanceof ServerLevel ) ) {
return ;
}
ServerLevel level = ( ServerLevel ) levelAccessor ;
BlockPos pos = event . getPos ( ) ;
BlockState state = level . m_8055_ ( pos ) ;
if ( state . m_60713_ ( Blocks . f_50261_ ) ) {
BlockIndexManager . register ( level , pos , BlockType . LAMP ) ;
} else if ( state . m_60713_ ( Blocks . f_152588_ ) ) {
BlockIndexManager . register ( level , pos , BlockType . DRIPSTONE ) ;
} else if ( BlockIndexManager . isGlassBlock ( state ) ) {
BlockIndexManager . register ( level , pos , BlockType . GLASS ) ;
}
}
@SubscribeEvent
public static void onBlockBroken ( BlockEvent . BreakEvent event ) {
if ( ! ENABLED ) {
return ;
}
LevelAccessor levelAccessor = event . getLevel ( ) ;
if ( ! ( levelAccessor instanceof ServerLevel ) ) {
return ;
}
ServerLevel level = ( ServerLevel ) levelAccessor ;
BlockPos pos = event . getPos ( ) ;
BlockIndexManager . unregister ( level , pos , BlockType . LAMP ) ;
BlockIndexManager . unregister ( level , pos , BlockType . DRIPSTONE ) ;
BlockIndexManager . unregister ( level , pos , BlockType . GLASS ) ;
}
public static boolean isPlayerAuthorized ( ServerPlayer player ) {
if ( player = = null ) {
return false ;
}
return player . m_20310_ ( 2 ) | | isSingleplayer ;
}
@SubscribeEvent
public static void onPlayerJoinServer ( PlayerEvent . PlayerLoggedInEvent event ) {
if ( ! ENABLED ) {
return ;
}
if ( BlockIndexManager . isChunkScanningDisabled ( ) ) {
return ;
}
Player player = event . getEntity ( ) ;
if ( ! ( player instanceof ServerPlayer ) ) {
return ;
}
ServerPlayer player2 = ( ServerPlayer ) player ;
if ( BlockIndexManager . isPlayerAuthorized ( player2 ) ) {
ServerLevel level = player2 . m_284548_ ( ) ;
MinecraftServer server = level . m_7654_ ( ) ;
boolean saveFileExists = BlockIndexManager . hasSaveFile ( server ) ;
if ( isSingleplayer ) {
if ( ! ( waitingForUserInput | | waitingForLoadDecision | | scanningComplete ) ) {
chunkScanQueue . clear ( ) ;
totalChunksToScan = 0 ;
chunksScanned = 0 ;
}
if ( ! ( waitingForUserInput | | waitingForLoadDecision | | BlockIndexManager . isScanning ( ) | | scanningComplete | | userDeclinedScan ) ) {
pendingScanLevel = level ;
if ( saveFileExists ) {
waitingForLoadDecision = true ;
hasSaveFile = true ;
try {
ScanLoadPromptPacket packet = new ScanLoadPromptPacket ( true ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player2 ) , ( Object ) packet ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: showing load prompt for existing save file in world: {} " , ( Object ) level . m_46472_ ( ) . m_135782_ ( ) ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send load prompt: {} " , ( Object ) e . getMessage ( ) ) ;
}
} else {
waitingForUserInput = true ;
hasSaveFile = false ;
BlockIndexManager . sendScanPromptPacket ( true ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: waiting for user input to start scan in world: {} " , ( Object ) level . m_46472_ ( ) . m_135782_ ( ) ) ;
}
} else if ( BlockIndexManager . isScanning ( ) | | ! chunkScanQueue . isEmpty ( ) & & totalChunksToScan > 0 ) {
try {
ScanProgressPacket packet = new ScanProgressPacket ( totalChunksToScan , chunksScanned , scanningComplete , lampsFound , dripstonesFound , glassBlocksFound ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player2 ) , ( Object ) packet ) ;
ExplosionOverhaul . LOGGER . debug ( " Sent scan progress to rejoining OP player: {}/{} chunks " , ( Object ) chunksScanned , ( Object ) totalChunksToScan ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send scan progress to rejoining OP player: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
} else if ( ! serverScanDecisionMade & & ! userDeclinedScan ) {
if ( ! serverScanPromptShown ) {
serverScanPromptShown = true ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: first OP joined server, waiting for scan decision from: {} " , ( Object ) player2 . m_7755_ ( ) . getString ( ) ) ;
} else {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: additional OP joined server, showing pending decision to: {} " , ( Object ) player2 . m_7755_ ( ) . getString ( ) ) ;
}
pendingScanLevel = level ;
if ( saveFileExists ) {
waitingForLoadDecision = true ;
hasSaveFile = true ;
try {
ScanLoadPromptPacket packet = new ScanLoadPromptPacket ( true ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player2 ) , ( Object ) packet ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: showing load prompt for existing save file to OP: {} " , ( Object ) player2 . m_7755_ ( ) . getString ( ) ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send load prompt: {} " , ( Object ) e . getMessage ( ) ) ;
}
} else {
waitingForUserInput = true ;
hasSaveFile = false ;
try {
ScanPromptPacket packet = new ScanPromptPacket ( true ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player2 ) , ( Object ) packet ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: showing scan prompt to OP: {} " , ( Object ) player2 . m_7755_ ( ) . getString ( ) ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send scan prompt: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
} else if ( totalChunksToScan > 0 & & ! scanningComplete ) {
try {
ScanProgressPacket packet = new ScanProgressPacket ( totalChunksToScan , chunksScanned , scanningComplete , lampsFound , dripstonesFound , glassBlocksFound ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player2 ) , ( Object ) packet ) ;
ExplosionOverhaul . LOGGER . debug ( " Sent scan progress to joining OP player: {}/{} chunks " , ( Object ) chunksScanned , ( Object ) totalChunksToScan ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to send scan progress to joining OP player: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
}
}
@SubscribeEvent
public static void onServerTick ( TickEvent . ServerTickEvent event ) {
ScanTask task ;
if ( event . phase ! = TickEvent . Phase . END ) {
return ;
}
if ( BlockIndexManager . isChunkScanningDisabled ( ) ) {
return ;
}
if ( userDeclinedScan ) {
return ;
}
if ( ! isSingleplayer & & ! serverScanDecisionMade ) {
return ;
}
int chunksToScan = BlockIndexManager . getChunksToScanPerTick ( ) ;
for ( int processed = 0 ; processed < chunksToScan & & ( task = chunkScanQueue . poll ( ) ) ! = null ; + + processed ) {
MinecraftServer server = event . getServer ( ) ;
ServerLevel level = server . m_129880_ ( task . dim ( ) ) ;
if ( level = = null ) continue ;
try {
BlockIndexManager . scanChunk ( level , task . chunkPos ( ) ) ;
long ck = BlockIndexManager . chunkKey ( task . chunkPos ( ) ) ;
Set < Long > pending = BlockIndexManager . getOrCreatePendingSet ( level ) ;
pending . remove ( ck ) ;
continue ;
}
catch ( Exception ex ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: error scanning chunk {} {}: {} " , new Object [ ] { task . chunkPos ( ) . f_45578_ , task . chunkPos ( ) . f_45579_ , ex . toString ( ) } ) ;
}
}
}
@SubscribeEvent
public static void onServerStarted ( ServerStartedEvent event ) {
currentServer = event . getServer ( ) ;
abortCurrentScan = false ;
if ( ! ENABLED ) {
return ;
}
if ( BlockIndexManager . isChunkScanningDisabled ( ) ) {
return ;
}
if ( INITIAL_SCAN_ENQUEUED ) {
return ;
}
MinecraftServer server = event . getServer ( ) ;
isSingleplayer = server . m_129792_ ( ) ;
if ( ! isSingleplayer ) {
userDeclinedScan = false ;
}
serverScanPromptShown = false ;
serverScanDecisionMade = false ;
if ( isSingleplayer ) {
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: singleplayer detected, will scan on world entry " ) ;
INITIAL_SCAN_ENQUEUED = true ;
return ;
}
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: multiplayer server detected, will wait for user decision before scanning " ) ;
INITIAL_SCAN_ENQUEUED = true ;
}
private static void scanExistingChunksOnServer ( MinecraftServer server ) {
try {
int totalChunks ;
chunksScanned = 0 ;
scanningComplete = false ;
lampsFound = 0 ;
dripstonesFound = 0 ;
glassBlocksFound = 0 ;
chunkScanQueue . clear ( ) ;
pendingChunkKeys . clear ( ) ;
HyperScanner scanner = new HyperScanner ( server ) ;
totalChunksToScan = totalChunks = scanner . prepareTasks ( ) ;
INITIAL_SCAN_ENQUEUED = true ;
BlockIndexManager . sendProgressUpdate ( ) ;
CompletableFuture . runAsync ( ( ) - > {
try {
scanner . run ( ) ;
chunksScanned = scanner . getProcessedChunks ( ) ;
lampsFound = scanner . getLampsFound ( ) ;
dripstonesFound = scanner . getDripstonesFound ( ) ;
glassBlocksFound = scanner . getGlassFound ( ) ;
scanningComplete = true ;
server . execute ( ( ) - > {
BlockIndexManager . sendProgressUpdate ( ) ;
BlockIndexManager . saveIndexDataAfterScan ( server ) ;
ExplosionOverhaul . LOGGER . info ( " HyperScanner: background scan complete. Found {} lamps, {} dripstones, {} glass. " , new Object [ ] { lampsFound , dripstonesFound , glassBlocksFound } ) ;
} ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . error ( " HyperScanner: background scan failed " , ( Throwable ) e ) ;
}
} ) ;
}
catch ( Exception ex ) {
ExplosionOverhaul . LOGGER . warn ( " BlockIndexManager: failed to start scan: {} " , ( Object ) ex . toString ( ) ) ;
}
}
@SubscribeEvent
public static void onServerStopped ( ServerStoppedEvent event ) {
if ( ! ENABLED ) {
return ;
}
abortCurrentScan = true ;
if ( ! indexByDimension . isEmpty ( ) & & scanningComplete ) {
try {
BlockIndexManager . saveIndexData ( event . getServer ( ) ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: saved data before server shutdown " ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " Failed to save data on server stop: {} " , ( Object ) e . getMessage ( ) ) ;
}
}
userDeclinedScan = false ;
serverScanPromptShown = false ;
serverScanDecisionMade = false ;
waitingForUserInput = false ;
waitingForLoadDecision = false ;
pendingScanLevel = null ;
isRescanMode = false ;
rescanRequestingPlayer = null ;
hasSaveFile = false ;
currentWorldId = null ;
INITIAL_SCAN_ENQUEUED = false ;
try {
ScanProgressPacket packet = new ScanProgressPacket ( 0 , 0 , true , 0 , 0 , 0 ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . ALL . noArg ( ) , ( Object ) packet ) ;
}
catch ( Exception exception ) {
// empty catch block
}
currentServer = null ;
scanningComplete = false ;
totalChunksToScan = 0 ;
chunksScanned = 0 ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: reset scan state and aborted background tasks on server stop " ) ;
}
@SubscribeEvent
public static void onPlayerLogout ( PlayerEvent . PlayerLoggedOutEvent event ) {
Player player ;
if ( ! ENABLED ) {
return ;
}
if ( isSingleplayer ) {
abortCurrentScan = true ;
waitingForUserInput = false ;
waitingForLoadDecision = false ;
pendingScanLevel = null ;
userDeclinedScan = false ;
hasSaveFile = false ;
scanningComplete = false ;
totalChunksToScan = 0 ;
chunksScanned = 0 ;
BlockIndexManager . clearAndSaveData ( ) ;
ExplosionOverhaul . LOGGER . info ( " BlockIndexManager: saved and cleared scan data on singleplayer logout " ) ;
}
if ( ( player = event . getEntity ( ) ) instanceof ServerPlayer ) {
ServerPlayer player2 = ( ServerPlayer ) player ;
try {
ScanProgressPacket packet = new ScanProgressPacket ( 0 , 0 , true , 0 , 0 , 0 ) ;
PacketHandler . INSTANCE . send ( PacketDistributor . PLAYER . with ( ( ) - > player2 ) , ( Object ) packet ) ;
}
catch ( Exception exception ) {
// empty catch block
}
}
}
private static Path resolveRegionPath ( ServerLevel lvl ) {
try {
Path worldPath = lvl . m_7654_ ( ) . m_129843_ ( LevelResource . f_78182_ ) ;
String dimPath = lvl . m_46472_ ( ) . m_135782_ ( ) . m_135815_ ( ) ;
if ( " overworld " . equals ( dimPath ) ) {
return worldPath . resolve ( " region " ) ;
}
if ( " the_nether " . equals ( dimPath ) ) {
return worldPath . resolve ( " DIM-1 " ) . resolve ( " region " ) ;
}
if ( " the_end " . equals ( dimPath ) ) {
return worldPath . resolve ( " DIM1 " ) . resolve ( " region " ) ;
}
return worldPath . resolve ( " dimensions " ) . resolve ( lvl . m_46472_ ( ) . m_135782_ ( ) . m_135827_ ( ) ) . resolve ( lvl . m_46472_ ( ) . m_135782_ ( ) . m_135815_ ( ) ) . resolve ( " region " ) ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " HyperScanner: failed to resolve region path: {} " , ( Object ) e . getMessage ( ) ) ;
return null ;
}
}
private static /* synthetic */ ServerPlayer lambda$loadExistingData$11 ( ServerPlayer serverPlayer ) {
return serverPlayer ;
}
static {
indexByDimension = new ConcurrentHashMap < ResourceKey < Level > , ConcurrentHashMap < Long , Set < Long > > > ( ) ;
chunkScanQueue = new ConcurrentLinkedQueue ( ) ;
pendingChunkKeys = new ConcurrentHashMap < ResourceKey < Level > , Set < Long > > ( ) ;
ENABLED = true ;
INITIAL_SCAN_ENQUEUED = false ;
totalChunksToScan = 0 ;
chunksScanned = 0 ;
scanningComplete = false ;
abortCurrentScan = false ;
lampsFound = 0 ;
dripstonesFound = 0 ;
glassBlocksFound = 0 ;
lastScannedDimension = null ;
waitingForUserInput = false ;
pendingScanLevel = null ;
currentScannedWorld = null ;
isSingleplayer = false ;
serverScanPromptShown = false ;
serverScanDecisionMade = false ;
isRescanMode = false ;
rescanRequestingPlayer = null ;
userDeclinedScan = false ;
waitingForLoadDecision = false ;
hasSaveFile = false ;
currentWorldId = null ;
DEFAULT_REINFORCED_GLASS_BLACKLIST = List . of ( " securitycraft:reinforced_white_stained_glass " , " securitycraft:reinforced_orange_stained_glass " , " securitycraft:reinforced_magenta_stained_glass " , " securitycraft:reinforced_light_blue_stained_glass " , " securitycraft:reinforced_yellow_stained_glass " , " securitycraft:reinforced_lime_stained_glass " , " securitycraft:reinforced_pink_stained_glass " , " securitycraft:reinforced_gray_stained_glass " , " securitycraft:reinforced_light_gray_stained_glass " , " securitycraft:reinforced_cyan_stained_glass " , " securitycraft:reinforced_purple_stained_glass " , " securitycraft:reinforced_blue_stained_glass " , " securitycraft:reinforced_brown_stained_glass " , " securitycraft:reinforced_green_stained_glass " , " securitycraft:reinforced_red_stained_glass " , " securitycraft:reinforced_black_stained_glass " , " securitycraft:reinforced_white_stained_glass_pane " , " securitycraft:reinforced_orange_stained_glass_pane " , " securitycraft:reinforced_magenta_stained_glass_pane " , " securitycraft:reinforced_light_blue_stained_glass_pane " , " securitycraft:reinforced_yellow_stained_glass_pane " , " securitycraft:reinforced_lime_stained_glass_pane " , " securitycraft:reinforced_pink_stained_glass_pane " , " securitycraft:reinforced_gray_stained_glass_pane " , " securitycraft:reinforced_light_gray_stained_glass_pane " , " securitycraft:reinforced_cyan_stained_glass_pane " , " securitycraft:reinforced_purple_stained_glass_pane " , " securitycraft:reinforced_blue_stained_glass_pane " , " securitycraft:reinforced_brown_stained_glass_pane " , " securitycraft:reinforced_green_stained_glass_pane " , " securitycraft:reinforced_red_stained_glass_pane " , " securitycraft:reinforced_black_stained_glass_pane " , " securitycraft:reinforced_glass " , " securitycraft:reinforced_glass_pane " , " create_tank_defenses:titanium_morning_reinforced_blast_glass " , " mofus_better_end_:reinforced_glass " , " crusty_chunks:reinforced_glass " , " createnuclear:reinforced_glass " , " mekanismgenerators:reactor_glass " , " mekanism:structural_glass " ) ;
MOD_CONFIG_DIRECTORY = Paths . get ( " config " , " explosionoverhaul " ) ;
GLASS_BLACKLIST_FILE = MOD_CONFIG_DIRECTORY . resolve ( " GlassBlacklist.json " ) ;
GLASS_BLACKLIST = new CopyOnWriteArrayList < String > ( DEFAULT_REINFORCED_GLASS_BLACKLIST ) ;
GLASS_BLACKLIST_LOOKUP = new AtomicReference < HashSet < String > > ( new HashSet < String > ( DEFAULT_REINFORCED_GLASS_BLACKLIST ) ) ;
BlockIndexManager . loadGlassBlacklistFromJson ( ) ;
}
private record ScanTask ( ResourceKey < Level > dim , ChunkPos chunkPos ) {
}
public static enum BlockType {
LAMP ,
DRIPSTONE ,
GLASS ;
}
private static class HyperScanner {
private static final int SECTOR_BYTES = 4096 ;
private final MinecraftServer server ;
private final ConcurrentLinkedQueue < RegionTask > tasks = new ConcurrentLinkedQueue ( ) ;
private final AtomicInteger processedChunks = new AtomicInteger ( ) ;
private final LongAdder lamps = new LongAdder ( ) ;
private final LongAdder drips = new LongAdder ( ) ;
private final LongAdder glass = new LongAdder ( ) ;
private final int workerCount ;
HyperScanner ( MinecraftServer server ) {
this . server = server ;
this . workerCount = this . resolveWorkerCount ( ) ;
}
int prepareTasks ( ) {
int total = 0 ;
for ( ServerLevel level : this . server . m_129785_ ( ) ) {
Path regionPath = BlockIndexManager . resolveRegionPath ( level ) ;
if ( regionPath = = null | | ! Files . exists ( regionPath , new LinkOption [ 0 ] ) ) {
ExplosionOverhaul . LOGGER . info ( " HyperScanner: region path not found for {} " , ( Object ) level . m_46472_ ( ) . m_135782_ ( ) ) ;
continue ;
}
try {
Stream < Path > regionFiles = Files . list ( regionPath ) ;
try {
for ( Path regionFile : regionFiles . filter ( p - > p . toString ( ) . endsWith ( " .mca " ) ) . toList ( ) ) {
RegionInfo info = this . parseRegion ( regionFile ) ;
if ( info = = null | | info . chunkCount < = 0 ) continue ;
this . tasks . add ( new RegionTask ( level , regionFile , info . regionX , info . regionZ ) ) ;
total + = info . chunkCount ;
}
}
finally {
if ( regionFiles = = null ) continue ;
regionFiles . close ( ) ;
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " HyperScanner: failed to enumerate region files in {}: {} " , ( Object ) regionPath , ( Object ) e . getMessage ( ) ) ;
}
}
ExplosionOverhaul . LOGGER . info ( " HyperScanner: prepared {} region tasks, total chunks {} " , ( Object ) this . tasks . size ( ) , ( Object ) total ) ;
return total ;
}
void run ( ) {
if ( this . tasks . isEmpty ( ) ) {
ExplosionOverhaul . LOGGER . info ( " HyperScanner: no tasks to run " ) ;
return ;
}
ExecutorService pool = Executors . newFixedThreadPool ( this . workerCount , r - > {
Thread t = new Thread ( r , " EO-HyperScanner " ) ;
t . setDaemon ( true ) ;
return t ;
} ) ;
for ( int i = 0 ; i < this . workerCount ; + + i ) {
pool . execute ( this : : workerLoop ) ;
}
pool . shutdown ( ) ;
try {
while ( ! pool . isTerminated ( ) ) {
pool . awaitTermination ( 1L , TimeUnit . SECONDS ) ;
}
}
catch ( InterruptedException ie ) {
Thread . currentThread ( ) . interrupt ( ) ;
}
}
int getProcessedChunks ( ) {
return this . processedChunks . get ( ) ;
}
int getLampsFound ( ) {
return this . lamps . intValue ( ) ;
}
int getDripstonesFound ( ) {
return this . drips . intValue ( ) ;
}
int getGlassFound ( ) {
return this . glass . intValue ( ) ;
}
private void workerLoop ( ) {
RegionTask task ;
while ( ! abortCurrentScan & & ( task = this . tasks . poll ( ) ) ! = null ) {
this . scanRegion ( task ) ;
}
}
private void scanRegion ( RegionTask task ) {
try ( RandomAccessFile raf = new RandomAccessFile ( task . file ( ) . toFile ( ) , " r " ) ; ) {
byte [ ] header = new byte [ 4096 ] ;
int read = raf . read ( header ) ;
if ( read < 4096 ) {
return ;
}
for ( int i = 0 ; i < 1024 ; + + i ) {
if ( abortCurrentScan ) {
return ;
}
int offsetIndex = i * 4 ;
int chunkOffset = ( header [ offsetIndex ] & 0xFF ) < < 16 | ( header [ offsetIndex + 1 ] & 0xFF ) < < 8 | header [ offsetIndex + 2 ] & 0xFF ;
int sectorCount = header [ offsetIndex + 3 ] & 0xFF ;
if ( chunkOffset = = 0 | | sectorCount = = 0 ) continue ;
int localX = i % 32 ;
int localZ = i / 32 ;
int chunkX = task . regionX ( ) * 32 + localX ;
int chunkZ = task . regionZ ( ) * 32 + localZ ;
try {
this . processChunk ( raf , chunkOffset , sectorCount , new ChunkPos ( chunkX , chunkZ ) , task . level ( ) ) ;
continue ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " HyperScanner: failed to process chunk {} {} in {}: {} " , new Object [ ] { chunkX , chunkZ , task . file ( ) . getFileName ( ) , e . getMessage ( ) } ) ;
}
}
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " HyperScanner: failed to scan region {}: {} " , ( Object ) task . file ( ) . getFileName ( ) , ( Object ) e . getMessage ( ) ) ;
}
}
private void processChunk ( RandomAccessFile raf , int chunkOffsetSectors , int sectorCount , ChunkPos chunkPos , ServerLevel level ) throws IOException {
InflaterInputStream in ;
long filePos = ( long ) chunkOffsetSectors * 4096L ;
raf . seek ( filePos ) ;
int length = raf . readInt ( ) ;
if ( length < = 0 | | length > sectorCount * 4096 ) {
return ;
}
int compressionType = raf . readUnsignedByte ( ) ;
byte [ ] data = new byte [ length - 1 ] ;
raf . readFully ( data ) ;
ByteArrayInputStream base = new ByteArrayInputStream ( data ) ;
if ( compressionType = = 2 ) {
in = new InflaterInputStream ( base ) ;
} else if ( compressionType = = 1 ) {
in = new GZIPInputStream ( base ) ;
} else {
return ;
}
CompoundTag root = NbtIo . m_128928_ ( ( DataInput ) new DataInputStream ( in ) ) ;
if ( root = = null ) {
return ;
}
CompoundTag levelTag = root . m_128425_ ( " Level " , 10 ) ? root . m_128469_ ( " Level " ) : ( root . m_128425_ ( " level " , 10 ) ? root . m_128469_ ( " level " ) : root ) ;
ListTag sections = levelTag . m_128437_ ( " sections " , 10 ) ;
HashSet < Long > found = new HashSet < Long > ( ) ;
for ( int i = 0 ; i < sections . size ( ) ; + + i ) {
CompoundTag section = sections . m_128728_ ( i ) ;
this . processSection ( section , chunkPos , level , found ) ;
}
if ( ! found . isEmpty ( ) ) {
ConcurrentHashMap < Long , Set < Long > > dimIndex = BlockIndexManager . getOrCreateDimIndex ( level ) ;
dimIndex . put ( BlockIndexManager . chunkKey ( chunkPos ) , found ) ;
}
if ( abortCurrentScan ) {
return ;
}
int processed = this . processedChunks . incrementAndGet ( ) ;
this . maybeSendProgress ( processed ) ;
}
private void processSection ( CompoundTag section , ChunkPos chunkPos , ServerLevel level , Set < Long > out ) {
if ( ! section . m_128425_ ( " block_states " , 10 ) ) {
return ;
}
CompoundTag blockStatesTag = section . m_128469_ ( " block_states " ) ;
if ( blockStatesTag . m_128425_ ( " palette " , 9 ) ) {
ListTag palette = blockStatesTag . m_128437_ ( " palette " , 10 ) ;
boolean hasInterestingBlock = false ;
for ( int i = 0 ; i < palette . size ( ) ; + + i ) {
String name = palette . m_128728_ ( i ) . m_128461_ ( " Name " ) ;
if ( ! name . equals ( " minecraft:redstone_lamp " ) & & ! name . equals ( " minecraft:pointed_dripstone " ) & & ( ! name . contains ( " glass " ) | | BlockIndexManager . isGlassProtected ( name ) ) ) continue ;
hasInterestingBlock = true ;
break ;
}
if ( ! hasInterestingBlock ) {
return ;
}
}
try {
DataResult result = PalettedContainer . m_238371_ ( ( IdMap ) Block . f_49791_ , ( Codec ) BlockState . f_61039_ , ( PalettedContainer . Strategy ) PalettedContainer . Strategy . f_188137_ , ( Object ) Blocks . f_50016_ . m_49966_ ( ) ) . parse ( ( DynamicOps ) NbtOps . f_128958_ , ( Object ) blockStatesTag ) ;
result . resultOrPartial ( err - > { } ) . ifPresent ( container - > {
byte sectionY = section . m_128445_ ( " Y " ) ;
int baseY = sectionY * 16 ;
if ( baseY < level . m_141937_ ( ) | | baseY > = level . m_151558_ ( ) ) {
return ;
}
IdentityHashMap < BlockState , Byte > stateTypeCache = new IdentityHashMap < BlockState , Byte > ( ) ;
for ( int y = 0 ; y < 16 ; + + y ) {
int worldY = baseY + y ;
if ( worldY < level . m_141937_ ( ) | | worldY > = level . m_151558_ ( ) ) continue ;
for ( int z = 0 ; z < 16 ; + + z ) {
for ( int x = 0 ; x < 16 ; + + x ) {
BlockState state = ( BlockState ) container . m_63087_ ( x , y , z ) ;
Byte type = ( Byte ) stateTypeCache . get ( state ) ;
if ( type = = null ) {
type = state . m_60713_ ( Blocks . f_50261_ ) ? Byte . valueOf ( ( byte ) 1 ) : ( state . m_60713_ ( Blocks . f_152588_ ) ? Byte . valueOf ( ( byte ) 2 ) : ( BlockIndexManager . isGlassBlock ( state ) ? Byte . valueOf ( ( byte ) 3 ) : Byte . valueOf ( ( byte ) 0 ) ) ) ;
stateTypeCache . put ( state , type ) ;
}
if ( type = = 0 ) continue ;
int worldX = chunkPos . m_45604_ ( ) + x ;
int worldZ = chunkPos . m_45605_ ( ) + z ;
out . add ( BlockPos . m_121882_ ( ( int ) worldX , ( int ) worldY , ( int ) worldZ ) ) ;
if ( type = = 1 ) {
this . lamps . increment ( ) ;
continue ;
}
if ( type = = 2 ) {
this . drips . increment ( ) ;
continue ;
}
if ( type ! = 3 ) continue ;
this . glass . increment ( ) ;
}
}
}
} ) ;
}
catch ( Exception exception ) {
// empty catch block
}
}
private void maybeSendProgress ( int processed ) {
if ( processed % 50 = = 0 | | processed = = totalChunksToScan ) {
int l = this . lamps . intValue ( ) ;
int d = this . drips . intValue ( ) ;
int g = this . glass . intValue ( ) ;
this . server . execute ( ( ) - > BlockIndexManager . sendMultiThreadProgressUpdate ( processed , l , d , g ) ) ;
}
}
private RegionInfo parseRegion ( Path regionFile ) {
int regionZ ;
int regionX ;
String fileName = regionFile . getFileName ( ) . toString ( ) ;
if ( ! fileName . startsWith ( " r. " ) | | ! fileName . endsWith ( " .mca " ) ) {
return null ;
}
String [ ] parts = fileName . substring ( 2 , fileName . length ( ) - 4 ) . split ( " \\ . " ) ;
if ( parts . length ! = 2 ) {
return null ;
}
try {
regionX = Integer . parseInt ( parts [ 0 ] ) ;
regionZ = Integer . parseInt ( parts [ 1 ] ) ;
}
catch ( NumberFormatException nfe ) {
return null ;
}
int chunkCount = this . countChunksInRegion ( regionFile ) ;
return new RegionInfo ( regionX , regionZ , chunkCount ) ;
}
/ *
* Enabled aggressive block sorting
* Enabled unnecessary exception pruning
* Enabled aggressive exception aggregation
* /
private int countChunksInRegion ( Path regionFile ) {
int count = 0 ;
try ( RandomAccessFile raf = new RandomAccessFile ( regionFile . toFile ( ) , " r " ) ; ) {
byte [ ] header = new byte [ 4096 ] ;
int read = raf . read ( header ) ;
if ( read < 4096 ) {
int n = 0 ;
return n ;
}
int i = 0 ;
while ( i < 1024 ) {
int offset = i * 4 ;
int chunkOffset = ( header [ offset ] & 0xFF ) < < 16 | ( header [ offset + 1 ] & 0xFF ) < < 8 | header [ offset + 2 ] & 0xFF ;
int sectorCount = header [ offset + 3 ] & 0xFF ;
if ( chunkOffset > 0 & & sectorCount > 0 ) {
+ + count ;
}
+ + i ;
}
return count ;
}
catch ( Exception e ) {
ExplosionOverhaul . LOGGER . warn ( " HyperScanner: failed to read region header {}: {} " , ( Object ) regionFile . getFileName ( ) , ( Object ) e . getMessage ( ) ) ;
}
return count ;
}
private int resolveWorkerCount ( ) {
int configured = ( Integer ) Config . COMMON . scan . maxScanThreads . get ( ) ;
int cores = Runtime . getRuntime ( ) . availableProcessors ( ) ;
if ( configured < = 0 ) {
return Math . max ( 1 , cores ) ;
}
return Math . max ( 1 , Math . min ( configured , cores ) ) ;
}
}
private record RegionInfo ( int regionX , int regionZ , int chunkCount ) {
}
private record RegionTask ( ServerLevel level , Path file , int regionX , int regionZ ) {
}
}