2026-05-04 12:09:37 +02:00
/ *
* Decompiled with CFR 0 . 152 .
* /
package com.vinlanx.explosionoverhaul ;
2026-05-04 13:06:40 +02:00
import net.minecraft.core.registries.BuiltInRegistries ;
2026-05-04 12:09:37 +02:00
import com.google.gson.Gson ;
import com.google.gson.GsonBuilder ;
import com.google.gson.reflect.TypeToken ;
import com.mojang.blaze3d.vertex.DefaultVertexFormat ;
import com.mojang.logging.LogUtils ;
import com.vinlanx.explosionoverhaul.AmbientExplosionManager ;
import com.vinlanx.explosionoverhaul.AsyncCraterManager ;
import com.vinlanx.explosionoverhaul.BlockIndexManager ;
import com.vinlanx.explosionoverhaul.ClientSoundHandler ;
import com.vinlanx.explosionoverhaul.Config ;
import com.vinlanx.explosionoverhaul.CustomGlowParticleOptions ;
import com.vinlanx.explosionoverhaul.DripstoneEffects ;
import com.vinlanx.explosionoverhaul.GlassBreakingEffects ;
import com.vinlanx.explosionoverhaul.ModBlockEntities ;
import com.vinlanx.explosionoverhaul.ModBlocks ;
import com.vinlanx.explosionoverhaul.ModCreativeTabs ;
import com.vinlanx.explosionoverhaul.ModItems ;
import com.vinlanx.explosionoverhaul.ModParticles ;
import com.vinlanx.explosionoverhaul.ModSounds ;
import com.vinlanx.explosionoverhaul.PacketHandler ;
import com.vinlanx.explosionoverhaul.PlasmaParticleOptions ;
import com.vinlanx.explosionoverhaul.RedstoneLampEffects ;
import com.vinlanx.explosionoverhaul.ScanInfoHUD ;
import com.vinlanx.explosionoverhaul.ScanKeyHandler ;
import com.vinlanx.explosionoverhaul.ScanLoadInfoHUD ;
import com.vinlanx.explosionoverhaul.ScanLoadPromptHUD ;
import com.vinlanx.explosionoverhaul.ScanProgressHUD ;
import com.vinlanx.explosionoverhaul.ScanPromptHUD ;
import com.vinlanx.explosionoverhaul.ServerExplosionHandler ;
import com.vinlanx.explosionoverhaul.VinlanxTheLightRenderer ;
import com.vinlanx.explosionoverhaul.client.Blur ;
import com.vinlanx.explosionoverhaul.client.ClientEffects ;
import com.vinlanx.explosionoverhaul.client.ConcussionAudioEffect ;
import com.vinlanx.explosionoverhaul.client.CustomGlowParticleProvider ;
import com.vinlanx.explosionoverhaul.client.ExplosionTextureManager ;
import com.vinlanx.explosionoverhaul.client.ExplosionWindController ;
import com.vinlanx.explosionoverhaul.client.FirstTimeSetupHandler ;
import com.vinlanx.explosionoverhaul.client.IntroMusicTickHandler ;
import com.vinlanx.explosionoverhaul.client.LineSparkParticleProvider ;
import com.vinlanx.explosionoverhaul.client.ModConfigScreen ;
import com.vinlanx.explosionoverhaul.client.ModKeyMappings ;
import com.vinlanx.explosionoverhaul.client.PlasmaParticle ;
import com.vinlanx.explosionoverhaul.client.SmokeParticle ;
import com.vinlanx.explosionoverhaul.client.SoundPhysicsCompatibility ;
import java.io.BufferedReader ;
import java.io.BufferedWriter ;
import java.io.IOException ;
import java.io.Reader ;
import java.nio.file.CopyOption ;
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.StandardOpenOption ;
import java.nio.file.attribute.FileAttribute ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.concurrent.CompletableFuture ;
import java.util.concurrent.Executor ;
import net.minecraft.client.Minecraft ;
import net.minecraft.client.multiplayer.ClientLevel ;
import net.minecraft.client.renderer.ShaderInstance ;
import net.minecraft.client.renderer.texture.TextureManager ;
import net.minecraft.core.BlockPos ;
import net.minecraft.core.Holder ;
import net.minecraft.core.particles.BlockParticleOption ;
import net.minecraft.core.particles.ParticleOptions ;
import net.minecraft.core.particles.ParticleType ;
import net.minecraft.core.particles.ParticleTypes ;
import net.minecraft.network.protocol.Packet ;
import net.minecraft.network.protocol.game.ClientboundSoundPacket ;
import net.minecraft.resources.ResourceLocation ;
import net.minecraft.server.level.ServerLevel ;
import net.minecraft.server.level.ServerPlayer ;
import net.minecraft.server.packs.resources.PreparableReloadListener ;
import net.minecraft.server.packs.resources.ResourceManager ;
import net.minecraft.sounds.SoundEvent ;
import net.minecraft.sounds.SoundSource ;
import net.minecraft.util.RandomSource ;
import net.minecraft.util.profiling.ProfilerFiller ;
import net.minecraft.world.level.LevelAccessor ;
import net.minecraft.world.level.block.Block ;
import net.minecraft.world.level.block.entity.BlockEntityType ;
import net.minecraft.world.level.block.state.BlockState ;
import net.minecraft.world.phys.Vec3 ;
import net.neoforged.api.distmarker.Dist ;
import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent ;
import net.neoforged.neoforge.client.event.EntityRenderersEvent ;
import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent ;
import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent ;
import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent ;
import net.neoforged.neoforge.client.event.RegisterShadersEvent ;
2026-05-04 13:06:40 +02:00
import net.neoforged.neoforge.client.event.RenderGuiEvent ;
import net.neoforged.neoforge.client.gui.IConfigScreenFactory ;
import net.neoforged.neoforge.common.NeoForge ;
2026-05-04 12:09:37 +02:00
import net.neoforged.neoforge.event.TickEvent ;
import net.neoforged.neoforge.event.entity.player.PlayerEvent ;
import net.neoforged.neoforge.event.level.LevelEvent ;
import net.neoforged.neoforge.event.server.ServerStoppedEvent ;
import net.neoforged.bus.api.IEventBus ;
import net.neoforged.bus.api.SubscribeEvent ;
import net.neoforged.fml.DistExecutor ;
2026-05-04 13:06:40 +02:00
import net.neoforged.fml.ModContainer ;
2026-05-04 12:09:37 +02:00
import net.neoforged.fml.common.Mod ;
import net.neoforged.fml.config.ModConfig ;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent ;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent ;
import net.neoforged.neoforge.server.ServerLifecycleHooks ;
import org.slf4j.Logger ;
import software.bernie.geckolib.GeckoLib ;
@Mod ( value = " explosionoverhaul " )
public class ExplosionOverhaul {
public static final String MODID = " explosionoverhaul " ;
public static final Logger LOGGER = LogUtils . getLogger ( ) ;
private static final Map < String , ExplosionSourceMode > SOURCE_MODES = new HashMap < String , ExplosionSourceMode > ( ) ;
private static final List < String > EXPLOSION_BLACKLIST = new ArrayList < String > ( ) ;
private static final List < String > DEFAULT_BLACKLIST = List . of ( " ancient_elements:block_of_raw_infernal_ore " , " ancient_elements:celestium_ore " , " ancient_elements:deepslate_frost_ore " , " ancient_elements:deepslate_jungle_steel_ore " , " ancient_elements:deepslate_lead_ore " , " ancient_elements:deepslate_steel_ore " , " ancient_elements:deepslate_tin_ore " , " ancient_elements:deepslate_titanium_ore " , " ancient_elements:ender_steel_ore " , " ancient_elements:endrium_ore " , " ancient_elements:frost_ore " , " ancient_elements:infernal_ore " , " ancient_elements:jungle_steel_ore " , " ancient_elements:lead_ore " , " ancient_elements:meteorite_ore " , " ancient_elements:nether_steel_ore " , " ancient_elements:palladium_ore " , " ancient_elements:spectrillium_ore " , " ancient_elements:steel_ore " , " ancient_elements:tin_ore " , " ancient_elements:titanium_ore " , " ancient_elements:void_steel_ore " , " mofus_better_end_:void_portal_block " , " mofus_better_end_:void_portal_igniter " , " mofus_better_end_:star_portal " ) ;
private static final Path CONFIG_DIR = Paths . get ( " config " , " explosionoverhaul " ) ;
private static final Path OLD_COMMON = Paths . get ( " config " , " explosionoverhaul-common.toml " ) ;
private static final Path OLD_CLIENT = Paths . get ( " config " , " explosionoverhaul-client.toml " ) ;
private static final Path NEW_COMMON = CONFIG_DIR . resolve ( " explosionoverhaul-common.toml " ) ;
private static final Path NEW_CLIENT = CONFIG_DIR . resolve ( " explosionoverhaul-client.toml " ) ;
private static final Path BLACKLIST_JSON = CONFIG_DIR . resolve ( " DestroyingBlacklist.json " ) ;
private static final Path SOURCE_MODES_JSON = CONFIG_DIR . resolve ( " ExplosionSourceBlacklist.json " ) ;
private static final List < DelayedSound > delayedSounds = Collections . synchronizedList ( new ArrayList ( ) ) ;
private static final List < DelayedParticle > delayedParticles = Collections . synchronizedList ( new ArrayList ( ) ) ;
@SubscribeEvent
public static void onServerTick ( TickEvent . LevelTickEvent event ) {
}
private static void ensureConfigDirectory ( ) {
try {
Files . createDirectories ( CONFIG_DIR , new FileAttribute [ 0 ] ) ;
ExplosionOverhaul . writeConfigReadme ( ) ;
}
catch ( Exception e ) {
LOGGER . warn ( " Failed to create config/explosionoverhaul directory " , ( Throwable ) e ) ;
}
}
private static void writeConfigReadme ( ) {
Path readmePath = CONFIG_DIR . resolve ( " README.md " ) ;
if ( Files . exists ( readmePath , new LinkOption [ 0 ] ) ) {
return ;
}
try {
String content = " # Explosion Overhaul Config Helpers \ n \ n## Blacklist Files (.json) \ n \ n### DestroyingBlacklist.json \ n- **Format**: `[ \" namespace:block_id \" , ...]` \ n- **Effect**: Blocks listed here are immune to explosion craters. \ n \ n### GlassBlacklist.json \ n- **Format**: `[ \" namespace:block_id \" , ...]` \ n- **Effect**: Blocks listed here won't be shattered by the glass-breaking system. \ n \ n### ExplosionSourceBlacklist.json \ n- **Format**: `{ \" namespace:entity_id \" : \" MODE \" }` \ n- **Modes**: \ n - `DEFAULT`: Standard mod behavior (crater + concussion + sounds). \ n - `VANILLA`: Reverts to vanilla explosion logic for this source. \ n - `NO_DESTRUCTION`: Concussions and sounds only (no crater or glass breaking). \ n - `NO_DESTRUCTION_GLASSWORKS`: Like NO_DESTRUCTION, but glass still shatters. \ n \ n**Note**: Invalid JSON (extra commas, comments) will cause the mod to use defaults. Restart the game/server for changes to take effect. \ n \ nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ " ;
Files . writeString ( readmePath , ( CharSequence ) content , new OpenOption [ 0 ] ) ;
}
catch ( Exception e ) {
LOGGER . warn ( " Failed to write config README.md " , ( Throwable ) e ) ;
}
}
private static void migrateOldTomlIfPresent ( ) {
try {
byte [ ] oldBytes ;
if ( Files . exists ( OLD_COMMON , new LinkOption [ 0 ] ) ) {
ExplosionOverhaul . ensureConfigDirectory ( ) ;
if ( ! Files . exists ( NEW_COMMON , new LinkOption [ 0 ] ) ) {
Files . move ( OLD_COMMON , NEW_COMMON , new CopyOption [ 0 ] ) ;
} else {
oldBytes = Files . readAllBytes ( OLD_COMMON ) ;
Files . write ( NEW_COMMON , oldBytes , StandardOpenOption . TRUNCATE_EXISTING , StandardOpenOption . CREATE ) ;
Files . delete ( OLD_COMMON ) ;
}
}
if ( Files . exists ( OLD_CLIENT , new LinkOption [ 0 ] ) ) {
ExplosionOverhaul . ensureConfigDirectory ( ) ;
if ( ! Files . exists ( NEW_CLIENT , new LinkOption [ 0 ] ) ) {
Files . move ( OLD_CLIENT , NEW_CLIENT , new CopyOption [ 0 ] ) ;
} else {
oldBytes = Files . readAllBytes ( OLD_CLIENT ) ;
Files . write ( NEW_CLIENT , oldBytes , StandardOpenOption . TRUNCATE_EXISTING , StandardOpenOption . CREATE ) ;
Files . delete ( OLD_CLIENT ) ;
}
}
}
catch ( Exception e ) {
LOGGER . warn ( " Failed to migrate old toml configs " , ( Throwable ) e ) ;
}
}
private static void loadBlacklistFromJson ( ) {
ExplosionOverhaul . ensureConfigDirectory ( ) ;
Gson gson = new GsonBuilder ( ) . setPrettyPrinting ( ) . create ( ) ;
try {
if ( ! Files . exists ( BLACKLIST_JSON , new LinkOption [ 0 ] ) ) {
try ( BufferedWriter w = Files . newBufferedWriter ( BLACKLIST_JSON , new OpenOption [ 0 ] ) ; ) {
gson . toJson ( DEFAULT_BLACKLIST , ( Appendable ) w ) ;
}
EXPLOSION_BLACKLIST . clear ( ) ;
EXPLOSION_BLACKLIST . addAll ( DEFAULT_BLACKLIST ) ;
return ;
}
try ( BufferedReader r = Files . newBufferedReader ( BLACKLIST_JSON ) ; ) {
List list = ( List ) gson . fromJson ( ( Reader ) r , new TypeToken < List < String > > ( ) { } . getType ( ) ) ;
if ( list ! = null ) {
EXPLOSION_BLACKLIST . clear ( ) ;
EXPLOSION_BLACKLIST . addAll ( list ) ;
}
}
}
catch ( Exception e ) {
LOGGER . warn ( " Failed to load DestroyingBlacklist.json, falling back to defaults " , ( Throwable ) e ) ;
EXPLOSION_BLACKLIST . clear ( ) ;
EXPLOSION_BLACKLIST . addAll ( DEFAULT_BLACKLIST ) ;
}
}
private static void loadSourceModesFromJson ( ) {
ExplosionOverhaul . ensureConfigDirectory ( ) ;
Gson gson = new GsonBuilder ( ) . setPrettyPrinting ( ) . create ( ) ;
try {
if ( ! Files . exists ( SOURCE_MODES_JSON , new LinkOption [ 0 ] ) ) {
ExplosionOverhaul . ensureConfigDirectory ( ) ;
SOURCE_MODES . clear ( ) ;
try ( BufferedWriter w = Files . newBufferedWriter ( SOURCE_MODES_JSON , new OpenOption [ 0 ] ) ; ) {
gson . toJson ( SOURCE_MODES , ( Appendable ) w ) ;
}
return ;
}
try ( BufferedReader r = Files . newBufferedReader ( SOURCE_MODES_JSON ) ; ) {
Map map = ( Map ) gson . fromJson ( ( Reader ) r , new TypeToken < Map < String , ExplosionSourceMode > > ( ) { } . getType ( ) ) ;
if ( map ! = null ) {
SOURCE_MODES . clear ( ) ;
SOURCE_MODES . putAll ( map ) ;
}
}
}
catch ( Exception e ) {
LOGGER . warn ( " Failed to load ExplosionSourceBlacklist.json " , ( Throwable ) e ) ;
}
}
public static ExplosionSourceMode getSourceMode ( String id ) {
return SOURCE_MODES . getOrDefault ( id , ExplosionSourceMode . DEFAULT ) ;
}
public static Map < String , ExplosionSourceMode > getSourceModes ( ) {
return new HashMap < String , ExplosionSourceMode > ( SOURCE_MODES ) ;
}
public static void setSourceModes ( Map < String , ExplosionSourceMode > modes ) {
SOURCE_MODES . clear ( ) ;
SOURCE_MODES . putAll ( modes ) ;
Gson gson = new GsonBuilder ( ) . setPrettyPrinting ( ) . create ( ) ;
try ( BufferedWriter w = Files . newBufferedWriter ( SOURCE_MODES_JSON , new OpenOption [ 0 ] ) ; ) {
gson . toJson ( SOURCE_MODES , ( Appendable ) w ) ;
}
catch ( Exception e ) {
LOGGER . warn ( " Failed to save ExplosionSourceBlacklist.json " , ( Throwable ) e ) ;
}
}
public static List < String > getExplosionBlacklistList ( ) {
return new ArrayList < String > ( EXPLOSION_BLACKLIST ) ;
}
public static List < String > getDefaultExplosionBlacklist ( ) {
return new ArrayList < String > ( DEFAULT_BLACKLIST ) ;
}
public static void setExplosionBlacklistFromList ( List < String > list ) {
EXPLOSION_BLACKLIST . clear ( ) ;
for ( String s : list ) {
if ( s = = null | | s . isBlank ( ) ) continue ;
EXPLOSION_BLACKLIST . add ( s . trim ( ) ) ;
}
Gson gson = new GsonBuilder ( ) . setPrettyPrinting ( ) . create ( ) ;
try ( BufferedWriter w = Files . newBufferedWriter ( BLACKLIST_JSON , new OpenOption [ 0 ] ) ; ) {
gson . toJson ( ExplosionOverhaul . getExplosionBlacklistList ( ) , ( Appendable ) w ) ;
}
catch ( Exception e ) {
LOGGER . warn ( " Failed to save DestroyingBlacklist.json " , ( Throwable ) e ) ;
}
}
public static boolean isBlockBlacklisted ( Block block ) {
2026-05-04 13:06:40 +02:00
ResourceLocation blockId = BuiltInRegistries . BLOCK . getKey ( block ) ;
2026-05-04 12:09:37 +02:00
if ( blockId ! = null ) {
String blockName = blockId . toString ( ) ;
return EXPLOSION_BLACKLIST . contains ( blockName ) ;
}
return false ;
}
public static boolean isBlockStateBlacklisted ( BlockState state ) {
2026-05-04 13:06:40 +02:00
ResourceLocation blockId = BuiltInRegistries . BLOCK . getKey ( state . m_60734_ ( ) ) ;
2026-05-04 12:09:37 +02:00
if ( blockId ! = null ) {
String blockName = blockId . toString ( ) ;
return EXPLOSION_BLACKLIST . contains ( blockName ) ;
}
return false ;
}
2026-05-04 13:06:40 +02:00
public ExplosionOverhaul ( IEventBus modEventBus , ModContainer modContainer ) {
2026-05-04 12:09:37 +02:00
GeckoLib . initialize ( ) ;
2026-05-04 13:06:40 +02:00
modContainer . registerConfig ( ModConfig . Type . COMMON , Config . COMMON_SPEC , " explosionoverhaul/explosionoverhaul-common.toml " ) ;
modContainer . registerConfig ( ModConfig . Type . CLIENT , Config . CLIENT_SPEC , " explosionoverhaul/explosionoverhaul-client.toml " ) ;
modContainer . registerExtensionPoint ( IConfigScreenFactory . class , ( client , parent ) - > ModConfigScreen . create ( parent ) ) ;
2026-05-04 12:09:37 +02:00
ModSounds . register ( modEventBus ) ;
ModParticles . register ( modEventBus ) ;
ModBlocks . register ( modEventBus ) ;
ModItems . register ( modEventBus ) ;
ModBlockEntities . register ( modEventBus ) ;
ModCreativeTabs . register ( modEventBus ) ;
2026-05-04 13:06:40 +02:00
NeoForge . EVENT_BUS . register ( ExplosionOverhaul . class ) ;
NeoForge . EVENT_BUS . register ( ( Object ) new ServerExplosionHandler ( ) ) ;
NeoForge . EVENT_BUS . register ( BlockIndexManager . class ) ;
2026-05-04 12:09:37 +02:00
modEventBus . addListener ( this : : commonSetup ) ;
DistExecutor . unsafeRunWhenOn ( ( Dist ) Dist . CLIENT , ( ) - > ( ) - > {
SoundPhysicsCompatibility . init ( ) ;
modEventBus . addListener ( ClientSetup : : init ) ;
modEventBus . addListener ( ClientSetup : : registerParticleFactories ) ;
modEventBus . addListener ( ClientSetup : : registerBlockEntityRenderers ) ;
modEventBus . addListener ( ClientSetup : : registerShaders ) ;
modEventBus . addListener ( ClientSetup : : registerReloadListeners ) ;
modEventBus . addListener ( ClientSetup : : onRegisterKeyMappings ) ;
2026-05-04 13:06:40 +02:00
NeoForge . EVENT_BUS . register ( ClientSetup . class ) ;
NeoForge . EVENT_BUS . register ( ( Object ) new ClientSoundHandler ( ) ) ;
NeoForge . EVENT_BUS . register ( ScanProgressHUD . class ) ;
NeoForge . EVENT_BUS . register ( ScanPromptHUD . class ) ;
NeoForge . EVENT_BUS . register ( ScanInfoHUD . class ) ;
NeoForge . EVENT_BUS . register ( ScanLoadPromptHUD . class ) ;
NeoForge . EVENT_BUS . register ( ScanLoadInfoHUD . class ) ;
NeoForge . EVENT_BUS . register ( ( Object ) new ScanKeyHandler ( ) ) ;
NeoForge . EVENT_BUS . register ( FirstTimeSetupHandler . class ) ;
NeoForge . EVENT_BUS . register ( IntroMusicTickHandler . class ) ;
NeoForge . EVENT_BUS . register ( ConcussionAudioEffect . class ) ;
2026-05-04 12:09:37 +02:00
} ) ;
}
private void commonSetup ( FMLCommonSetupEvent event ) {
ExplosionOverhaul . migrateOldTomlIfPresent ( ) ;
ExplosionOverhaul . loadBlacklistFromJson ( ) ;
ExplosionOverhaul . loadSourceModesFromJson ( ) ;
event . enqueueWork ( PacketHandler : : register ) ;
}
@SubscribeEvent
public static void onServerTick ( TickEvent . ServerTickEvent event ) {
if ( event . phase = = TickEvent . Phase . END ) {
RedstoneLampEffects . onServerTick ( ) ;
if ( event . getServer ( ) ! = null ) {
for ( ServerLevel level : event . getServer ( ) . m_129785_ ( ) ) {
DripstoneEffects . onServerTick ( level ) ;
}
}
GlassBreakingEffects . onServerTick ( ) ;
AmbientExplosionManager . onServerTick ( event . getServer ( ) . m_129783_ ( ) ) ;
if ( ServerLifecycleHooks . getCurrentServer ( ) ! = null ) {
AsyncCraterManager . onServerTick ( ServerLifecycleHooks . getCurrentServer ( ) ) ;
}
delayedSounds . removeIf ( delayedSound - > {
- - delayedSound . ticksRemaining ;
if ( delayedSound . ticksRemaining < = 0L ) {
delayedSound . play ( ) ;
return true ;
}
return false ;
} ) ;
delayedParticles . removeIf ( delayedParticle - > {
- - delayedParticle . ticksRemaining ;
if ( delayedParticle . ticksRemaining < = 0L ) {
delayedParticle . spawn ( ) ;
if ( delayedParticle . durationTicks > 1L ) {
- - delayedParticle . durationTicks ;
delayedParticle . ticksRemaining = 1L ;
return false ;
}
return true ;
}
return false ;
} ) ;
}
}
@SubscribeEvent
public static void onLevelUnload ( LevelEvent . Unload event ) {
LevelAccessor levelAccessor = event . getLevel ( ) ;
if ( levelAccessor instanceof ServerLevel ) {
ServerLevel level = ( ServerLevel ) levelAccessor ;
AsyncCraterManager . onLevelUnload ( level ) ;
}
}
@SubscribeEvent
public static void onServerStopped ( ServerStoppedEvent event ) {
AsyncCraterManager . shutdown ( ) ;
}
@SubscribeEvent
public static void onPlayerLogin ( PlayerEvent . PlayerLoggedInEvent event ) {
if ( event . getEntity ( ) instanceof ServerPlayer ) {
AmbientExplosionManager . onPlayerLoggedIn ( ( ServerPlayer ) event . getEntity ( ) ) ;
}
}
@SubscribeEvent
public static void onPlayerLogout ( PlayerEvent . PlayerLoggedOutEvent event ) {
if ( event . getEntity ( ) instanceof ServerPlayer ) {
AmbientExplosionManager . onPlayerLoggedOut ( ( ServerPlayer ) event . getEntity ( ) ) ;
}
}
public static void addDelayedSound ( ServerPlayer player , SoundEvent sound , SoundSource source , float x , float y , float z , float volume , float pitch , long seed , long delayTicks ) {
if ( player . f_8906_ = = null ) {
return ;
}
if ( delayTicks < = 0L ) {
2026-05-04 13:06:40 +02:00
player . f_8906_ . m_9829_ ( ( Packet ) new ClientboundSoundPacket ( ( Holder ) BuiltInRegistries . SOUND_EVENT . wrapAsHolder ( sound ) , source , ( double ) x , ( double ) y , ( double ) z , volume , pitch , seed ) ) ;
2026-05-04 12:09:37 +02:00
} else {
delayedSounds . add ( new DelayedSound ( player , sound , source , x , y , z , volume , pitch , seed , delayTicks ) ) ;
}
}
public static void addDelayedParticle ( ServerLevel level , BlockParticleOption particleOption , double x , double y , double z , int count , double dx , double dy , double dz , double speed , long delayTicks , long durationTicks ) {
if ( delayTicks < = 0L & & durationTicks < = 1L ) {
level . m_8767_ ( ( ParticleOptions ) particleOption , x , y , z , count , dx , dy , dz , speed ) ;
} else {
delayedParticles . add ( new DelayedParticle ( level , particleOption , x , y , z , count , dx , dy , dz , speed , delayTicks , Math . max ( 1L , durationTicks ) ) ) ;
}
}
public static enum ExplosionSourceMode {
DEFAULT ,
VANILLA ,
NO_DESTRUCTION ,
NO_DESTRUCTION_GLASSWORKS ;
}
public static class DelayedSound {
public ServerPlayer player ;
public SoundEvent sound ;
public SoundSource source ;
public float x ;
public float y ;
public float z ;
public float volume ;
public float pitch ;
public long seed ;
public long ticksRemaining ;
public DelayedSound ( ServerPlayer player , SoundEvent sound , SoundSource source , float x , float y , float z , float volume , float pitch , long seed , long ticksRemaining ) {
this . player = player ;
this . sound = sound ;
this . source = source ;
this . x = x ;
this . y = y ;
this . z = z ;
this . volume = volume ;
this . pitch = pitch ;
this . seed = seed ;
this . ticksRemaining = ticksRemaining ;
}
public void play ( ) {
if ( this . player . f_8906_ = = null ) {
return ;
}
2026-05-04 13:06:40 +02:00
this . player . f_8906_ . m_9829_ ( ( Packet ) new ClientboundSoundPacket ( ( Holder ) BuiltInRegistries . SOUND_EVENT . wrapAsHolder ( this . sound ) , this . source , ( double ) this . x , ( double ) this . y , ( double ) this . z , this . volume , this . pitch , this . seed ) ) ;
2026-05-04 12:09:37 +02:00
}
}
public static class DelayedParticle {
public ServerLevel level ;
public BlockParticleOption particleOption ;
public double x ;
public double y ;
public double z ;
public int count ;
public double dx ;
public double dy ;
public double dz ;
public double speed ;
public long ticksRemaining ;
public long durationTicks ;
public DelayedParticle ( ServerLevel level , BlockParticleOption particleOption , double x , double y , double z , int count , double dx , double dy , double dz , double speed , long ticksRemaining , long durationTicks ) {
this . level = level ;
this . particleOption = particleOption ;
this . x = x ;
this . y = y ;
this . z = z ;
this . count = count ;
this . dx = dx ;
this . dy = dy ;
this . dz = dz ;
this . speed = speed ;
this . ticksRemaining = ticksRemaining ;
this . durationTicks = durationTicks ;
}
public void spawn ( ) {
int particlesPerSpawn = Math . max ( 1 , this . count / ( int ) Math . max ( 1L , this . durationTicks ) ) ;
this . level . m_8767_ ( ( ParticleOptions ) this . particleOption , this . x , this . y , this . z , particlesPerSpawn , this . dx , this . dy , this . dz , this . speed ) ;
}
}
public static class ClientSetup {
private static boolean hasPreWarmed = false ;
public static void init ( FMLClientSetupEvent event ) {
ExplosionTextureManager . getInstance ( ) . reload ( ) ;
}
public static void registerReloadListeners ( RegisterClientReloadListenersEvent event ) {
event . registerReloadListener ( ( PreparableReloadListener ) new TexturePreloader ( ) ) ;
}
@SubscribeEvent
public static void registerShaders ( RegisterShadersEvent event ) {
try {
event . registerShader ( new ShaderInstance ( event . getResourceProvider ( ) , new ResourceLocation ( ExplosionOverhaul . MODID , " blur " ) , DefaultVertexFormat . f_85817_ ) , Blur : : setShader ) ;
}
catch ( IOException e ) {
LOGGER . warn ( " Failed to load core shaders " , ( Throwable ) e ) ;
}
}
@SubscribeEvent
public static void registerParticleFactories ( RegisterParticleProvidersEvent event ) {
event . registerSpriteSet ( ( ParticleType ) ModParticles . CUSTOM_GLOW . get ( ) , CustomGlowParticleProvider : : new ) ;
event . registerSpriteSet ( ( ParticleType ) ModParticles . PLASMA . get ( ) , PlasmaParticle . Provider : : new ) ;
event . registerSpriteSet ( ( ParticleType ) ModParticles . CUSTOM_SMOKE . get ( ) , SmokeParticle . Provider : : new ) ;
event . registerSpriteSet ( ( ParticleType ) ModParticles . LINE_SPARK . get ( ) , LineSparkParticleProvider : : new ) ;
LOGGER . info ( " Registered custom particle providers. " ) ;
}
@SubscribeEvent
public static void onRegisterKeyMappings ( RegisterKeyMappingsEvent event ) {
ModKeyMappings . register ( event ) ;
}
@SubscribeEvent
public static void registerBlockEntityRenderers ( EntityRenderersEvent . RegisterRenderers event ) {
event . registerBlockEntityRenderer ( ( BlockEntityType ) ModBlockEntities . VINLANX_THE_LIGHT . get ( ) , VinlanxTheLightRenderer : : new ) ;
}
@SubscribeEvent
public static void onClientTick ( TickEvent . ClientTickEvent event ) {
if ( event . phase = = TickEvent . Phase . END ) {
ClientEffects . onClientTick ( ) ;
if ( ! hasPreWarmed & & Minecraft . m_91087_ ( ) . f_91073_ ! = null & & Minecraft . m_91087_ ( ) . f_91074_ ! = null ) {
LOGGER . info ( " Pre-warming explosion particle render pipeline... " ) ;
ClientLevel level = Minecraft . m_91087_ ( ) . f_91073_ ;
double x = Minecraft . m_91087_ ( ) . f_91074_ . m_20185_ ( ) ;
double y = - 200 . 0 ;
double z = Minecraft . m_91087_ ( ) . f_91074_ . m_20189_ ( ) ;
level . m_7106_ ( ( ParticleOptions ) new PlasmaParticleOptions ( 1 . 0f ) , x , y , z , 0 . 0 , 0 . 0 , 0 . 0 ) ;
LOGGER . info ( " Pre-warming 'glow' animation... " ) ;
level . m_7106_ ( ( ParticleOptions ) new CustomGlowParticleOptions ( 0 , 1 . 0f , 0 . 01f , 0 , ( float ) y , 4 . 0f , 0 . 0f ) , x , y , z , 0 . 0 , 0 . 0 , 0 . 0 ) ;
LOGGER . info ( " Pre-warming 'glow_2' animation... " ) ;
level . m_7106_ ( ( ParticleOptions ) new CustomGlowParticleOptions ( 0 , 1 . 0f , 0 . 01f , 1 , ( float ) y , 4 . 0f , 0 . 0f ) , x , y , z , 0 . 0 , 0 . 0 , 0 . 0 ) ;
LOGGER . info ( " Pre-warming 'sglow' animation... " ) ;
level . m_7106_ ( ( ParticleOptions ) new CustomGlowParticleOptions ( 0 , 1 . 0f , 0 . 01f , 2 , ( float ) y , 4 . 0f , 0 . 0f ) , x , y , z , 0 . 0 , 0 . 0 , 0 . 0 ) ;
hasPreWarmed = true ;
LOGGER . info ( " Explosion particle pipeline pre-warmed successfully. " ) ;
}
}
}
@SubscribeEvent
public static void onPlayerLogout ( ClientPlayerNetworkEvent . LoggingOut event ) {
LOGGER . info ( " Player logged out, resetting particle pre-warmer and scan HUDs. " ) ;
hasPreWarmed = false ;
ExplosionWindController . reset ( ) ;
ScanProgressHUD . reset ( ) ;
ScanPromptHUD . setVisible ( false ) ;
ScanLoadPromptHUD . setVisible ( false ) ;
ScanInfoHUD . setVisible ( false ) ;
ScanLoadInfoHUD . setVisible ( false ) ;
}
@SubscribeEvent
2026-05-04 13:06:40 +02:00
public static void onRenderGuiOverlay ( RenderGuiEvent . Post event ) {
Blur . render ( Blur . RenderStage . HUD ) ;
ClientEffects . renderFlash ( event ) ;
ConcussionAudioEffect . renderHeartbeatHUD ( event . getGuiGraphics ( ) ) ;
2026-05-04 12:09:37 +02:00
}
public static class TexturePreloader
implements PreparableReloadListener {
public CompletableFuture < Void > m_5540_ ( PreparableReloadListener . PreparationBarrier pPreparationBarrier , ResourceManager pResourceManager , ProfilerFiller pPreparationsProfiler , ProfilerFiller pReloadProfiler , Executor pBackgroundExecutor , Executor pGameExecutor ) {
CompletableFuture < List > prepareFuture = CompletableFuture . supplyAsync ( ( ) - > {
LOGGER . info ( " Scanning for all mod textures to preload... " ) ;
return pResourceManager . m_214159_ ( " textures " , path - > path . m_135827_ ( ) . equals ( ExplosionOverhaul . MODID ) & & path . m_135815_ ( ) . endsWith ( " .png " ) ) . keySet ( ) . stream ( ) . toList ( ) ;
} , pBackgroundExecutor ) ;
return prepareFuture . thenCompose ( locationsToLoad - > pPreparationBarrier . m_6769_ ( locationsToLoad ) . thenRunAsync ( ( ) - > {
if ( locationsToLoad . isEmpty ( ) ) {
LOGGER . warn ( " Did not find any textures to preload for mod '{}'. " , ( Object ) ExplosionOverhaul . MODID ) ;
return ;
}
LOGGER . info ( " Preloading {} textures from '{}' to GPU... " , ( Object ) locationsToLoad . size ( ) , ( Object ) ExplosionOverhaul . MODID ) ;
TextureManager textureManager = Minecraft . m_91087_ ( ) . m_91097_ ( ) ;
for ( ResourceLocation location : locationsToLoad ) {
textureManager . m_118506_ ( location ) ;
}
ExplosionTextureManager . getInstance ( ) . reload ( ) ;
LOGGER . info ( " Finished preloading all textures for the mod. " ) ;
} , pGameExecutor ) ) ;
}
}
}
public static class CaveEffects {
private static final int PLAYER_EFFECT_RADIUS = 10 ;
public static void spawnFallingBlocksAndDust ( ServerLevel level , Vec3 explosionPos , ServerPlayer player , float power , long initialDelayTicks ) {
RandomSource random = level . m_213780_ ( ) ;
long maxEffectOverallDurationTicks = ( 2 + random . m_188503_ ( 5 ) ) * 20 ;
int totalEffectSpawns = 15 + ( int ) ( power / 5 . 0f * 3 . 0f ) ;
totalEffectSpawns = Math . min ( totalEffectSpawns , 60 ) ;
for ( int i = 0 ; i < totalEffectSpawns ; + + i ) {
long individualDelay = initialDelayTicks + ( long ) random . m_188503_ ( ( int ) maxEffectOverallDurationTicks / 2 ) ;
long randomDurationTicks = 10 + random . m_188503_ ( 40 ) ;
CaveEffects . spawnEffectAtPlayer ( level , player , individualDelay , random , power , randomDurationTicks ) ;
}
}
private static void spawnEffectAtPlayer ( ServerLevel level , ServerPlayer player , long delayTicks , RandomSource random , float power , long durationTicks ) {
int xOffset = random . m_188503_ ( 21 ) - 10 ;
int zOffset = random . m_188503_ ( 21 ) - 10 ;
int ySearchStart = ( int ) player . m_20186_ ( ) + 3 + random . m_188503_ ( 4 ) ;
BlockPos playerBlockPos = player . m_20183_ ( ) ;
BlockPos checkPosBase = new BlockPos ( playerBlockPos . m_123341_ ( ) + xOffset , ySearchStart , playerBlockPos . m_123343_ ( ) + zOffset ) ;
BlockPos effectPos = null ;
for ( int i = 0 ; i < 8 ; + + i ) {
BlockState aboveState ;
BlockState currentState ;
BlockPos currentCheck = checkPosBase . m_6625_ ( i ) ;
if ( ! level . m_46749_ ( currentCheck ) | | ( double ) currentCheck . m_123342_ ( ) < = player . m_20186_ ( ) - 1 . 0 | | ! ( currentState = level . m_8055_ ( currentCheck ) ) . m_280296_ ( ) | | currentState . m_60795_ ( ) | | ! ( aboveState = level . m_8055_ ( currentCheck . m_7494_ ( ) ) ) . m_60795_ ( ) & & ! level . m_8055_ ( currentCheck . m_7495_ ( ) ) . m_60795_ ( ) ) continue ;
effectPos = currentCheck ;
break ;
}
if ( effectPos ! = null & & effectPos . m_123342_ ( ) > level . m_141937_ ( ) ) {
boolean isSpecialBlock ;
BlockState blockState = level . m_8055_ ( effectPos ) ;
if ( blockState . m_60795_ ( ) ) {
return ;
}
2026-05-04 13:06:40 +02:00
ResourceLocation registryName = BuiltInRegistries . BLOCK . getKey ( blockState . m_60734_ ( ) ) ;
2026-05-04 12:09:37 +02:00
String name = registryName ! = null ? registryName . m_135815_ ( ) : " " ;
boolean bl = isSpecialBlock = name . contains ( " bedrock " ) | | name . contains ( " end_portal " ) | | name . contains ( " end_portal_frame " ) | | name . contains ( " command_block " ) | | name . contains ( " barrier " ) ;
if ( isSpecialBlock ) {
return ;
}
int particleCount = 8 + ( int ) ( ( double ) ( power / 10 . 0f ) * 1 . 5 ) ;
particleCount = Math . min ( particleCount , 25 ) ;
ExplosionOverhaul . addDelayedParticle ( level , new BlockParticleOption ( ParticleTypes . f_123814_ , blockState ) , ( double ) effectPos . m_123341_ ( ) + 0 . 5 , ( double ) effectPos . m_123342_ ( ) + 0 . 2 , ( double ) effectPos . m_123343_ ( ) + 0 . 5 , particleCount , 0 . 3 , 0 . 3 , 0 . 3 , 0 . 02 , delayTicks , durationTicks ) ;
if ( random . m_188500_ ( ) < 0 . 3 + ( double ) power * 0 . 02 ) {
ExplosionOverhaul . addDelayedSound ( player , ( SoundEvent ) ModSounds . DUST_SOUND . get ( ) , SoundSource . BLOCKS , ( float ) effectPos . m_123341_ ( ) + 0 . 5f , ( float ) effectPos . m_123342_ ( ) + 0 . 5f , ( float ) effectPos . m_123343_ ( ) + 0 . 5f , 0 . 6f + random . m_188501_ ( ) * 0 . 4f , 0 . 85f + random . m_188501_ ( ) * 0 . 3f , player . m_217043_ ( ) . m_188505_ ( ) , delayTicks + ( long ) random . m_188503_ ( 10 ) ) ;
}
if ( random . m_188500_ ( ) < 0 . 03 + ( double ) power * 0 . 005 & & power > 10 . 0f ) {
ExplosionOverhaul . addDelayedSound ( player , ( SoundEvent ) ModSounds . FALLING_STONES_SOUND . get ( ) , SoundSource . BLOCKS , ( float ) effectPos . m_123341_ ( ) + 0 . 5f , ( float ) effectPos . m_123342_ ( ) + 0 . 5f , ( float ) effectPos . m_123343_ ( ) + 0 . 5f , 0 . 8f + random . m_188501_ ( ) * 0 . 2f , 0 . 9f + random . m_188501_ ( ) * 0 . 2f , player . m_217043_ ( ) . m_188505_ ( ) , delayTicks + ( long ) random . m_188503_ ( 20 ) ) ;
}
}
}
}
}