Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 174 additions & 110 deletions src/main/java/de/dafuqs/spectrum/registries/SpectrumEventListeners.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
import de.dafuqs.spectrum.progression.*;
import de.dafuqs.spectrum.registries.client.*;
import net.minecraft.advancements.*;
import net.minecraft.core.*;
import net.minecraft.core.component.*;
import net.minecraft.core.particles.*;
import net.minecraft.core.registries.*;
import net.minecraft.resources.*;
import net.minecraft.server.*;
import net.minecraft.server.level.*;
import net.minecraft.server.packs.*;
import net.minecraft.server.packs.resources.*;
Expand All @@ -44,84 +46,202 @@
import net.minecraft.world.level.block.state.*;
import net.minecraft.world.level.material.*;
import net.minecraft.world.phys.*;
import net.neoforged.bus.api.*;
import net.neoforged.fml.common.*;
import net.neoforged.fml.event.lifecycle.*;
import net.neoforged.neoforge.common.util.*;
import net.neoforged.neoforge.event.*;
import net.neoforged.neoforge.event.enchanting.*;
import net.neoforged.neoforge.event.entity.living.*;
import net.neoforged.neoforge.event.entity.player.*;
import net.neoforged.neoforge.event.level.*;
import net.neoforged.neoforge.event.server.*;
import net.neoforged.neoforge.event.tick.*;

import java.util.*;
import java.util.concurrent.atomic.*;

@EventBusSubscriber(modid = SpectrumCommon.MOD_ID)
public class SpectrumEventListeners {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one more thing: @SubscribeEvent must be used in conjunction with @EventBusSubscriber, the only other way is manually registering event listeners to the bus

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I just forgot to do that, change has been made



/**
* Caches the luminance states from fluids as int
* for blocks that react to the light level of fluids
* like the fusion shrine lighting up with lava or liquid crystal
*/
public static final HashMap<Fluid, Integer> fluidLuminance = new HashMap<>();

public static void register() {
AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> {
if (!world.isClientSide && !player.isSpectator()) {

ItemStack mainHandStack = player.getMainHandItem();
if (mainHandStack.getItem() instanceof ExchangeStaffItem exchangeStaffItem) {
BlockState targetBlockState = world.getBlockState(pos);
if (exchangeStaffItem.canInteractWith(targetBlockState, world, pos, player)) {
Optional<Block> storedBlock = ExchangeStaffItem.getStoredBlock(player.getMainHandItem());

if (storedBlock.isPresent()
&& storedBlock.get() != targetBlockState.getBlock()
&& storedBlock.get().asItem() != Items.AIR
&& ExchangeStaffItem.exchange(world, pos, player, storedBlock.get(), player.getMainHandItem(), true, direction)) {

return InteractionResult.SUCCESS;
}

// I'm putting all event listeners here, they can be moved later so nbd

@SubscribeEvent
public InteractionResult exchangeBlock(PlayerInteractEvent.LeftClickBlock event) {
Level world = event.getLevel();
BlockPos pos = event.getPos();
Player player = event.getEntity();
Direction direction = event.getFace();

if (!world.isClientSide && !player.isSpectator()) {

ItemStack mainHandStack = player.getMainHandItem();
if (mainHandStack.getItem() instanceof ExchangeStaffItem exchangeStaffItem) {
BlockState targetBlockState = world.getBlockState(pos);
if (exchangeStaffItem.canInteractWith(targetBlockState, world, pos, player)) {
Optional<Block> storedBlock = ExchangeStaffItem.getStoredBlock(player.getMainHandItem());

if (storedBlock.isPresent()
&& storedBlock.get() != targetBlockState.getBlock()
&& storedBlock.get().asItem() != Items.AIR
&& ExchangeStaffItem.exchange(world, pos, player, storedBlock.get(), player.getMainHandItem(),
true, direction)) {
return InteractionResult.SUCCESS;
}
world.playSound(null, player.blockPosition(), SoundEvents.DISPENSER_FAIL, SoundSource.PLAYERS, 1.0F, 1.0F);
return InteractionResult.FAIL;
}
world.playSound(null, player.blockPosition(), SoundEvents.DISPENSER_FAIL, SoundSource.PLAYERS, 1.0F, 1.0F);
return InteractionResult.FAIL;
}
return InteractionResult.PASS;
});
}

CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> {
if (client) {
SpectrumColorProviders.resetToggleableProviders();
return InteractionResult.PASS;
}

@SubscribeEvent
public void resetColorProviders(TagsUpdatedEvent event) {
if (event.getUpdateCause() == TagsUpdatedEvent.UpdateCause.CLIENT_PACKET_RECEIVED) {
SpectrumColorProviders.resetToggleableProviders();
}
}

@SubscribeEvent
public void handleInertia(BlockEvent.BreakEvent event) {
Player player = event.getPlayer();
BlockPos pos = event.getPos();
Level level = event.getPlayer().level();
BlockState state = event.getState();
if (player instanceof ServerPlayer serverPlayerEntity) {
ItemStack handStack = player.getItemInHand(serverPlayerEntity.getUsedItemHand());
if (SpectrumEnchantmentHelper.hasEnchantment(player.level().registryAccess(), SpectrumEnchantments.INERTIA, handStack)) {
InertiaComponent.onInertiaBlockBreak(level, pos, state, serverPlayerEntity, handStack);
}
});

SpectrumAdvancementCriteria.BLOCK_BROKEN.trigger(serverPlayerEntity, state);
}
}

//Curious: I'm like 90% sure this isn't needed anymore since Enchantments are stored as components now so I'm just commenting both instances of this
//Curious TODO: Look into whether enchantments still need this event check for the Indestructible enchant
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd say yes simply because the current enchantable tag is just a generic one and not a negative tag for #spectrum:indestructible_blacklisted

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could just be made to exclude #spectrum:indestructible_blacklisted though, we have exclusionslib specifically for that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do note, however, that it's optional currently

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I should be fine to delete that handler, yes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as long as you replace it with equivalent functionality
since exclusionslib is optional, i strongly advise you to write an equivalent handler

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do that later then, need to look into spectrum's code more

Copy link
Owner

@DaFuqs DaFuqs Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neoforge has it's own tag exclusion mechanism: https://docs.neoforged.net/docs/resources/server/tags/, so we are good without a dedicated blacklist tag.

{
    "values": [
       ...
    ],
    "remove": [
        "minecraft:iron_ingot"
    ]
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's really useful

// @SubscribeEvent
// public TriState preventIndestructible(GetEnchantmentLevelEvent.) {
// RegistryE
// if (registryEntry.is(SpectrumEnchantments.INDESTRUCTIBLE) && itemStack.is(SpectrumItemTags.INDESTRUCTIBLE_BLACKLISTED)) {
// return TriState.FALSE;
// }
// return TriState.DEFAULT;
// }

@SubscribeEvent
public InteractionResult triggerPrioritizedEntityInteraction(PlayerInteractEvent.EntityInteract event){

PlayerBlockBreakEvents.AFTER.register((level, player, pos, state, blockEntity) -> {
if (player instanceof ServerPlayer serverPlayerEntity) {
ItemStack handStack = player.getItemInHand(serverPlayerEntity.getUsedItemHand());
if (SpectrumEnchantmentHelper.hasEnchantment(player.level().registryAccess(), SpectrumEnchantments.INERTIA, handStack)) {
InertiaComponent.onInertiaBlockBreak(level, pos, state, serverPlayerEntity, handStack);
}

SpectrumAdvancementCriteria.BLOCK_BROKEN.trigger(serverPlayerEntity, state);
}
});
Player player = event.getEntity();
Entity entity = event.getTarget();
InteractionHand hand = event.getHand();
ItemStack handStack = player.getItemInHand(hand);

EnchantmentEvents.ALLOW_ENCHANTING.register((registryEntry, itemStack, enchantingContext) -> {
if (registryEntry.is(SpectrumEnchantments.INDESTRUCTIBLE) && itemStack.is(SpectrumItemTags.INDESTRUCTIBLE_BLACKLISTED)) {
return TriState.FALSE;
}
return TriState.DEFAULT;
});
if (handStack.getItem() instanceof PrioritizedEntityInteraction && entity instanceof LivingEntity livingEntity) {
return handStack.interactLivingEntity(player, livingEntity, hand);
}
return InteractionResult.PASS;

}

@SubscribeEvent
public InteractionResult triggerPrioritizedBlockInteraction(PlayerInteractEvent.RightClickBlock event) {

Player player = event.getEntity();
InteractionHand hand = event.getHand();
BlockHitResult hitResult = event.getHitVec();
ItemStack handStack = player.getItemInHand(hand);

if (handStack.getItem() instanceof PrioritizedBlockInteraction) {
return handStack.useOn(new UseOnContext(player, hand, hitResult));
}
return InteractionResult.PASS;

}

//Curious: I'm basically checking the sleep timer when the player wakes up here
//Curious: Miraculously this works due to a single line in the Player class which is awesome
@SubscribeEvent
public void triggerWhispyCirclet(PlayerWakeUpEvent event) {
Player player = event.getEntity();

if(player.getSleepTimer() == 100 && SpectrumTrinketItem.hasEquipped(player, SpectrumItems.WHISPY_CIRCLET.asItem())) {
player.setHealth(player.getMaxHealth());
WhispyCircletItem.removeNegativeStatusEffects(player);
}

UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
ItemStack handStack = player.getItemInHand(hand);
if (handStack.getItem() instanceof PrioritizedEntityInteraction && entity instanceof LivingEntity livingEntity) {
return handStack.interactLivingEntity(player, livingEntity, hand);
}

@SubscribeEvent
public void triggerJeopardantKillCriterion(LivingDeathEvent event) {
Entity player = event.getSource().getEntity();
LivingEntity target = event.getEntity();

if(player instanceof ServerPlayer && SpectrumTrinketItem.hasEquipped((LivingEntity) player, SpectrumItems.JEOPARDANT.asItem())) {
SpectrumAdvancementCriteria.JEOPARDANT_KILL.trigger((ServerPlayer) player, target);
}
}

@SubscribeEvent
public void tickSpawners(LevelTickEvent.Pre event) {
Level level = event.getLevel();
ServerLevel world = level.getServer().getLevel(level.dimension());

if(!world.tickRateManager().runsNormally()) {
return;
}

if (world.getGameTime() % 100 == 0 && !world.isClientSide) {
if (TimeHelper.getTimeOfDay(world).isNight()) { // 90 chances in a night
if (SpectrumCommon.CONFIG.ShootingStarWorlds.contains(world.dimension().location().toString())) {
ShootingStarSpawner.INSTANCE.tick((ServerLevel) world, true, true);
}
}
return InteractionResult.PASS;
});

/* TODO: Monstrosity
if (world.getRegistryKey() == SpectrumDimensions.DIMENSION_KEY) {
MonstrositySpawner.INSTANCE.spawn(world, true, true);
}*/
}

UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
ItemStack handStack = player.getItemInHand(hand);
if (handStack.getItem() instanceof PrioritizedBlockInteraction) {
return handStack.useOn(new UseOnContext(player, hand, hitResult));
}

@SubscribeEvent
public void updateFluidLuminance(ServerStartedEvent event) {
SpectrumCommon.logInfo("Querying fluid luminance...");
for (Iterator<Block> it = BuiltInRegistries.BLOCK.stream().iterator(); it.hasNext(); ) {
Block block = it.next();
if (block instanceof LiquidBlock fluidBlock) {
fluidLuminance.put(fluidBlock.fluid, fluidBlock.defaultBlockState().getLightEmission());
}
return InteractionResult.PASS;
});
}
}

@SubscribeEvent
public void injectDynamicRecipe(ServerStartedEvent event) {
MinecraftServer server = event.getServer();

SpectrumCommon.logInfo("Injecting dynamic recipes into recipe manager...");
FirestarterIdolBlock.addBlockSmeltingRecipes(server);
}





public static void register() {

ServerTickEvents.END_SERVER_TICK.register(server -> {
if (!server.tickRateManager().runsNormally()) {
return;
Expand All @@ -145,63 +265,7 @@ public static void register() {
}
}
});

ServerTickEvents.START_WORLD_TICK.register(world -> {
if (!world.tickRateManager().runsNormally()) {
return;
}

// these would actually be nicer to have as Spawners in ServerWorld
// to have them run in tickSpawners()
// but getting them in there would require some ugly mixins

if (world.getGameTime() % 100 == 0) {
if (TimeHelper.getTimeOfDay(world).isNight()) { // 90 chances in a night
if (SpectrumCommon.CONFIG.ShootingStarWorlds.contains(world.dimension().location().toString())) {
ShootingStarSpawner.INSTANCE.tick(world, true, true);
}
}

/* TODO: Monstrosity
if (world.getRegistryKey() == SpectrumDimensions.DIMENSION_KEY) {
MonstrositySpawner.INSTANCE.spawn(world, true, true);
}*/
}
});

ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
SpectrumCommon.logInfo("Querying fluid luminance...");
for (Iterator<Block> it = BuiltInRegistries.BLOCK.stream().iterator(); it.hasNext(); ) {
Block block = it.next();
if (block instanceof LiquidBlock fluidBlock) {
fluidLuminance.put(fluidBlock.fluid, fluidBlock.defaultBlockState().getLightEmission());
}
}

SpectrumCommon.logInfo("Injecting dynamic recipes into recipe manager...");
FirestarterIdolBlock.addBlockSmeltingRecipes(server);
//injectEnchantmentUpgradeRecipes(server);
});

EntitySleepEvents.STOP_SLEEPING.register((entity, sleepingPos) -> {
// If the player wears a Whispy Cirlcet and sleeps
// they get fully healed and all negative status effects removed
// When the sleep timer reached 100 the player is fully asleep
if (entity instanceof ServerPlayer serverPlayerEntity
&& serverPlayerEntity.getSleepTimer() == 100
&& SpectrumTrinketItem.hasEquipped(entity, SpectrumItems.WHISPY_CIRCLET)) {

entity.setHealth(entity.getMaxHealth());
WhispyCircletItem.removeNegativeStatusEffects(entity);
}
});

ServerEntityCombatEvents.AFTER_KILLED_OTHER_ENTITY.register((world, entity, killedEntity) -> {
if (entity instanceof ServerPlayer serverPlayerEntity && SpectrumTrinketItem.hasEquipped(serverPlayerEntity, SpectrumItems.JEOPARDANT)) {
SpectrumAdvancementCriteria.JEOPARDANT_KILL.trigger(serverPlayerEntity, killedEntity);
}
});


ServerEntityEvents.EQUIPMENT_CHANGE.register((livingEntity, equipmentSlot, previousStack, currentStack) -> {
var oldInexorable = SpectrumEnchantmentHelper.getLevel(livingEntity.level().registryAccess(), SpectrumEnchantments.INEXORABLE, previousStack);
var newInexorable = SpectrumEnchantmentHelper.getLevel(livingEntity.level().registryAccess(), SpectrumEnchantments.INEXORABLE, currentStack);
Expand Down
Loading