From fb4c73daad90d2beb3e043d5877cc6e1bd724284 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Wed, 24 Jun 2026 15:50:06 +0300 Subject: [PATCH 01/21] Events and hooks port --- .../twilightforest/asmhooks/EntityHooks.java | 12 +- .../asmhooks/MultipartHooks.java | 4 +- .../twilightforest/asmhooks/PlayerHooks.java | 4 +- .../event}/CapabilityEvents.java | 21 +- .../client/event/ClientGameEvents.java | 146 ++++++++----- .../event/ClientRegistrationEvents.java | 106 ++++----- .../client/event/CloudEvents.java | 96 ++++----- .../client/event/FogHandler.java | 83 +++---- .../client/event/LockedBiomeToastHandler.java | 3 +- .../client/event/OverlayHandler.java | 114 ++++++---- .../event}/ProgressionEvents.java | 12 +- .../event}/RegistrationEvents.java | 68 +++--- .../{events => client/event}/ToolEvents.java | 40 ++-- .../client/event/TravellersClientEvents.java | 195 +++++++++++++---- .../event}/TravellersGearEvents.java | 37 ++-- .../twilightforest/events/CharmEvents.java | 202 +++++++++++++----- .../twilightforest/events/EntityEvents.java | 130 +++++++---- .../twilightforest/events/MiscEvents.java | 5 +- 18 files changed, 778 insertions(+), 500 deletions(-) rename src/main/java/twilightforest/{events => client/event}/CapabilityEvents.java (84%) rename src/main/java/twilightforest/{events => client/event}/ProgressionEvents.java (98%) rename src/main/java/twilightforest/{events => client/event}/RegistrationEvents.java (94%) rename src/main/java/twilightforest/{events => client/event}/ToolEvents.java (84%) rename src/main/java/twilightforest/{events => client/event}/TravellersGearEvents.java (92%) diff --git a/src/main/java/twilightforest/asmhooks/EntityHooks.java b/src/main/java/twilightforest/asmhooks/EntityHooks.java index 70c27121be..76b2c4a403 100644 --- a/src/main/java/twilightforest/asmhooks/EntityHooks.java +++ b/src/main/java/twilightforest/asmhooks/EntityHooks.java @@ -5,11 +5,11 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.common.NeoForgeMod; import net.neoforged.neoforge.fluids.FluidType; -import org.jetbrains.annotations.Nullable; import twilightforest.init.TFDataAttachments; import twilightforest.init.custom.TravellersModifiersManager; import twilightforest.item.travellers_gear.TravellersGearLogic; @@ -25,7 +25,6 @@ public class EntityHooks { * Injection Point:
* {@link net.minecraft.world.entity.LivingEntity#canStandOnFluid(FluidState)} */ - @Nullable public static boolean processWaterWalking(boolean o, LivingEntity livingEntity, FluidState fluidState) { if (!fluidState.is(FluidTags.WATER)) return o; @@ -34,8 +33,10 @@ public static boolean processWaterWalking(boolean o, LivingEntity livingEntity, return o; boolean isWaterWalking = TravellersGearLogic.isBelowMaxWaterWalkingSubmergedHeight(livingEntity) && !livingEntity.isShiftKeyDown(); - if (livingEntity.getFluidTypeHeight(NeoForgeMod.WATER_TYPE.value()) > 0 && isWaterWalking && livingEntity.level().getGameTime() % 3 == 1) + + if (livingEntity.getFluidHeight(FluidTags.WATER) > 0.0D && isWaterWalking && livingEntity.level().getGameTime() % 3 == 1) TravellersGearLogic.waterWalkingSplashEffect(livingEntity); + return isWaterWalking; } @@ -99,9 +100,10 @@ public static float resetFactorWithUnrestrained(float o, Entity entity) { * {@link net.minecraft.world.entity.Entity#move(MoverType, Vec3)}
*/ public static Entity resetStuckUnrestrained(Entity entity) { - if (!(entity instanceof LivingEntity living) || living.stuckSpeedMultiplier.lengthSqr() <= 1.0E-7 || !TravellersModifiersManager.isModifierActive(entity, TravellersModifiersManager.UNRESTRAINED_MODIFIER)) + if (!(entity instanceof LivingEntity living) || !TravellersModifiersManager.isModifierActive(entity, TravellersModifiersManager.UNRESTRAINED_MODIFIER)) return entity; - living.stuckSpeedMultiplier = Vec3.ZERO; + + living.makeStuckInBlock(Blocks.AIR.defaultBlockState(), new Vec3(1.0D, 1.0D, 1.0D)); return entity; } diff --git a/src/main/java/twilightforest/asmhooks/MultipartHooks.java b/src/main/java/twilightforest/asmhooks/MultipartHooks.java index a21d18315f..c9b3eb2099 100644 --- a/src/main/java/twilightforest/asmhooks/MultipartHooks.java +++ b/src/main/java/twilightforest/asmhooks/MultipartHooks.java @@ -3,13 +3,11 @@ import net.minecraft.client.Camera; import net.minecraft.client.DeltaTracker; import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.world.entity.Entity; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import tamaized.beanification.Autowired; -import twilightforest.asm.transformers.multipart.SendDirtyEntityDataTransformer; import twilightforest.util.multiparts.MultipartEntityUtil; import java.util.Iterator; @@ -39,7 +37,7 @@ public static Iterator resolveEntitiesForRendering(Iterator iter * Targets: {@link net.minecraft.client.renderer.entity.EntityRenderDispatcher#renderers} */ @Nullable - public static EntityRenderer resolveEntityRenderer(@Nullable EntityRenderer renderer, Entity entity) { + public static EntityRenderer resolveEntityRenderer(@Nullable EntityRenderer renderer, Entity entity) { return multipartEntityUtil.tryLookupTFPartRenderer(renderer, entity); } diff --git a/src/main/java/twilightforest/asmhooks/PlayerHooks.java b/src/main/java/twilightforest/asmhooks/PlayerHooks.java index f1aae7045b..b717a3f5e9 100644 --- a/src/main/java/twilightforest/asmhooks/PlayerHooks.java +++ b/src/main/java/twilightforest/asmhooks/PlayerHooks.java @@ -8,8 +8,8 @@ import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -import twilightforest.asm.transformers.player.GetFieldOfViewModifierTransformer; -import twilightforest.asm.transformers.player.ReduceMovementFoodExhaustionTransformer; +//import twilightforest.asm.transformers.player.GetFieldOfViewModifierTransformer; +//import twilightforest.asm.transformers.player.ReduceMovementFoodExhaustionTransformer; import twilightforest.init.TFAttributeModifiers; import twilightforest.init.TFDataAttachments; import twilightforest.init.TFDataComponents; diff --git a/src/main/java/twilightforest/events/CapabilityEvents.java b/src/main/java/twilightforest/client/event/CapabilityEvents.java similarity index 84% rename from src/main/java/twilightforest/events/CapabilityEvents.java rename to src/main/java/twilightforest/client/event/CapabilityEvents.java index f487228b61..045500ba76 100644 --- a/src/main/java/twilightforest/events/CapabilityEvents.java +++ b/src/main/java/twilightforest/client/event/CapabilityEvents.java @@ -1,6 +1,7 @@ -package twilightforest.events; +package twilightforest.client.event; import net.minecraft.core.BlockPos; +import net.minecraft.core.GlobalPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -9,6 +10,8 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; import net.neoforged.neoforge.event.entity.player.PlayerEvent; @@ -25,6 +28,7 @@ @Component public class CapabilityEvents { + private static final int COMMON_INVULNERABLE_DURATION = 20; @PostConstruct private void setup() { @@ -43,7 +47,7 @@ private void updateShields(EntityTickEvent.Post event) { private void updatePlayerCaps(PlayerTickEvent.Post event) { if (event.getEntity().getData(TFDataAttachments.FEATHER_FAN)) { - event.getEntity().setIgnoreFallDamageFromCurrentImpulse(true); + event.getEntity().setIgnoreFallDamageFromCurrentImpulse(true, Vec3.ZERO); event.getEntity().currentImpulseImpactPos = event.getEntity().position(); if (event.getEntity().onGround() || event.getEntity().isSwimming() || event.getEntity().isInWater()) { @@ -63,7 +67,7 @@ private void absorbShieldHits(LivingIncomingDamageEvent event) { if (living.invulnerableTime <= 0) { attachment.breakShield(living, false); FortificationShieldAttachment.addShieldBreakParticles(event.getSource(), living); - living.invulnerableTime = living.invulnerableDuration; + living.invulnerableTime = COMMON_INVULNERABLE_DURATION; } event.setCanceled(true); } @@ -73,7 +77,7 @@ private void absorbShieldHits(LivingIncomingDamageEvent event) { private void spawnInTFIfNecessary(PlayerEvent.PlayerRespawnEvent event) { if (!(event.getEntity() instanceof ServerPlayer serverPlayer)) return; - if (serverPlayer.getRespawnPosition() == null) { + if (serverPlayer.getRespawnConfig() == null) { newSpawnInTwilightForest(serverPlayer); } } @@ -92,16 +96,17 @@ public void playerLogsIn(PlayerEvent.PlayerLoggedInEvent event) { private static void newSpawnInTwilightForest(ServerPlayer player) { if (!TFConfig.newPlayersSpawnInTF) return; - ServerLevel level = player.getServer().getLevel(TFDimension.DIMENSION_KEY); + ServerLevel level = player.level().getServer().getLevel(TFDimension.DIMENSION_KEY); if (level == null) return; BlockPos newDefaultSpawn = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, player.blockPosition()); - player.changeDimension(TFConfig.portalForNewPlayerSpawn ? + player.teleport(TFConfig.portalForNewPlayerSpawn ? TFTeleporter.createTransition(player, level, newDefaultSpawn, true) : NoReturnTeleporter.createNoPortalTransition(level, player, newDefaultSpawn)); - player.setRespawnPosition(TFDimension.DIMENSION_KEY, newDefaultSpawn, player.getYRot(), true, false); + ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig(new LevelData.RespawnData(new GlobalPos(TFDimension.DIMENSION_KEY, newDefaultSpawn), player.getYRot(), player.getXRot()), true); + player.setRespawnPosition(respawnConfig, false); player.setData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST, Unit.INSTANCE); } @@ -110,7 +115,7 @@ private static void dataFixLegacyBanish(ServerPlayer player) { CompoundTag tagCompound = player.getPersistentData(); if (!tagCompound.contains(Player.PERSISTED_NBT_TAG)) return; - CompoundTag playerData = tagCompound.getCompound(Player.PERSISTED_NBT_TAG); + CompoundTag playerData = tagCompound.getCompound(Player.PERSISTED_NBT_TAG).get(); if (!playerData.contains("twilightforest_banished")) return; diff --git a/src/main/java/twilightforest/client/event/ClientGameEvents.java b/src/main/java/twilightforest/client/event/ClientGameEvents.java index 48cbe65594..6e9f5e4c1c 100644 --- a/src/main/java/twilightforest/client/event/ClientGameEvents.java +++ b/src/main/java/twilightforest/client/event/ClientGameEvents.java @@ -1,7 +1,6 @@ package twilightforest.client.event; import com.ibm.icu.text.RuleBasedNumberFormat; -import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; @@ -9,16 +8,16 @@ import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.model.HeadedModel; import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.LevelRenderer; -import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShapeRenderer; +import net.minecraft.client.renderer.rendertype.RenderTypes; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.sounds.Music; import net.minecraft.sounds.Musics; import net.minecraft.util.Mth; @@ -36,6 +35,7 @@ import net.minecraft.world.phys.shapes.VoxelShape; import net.neoforged.api.distmarker.Dist; import net.neoforged.fml.ModList; +import net.neoforged.fml.util.ObfuscationReflectionHelper; import net.neoforged.neoforge.client.event.*; import net.neoforged.neoforge.client.gui.VanillaGuiLayers; import net.neoforged.neoforge.common.NeoForge; @@ -53,14 +53,16 @@ import twilightforest.client.renderer.entity.MagicPaintingRenderer; import twilightforest.compat.curios.CuriosCompat; import twilightforest.config.TFConfig; -import twilightforest.data.tags.ItemTagGenerator; import twilightforest.entity.boss.bar.ClientTFBossBar; import twilightforest.events.HostileMountEvents; import twilightforest.init.*; import twilightforest.item.*; +import twilightforest.tags.TFItemTags; import twilightforest.util.HolderMatcher; import twilightforest.util.entities.EntityRenderingUtil; +import java.awt.*; +import java.lang.reflect.Field; import java.time.LocalDate; import java.time.Month; import java.util.HashSet; @@ -81,6 +83,8 @@ public class ClientGameEvents { private int aurora = 0; private int lastAurora = 0; + private static Field skyFlashTimeField; + @Autowired(dist = Dist.CLIENT) private HolderMatcher holderMatcher; @@ -102,8 +106,8 @@ private void setup() { NeoForge.EVENT_BUS.addListener(this::unrenderHeadWithTrophies); NeoForge.EVENT_BUS.addListener(this::updateBowFOV); - NeoForge.EVENT_BUS.addListener(CloudEvents::renderPrecipitation); - NeoForge.EVENT_BUS.addListener(CloudEvents::tickWeatherEffects); +// NeoForge.EVENT_BUS.addListener(CloudEvents::renderPrecipitation); +// NeoForge.EVENT_BUS.addListener(CloudEvents::tickWeatherEffects); NeoForge.EVENT_BUS.addListener(FogHandler::renderFog); NeoForge.EVENT_BUS.addListener(FogHandler::unloadFog); @@ -143,7 +147,19 @@ private void clearEntityRenderUtilMap(ScreenEvent.Closing event) { private void setMusicInDimension(SelectMusicEvent event) { Music music = event.getOriginalMusic(); if (Minecraft.getInstance().level != null && Minecraft.getInstance().player != null && (music == Musics.CREATIVE || music == Musics.UNDER_WATER) && TFDimension.isTwilightWorldOnClient(Minecraft.getInstance().level)) { - event.setMusic(Minecraft.getInstance().level.getBiomeManager().getNoiseBiomeAtPosition(Minecraft.getInstance().player.blockPosition()).value().getBackgroundMusic().orElse(Musics.GAME)); + var biomeHolder = Minecraft.getInstance().level.getBiomeManager().getNoiseBiomeAtPosition(Minecraft.getInstance().player.blockPosition()); + var biomeKeyOpt = biomeHolder.unwrapKey(); + if (biomeKeyOpt.isPresent()) { + var biomeKey = biomeKeyOpt.get(); + Music selectedMusic = Musics.GAME; // Default fallback + String keyString = biomeKey.toString(); + if (keyString.contains("lake") || keyString.contains("swamp")) { + selectedMusic = Musics.UNDER_WATER; + } else if (keyString.contains("enchanted")) { + selectedMusic = Musics.CREDITS; + } + event.setMusic(selectedMusic); + } } } @@ -161,30 +177,33 @@ private void removeHostileMountHealth(RenderGuiLayerEvent.Pre event) { /** * Render aurora effect as needed */ - private void renderAurora(RenderLevelStageEvent event) { + private void renderAurora(RenderLevelStageEvent.AfterWeather event) { if (Minecraft.getInstance().level == null) return; - if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_WEATHER && (aurora > 0 || lastAurora > 0) && TFShaders.AURORA != null) { - Tesselator tesselator = Tesselator.getInstance(); - BufferBuilder buffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + if (aurora > 0 || lastAurora > 0) { + var bufferSource = Minecraft.getInstance().renderBuffers().bufferSource(); + + VertexConsumer consumer = bufferSource.getBuffer(TFShaders.AURORA); + + float partialTicks = Minecraft.getInstance().getDeltaTracker().getGameTimeDeltaTicks(); + float alpha = (Mth.lerp(partialTicks, lastAurora, aurora)) / 60F * 0.5F; - final float scale = 2048F * (Minecraft.getInstance().gameRenderer.getRenderDistance() / 32F); - Vec3 pos = event.getCamera().getPosition(); + final float scale = 2048F * (Minecraft.getInstance().options.renderDistance().get() / 32F); + Vec3 pos = Minecraft.getInstance().gameRenderer.getMainCamera().position(); float y = (float) (256F - pos.y()); - buffer.addVertex(-scale, y, scale).setColor(1F, 1F, 1F, 1F); - buffer.addVertex(-scale, y, -scale).setColor(1F, 1F, 1F, 1F); - buffer.addVertex(scale, y, -scale).setColor(1F, 1F, 1F, 1F); - buffer.addVertex(scale, y, scale).setColor(1F, 1F, 1F, 1F); - - RenderSystem.enableBlend(); - RenderSystem.enableDepthTest(); - RenderSystem.setShaderColor(1F, 1F, 1F, (Mth.lerp(event.getPartialTick().getGameTimeDeltaTicks(), lastAurora, aurora)) / 60F * 0.5F); - TFShaders.AURORA.invokeThenEndTesselator( - Minecraft.getInstance().level == null ? 0 : Mth.abs((int) Minecraft.getInstance().level.getBiomeManager().biomeZoomSeed), - (float) pos.x(), (float) pos.y(), (float) pos.z(), buffer); - RenderSystem.setShaderColor(1F, 1F, 1F, 1F); - RenderSystem.disableDepthTest(); - RenderSystem.disableBlend(); + + PoseStack poseStack = event.getPoseStack(); + poseStack.pushPose(); + + PoseStack.Pose lastPose = poseStack.last(); + + consumer.addVertex(lastPose.pose(), -scale, y, scale).setColor(1F, 1F, 1F, alpha); + consumer.addVertex(lastPose.pose(), -scale, y, -scale).setColor(1F, 1F, 1F, alpha); + consumer.addVertex(lastPose.pose(), scale, y, -scale).setColor(1F, 1F, 1F, alpha); + consumer.addVertex(lastPose.pose(), scale, y, scale).setColor(1F, 1F, 1F, alpha); + + poseStack.popPose(); + bufferSource.endBatch(TFShaders.AURORA); } } @@ -207,9 +226,9 @@ private void clientTick(ClientTickEvent.Post event) { time++; lastAurora = aurora; - if (mc.level != null && mc.cameraEntity != null && !TFConfig.getValidAuroraBiomes(mc.level.registryAccess()).isEmpty()) { + if (mc.level != null && mc.getCameraEntity() != null && !TFConfig.getValidAuroraBiomes(mc.level.registryAccess()).isEmpty()) { RegistryAccess access = mc.level.registryAccess(); - Holder biome = mc.level.getBiome(mc.cameraEntity.blockPosition()); + Holder biome = mc.level.getBiome(mc.getCameraEntity().blockPosition()); if (TFConfig.getValidAuroraBiomes(access).stream().anyMatch(c -> holderMatcher.match(c, biome))) aurora++; else @@ -222,7 +241,9 @@ private void clientTick(ClientTickEvent.Post event) { BugModelAnimationHelper.animate(); if (mc.level != null) { - if (mc.level.getSkyFlashTime() > 0) { + Integer flashTime = ObfuscationReflectionHelper.getPrivateValue(ClientLevel.class, mc.level, "skyFlashTime"); + + if (flashTime != null && flashTime > 0) { MagicPaintingRenderer.lastLightning = mc.level.getGameTime(); } @@ -243,7 +264,7 @@ private void clientTick(ClientTickEvent.Post event) { Player player = mc.player; shakeIntensity = (float) (1.0F - mc.player.distanceToSqr(Vec3.atCenterOf(beanstalk.getBlockPos())) / Math.pow(16, 2)); if (shakeIntensity > 0) { - player.moveTo(player.getX(), player.getY(), player.getZ(), + player.absSnapTo(player.getX(), player.getY(), player.getZ(), player.getYRot() + (player.getRandom().nextFloat() - 0.5F) * shakeIntensity, player.getXRot() + (player.getRandom().nextFloat() * 2.5F - 1.25F) * shakeIntensity); shakeIntensity = 0.0F; @@ -273,7 +294,7 @@ private void addCustomTooltips(ItemTooltipEvent event) { event.getToolTip().add(1, EMPERORS_CLOTH_TOOLTIP); } - if (item.is(ItemTagGenerator.WIP)) { + if (item.is(TFItemTags.WIP)) { event.getToolTip().add(WIP_TEXT); } } @@ -293,12 +314,16 @@ private void updateBowFOV(ComputeFovModifierEvent event) { } } - private void unrenderHeadWithTrophies(RenderLivingEvent.Pre event) { - ItemStack stack = event.getEntity().getItemBySlot(EquipmentSlot.HEAD); - boolean visible = !(stack.getItem() instanceof TrophyItem) && !areCuriosEquipped(event.getEntity()); - boolean isPlayer = event.getEntity() instanceof Player; + private void unrenderHeadWithTrophies(RenderLivingEvent.Pre event) { + LocalPlayer localPlayer = Minecraft.getInstance().player; + if (localPlayer == null) { + return; + } + ItemStack stack = localPlayer.getItemBySlot(EquipmentSlot.HEAD); + boolean visible = !(stack.is(TFItemTags.TROPHIES)) && !areCuriosEquipped(localPlayer); + boolean isPlayer = true; if (event.getRenderer().getModel() instanceof HeadedModel headedModel) { - headedModel.getHead().visible = visible && (!isPlayer || headedModel.getHead().visible); // some mods like Better Combat can move player's head and hide it in the first person view + headedModel.getHead().visible = visible && (!isPlayer || headedModel.getHead().visible); if (event.getRenderer().getModel() instanceof HumanoidModel humanoidModel) { humanoidModel.hat.visible = visible && (!isPlayer || humanoidModel.hat.visible); } @@ -307,7 +332,7 @@ private void unrenderHeadWithTrophies(RenderLivingEvent.Pre event) { private boolean areCuriosEquipped(LivingEntity entity) { if (ModList.get().isLoaded("curios")) { - return CuriosCompat.isCurioEquippedAndVisible(entity, stack -> stack.getItem() instanceof TrophyItem); + return CuriosCompat.isCurioEquippedAndVisible(entity, stack -> stack.is(TFItemTags.TROPHIES)); } return false; } @@ -327,24 +352,37 @@ private void translateBookAuthor(ItemTooltipEvent event) { } } - private void renderGiantBlockOutlines(RenderHighlightEvent.Block event) { - BlockPos pos = event.getTarget().getBlockPos(); - BlockState state = event.getCamera().getEntity().level().getBlockState(pos); + private void renderGiantBlockOutlines(ExtractBlockOutlineRenderStateEvent event) { + BlockPos pos = event.getBlockPos(); + BlockState state = event.getBlockState(); - if (state.getBlock() instanceof MiniatureStructureBlock) { - event.setCanceled(true); - return; - } + boolean isMiniature = state.getBlock() instanceof MiniatureStructureBlock; LocalPlayer player = Minecraft.getInstance().player; - if (player != null && (player.getMainHandItem().getItem() instanceof GiantPickItem || (player.getMainHandItem().getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof GiantBlock))) { - event.setCanceled(true); - if (!state.isAir() && player.level().getWorldBorder().isWithinBounds(pos)) { - BlockPos offsetPos = new BlockPos(pos.getX() & ~0b11, pos.getY() & ~0b11, pos.getZ() & ~0b11); - VertexConsumer consumer = event.getMultiBufferSource().getBuffer(RenderType.lines()); - Vec3 xyz = Vec3.atLowerCornerOf(offsetPos).subtract(event.getCamera().getPosition()); - LevelRenderer.renderShape(event.getPoseStack(), consumer, GIANT_BLOCK, xyz.x(), xyz.y(), xyz.z(), 0.0F, 0.0F, 0.0F, 0.45F); - } + boolean isHoldingGiantTool = player != null && (player.getMainHandItem().getItem() instanceof GiantPickItem + || (player.getMainHandItem().getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof GiantBlock)); + + if (isMiniature || (isHoldingGiantTool && !state.isAir() && player.level().getWorldBorder().isWithinBounds(pos))) { + Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().position(); + BlockPos offsetPos = new BlockPos(pos.getX() & ~0b11, pos.getY() & ~0b11, pos.getZ() & ~0b11); + Vec3 xyz = Vec3.atLowerCornerOf(offsetPos).subtract(cameraPos); + + int outlineColor = 0x7F000000; + event.addCustomRenderer((_, bufferSource, poseStack, _, _) -> { + + if (isHoldingGiantTool && !isMiniature) { + VertexConsumer consumer = bufferSource.getBuffer(RenderTypes.lines()); + ShapeRenderer.renderShape( + poseStack, + consumer, + GIANT_BLOCK, + xyz.x(), xyz.y(), xyz.z(), + outlineColor, 1.0F + ); + } + + return false; + }); } } diff --git a/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java b/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java index 20793f8bb7..beda56852a 100644 --- a/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java +++ b/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java @@ -1,12 +1,14 @@ package twilightforest.client.event; import net.minecraft.client.Minecraft; -import net.minecraft.client.model.EntityModel; import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.model.animal.wolf.AdultWolfModel; import net.minecraft.client.model.geom.LayerDefinitions; +import net.minecraft.client.model.geom.PartPose; import net.minecraft.client.model.geom.builders.CubeDeformation; +import net.minecraft.client.model.geom.builders.CubeListBuilder; import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; import net.minecraft.client.model.monster.silverfish.SilverfishModel; import net.minecraft.client.model.monster.slime.SlimeModel; import net.minecraft.client.model.monster.spider.SpiderModel; @@ -24,8 +26,6 @@ import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; @@ -47,7 +47,6 @@ import twilightforest.client.model.TFModelLayers; import twilightforest.client.model.armor.*; import twilightforest.client.model.block.BrazierModel; -import twilightforest.client.model.block.ReactorDebrisModel; import twilightforest.client.model.block.aurorablock.UnbakedNoiseVaryingBlockStateModel; import twilightforest.client.model.block.carpet.RoyalRagsModelLoader; import twilightforest.client.model.block.connected.ConnectedTextureModelLoader; @@ -56,7 +55,6 @@ import twilightforest.client.model.block.patch.PatchModelLoader; import twilightforest.client.model.entity.*; import twilightforest.client.model.item.TravellersGearItemModel; -import twilightforest.client.model.item.TrollsteinnModel; import twilightforest.client.particle.*; import twilightforest.client.properties.*; import twilightforest.client.renderer.armor.TFArmorRenderer; @@ -67,6 +65,7 @@ import twilightforest.client.renderer.entity.layers.ShieldLayer; import twilightforest.client.renderer.map.ConqueredMapIconRenderer; import twilightforest.client.renderer.map.MagicMapPlayerIconRenderer; +import twilightforest.client.renderer.special.*; import twilightforest.client.renderer.tooltip.ItemDisplayTooltipComponent; import twilightforest.client.renderer.tooltip.PotionFlaskTooltipComponent; import twilightforest.client.renderer.tooltip.TravellersBeltTooltipComponent; @@ -77,8 +76,6 @@ import twilightforest.item.travellers_gear.TravellersGogglesItem; import twilightforest.util.woods.TFWoodTypes; -import java.util.Objects; - @Component(dist = Dist.CLIENT) public class ClientRegistrationEvents { @@ -87,10 +84,7 @@ public class ClientRegistrationEvents { @PostConstruct private void setup(IEventBus bus) { bus.addListener(EntityRenderersEvent.AddLayers.class, this::attachRenderLayers); - bus.addListener(this::bakeCustomModels); - bus.addListener(this::cacheJarLids); bus.addListener(this::clientSetup); - bus.addListener(this::registerAdditionalModels); bus.addListener(this::registerClientReloadListeners); bus.addListener(this::registerAtlases); bus.addListener(this::registerEntityRenderers); @@ -105,14 +99,13 @@ private void setup(IEventBus bus) { bus.addListener(this::registerRangeProperties); bus.addListener(this::registerSelectProperties); bus.addListener(this::registerItemModels); + bus.addListener(this::registerSpecialModelRenderers); bus.addListener(RegisterKeyMappingsEvent.class, event -> TFKeyBinds.KEY_MAPPINGS.forEach(event::register)); bus.addListener(ColorHandler::registerBlockColors); bus.addListener(ColorHandler::registerItemColors); - bus.addListener(TFShaders::registerShaders); - bus.addListener(OverlayHandler::registerOverlays); bus.addListener(RegisterClientTooltipComponentFactoriesEvent.class, event -> { @@ -152,36 +145,6 @@ private void registerSelectProperties(RegisterSelectItemModelPropertyEvent event event.register(TwilightForestMod.prefix("experiment_115_variant"), Experiment115Type.TYPE); } - private void bakeCustomModels(ModelEvent.ModifyBakingResult event) { - BakedModel oldModel = event.getModels().get(ModelResourceLocation.inventory(TwilightForestMod.prefix("trollsteinn"))); - models.put(ModelResourceLocation.inventory(TwilightForestMod.prefix("trollsteinn")), new TrollsteinnModel(oldModel)); - BakedModel defaultReactorDebrisModel = event.getModels().get(ModelResourceLocation.vanilla("netherrack", "")); - models.put(new ModelResourceLocation(TwilightForestMod.prefix("reactor_debris"), ""), new ReactorDebrisModel(defaultReactorDebrisModel)); - } - - private void registerAdditionalModels(ModelEvent.RegisterAdditional event) { - event.register(ShieldLayer.LOC); - event.register(ModelResourceLocation.standalone(TwilightForestMod.prefix("item/trophy"))); - event.register(ModelResourceLocation.standalone(TwilightForestMod.prefix("item/trophy_minor"))); - event.register(ModelResourceLocation.standalone(TwilightForestMod.prefix("item/trophy_quest"))); - event.register(TrollsteinnModel.LIT_TROLLSTEINN); - - for (JarRenderer.LidResource lid : JarRenderer.LID_LOCATION_LIST.get()) { - Identifier location = lid.identifier(); - String name = location.getPath(); - if (lid.customPath() != null) name = lid.customPath(); - event.register(ModelResourceLocation.standalone(TwilightForestMod.prefix("block/lid/" + name))); - } - } - - private void cacheJarLids(ModelEvent.BakingCompleted event) { - JarRenderer.LID_LOCATION_LIST.get().forEach((lid) -> { - String name = lid.identifier().getPath(); - if (lid.customPath() != null) name = lid.customPath(); - JarRenderer.LIDS.put(lid.lid(), event.getModels().get(ModelResourceLocation.standalone(TwilightForestMod.prefix("block/lid/" + name)))); - }); - } - private void clientSetup(FMLClientSetupEvent evt) { try { Class.forName("net.optifine.Config"); @@ -207,7 +170,7 @@ private void registerAtlases(RegisterTextureAtlasesEvent event) { } private void registerClientReloadListeners(AddClientReloadListenersEvent event) { - event.addListener(TwilightForestMod.prefix("texture_generator"), TextureGeneratorReloadListener.INSTANCE); +// event.addListener(TwilightForestMod.prefix("texture_generator"), TextureGeneratorReloadListener.INSTANCE); event.addListener(TwilightForestMod.prefix("armor_cache"), new TFArmorRenderer.ResourceReloadListener()); } @@ -321,6 +284,11 @@ private void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers even event.registerBlockEntityRenderer(TFBlockEntities.DRYING_RACK.get(), DryingRackRenderer::new); } + private static LayerDefinition createFixedLayer(MeshDefinition mesh, int texWidth, int texHeight) { + mesh.getRoot().addOrReplaceChild("hat", CubeListBuilder.create(), PartPose.ZERO); + return LayerDefinition.create(mesh, texWidth, texHeight); + } + private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefinitions event) { event.registerLayerDefinition(TFModelLayers.ARCTIC_ARMOR_INNER, () -> LayerDefinition.create(ArcticArmorModel.addPieces(LayerDefinitions.INNER_ARMOR_DEFORMATION), 64, 32)); event.registerLayerDefinition(TFModelLayers.ARCTIC_ARMOR_OUTER, () -> LayerDefinition.create(ArcticArmorModel.addPieces(LayerDefinitions.OUTER_ARMOR_DEFORMATION), 64, 32)); @@ -350,7 +318,6 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.ADHERENT, AdherentModel::create); event.registerLayerDefinition(TFModelLayers.ALPHA_YETI, AlphaYetiModel::create); - event.registerLayerDefinition(TFModelLayers.ARMORED_GIANT, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 32)); event.registerLayerDefinition(TFModelLayers.BIGHORN_SHEEP, BighornModel::create); event.registerLayerDefinition(TFModelLayers.BIGHORN_SHEEP_BABY, () -> BighornModel.create().apply(BighornModel.BABY_TRANSFORMER)); event.registerLayerDefinition(TFModelLayers.BLOCKCHAIN_GOBLIN, BlockChainGoblinModel::create); @@ -367,7 +334,6 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.DEER, DeerModel::create); event.registerLayerDefinition(TFModelLayers.DEER_BABY, () -> DeerModel.create().apply(DeerModel.BABY_TRANSFORMER)); event.registerLayerDefinition(TFModelLayers.FIRE_BEETLE, FireBeetleModel::create); - event.registerLayerDefinition(TFModelLayers.GIANT_MINER, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 32)); event.registerLayerDefinition(TFModelLayers.HARBINGER_CUBE, HarbingerCubeModel::create); event.registerLayerDefinition(TFModelLayers.HEDGE_SPIDER, SpiderModel::createSpiderBodyLayer); event.registerLayerDefinition(TFModelLayers.HELMET_CRAB, HelmetCrabModel::create); @@ -380,10 +346,8 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.KING_SPIDER, SpiderModel::createSpiderBodyLayer); event.registerLayerDefinition(TFModelLayers.KNIGHT_PHANTOM, KnightPhantomModel::create); event.registerLayerDefinition(TFModelLayers.KOBOLD, KoboldModel::create); - event.registerLayerDefinition(TFModelLayers.LICH_MINION, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); event.registerLayerDefinition(TFModelLayers.LICH, LichModel::create); event.registerLayerDefinition(TFModelLayers.LOWER_GOBLIN_KNIGHT, LowerGoblinKnightModel::create); - event.registerLayerDefinition(TFModelLayers.LOYAL_ZOMBIE, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); event.registerLayerDefinition(TFModelLayers.MAZE_SLIME, SlimeModel::createInnerBodyLayer); event.registerLayerDefinition(TFModelLayers.MAZE_SLIME_OUTER, SlimeModel::createOuterBodyLayer); event.registerLayerDefinition(TFModelLayers.MINOSHROOM, MinoshroomModel::create); @@ -392,7 +356,6 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.MOSQUITO_SWARM, MosquitoSwarmModel::create); event.registerLayerDefinition(TFModelLayers.NAGA, NagaModel::create); event.registerLayerDefinition(TFModelLayers.NAGA_BODY, NagaModel::create); - event.registerLayerDefinition(TFModelLayers.NOOP, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 0, 0)); event.registerLayerDefinition(TFModelLayers.PENGUIN, PenguinModel::create); event.registerLayerDefinition(TFModelLayers.PENGUIN_BABY, () -> PenguinModel.create().apply(PenguinModel.BABY_TRANSFORMER)); event.registerLayerDefinition(TFModelLayers.PINCH_BEETLE, PinchBeetleModel::create); @@ -400,9 +363,6 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.QUEST_RAM, QuestRamModel::create); event.registerLayerDefinition(TFModelLayers.RAVEN, RavenModel::create); event.registerLayerDefinition(TFModelLayers.REDCAP, RedcapModel::create); - event.registerLayerDefinition(TFModelLayers.REDCAP_ARMOR_INNER, () -> LayerDefinition.create(HumanoidModel.createMesh(new CubeDeformation(0.25F), 0.7F), 64, 32)); - event.registerLayerDefinition(TFModelLayers.REDCAP_ARMOR_OUTER, () -> LayerDefinition.create(HumanoidModel.createMesh(new CubeDeformation(0.65F), 0.7F), 64, 32)); - event.registerLayerDefinition(TFModelLayers.RISING_ZOMBIE, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); event.registerLayerDefinition(TFModelLayers.ROVING_CUBE, CubeOfAnnihilationModel::create); event.registerLayerDefinition(TFModelLayers.SKELETON_DRUID, SkeletonDruidModel::create); event.registerLayerDefinition(TFModelLayers.SLIME_BEETLE, SlimeBeetleModel::create); @@ -432,6 +392,15 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.RED_THREAD, RedThreadModel::create); event.registerLayerDefinition(TFModelLayers.KNIGHTMETAL_SHIELD, KnightmetalShieldModel::create); + + event.registerLayerDefinition(TFModelLayers.ARMORED_GIANT, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 32)); + event.registerLayerDefinition(TFModelLayers.GIANT_MINER, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 32)); + event.registerLayerDefinition(TFModelLayers.LICH_MINION, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); + event.registerLayerDefinition(TFModelLayers.LOYAL_ZOMBIE, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); + event.registerLayerDefinition(TFModelLayers.NOOP, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 0, 0)); + event.registerLayerDefinition(TFModelLayers.REDCAP_ARMOR_INNER, () -> createFixedLayer(HumanoidModel.createMesh(new CubeDeformation(0.25F), 0.7F), 64, 32)); + event.registerLayerDefinition(TFModelLayers.REDCAP_ARMOR_OUTER, () -> createFixedLayer(HumanoidModel.createMesh(new CubeDeformation(0.65F), 0.7F), 64, 32)); + event.registerLayerDefinition(TFModelLayers.RISING_ZOMBIE, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); } private void registerParticleFactories(RegisterParticleProvidersEvent event) { @@ -466,6 +435,21 @@ private void registerParticleFactories(RegisterParticleProvidersEvent event) { event.registerSpriteSet(TFParticleType.SHIELD_BREAK.get(), CustomTextureParticle.ShieldBreak::new); } + private void registerSpecialModelRenderers(RegisterSpecialModelRendererEvent event) { + event.register(TwilightForestMod.prefix("item/template_skull_candle"), SkullCandleSpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("item/template_trophy"), TrophySpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("block/mason_jar"), MasonJarSpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("block/candelabra"), CandelabraSpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("item/mystic_crown"), MysticCrownSpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("item/knightmetal_shield"), KnightmetalShieldSpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("item/keepsake_casket"), KeepsakeCasketSpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("item/skull_chest"), SkullChestSpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("item/cicada"), CicadaSpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("item/firefly"), FireflySpecialRenderer.Unbaked.MAP_CODEC); + event.register(TwilightForestMod.prefix("item/moonworm"), MoonwormSpecialRenderer.Unbaked.MAP_CODEC); + } + + private void registerClientExtensions(RegisterClientExtensionsEvent event) { event.registerBlock(new IClientBlockExtensions() { @Override @@ -586,20 +570,22 @@ private void attachRenderLayers(EntityRenderersEvent.AddLayers event) { BakedMultiPartRenderers.bakeMultiPartRenderers(event.getContext()); for (EntityType type : event.getEntityTypes()) { var renderer = event.getRenderer(type); - if (renderer instanceof LivingEntityRenderer living) { - attachRenderLayers(living); + if (renderer instanceof LivingEntityRenderer living) { + attachToLiving(living); } } - - event.getSkins().forEach(renderer -> { - LivingEntityRenderer> skin = event.getSkin(renderer); - attachRenderLayers(Objects.requireNonNull(skin)); + event.getSkins().forEach(skinName -> { + var skinRenderer = event.getPlayerRenderer(skinName); + if (skinRenderer != null) { + attachToLiving(skinRenderer); + } }); } - private > void attachRenderLayers(LivingEntityRenderer renderer) { - renderer.addLayer(new ShieldLayer<>(renderer)); - renderer.addLayer(new IceLayer<>(renderer)); + @SuppressWarnings({"unchecked", "rawtypes"}) + private static void attachToLiving(net.minecraft.client.renderer.entity.LivingEntityRenderer renderer) { + renderer.addLayer(new ShieldLayer(renderer)); + renderer.addLayer(new IceLayer(renderer)); } public static boolean isOptifinePresent() { diff --git a/src/main/java/twilightforest/client/event/CloudEvents.java b/src/main/java/twilightforest/client/event/CloudEvents.java index 7c17e4ab1b..1f1548d8eb 100644 --- a/src/main/java/twilightforest/client/event/CloudEvents.java +++ b/src/main/java/twilightforest/client/event/CloudEvents.java @@ -1,17 +1,17 @@ package twilightforest.client.event; -import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; +import net.minecraft.client.GraphicsPreset; import net.minecraft.client.Minecraft; -import net.minecraft.client.ParticleStatus; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.LevelRenderer; -import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.rendertype.RenderTypes; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ParticleStatus; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.FluidTags; @@ -38,6 +38,7 @@ public class CloudEvents { private static final List RENDER_HELPER = new ArrayList<>(); + private static int twilightRainSoundTime = 0; record PrecipitationRenderHelper(BlockPos cloudPos, Biome.Precipitation precipitation, float precipitationLevel, int rainOnY) { @@ -48,7 +49,7 @@ protected static void tickWeatherEffects(ClientTickEvent.Post event) { if (!mc.isPaused()) { if (mc.level != null && TFConfig.getClientCloudBlockPrecipitationDistance() > 0) { // Semi vanilla copy of the weather tick, but made to work with cloud blocks instead - Vec3 vec3 = mc.gameRenderer.getMainCamera().getPosition(); + Vec3 vec3 = mc.gameRenderer.getMainCamera().position(); if (mc.level.getGameTime() % 10L == 0L) { RENDER_HELPER.clear(); @@ -60,7 +61,7 @@ protected static void tickWeatherEffects(ClientTickEvent.Post event) { int floorY = Mth.floor(camY); int floorZ = Mth.floor(camZ); - int renderDistance = Minecraft.useFancyGraphics() ? 10 : 5; + int renderDistance = Minecraft.getInstance().options.graphicsPreset().get() != GraphicsPreset.FAST ? 10 : 5; int precipitationDistance = TFConfig.getClientCloudBlockPrecipitationDistance(); BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); @@ -115,8 +116,8 @@ protected static void tickWeatherEffects(ClientTickEvent.Post event) { if (!Heightmap.Types.MOTION_BLOCKING.isOpaque().test(mc.level.getBlockState(highestRainyPos.below()))) continue; - if (yetToMakeASound && particlePos != null && randomsource.nextInt(3) < mc.levelRenderer.rainSoundTime++) { - mc.levelRenderer.rainSoundTime = 0; + if (yetToMakeASound && particlePos != null && randomsource.nextInt(3) < twilightRainSoundTime++) { + twilightRainSoundTime = 0; if (particlePos.getY() > camPos.getY() + 1 && mc.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, camPos).getY() > Mth.floor((float) camPos.getY())) { mc.level.playLocalSound(particlePos, SoundEvents.WEATHER_RAIN_ABOVE, SoundSource.WEATHER, 0.1F, 0.5F, false); } else { @@ -149,16 +150,14 @@ protected static void tickWeatherEffects(ClientTickEvent.Post event) { } } - protected static void renderPrecipitation(RenderLevelStageEvent event) { - if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_WEATHER && TFConfig.getClientCloudBlockPrecipitationDistance() > 0 && !RENDER_HELPER.isEmpty()) { + protected static void renderPrecipitation(RenderLevelStageEvent.AfterWeather event) { + if (TFConfig.getClientCloudBlockPrecipitationDistance() > 0 && !RENDER_HELPER.isEmpty()) { Minecraft minecraft = Minecraft.getInstance(); if (minecraft.level == null) return; - float partialTick = minecraft.getTimer().getGameTimeDeltaPartialTick(false); - LightTexture lightTexture = minecraft.gameRenderer.lightTexture(); + float partialTick = minecraft.getDeltaTracker().getGameTimeDeltaPartialTick(false); int ticks = minecraft.levelRenderer.getTicks(); - lightTexture.turnOnLightLayer(); - Vec3 vec3 = event.getCamera().getPosition(); + Vec3 vec3 = minecraft.gameRenderer.getMainCamera().position(); double camX = vec3.x(); double camY = vec3.y(); double camZ = vec3.z(); @@ -167,18 +166,9 @@ protected static void renderPrecipitation(RenderLevelStageEvent event) { int floorY = Mth.floor(camY); int floorZ = Mth.floor(camZ); - Tesselator tesselator = Tesselator.getInstance(); - BufferBuilder bufferbuilder = null; - RenderSystem.disableCull(); - RenderSystem.enableBlend(); - RenderSystem.enableDepthTest(); - RenderSystem.depthMask(Minecraft.useShaderTransparency()); + int renderDistance = minecraft.options.graphicsPreset().get() != GraphicsPreset.FAST ? 10 : 5; - int renderDistance = Minecraft.useFancyGraphics() ? 10 : 5; - - int tesselatorCheck = -1; float fullTick = (float) ticks + partialTick; - RenderSystem.setShader(GameRenderer::getParticleShader); BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); for (PrecipitationRenderHelper helper : RENDER_HELPER) { @@ -198,13 +188,7 @@ protected static void renderPrecipitation(RenderLevelStageEvent event) { RandomSource random = RandomSource.create((long) roofX * roofX * 3121 + roofX * 45238971L ^ (long) roofZ * roofZ * 418711 + roofZ * 13761L); if (helper.precipitation() == Biome.Precipitation.RAIN) { - if (tesselatorCheck != 0) { - if (tesselatorCheck >= 0) BufferUploader.drawWithShader(bufferbuilder.buildOrThrow()); - - tesselatorCheck = 0; - RenderSystem.setShaderTexture(0, TFWeatherRenderer.RAIN_TEXTURES); - bufferbuilder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE); - } + RenderType renderType = RenderTypes.entityTranslucent(TFWeatherRenderer.RAIN_TEXTURES); int offset = ticks + roofX * roofX * 3121 + roofX * 45238971 + roofZ * roofZ * 418711 + roofZ * 13761 & 31; float uvOffset = -((float) offset + partialTick) / 32.0F * (3.0F + random.nextFloat()); @@ -212,21 +196,17 @@ protected static void renderPrecipitation(RenderLevelStageEvent event) { double zDiff = (double) roofZ + 0.5D - camZ; float distance = (float) Math.sqrt(xDiff * xDiff + zDiff * zDiff) / (float) renderDistance; float alpha = ((1.0F - distance * distance) * 0.5F + 0.5F) * helper.precipitationLevel(); - mutableBlockPos.set(roofX, Math.max(helper.rainOnY(), floorY), roofZ); - int lightColor = LevelRenderer.getLightColor(minecraft.level, mutableBlockPos); - bufferbuilder.addVertex((float) (roofX - camX - rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F, (float) botY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(lightColor); - bufferbuilder.addVertex((float) (roofX - camX + rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F, (float) botY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(lightColor); - bufferbuilder.addVertex((float) (roofX - camX + rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F, (float) topY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(lightColor); - bufferbuilder.addVertex((float) (roofX - camX - rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F, (float) topY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(lightColor); - } else if (helper.precipitation() == Biome.Precipitation.SNOW) { - if (tesselatorCheck != 1) { - if (tesselatorCheck == 0) BufferUploader.drawWithShader(bufferbuilder.buildOrThrow()); + MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); + VertexConsumer vC = bufferSource.getBuffer(renderType); - tesselatorCheck = 1; - RenderSystem.setShaderTexture(0, TFWeatherRenderer.SNOW_TEXTURES); - bufferbuilder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE); - } + vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX - rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F, (float) botY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(0xF000F0); + vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX + rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F, (float) botY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(0xF000F0); + vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX + rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F, (float) topY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(0xF000F0); + vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX - rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F, (float) topY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(0xF000F0); + + } else if (helper.precipitation() == Biome.Precipitation.SNOW) { + RenderType renderType = RenderTypes.entityTranslucent(TFWeatherRenderer.SNOW_TEXTURES); float offset = -((float) (ticks & 511) + partialTick) / 512.0F; float uOffset = (float) (random.nextDouble() + (double) fullTick * 0.01D * (double) ((float) random.nextGaussian())); @@ -235,26 +215,24 @@ protected static void renderPrecipitation(RenderLevelStageEvent event) { double zDiff = (double) roofZ + 0.5D - camZ; float distance = (float) Math.sqrt(xDiff * xDiff + zDiff * zDiff) / (float) renderDistance; float alpha = ((1.0F - distance * distance) * 0.3F + 0.5F) * helper.precipitationLevel(); - mutableBlockPos.set(roofX, Math.max(helper.rainOnY(), floorY), roofZ); - int lightColor = LevelRenderer.getLightColor(minecraft.level, mutableBlockPos); - int v = lightColor >> 16 & '\uffff'; - int u = lightColor & '\uffff'; + int defaultLight = 0xF000F0; + int v = defaultLight >> 16 & '\uffff'; + int u = defaultLight & '\uffff'; v = (v * 3 + 240) / 4; u = (u * 3 + 240) / 4; + int packedWeatherLight = u | v << 16; - bufferbuilder.addVertex((float) (roofX - camX - rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F + uOffset, (float) botY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setUv2(u, v); - bufferbuilder.addVertex((float) (roofX - camX + rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F + uOffset, (float) botY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setUv2(u, v); - bufferbuilder.addVertex((float) (roofX - camX + rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F + uOffset, (float) topY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setUv2(u, v); - bufferbuilder.addVertex((float) (roofX - camX - rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F + uOffset, (float) topY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setUv2(u, v); - } - } + MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); + VertexConsumer vC = bufferSource.getBuffer(renderType); - if (tesselatorCheck >= 0) BufferUploader.drawWithShader(bufferbuilder.buildOrThrow());; + vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX - rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F + uOffset, (float) botY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(packedWeatherLight); + vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX + rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F + uOffset, (float) botY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(packedWeatherLight); + vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX + rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F + uOffset, (float) topY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(packedWeatherLight); + vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX - rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F + uOffset, (float) topY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(packedWeatherLight); + } - RenderSystem.enableCull(); - RenderSystem.disableBlend(); - lightTexture.turnOffLightLayer(); + } } } } diff --git a/src/main/java/twilightforest/client/event/FogHandler.java b/src/main/java/twilightforest/client/event/FogHandler.java index cf8630350d..14c01491ff 100644 --- a/src/main/java/twilightforest/client/event/FogHandler.java +++ b/src/main/java/twilightforest/client/event/FogHandler.java @@ -1,78 +1,59 @@ package twilightforest.client.event; -import net.minecraft.client.Minecraft; +import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.FogRenderer; import net.minecraft.util.Mth; import net.minecraft.world.level.material.FogType; +import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.client.event.ViewportEvent; import net.neoforged.neoforge.event.level.LevelEvent; -import twilightforest.client.TwilightForestRenderInfo; import twilightforest.init.TFBiomes; +import twilightforest.init.TFDimension; import javax.annotation.Nullable; public class FogHandler { - - private static boolean SKY_CHUNK_LOADED = false; - - private static float SKY_FAR = 0.0F; - private static float SKY_NEAR = 0.0F; - - private static boolean TERRAIN_CHUNK_LOADED = false; + private static boolean FOG_CHUNK_LOADED = false; private static float TERRAIN_FAR = 0.0F; private static float TERRAIN_NEAR = 0.0F; - protected static void renderFog(ViewportEvent.RenderFog event) { - if (event.getType().equals(FogType.NONE) && Minecraft.getInstance().cameraEntity instanceof LocalPlayer player && player.level() instanceof ClientLevel clientLevel && clientLevel.effects() instanceof TwilightForestRenderInfo) { - if (event.getMode().equals(FogRenderer.FogMode.FOG_SKY)) { - if (SKY_CHUNK_LOADED) { - event.setCanceled(true); - boolean spooky = isSpooky(clientLevel, player); - - float far = spooky ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); - float near = spooky ? 0.0F : event.getNearPlaneDistance(); - - SKY_FAR = Mth.lerp(0.003F, SKY_FAR, far); - SKY_NEAR = Mth.lerp(0.003F * (SKY_NEAR < near ? 0.5F : 2.0F), SKY_NEAR, near); - - event.setFarPlaneDistance(SKY_FAR); - event.setNearPlaneDistance(SKY_NEAR); - } else if (clientLevel.isLoaded(player.blockPosition())) { //We do a first-time set up after the chunk the player is in is loaded - SKY_CHUNK_LOADED = true; - SKY_FAR = isSpooky(clientLevel, player) ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); - SKY_NEAR = isSpooky(clientLevel, player) ? 0.0F : event.getNearPlaneDistance(); - } - } else { - if (TERRAIN_CHUNK_LOADED) { - event.setCanceled(true); - boolean spooky = isSpooky(clientLevel, player); - - float far = spooky ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); - float near = spooky ? far * 0.75F : event.getNearPlaneDistance(); - - TERRAIN_FAR = Mth.lerp(0.003F, TERRAIN_FAR, far); - TERRAIN_NEAR = Mth.lerp(0.003F * (TERRAIN_NEAR < near ? 0.5F : 2.0F), TERRAIN_NEAR, near); - - event.setFarPlaneDistance(TERRAIN_FAR); - event.setNearPlaneDistance(TERRAIN_NEAR); - } else if (SKY_CHUNK_LOADED || clientLevel.isLoaded(player.blockPosition())) { //SKY is always called first in vanilla, so we only need to check if the SKY flag is true, but just in case - TERRAIN_CHUNK_LOADED = true; - TERRAIN_FAR = isSpooky(clientLevel, player) ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); - TERRAIN_NEAR = isSpooky(clientLevel, player) ? TERRAIN_FAR * 0.75F : event.getNearPlaneDistance(); + @SubscribeEvent + public static void renderFog(ViewportEvent.RenderFog event) { + Camera camera = event.getCamera(); + + if (camera.getFluidInCamera() == FogType.NONE) { + if (camera.entity() instanceof LocalPlayer player && player.level() instanceof ClientLevel clientLevel) { + if (clientLevel.dimension() == TFDimension.DIMENSION_KEY) { + if (FOG_CHUNK_LOADED) { + boolean spooky = isSpooky(clientLevel, player); + float far = spooky ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); + float near = spooky ? far * 0.75F : event.getNearPlaneDistance(); + + TERRAIN_FAR = Mth.lerp(0.003F, TERRAIN_FAR, far); + TERRAIN_NEAR = Mth.lerp(0.003F * (TERRAIN_NEAR < near ? 0.5F : 2.0F), TERRAIN_NEAR, near); + event.setFarPlaneDistance(TERRAIN_FAR); + event.setNearPlaneDistance(TERRAIN_NEAR); + + } else if (clientLevel.isLoaded(player.blockPosition())) { + FOG_CHUNK_LOADED = true; + + TERRAIN_FAR = isSpooky(clientLevel, player) ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); + TERRAIN_NEAR = isSpooky(clientLevel, player) ? TERRAIN_FAR * 0.75F : event.getNearPlaneDistance(); + } } } } } - protected static void unloadFog(LevelEvent.Unload event) { //As supernatural as the fog is, it shouldn't follow the player between worlds - SKY_CHUNK_LOADED = false; - TERRAIN_CHUNK_LOADED = false; + @SubscribeEvent + public static void unloadFog(LevelEvent.Unload event) { + FOG_CHUNK_LOADED = false; } private static boolean isSpooky(@Nullable ClientLevel level, @Nullable LocalPlayer player) { - return level != null && player != null && level.getBiome(player.blockPosition()).is(TFBiomes.SPOOKY_FOREST); + if (level == null || player == null) return false; + return level.getBiome(player.blockPosition()).is(TFBiomes.SPOOKY_FOREST); } } diff --git a/src/main/java/twilightforest/client/event/LockedBiomeToastHandler.java b/src/main/java/twilightforest/client/event/LockedBiomeToastHandler.java index 09aaf72c24..6cc72b0fb5 100644 --- a/src/main/java/twilightforest/client/event/LockedBiomeToastHandler.java +++ b/src/main/java/twilightforest/client/event/LockedBiomeToastHandler.java @@ -12,7 +12,6 @@ import java.util.Optional; public class LockedBiomeToastHandler { - private static boolean shownToast = false; private static int timeUntilToast = 60; @@ -29,7 +28,7 @@ protected static void tickLockedToastLogic(ClientTickEvent.Post event) { if (restriction.isPresent() && restriction.get().lockedBiomeToast() != null) { timeUntilToast--; if (!shownToast && timeUntilToast <= 0) { - Minecraft.getInstance().getToasts().addToast(new LockedBiomeToast(restriction.get().lockedBiomeToast())); + Minecraft.getInstance().getToastManager().addToast(new LockedBiomeToast(restriction.get().lockedBiomeToast())); shownToast = true; } } else { diff --git a/src/main/java/twilightforest/client/event/OverlayHandler.java b/src/main/java/twilightforest/client/event/OverlayHandler.java index 8587f4040a..a787d8c507 100644 --- a/src/main/java/twilightforest/client/event/OverlayHandler.java +++ b/src/main/java/twilightforest/client/event/OverlayHandler.java @@ -1,20 +1,23 @@ package twilightforest.client.event; import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.datafixers.util.Pair; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.sprite.SpriteId; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.Identifier; import net.minecraft.world.InteractionHand; +import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ChunkPos; @@ -22,6 +25,7 @@ import net.neoforged.api.distmarker.Dist; import net.neoforged.neoforge.client.event.RegisterGuiLayersEvent; import net.neoforged.neoforge.client.gui.VanillaGuiLayers; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import twilightforest.TwilightForestMod; import tamaized.beanification.Autowired; @@ -57,9 +61,7 @@ protected static void registerOverlays(RegisterGuiLayersEvent event) { LocalPlayer player = minecraft.player; Gui gui = minecraft.gui; if (player != null && !minecraft.options.hideGui && TFConfig.showQuestRamCrosshairIndicator) { - RenderSystem.enableBlend(); renderIndicator(minecraft, graphics, gui, player, graphics.guiWidth(), graphics.guiHeight()); - RenderSystem.disableBlend(); } }); event.registerAbove(VanillaGuiLayers.VEHICLE_HEALTH, TwilightForestMod.prefix("hostile_mount_hunger_bar"), (graphics, partialTicks) -> { @@ -69,11 +71,11 @@ protected static void registerOverlays(RegisterGuiLayersEvent event) { if (!minecraft.options.hideGui && minecraft.gameMode.canHurtPlayer() && player != null && HostileMountEvents.isRidingUnfriendly(player)) { int xPos = graphics.guiWidth() / 2 + 91; int yPos = graphics.guiHeight() - gui.rightHeight; - gui.renderFood(graphics, player, yPos, xPos); + renderModdedFoodBar(graphics, player, xPos, yPos); gui.rightHeight += 10; } }); - event.registerAboveAll(TwilightForestMod.prefix("ore_meter_stats"), (graphics, partialTicks) -> { + event.registerAboveAll(TwilightForestMod.prefix("ore_meter_stats"), (graphics, _) -> { Minecraft minecraft = Minecraft.getInstance(); LocalPlayer player = minecraft.player; Gui gui = minecraft.gui; @@ -99,16 +101,25 @@ protected static void registerOverlays(RegisterGuiLayersEvent event) { if (player != null) { TFPortalAttachment portal = player.getData(TFDataAttachments.TF_PORTAL_COOLDOWN); if (portal.getPortalTimer() > 0) { - RenderSystem.disableDepthTest(); - RenderSystem.depthMask(false); - RenderSystem.enableBlend(); - graphics.setColor(1.0F, 1.0F, 1.0F, (float) portal.getPortalTimer() / (float) TFPortalAttachment.MAX_TICKS); - TextureAtlasSprite textureatlassprite = minecraft.getBlockRenderer().getBlockModelShaper().getParticleIcon(TFBlocks.TWILIGHT_PORTAL.get().defaultBlockState()); - graphics.blit(0, 0, -90, window.getGuiScaledWidth(), window.getGuiScaledHeight(), textureatlassprite); - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - RenderSystem.enableDepthTest(); - graphics.setColor(1.0F, 1.0F, 1.0F, 1.0F); + float alpha = (float) portal.getPortalTimer() / (float) TFPortalAttachment.MAX_TICKS; + int packedColor = ((int)(alpha * 255.0F) << 24) | 0xFFFFFF; + + Identifier portalTextureLocation = BuiltInRegistries.BLOCK.getKey(TFBlocks.TWILIGHT_PORTAL.get()).withPrefix("block/"); + + TextureAtlasSprite sprite = minecraft.getAtlasManager().get(new SpriteId(TextureAtlas.LOCATION_BLOCKS, portalTextureLocation)); + + graphics.blit( + RenderPipelines.GUI_TEXTURED, + TextureAtlas.LOCATION_BLOCKS, + 0, 0, + sprite.getU0() * sprite.contents().width(), + sprite.getV0() * sprite.contents().height(), + window.getGuiScaledWidth(), + window.getGuiScaledHeight(), + sprite.contents().width(), + sprite.contents().height(), + packedColor + ); } } }); @@ -120,21 +131,52 @@ protected static void registerOverlays(RegisterGuiLayersEvent event) { }); } - private static void renderIndicator(Minecraft minecraft, GuiGraphics graphics, Gui gui, Player player, int screenWidth, int screenHeight) { + private static void renderModdedFoodBar(GuiGraphicsExtractor graphics, Player player, int xPos, int yPos) { + Identifier guiIcons = Identifier.fromNamespaceAndPath("minecraft", "textures/gui/icons.png"); + + int foodLevel = player.getFoodData().getFoodLevel(); + boolean hasHungerEffect = player.hasEffect(MobEffects.HUNGER); + + int uOffset = hasHungerEffect ? 88 : 16; + int uIconOffset = hasHungerEffect ? 115 : 52; + + int textureWidth = 256; + int textureHeight = 256; + + for (int i = 0; i < 10; ++i) { + int idx = i * 2 + 1; + int x = xPos - i * 8; + + graphics.blit( + RenderPipelines.GUI_TEXTURED, + guiIcons, + x, yPos, + uOffset, 27, + 9, 9, + textureWidth, textureHeight + ); + + if (idx < foodLevel) { + graphics.blit(RenderPipelines.GUI_TEXTURED, guiIcons, x, yPos, uIconOffset, 27, 9, 9, textureWidth, textureHeight); + } else if (idx == foodLevel) { + graphics.blit(RenderPipelines.GUI_TEXTURED, guiIcons, x, yPos, uIconOffset + 9, 27, 9, 9, textureWidth, textureHeight); + } + } + } + + private static void renderIndicator(Minecraft minecraft, GuiGraphicsExtractor graphics, Gui gui, Player player, int screenWidth, int screenHeight) { if (minecraft.options.getCameraType().isFirstPerson() && (minecraft.gameMode.getPlayerMode() != GameType.SPECTATOR || gui.canRenderCrosshairForSpectator(minecraft.hitResult)) && minecraft.crosshairPickEntity instanceof QuestRam ram) { - ItemStack stack = player.getInventory().getItem(player.getInventory().selected); + ItemStack stack = player.getInventory().getItem(player.getInventory().getSelectedSlot()); if (!stack.isEmpty()) { for (var questEntry : questingRamCurrentContext.getContext().questItems().entrySet()) { if (questEntry.getValue().test(stack)) { - RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.ONE_MINUS_DST_COLOR, GlStateManager.DestFactor.ONE_MINUS_SRC_COLOR, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); int j = ((screenHeight - 1) / 2) - 11; int k = ((screenWidth - 1) / 2) - 3; if (!ram.isColorPresent(questEntry.getKey())) { - graphics.blitSprite(QUESTING_RAM_X_SPRITE, k, j, 7, 7); + graphics.blitSprite(RenderPipelines.GUI_INVERT, QUESTING_RAM_X_SPRITE, k, j, 7, 7); } else { - graphics.blitSprite(QUESTING_RAM_CHECK_SPRITE, k, j, 7, 7); + graphics.blitSprite(RenderPipelines.GUI_INVERT, QUESTING_RAM_CHECK_SPRITE, k, j, 7, 7); } - RenderSystem.defaultBlendFunc(); break; } } @@ -142,14 +184,14 @@ private static void renderIndicator(Minecraft minecraft, GuiGraphics graphics, G } } - private static void renderShieldCount(GuiGraphics graphics, Gui gui, int screenWidth, int screenHeight, int shieldCount) { + private static void renderShieldCount(GuiGraphicsExtractor graphics, Gui gui, int screenWidth, int screenHeight, int shieldCount) { for (int i = 0; i < Math.min(shieldCount, 10); i++) { - graphics.blitSprite(FORTIFICATION_SHIELD_SPRITE, screenWidth / 2 - 91 + (i * 8), screenHeight - gui.leftHeight, 9, 9); + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, FORTIFICATION_SHIELD_SPRITE, screenWidth / 2 - 91 + (i * 8), screenHeight - gui.leftHeight, 9, 9); } gui.leftHeight += 10; } - private static void renderOreMeterStats(GuiGraphics graphics, Player player) { + private static void renderOreMeterStats(GuiGraphicsExtractor graphics, Player player) { if (player.isHolding(TFItems.ORE_METER.get())) { InteractionHand handToUse = player.getItemInHand(InteractionHand.MAIN_HAND).is(TFItems.ORE_METER.get()) ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND; ItemStack selectedMeter = player.getItemInHand(handToUse); @@ -160,7 +202,7 @@ private static void renderOreMeterStats(GuiGraphics graphics, Player player) { component = component.copy().append("."); } graphics.fill(0, 0, 56, 16, 0x9b000000); - graphics.drawString(Minecraft.getInstance().font, component, 4, 4, 16777215, false); + graphics.text(Minecraft.getInstance().font, component, 4, 4, 16777215, false); } else { OreScannerData oreScannerData = selectedMeter.get(TFDataComponents.ORE_DATA); @@ -189,7 +231,7 @@ private static void initTooltips(long id, int range, OreScannerData data) { int totalScanned = data.totalScannedBlocks(); List headerRowTexts = ImmutableList.of( - Component.translatable("misc.twilightforest.ore_meter_range", range, pos.x, pos.z), + Component.translatable("misc.twilightforest.ore_meter_range", range, pos.x(), pos.z()), Component.translatable("misc.twilightforest.ore_meter_total", totalScanned) ); @@ -236,7 +278,7 @@ private static ComponentColumn withoutPrettyPrinting(int totalScanned, List oreNameKeys) { - ImmutableList.Builder toList = ImmutableList.builder(); + ImmutableList.Builder<@NotNull Component> toList = ImmutableList.builder(); toList.add(Component.translatable("misc.twilightforest.ore_meter_header_block").withStyle(ChatFormatting.GRAY)); @@ -249,7 +291,7 @@ private static ComponentColumn nameColumn(List oreNameKeys) { } private static ComponentColumn dashColumn(int size) { - ImmutableList.Builder toList = ImmutableList.builder(); + ImmutableList.Builder<@NotNull Component> toList = ImmutableList.builder(); toList.add(Component.empty()); @@ -261,7 +303,7 @@ private static ComponentColumn dashColumn(int size) { } private static ComponentColumn countColumn(List oreCounts) { - ImmutableList.Builder toList = ImmutableList.builder(); + ImmutableList.Builder<@NotNull Component> toList = ImmutableList.builder(); toList.add(Component.translatable("misc.twilightforest.ore_meter_header_count").withStyle(ChatFormatting.GRAY)); @@ -271,7 +313,7 @@ private static ComponentColumn countColumn(List oreCounts) { } private static ComponentColumn ratioColumn(int totalScanned, List oreCounts) { - ImmutableList.Builder toList = ImmutableList.builder(); + ImmutableList.Builder<@NotNull Component> toList = ImmutableList.builder(); toList.add(Component.translatable("misc.twilightforest.ore_meter_header_ratio").withStyle(ChatFormatting.GRAY)); @@ -294,11 +336,11 @@ public static ComponentColumn padding(int forcedExtraMaxWidthBySpaces) { return new ComponentColumn(List.of(), forcedExtraMaxWidthBySpaces * Minecraft.getInstance().font.width(" "), ComponentAlignment.LEFT); } - private int renderColumn(GuiGraphics graphics, ComponentColumn column, int xOff, int yOff, int verticalTextPixelsAdvance) { + private int renderColumn(GuiGraphicsExtractor graphics, ComponentColumn column, int xOff, int yOff, int verticalTextPixelsAdvance) { for (Component rowText : column.textRows) { int textPixelWidth = Minecraft.getInstance().font.width(rowText); int textXPos = xOff + this.textAlignment.getTextOffset(textPixelWidth, this.maxPixelWidth); - graphics.drawString(Minecraft.getInstance().font, rowText, textXPos, yOff, 0x00_ff_ff_ff, false); + graphics.text(Minecraft.getInstance().font, rowText, textXPos, yOff, 0x00_ff_ff_ff, false); yOff += verticalTextPixelsAdvance; } @@ -320,7 +362,7 @@ public static OreMeterInfoCache build(List headers, List event.addListener(new QuestReloadListener())); - NeoForge.EVENT_BUS.addListener(AddReloadListenerEvent.class, event -> event.addListener(TravellersModifiersManager.CacheInvalidationReloadListener.INSTANCE)); - NeoForge.EVENT_BUS.addListener(StalactiteReloadListener.INSTANCE::registerListener); - NeoForge.EVENT_BUS.addListener(this.structureTemplateDefinitions::registerListener); +// NeoForge.EVENT_BUS.addListener(AddClientReloadListenersEvent.class, event -> event.addListener(new QuestReloadListener())); +// NeoForge.EVENT_BUS.addListener(AddClientReloadListenersEvent.class, event -> event.addListener(TravellersModifiersManager.CacheInvalidationReloadListener.INSTANCE)); +// NeoForge.EVENT_BUS.addListener(StalactiteReloadListener.INSTANCE::registerListener); +// NeoForge.EVENT_BUS.addListener(this.structureTemplateDefinitions::registerListener); NeoForge.EVENT_BUS.addListener(ConfigSetup::syncUncraftingConfig); } private void registerGenericItemHandlers(RegisterCapabilitiesEvent event) { - IBlockCapabilityProvider itemHandlerProvider = (level, pos, state, blockEntity, side) -> level.getBlockEntity(pos) instanceof ChestBlockEntity tfChestBlock ? new InvWrapper(tfChestBlock) : null; + IBlockCapabilityProvider<@NotNull ResourceHandler<@NotNull ItemResource>, @Nullable Direction> itemHandlerProvider = (level, pos, state, blockEntity, side) -> level.getBlockEntity(pos) instanceof ChestBlockEntity tfChestBlock ? VanillaContainerWrapper.of(tfChestBlock) : null; event.registerBlock( - Capabilities.ItemHandler.BLOCK, + Capabilities.Item.BLOCK, itemHandlerProvider, TFBlocks.TWILIGHT_OAK_CHEST.get(), TFBlocks.TWILIGHT_OAK_TRAPPED_CHEST.get(), @@ -139,11 +136,21 @@ private void registerGenericItemHandlers(RegisterCapabilitiesEvent event) { TFBlocks.SORTING_TRAPPED_CHEST.get() ); - event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, TFBlockEntities.MASON_JAR.get(), (masonJarBlock, side) -> + event.registerBlockEntity(Capabilities.Item.BLOCK, TFBlockEntities.MASON_JAR.get(), (masonJarBlock, side) -> side == Direction.UP ? masonJarBlock.getItemHandler() : null); - event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, TFBlockEntities.DRYING_RACK.get(), (entity, side) -> new DryingRackBlockEntity.DryingRackHandler(entity)); - event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, TFBlockEntities.CHISELED_CANOPY_BOOKSHELF.get(), (entity, side) -> new ChiseledCanopyBookshelfWrapper(entity)); + event.registerBlockEntity(Capabilities.Item.BLOCK, TFBlockEntities.DRYING_RACK.get(), (entity, _) -> new ItemStackResourceHandler() { + @Override + protected ItemStack getStack() { + return entity.getTheItem(); + } + + @Override + protected void setStack(ItemStack stack) { + + } + }); + event.registerBlockEntity(Capabilities.Item.BLOCK, TFBlockEntities.CHISELED_CANOPY_BOOKSHELF.get(), (entity, side) -> VanillaContainerWrapper.of(entity)); } public void addBlockEntityTypes(BlockEntityTypeAddBlocksEvent event) { @@ -211,7 +218,7 @@ public void sendIMCs(InterModEnqueueEvent event) { } public void setupPackets(RegisterPayloadHandlersEvent event) { - PayloadRegistrar registrar = event.registrar(TwilightForestMod.ID).versioned("1.0.0").optional(); + PayloadRegistrar registrar = event.registrar(TwilightForestMod.ID); registrar.playToClient(AreaProtectionPacket.TYPE, AreaProtectionPacket.STREAM_CODEC, AreaProtectionPacket::handle); registrar.playToClient(CreateMovingCicadaSoundPacket.TYPE, CreateMovingCicadaSoundPacket.STREAM_CODEC, CreateMovingCicadaSoundPacket::handle); registrar.playToClient(EnforceProgressionStatusPacket.TYPE, EnforceProgressionStatusPacket.STREAM_CODEC, EnforceProgressionStatusPacket::handle); @@ -220,8 +227,8 @@ public void setupPackets(RegisterPayloadHandlersEvent event) { registrar.playToClient(MissingAdvancementToastPacket.TYPE, MissingAdvancementToastPacket.STREAM_CODEC, MissingAdvancementToastPacket::handle); registrar.playToClient(MovePlayerPacket.TYPE, MovePlayerPacket.STREAM_CODEC, MovePlayerPacket::handle); registrar.playToClient(ParticlePacket.TYPE, ParticlePacket.STREAM_CODEC, ParticlePacket::handle); - registrar.playBidirectional(GogglesZoomPacket.TYPE, GogglesZoomPacket.STREAM_CODEC, GogglesZoomPacket::handle); - registrar.playBidirectional(GradualGlidePacket.TYPE, GradualGlidePacket.STREAM_CODEC, GradualGlidePacket::handle); + registrar.playToServer(GogglesZoomPacket.TYPE, GogglesZoomPacket.STREAM_CODEC, GogglesZoomPacket::handle); + registrar.playBidirectional(GradualGlidePacket.TYPE, GradualGlidePacket.STREAM_CODEC, GradualGlidePacket::handle, GradualGlidePacket::handle); registrar.playToServer(PerformDoubleJumpPacket.TYPE, PerformDoubleJumpPacket.STREAM_CODEC, PerformDoubleJumpPacket::handle); registrar.playToServer(SwapHotbarPacket.TYPE, SwapHotbarPacket.STREAM_CODEC, SwapHotbarPacket::handle); registrar.playToServer(PerformSidestepPacket.TYPE, PerformSidestepPacket.STREAM_CODEC, PerformSidestepPacket::handle); @@ -248,10 +255,11 @@ public void init(FMLCommonSetupEvent evt) { TFDispenserBehaviors.init(); TFStats.init(); - CauldronInteraction.WATER.map().put(TFItems.ARCTIC_HELMET.get(), CauldronInteraction.DYED_ITEM); - CauldronInteraction.WATER.map().put(TFItems.ARCTIC_CHESTPLATE.get(), CauldronInteraction.DYED_ITEM); - CauldronInteraction.WATER.map().put(TFItems.ARCTIC_LEGGINGS.get(), CauldronInteraction.DYED_ITEM); - CauldronInteraction.WATER.map().put(TFItems.ARCTIC_BOOTS.get(), CauldronInteraction.DYED_ITEM); + // No analogs in 26.1.x +// CauldronInteractions.WATER.map().put(TFItems.ARCTIC_HELMET.get(), CauldronInteractions.DYED_ITEM); +// CauldronInteractions.WATER.map().put(TFItems.ARCTIC_CHESTPLATE.get(), CauldronInteractions.DYED_ITEM); +// CauldronInteractions.WATER.map().put(TFItems.ARCTIC_LEGGINGS.get(), CauldronInteractions.DYED_ITEM); +// CauldronInteractions.WATER.map().put(TFItems.ARCTIC_BOOTS.get(), CauldronInteractions.DYED_ITEM); AxeItem.STRIPPABLES = Maps.newHashMap(AxeItem.STRIPPABLES); AxeItem.STRIPPABLES.put(TFBlocks.TWILIGHT_OAK_LOG.get(), TFBlocks.STRIPPED_TWILIGHT_OAK_LOG.get()); diff --git a/src/main/java/twilightforest/events/ToolEvents.java b/src/main/java/twilightforest/client/event/ToolEvents.java similarity index 84% rename from src/main/java/twilightforest/events/ToolEvents.java rename to src/main/java/twilightforest/client/event/ToolEvents.java index 084546a805..3e492c37c6 100644 --- a/src/main/java/twilightforest/events/ToolEvents.java +++ b/src/main/java/twilightforest/client/event/ToolEvents.java @@ -1,6 +1,7 @@ -package twilightforest.events; +package twilightforest.client.event; import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.network.protocol.game.ClientboundAnimatePacket; @@ -34,16 +35,17 @@ import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; import net.neoforged.neoforge.event.entity.living.MobEffectEvent; import net.neoforged.neoforge.event.level.BlockEvent; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; import twilightforest.block.GiantBlock; import twilightforest.components.entity.GiantPickaxeMiningAttachment; -import twilightforest.data.tags.BlockTagGenerator; import twilightforest.init.TFDataAttachments; import twilightforest.init.TFItems; import twilightforest.item.*; import twilightforest.loot.modifiers.GiantToolGroupingModifier; +import twilightforest.tags.TFBlockTags; import java.util.List; @@ -70,7 +72,7 @@ private void onEnderBowHit(ProjectileImpactEvent evt) { if (arrow.getOwner() instanceof Player player && evt.getRayTraceResult() instanceof EntityHitResult result && result.getEntity() instanceof LivingEntity living - && arrow.getOwner() != result.getEntity() && !result.getEntity().getType().is(Tags.EntityTypes.BOSSES)) { + && arrow.getOwner() != result.getEntity() && !result.getEntity().is(Tags.EntityTypes.BOSSES)) { if (arrow.getPersistentData().contains(EnderBowItem.KEY)) { double sourceX = player.getX(), sourceY = player.getY(), sourceZ = player.getZ(); @@ -82,7 +84,7 @@ private void onEnderBowHit(ProjectileImpactEvent evt) { player.invulnerableTime = 40; player.level().broadcastEntityEvent(player, (byte) 46); if (living.isPassenger() && living.getVehicle() != null) { - player.startRiding(living.getVehicle(), true); + player.startRiding(living.getVehicle(), true, false); living.stopRiding(); } player.playSound(SoundEvents.CHORUS_FRUIT_TELEPORT, 1.0F, 1.0F); @@ -92,7 +94,7 @@ private void onEnderBowHit(ProjectileImpactEvent evt) { living.teleportTo(sourceX, sourceY, sourceZ); living.level().broadcastEntityEvent(player, (byte) 46); if (playerVehicle != null) { - living.startRiding(playerVehicle, true); + living.startRiding(playerVehicle, true, false); player.stopRiding(); } living.playSound(SoundEvents.CHORUS_FRUIT_TELEPORT, 1.0F, 1.0F); @@ -123,11 +125,11 @@ private void doKnightmetalToolLogic(LivingIncomingDamageEvent event) { container.setNewDamage(container.getNewDamage() + KNIGHTMETAL_BONUS_DAMAGE); } // enchantment attack sparkles - ((ServerLevel) target.level()).getChunkSource().broadcastAndSend(target, new ClientboundAnimatePacket(target, 5)); + ((ServerLevel) target.level()).getChunkSource().sendToTrackingPlayers(target, new ClientboundAnimatePacket(target, 5)); } else if (target.getArmorValue() == 0 && weapon.is(TFItems.KNIGHTMETAL_AXE.get())) { container.setNewDamage(container.getOriginalDamage() + KNIGHTMETAL_BONUS_DAMAGE); // enchantment attack sparkles - ((ServerLevel) target.level()).getChunkSource().broadcastAndSend(target, new ClientboundAnimatePacket(target, 5)); + ((ServerLevel) target.level()).getChunkSource().sendToTrackingPlayers(target, new ClientboundAnimatePacket(target, 5)); } } } @@ -143,7 +145,7 @@ private void addExtraAxeChargingDamage(LivingIncomingDamageEvent event) { if (!weapon.isEmpty() && weapon.getItem() instanceof MinotaurAxeItem) { container.setNewDamage(container.getNewDamage() + MINOTAUR_AXE_BONUS_DAMAGE); // enchantment attack sparkles - ((ServerLevel) target.level()).getChunkSource().broadcastAndSend(target, new ClientboundAnimatePacket(target, 5)); + ((ServerLevel) target.level()).getChunkSource().sendToTrackingPlayers(target, new ClientboundAnimatePacket(target, 5)); } } } @@ -151,7 +153,7 @@ private void addExtraAxeChargingDamage(LivingIncomingDamageEvent event) { private void damageNonMazebreakerToolsMore(BlockEvent.BreakEvent event) { ItemStack stack = event.getPlayer().getMainHandItem(); - if (event.getState().is(BlockTagGenerator.MAZEBREAKER_ACCELERATED)) { + if (event.getState().is(TFBlockTags.MAZEBREAKER_ACCELERATED)) { if (stack.isDamageableItem() && !(stack.getItem() instanceof MazebreakerPickItem)) { stack.hurtAndBreak(16, event.getPlayer(), EquipmentSlot.MAINHAND); } @@ -159,7 +161,7 @@ private void damageNonMazebreakerToolsMore(BlockEvent.BreakEvent event) { } private void preventFatigueWithPocketWatch(MobEffectEvent.Applicable event) { - if (event.getApplicationResult() && event.getEffectInstance().is(MobEffects.DIG_SLOWDOWN) && event.getEntity().isHolding(TFItems.POCKET_WATCH.get())) { + if (event.getApplicationResult() && event.getEffectInstance().is(MobEffects.MINING_FATIGUE) && event.getEntity().isHolding(TFItems.POCKET_WATCH.get())) { event.setResult(MobEffectEvent.Applicable.Result.DO_NOT_APPLY); } } @@ -174,7 +176,7 @@ private void handleGiantPickaxeMining(BlockEvent.BreakEvent event) { if (shouldBreakGiantBlock(player, attachment)) { attachment.setBreaking(true); // Tell the capability that a block breaking loop is happening, so it knows to fail the if check above. Otherwise, this would go on forever - LootParams.Builder builder = new LootParams.Builder(player.serverLevel()) + LootParams.Builder builder = new LootParams.Builder(player.level()) .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos)) .withParameter(LootContextParams.BLOCK_STATE, state) .withOptionalParameter(LootContextParams.THIS_ENTITY, player) @@ -224,21 +226,21 @@ private void refreshOreMagnetCache(TagsUpdatedEvent event) { OreMagnetItem.TREE_ORE_TO_BLOCK_REPLACEMENTS.clear(); //collect all tags - for (TagKey tag : BuiltInRegistries.BLOCK.getTagNames().filter(location -> location.location().getNamespace().equals("c")).toList()) { + for (TagKey<@NotNull Block> tag : BuiltInRegistries.BLOCK.getTags().filter(location -> location.key().location().getNamespace().equals("c")).map(HolderSet.Named::key).toList()) { //check if the tag is a valid ore tag if (tag.location().getPath().contains("ores_in_ground/")) { //grab the part after the slash for use later String oreground = tag.location().getPath().substring(15); //check if a tag for ore grounds matches up with our ores in ground tag - if (BuiltInRegistries.BLOCK.getTagNames().filter(location -> location.location().getNamespace().equals("c")).anyMatch(blockTagKey -> blockTagKey.location().getPath().equals("ore_bearing_ground/" + oreground))) { + if (BuiltInRegistries.BLOCK.getTags().filter(location -> location.key().location().getNamespace().equals("c")).anyMatch(blockTagKey -> blockTagKey.key().location().getPath().equals("ore_bearing_ground/" + oreground))) { //add each ground type to each ore - BuiltInRegistries.BLOCK.getTag(TagKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath("c", "ore_bearing_ground/" + oreground))).get().forEach(ground -> - BuiltInRegistries.BLOCK.getTag(tag).get().forEach(ore -> { + BuiltInRegistries.BLOCK.get(TagKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath("c", "ore_bearing_ground/" + oreground))).get().forEach(ground -> + BuiltInRegistries.BLOCK.get(tag).get().forEach(ore -> { //exclude ignored ores - if (!ore.value().defaultBlockState().is(BlockTagGenerator.ORE_MAGNET_IGNORE)) { + if (!ore.value().defaultBlockState().is(TFBlockTags.ORE_MAGNET_IGNORE)) { OreMagnetItem.MAGNET_ORE_TO_BLOCK_REPLACEMENTS.put(ore.value(), ground.value()); } - if (!ore.value().defaultBlockState().is(BlockTagGenerator.MINING_CORE_EXCLUDED)) { + if (!ore.value().defaultBlockState().is(TFBlockTags.MINING_CORE_EXCLUDED)) { OreMagnetItem.TREE_ORE_TO_BLOCK_REPLACEMENTS.put(ore.value(), ground.value()); } })); @@ -248,11 +250,11 @@ private void refreshOreMagnetCache(TagsUpdatedEvent event) { //Gonna need to special case this one as it isn't covered by tags. //Ancient debris isn't exactly an ore, so it makes sense that the tag doesn't include it - if (!Blocks.ANCIENT_DEBRIS.defaultBlockState().is(BlockTagGenerator.ORE_MAGNET_IGNORE) && !OreMagnetItem.MAGNET_ORE_TO_BLOCK_REPLACEMENTS.containsKey(Blocks.ANCIENT_DEBRIS)) { + if (!Blocks.ANCIENT_DEBRIS.defaultBlockState().is(TFBlockTags.ORE_MAGNET_IGNORE) && !OreMagnetItem.MAGNET_ORE_TO_BLOCK_REPLACEMENTS.containsKey(Blocks.ANCIENT_DEBRIS)) { OreMagnetItem.MAGNET_ORE_TO_BLOCK_REPLACEMENTS.put(Blocks.ANCIENT_DEBRIS, Blocks.NETHERRACK); } - if (!Blocks.ANCIENT_DEBRIS.defaultBlockState().is(BlockTagGenerator.MINING_CORE_EXCLUDED) && !OreMagnetItem.TREE_ORE_TO_BLOCK_REPLACEMENTS.containsKey(Blocks.ANCIENT_DEBRIS)) { + if (!Blocks.ANCIENT_DEBRIS.defaultBlockState().is(TFBlockTags.MINING_CORE_EXCLUDED) && !OreMagnetItem.TREE_ORE_TO_BLOCK_REPLACEMENTS.containsKey(Blocks.ANCIENT_DEBRIS)) { OreMagnetItem.TREE_ORE_TO_BLOCK_REPLACEMENTS.put(Blocks.ANCIENT_DEBRIS, Blocks.NETHERRACK); } } diff --git a/src/main/java/twilightforest/client/event/TravellersClientEvents.java b/src/main/java/twilightforest/client/event/TravellersClientEvents.java index 516211006a..9ec55bfd32 100644 --- a/src/main/java/twilightforest/client/event/TravellersClientEvents.java +++ b/src/main/java/twilightforest/client/event/TravellersClientEvents.java @@ -3,17 +3,24 @@ import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; +import net.minecraft.client.input.KeyEvent; import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.Model; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.player.AbstractClientPlayer; -import net.minecraft.client.player.Input; +import net.minecraft.client.player.ClientInput; import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.entity.player.PlayerRenderer; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.entity.player.AvatarRenderer; +import net.minecraft.client.renderer.entity.state.HumanoidRenderState; +import net.minecraft.client.renderer.rendertype.RenderTypes; import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.resources.model.EquipmentClientInfo; import net.minecraft.core.component.DataComponents; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; +import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.HumanoidArm; @@ -24,23 +31,25 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ProjectileWeaponItem; import net.minecraft.world.item.component.ItemContainerContents; +import net.minecraft.world.phys.Vec3; import net.neoforged.api.distmarker.Dist; import net.neoforged.neoforge.attachment.AttachmentType; import net.neoforged.neoforge.client.event.*; import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.registries.DeferredHolder; +import org.jetbrains.annotations.NotNull; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; import twilightforest.TwilightForestMod; import twilightforest.config.TFConfig; -import twilightforest.data.tags.ItemTagGenerator; import twilightforest.init.*; import twilightforest.init.custom.TravellersModifiersManager; import twilightforest.item.travellers_gear.TravellersArmorBeltItem; import twilightforest.item.travellers_gear.TravellersGearLogic; import twilightforest.item.travellers_gear.modifiers.TravellersModifier; import twilightforest.network.*; +import twilightforest.tags.TFItemTags; @Component(dist = Dist.CLIENT) public class TravellersClientEvents { @@ -66,67 +75,135 @@ private void setup() { NeoForge.EVENT_BUS.addListener(this::renderGlovesInFirstPerson); } + private static final Identifier AGILE_RANGER_ID = Identifier.fromNamespaceAndPath("twilightforest", "agile_ranger_speed"); + private void handleAgileRanger(MovementInputUpdateEvent event) { if (!(event.getEntity() instanceof LocalPlayer localPlayer)) return; + + var speedAttribute = localPlayer.getAttribute(Attributes.MOVEMENT_SPEED); + if (speedAttribute == null) + return; + ItemStack leggingsStack = localPlayer.getItemBySlot(EquipmentSlot.LEGS); Float agileRangerModifier = leggingsStack.get(TFDataComponents.AGILE_RANGER_MODIFIER); - if (!TravellersModifiersManager.isModifierActive(localPlayer, leggingsStack, TravellersModifiersManager.AGILE_RANGER_MODIFIER) || agileRangerModifier == null) - return; + + boolean isModifierActive = TravellersModifiersManager.isModifierActive(localPlayer, leggingsStack, TravellersModifiersManager.AGILE_RANGER_MODIFIER) && agileRangerModifier != null; ItemStack stack = localPlayer.getUseItem(); - boolean isLegalItem = (stack.getItem() instanceof ProjectileWeaponItem || stack.is(ItemTagGenerator.TRAVELLERS_AGILE_RANGER_WHITELISTED)) && !stack.is(ItemTagGenerator.TRAVELLERS_AGILE_RANGER_BLACKLISTED); - if (localPlayer.isUsingItem() && !localPlayer.isPassenger() && isLegalItem) { - Input input = event.getInput(); - input.leftImpulse *= agileRangerModifier; - input.forwardImpulse *= agileRangerModifier; + boolean isLegalItem = (stack.getItem() instanceof ProjectileWeaponItem || stack.is(TFItemTags.TRAVELLERS_AGILE_RANGER_WHITELISTED)) && !stack.is(TFItemTags.TRAVELLERS_AGILE_RANGER_BLACKLISTED); + + if (isModifierActive && localPlayer.isUsingItem() && !localPlayer.isPassenger() && isLegalItem) { + if (!speedAttribute.hasModifier(AGILE_RANGER_ID)) { + speedAttribute.addTransientModifier(new AttributeModifier( + AGILE_RANGER_ID, + agileRangerModifier, + AttributeModifier.Operation.ADD_MULTIPLIED_BASE + )); + } + } else { + if (speedAttribute.hasModifier(AGILE_RANGER_ID)) { + speedAttribute.removeModifier(AGILE_RANGER_ID); + } } } + private static final Identifier STRAIGHT_AHEAD_ID = Identifier.fromNamespaceAndPath("twilightforest", "straight_ahead_speed"); + private void handleStraightAhead(MovementInputUpdateEvent event) { if (!(event.getEntity() instanceof LocalPlayer localPlayer)) return; - ItemStack bootsStack = localPlayer.getItemBySlot(EquipmentSlot.FEET); - Double multiplier = bootsStack.get(TFDataComponents.STRAIGHT_AHEAD_MULTIPLIER); - AttributeInstance attributeInstance = localPlayer.getAttributes().getInstance(Attributes.MOVEMENT_SPEED); + + AttributeInstance attributeInstance = localPlayer.getAttribute(Attributes.MOVEMENT_SPEED); if (attributeInstance == null) return; - Input input = localPlayer.input; - if (!TravellersModifiersManager.isModifierActive(localPlayer, bootsStack, TravellersModifiersManager.STRAIGHT_AHEAD_MODIFIER) || multiplier == null || input.forwardImpulse <= 0) - multiplier = 1D; - attributeInstance.addOrUpdateTransientModifier(new AttributeModifier(TFAttributeModifiers.STRAIGHT_AHEAD_ATTRIBUTE_MODIFIER_LOCATION, multiplier - 1, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)); - input.leftImpulse /= multiplier; + ItemStack bootsStack = localPlayer.getItemBySlot(EquipmentSlot.FEET); + Double multiplier = bootsStack.get(TFDataComponents.STRAIGHT_AHEAD_MULTIPLIER); + + ClientInput input = event.getInput(); + + boolean isMovingForward = input.keyPresses.forward(); + boolean isStrafing = input.keyPresses.left() || input.keyPresses.right(); + boolean isModifierActive = TravellersModifiersManager.isModifierActive(localPlayer, bootsStack, TravellersModifiersManager.STRAIGHT_AHEAD_MODIFIER) && multiplier != null; + + if (isModifierActive && isMovingForward) { + if (!attributeInstance.hasModifier(STRAIGHT_AHEAD_ID)) { + attributeInstance.addTransientModifier(new AttributeModifier( + STRAIGHT_AHEAD_ID, + multiplier - 1.0, + AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL + )); + } + + if (isStrafing) { + Vec3 currentVelocity = localPlayer.getDeltaMovement(); + + Vec3 lookDirection = localPlayer.getLookAngle(); + Vec3 rightVector = new Vec3(-lookDirection.z, 0, lookDirection.x).normalize(); + + double sideVelocity = currentVelocity.dot(rightVector); + + double reductionFactor = 1.0 - (1.0 / multiplier); + Vec3 counterImpulse = rightVector.scale(-sideVelocity * reductionFactor); + + localPlayer.setDeltaMovement(currentVelocity.add(counterImpulse)); + } + } else { + if (attributeInstance.hasModifier(STRAIGHT_AHEAD_ID)) { + attributeInstance.removeModifier(STRAIGHT_AHEAD_ID); + } + } } + private static final Identifier GLIDE_SNEAK_SPEED_ID = Identifier.fromNamespaceAndPath("twilightforest", "glide_sneak_speed"); + private void speedUpControlledWhileSneaking(MovementInputUpdateEvent event) { - if (!(event.getEntity() instanceof LocalPlayer localPlayer) || !localPlayer.getData(TFDataAttachments.IS_GRADUALLY_GLIDING) || !localPlayer.isShiftKeyDown()) + if (!(event.getEntity() instanceof LocalPlayer localPlayer)) + return; + + var sneakSpeedAttribute = localPlayer.getAttribute(Attributes.SNEAKING_SPEED); + if (sneakSpeedAttribute == null) return; - localPlayer.input.forwardImpulse /= 0.2F; - localPlayer.input.leftImpulse /= 0.2F; + + boolean isGlidingAndSneaking = localPlayer.getData(TFDataAttachments.IS_GRADUALLY_GLIDING) && localPlayer.isShiftKeyDown(); + + if (isGlidingAndSneaking) { + if (!sneakSpeedAttribute.hasModifier(GLIDE_SNEAK_SPEED_ID)) { + sneakSpeedAttribute.addTransientModifier(new AttributeModifier( + GLIDE_SNEAK_SPEED_ID, + 2.333333F, + AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL + )); + } + } else { + if (sneakSpeedAttribute.hasModifier(GLIDE_SNEAK_SPEED_ID)) { + sneakSpeedAttribute.removeModifier(GLIDE_SNEAK_SPEED_ID); + } + } } private void handleSidestep(MovementInputUpdateEvent event) { if (!(event.getEntity() instanceof LocalPlayer localPlayer) || !localPlayer.onGround()) return; - Input input = localPlayer.input; + ClientInput input = localPlayer.input; boolean lastImpulseZero = localPlayer.getData(TFDataAttachments.LAST_HORIZONTAL_IMPULSE) == 0; - boolean sameImpulseDirection = Math.signum(localPlayer.getData(TFDataAttachments.LAST_NON_ZERO_HORIZONTAL_IMPULSE)) == Math.signum(input.leftImpulse); + boolean sameImpulseDirection = Math.signum(localPlayer.getData(TFDataAttachments.LAST_NON_ZERO_HORIZONTAL_IMPULSE)) == Math.signum(input.getMoveVector().x); int currentTime = localPlayer.tickCount; int lastWalkingTime = localPlayer.getData(TFDataAttachments.LAST_HORIZONTAL_WALKING_TIME); boolean hasDoubleTapped = currentTime - lastWalkingTime < 4; - if (lastImpulseZero && sameImpulseDirection && hasDoubleTapped && input.leftImpulse != 0) { - boolean isLeftSidestep = input.leftImpulse > 0; + if (lastImpulseZero && sameImpulseDirection && hasDoubleTapped && input.getMoveVector().x != 0) { + boolean isLeftSidestep = input.getMoveVector().x > 0; if (TravellersGearLogic.tryPerformSidestep(localPlayer, isLeftSidestep)) { localPlayer.connection.send(new PerformSidestepPacket(isLeftSidestep)); } } - localPlayer.setData(TFDataAttachments.LAST_HORIZONTAL_IMPULSE, input.leftImpulse); - if (input.leftImpulse != 0) { + localPlayer.setData(TFDataAttachments.LAST_HORIZONTAL_IMPULSE, input.getMoveVector().x); + if (input.getMoveVector().x != 0) { localPlayer.setData(TFDataAttachments.LAST_HORIZONTAL_WALKING_TIME, currentTime); - localPlayer.setData(TFDataAttachments.LAST_NON_ZERO_HORIZONTAL_IMPULSE, input.leftImpulse); + localPlayer.setData(TFDataAttachments.LAST_NON_ZERO_HORIZONTAL_IMPULSE, input.getMoveVector().x); } } @@ -239,25 +316,53 @@ private void slowZoomSensitivity(CalculatePlayerTurnEvent event) { } private boolean ignoreKeyEvent(InputEvent.Key event, KeyMapping key) { - return !key.matches(event.getKey(), event.getScanCode()) || event.getAction() != InputConstants.PRESS || Minecraft.getInstance().screen != null; + return !key.matches(new KeyEvent(event.getKey(), event.getScanCode(), event.getModifiers())) || event.getAction() != InputConstants.PRESS || Minecraft.getInstance().screen != null; } - @SuppressWarnings("unchecked") //meh + @SuppressWarnings("unchecked") private void renderGlovesInFirstPerson(RenderArmEvent event) { - if (TFConfig.firstPersonGloveOverlay) { - AbstractClientPlayer player = event.getPlayer(); - ItemStack chestStack = player.getItemBySlot(EquipmentSlot.CHEST); - if (chestStack.has(TFDataComponents.TRAVELLERS_HAS_GLOVES) && !chestStack.has(TFDataComponents.EMPERORS_CLOTH)) { - PlayerRenderer renderer = (PlayerRenderer) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player); - HumanoidModel model = (HumanoidModel) IClientItemExtensions.of(TFItems.TRAVELLERS_GLOVES.get()).getHumanoidArmorModel(player, chestStack, EquipmentSlot.CHEST, renderer.getModel()); - ModelPart armPart = event.getArm() == HumanoidArm.RIGHT ? model.rightArm : model.leftArm; - model.attackTime = 0.0F; - model.crouching = false; - model.swimAmount = 0.0F; - model.setupAnim(player, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F); - armPart.xRot = 0.0F; - Identifier gloveLocation = TwilightForestMod.prefix("textures/models/armor/travellers_layer_1.png"); - armPart.render(event.getPoseStack(), event.getMultiBufferSource().getBuffer(RenderType.armorCutoutNoCull(gloveLocation)), event.getPackedLight(), OverlayTexture.NO_OVERLAY); + if (!TFConfig.firstPersonGloveOverlay) { + return; + } + + AbstractClientPlayer player = event.getPlayer(); + ItemStack chestStack = player.getItemBySlot(EquipmentSlot.CHEST); + + if (chestStack.has(TFDataComponents.TRAVELLERS_HAS_GLOVES) && !chestStack.has(TFDataComponents.EMPERORS_CLOTH)) { + EntityRenderDispatcher entityRenderDispatcher = Minecraft.getInstance().getEntityRenderDispatcher(); + + if (entityRenderDispatcher.getRenderer(player) instanceof AvatarRenderer avatarRenderer) { + var playerModel = avatarRenderer.getModel(); + + Model<@NotNull HumanoidRenderState> armorModel = IClientItemExtensions.of(TFItems.TRAVELLERS_GLOVES.get()) + .getHumanoidArmorModel(chestStack, EquipmentClientInfo.LayerType.HUMANOID, playerModel); + + if (armorModel instanceof HumanoidModel<@NotNull HumanoidRenderState> humanoidArmorModel) { + ModelPart armPart = humanoidArmorModel.getArm(event.getArm()); + + var dummyState = new HumanoidRenderState(); + humanoidArmorModel.setupAnim(dummyState); + + armPart.xRot = 0.0F; + + Identifier gloveLocation = TwilightForestMod.prefix("textures/models/armor/travellers_layer_1.png"); + + SubmitNodeCollector collector = event.getSubmitNodeCollector(); + + collector.submitModelPart( + armPart, + event.getPoseStack(), + RenderTypes.armorCutoutNoCull(gloveLocation), + event.getPackedLight(), + OverlayTexture.NO_OVERLAY, + null, + false, // sheeted + false, // hasFoil + -1, // tintedColor + null, // crumblingOverlay + 0 // index/layer + ); + } } } } diff --git a/src/main/java/twilightforest/events/TravellersGearEvents.java b/src/main/java/twilightforest/client/event/TravellersGearEvents.java similarity index 92% rename from src/main/java/twilightforest/events/TravellersGearEvents.java rename to src/main/java/twilightforest/client/event/TravellersGearEvents.java index 3a31cf64b7..e1fa75f97c 100644 --- a/src/main/java/twilightforest/events/TravellersGearEvents.java +++ b/src/main/java/twilightforest/client/event/TravellersGearEvents.java @@ -1,4 +1,4 @@ -package twilightforest.events; +package twilightforest.client.event; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; @@ -8,7 +8,6 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; @@ -18,8 +17,8 @@ import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.projectile.AbstractArrow; import net.minecraft.world.entity.projectile.Projectile; +import net.minecraft.world.entity.projectile.arrow.AbstractArrow; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.ItemAttributeModifiers; import net.minecraft.world.item.component.ItemContainerContents; @@ -40,10 +39,10 @@ import net.neoforged.neoforge.event.entity.player.PlayerSpawnPhantomsEvent; import net.neoforged.neoforge.event.tick.EntityTickEvent; import net.neoforged.neoforge.event.tick.PlayerTickEvent; -import net.neoforged.neoforge.items.ItemHandlerHelper; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.server.ServerLifecycleHooks; +import org.jetbrains.annotations.NotNull; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; import twilightforest.components.entity.SlimySolesAttachment; @@ -62,7 +61,7 @@ @Component public class TravellersGearEvents { - private static final List, ? extends AttachmentType>> ATTACHMENTS_TO_PRESERVE_ON_DEATH = List.of( + private static final List, ? extends @NotNull AttachmentType>> ATTACHMENTS_TO_PRESERVE_ON_DEATH = List.of( TFDataAttachments.TRAVELLERS_GOGGLES_RED_THREAD_VISION ); @@ -103,7 +102,7 @@ private void magnetizeArrows(ProjectileImpactEvent event) { } AbstractArrow.Pickup pickup = arrow.pickup; if (!player.hasInfiniteMaterials() && pickup.equals(AbstractArrow.Pickup.ALLOWED)) { - ItemHandlerHelper.giveItemToPlayer(player, arrow.getPickupItemStackOrigin()); + player.addItem(arrow.getPickupItemStackOrigin()); player.getInventory().setChanged(); } if (pickup.equals(AbstractArrow.Pickup.ALLOWED) || pickup.equals(AbstractArrow.Pickup.CREATIVE_ONLY) && player.isCreative()) @@ -123,7 +122,7 @@ private void performPerfectDodge(ProjectileImpactEvent event) { event.setCanceled(true); // always cancel on the client side because the game sends a damage packet when it hits the player return; } - if (probability <= level.random.nextFloat()) + if (probability <= level.getRandom().nextFloat()) return; Entity projectile = event.getEntity(); Vec3 hitPosition = projectile.position().add(projectile.getDeltaMovement()); @@ -132,9 +131,9 @@ private void performPerfectDodge(ProjectileImpactEvent event) { ParticlePacket particlePacket = new ParticlePacket(); for (int particleNumber = 0; particleNumber < 20; particleNumber++) { Vec3 particleVelocity = new Vec3( - (level.random.nextDouble() - 0.5), - (level.random.nextDouble() - 0.5), - (level.random.nextDouble() - 0.5) + (level.getRandom().nextDouble() - 0.5), + (level.getRandom().nextDouble() - 0.5), + (level.getRandom().nextDouble() - 0.5) ); ParticleOptions type = TFParticleType.PERFECT_DODGE.get(); particlePacket.queueParticle(type, false, hitPosition, particleVelocity); @@ -256,7 +255,7 @@ private void activateAndDeactivateTravellersModifiers(ItemAttributeModifierEvent if (armor.has(TFDataComponents.STORED_BROKEN_ATTRIBUTES)) { entries.addAll(armor.get(TFDataComponents.STORED_BROKEN_ATTRIBUTES).modifiers()); } - armor.set(TFDataComponents.STORED_BROKEN_ATTRIBUTES, new ItemAttributeModifiers(entries.stream().toList(), armor.get(DataComponents.ATTRIBUTE_MODIFIERS).showInTooltip())); + armor.set(TFDataComponents.STORED_BROKEN_ATTRIBUTES, new ItemAttributeModifiers(entries.stream().toList())); event.clearModifiers(); } } else { @@ -278,7 +277,7 @@ private void stopDamagingTravellersGear(ArmorHurtEvent event) { if (damagedStack.getDamageValue() + event.getNewDamage(slot) >= damagedStack.getMaxDamage()) { event.setNewDamage(slot, damagedStack.getMaxDamage() - damagedStack.getDamageValue() - 1); } else if (damagedStack.getDamageValue() + event.getNewDamage(slot) >= damagedStack.getMaxDamage() - 1 && event.getEntity() instanceof ServerPlayer player) { - player.playNotifySound(SoundEvents.ITEM_BREAK, SoundSource.PLAYERS, 1.0F, player.getVoicePitch()); + player.playSound(SoundEvents.ITEM_BREAK.value(), 1.0F, player.getVoicePitch()); } }); } @@ -311,7 +310,7 @@ private void removeModifiersFromTravellersGear(GrindstoneEvent.OnPlaceItem event return; } ItemStack inputStack = travellersItemStacks.getFirst(); - List> modifiers = TravellersModifiersManager.findAllInsertableModifiers(access, inputStack); + List> modifiers = TravellersModifiersManager.findAllInsertableModifiers(access, inputStack); if (modifiers.isEmpty()) { event.setCanceled(true); return; @@ -326,7 +325,7 @@ private void extractItemsFromSwapHotbarModifier(GrindstoneEvent.OnTakeItem event returnModifierItems(event, TravellersModifiersManager.SWAP_HOTBAR_MODIFIER, DataComponents.CONTAINER, - ItemContainerContents::nonEmptyStream + ItemContainerContents::nonEmptyItemCopyStream ); returnModifierItems(event, @@ -336,7 +335,7 @@ private void extractItemsFromSwapHotbarModifier(GrindstoneEvent.OnTakeItem event ); } - private void returnModifierItems(GrindstoneEvent.OnTakeItem event, ResourceKey modifierKey, DataComponentType componentType, Function> itemStreamExtractor) { + private void returnModifierItems(GrindstoneEvent.OnTakeItem event, ResourceKey<@NotNull TravellersModifier> modifierKey, DataComponentType<@NotNull T> componentType, Function> itemStreamExtractor) { if (event.getPlayer() == null) return; @@ -345,7 +344,7 @@ private void returnModifierItems(GrindstoneEvent.OnTakeItem event, ResourceK ).map(stack -> stack.get(componentType)) .ifPresent(component -> itemStreamExtractor.apply(component) - .forEach(itemStack -> ItemHandlerHelper.giveItemToPlayer(event.getPlayer(), itemStack)) + .forEach(itemStack -> event.getPlayer().addItem(itemStack)) ); } @@ -374,20 +373,20 @@ private void fireCraftingModifierTrigger(PlayerEvent.ItemCraftedEvent event) { var oldMods = TravellersModifiersManager.findAllInsertableModifiers(player, compareStack); TravellersModifiersManager.findAllInsertableModifiers(player, event.getCrafting()).stream() .filter(modifier -> !oldMods.contains(modifier)).toList() - .forEach(modifier -> TFAdvancements.ADD_MODIFIER.get().trigger(player, modifier.key().location())); + .forEach(modifier -> TFAdvancements.ADD_MODIFIER.get().trigger(player, modifier.key().identifier())); } } } public void keepAttachmentsOnDeath(PlayerEvent.Clone event) { if (event.isWasDeath()) { - for (DeferredHolder, ? extends AttachmentType> attachmentHolder : ATTACHMENTS_TO_PRESERVE_ON_DEATH) { + for (DeferredHolder<@NotNull AttachmentType, ? extends @NotNull AttachmentType> attachmentHolder : ATTACHMENTS_TO_PRESERVE_ON_DEATH) { copyAttachmentData(event.getOriginal(), event.getEntity(), attachmentHolder.get()); } } } - private void copyAttachmentData(Player source, Player target, AttachmentType type) { + private void copyAttachmentData(Player source, Player target, AttachmentType<@NotNull T> type) { if (source.hasData(type)) { target.setData(type, source.getData(type)); } diff --git a/src/main/java/twilightforest/events/CharmEvents.java b/src/main/java/twilightforest/events/CharmEvents.java index 51e14c1eb1..d8d301b425 100644 --- a/src/main/java/twilightforest/events/CharmEvents.java +++ b/src/main/java/twilightforest/events/CharmEvents.java @@ -5,20 +5,29 @@ import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; +import net.minecraft.resources.RegistryOps; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.ProblemReporter; +import net.minecraft.world.ItemStackWithSlot; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.EntityEquipment; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.ResolvableProfile; -import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.TagValueOutput; import net.neoforged.bus.api.EventPriority; import net.neoforged.fml.ModList; import net.neoforged.neoforge.common.NeoForge; @@ -27,19 +36,20 @@ import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.registries.DeferredItem; +import org.jetbrains.annotations.NotNull; import tamaized.beanification.PostConstruct; import twilightforest.TwilightForestMod; import twilightforest.block.KeepsakeCasketBlock; import twilightforest.block.entity.SkullChestBlockEntity; import twilightforest.compat.curios.CuriosCompat; import twilightforest.config.TFConfig; -import twilightforest.data.tags.ItemTagGenerator; import twilightforest.enums.BlockLoggingEnum; import twilightforest.init.TFBlocks; import twilightforest.init.TFItems; import twilightforest.init.TFSounds; import twilightforest.init.TFStats; import twilightforest.network.SpawnCharmPacket; +import twilightforest.tags.TFItemTags; import twilightforest.util.TFItemStackUtils; import java.util.ArrayList; @@ -78,12 +88,11 @@ private void applyKeepingAndCasket(LivingDeathEvent event) { if (event.isCanceled() || living.level().isClientSide() || !(living instanceof Player player) || living instanceof FakePlayer || player.isCreative() || player.isSpectator()) return; - if (!living.level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY)) { - // Did the player recover? No? Let's give them their stuff based on the keeping charms - handleCharmOfKeeping(player); - - // Then let's store the rest of their stuff in the casket - stockKeepsakeCasket(player); + if (living.level() instanceof ServerLevel serverLevel) { + if (!serverLevel.getGameRules().get(GameRules.KEEP_INVENTORY)) { + handleCharmOfKeeping(player); + stockKeepsakeCasket(player); + } } } @@ -108,7 +117,7 @@ private static boolean handleCharmOfLife(Player player) { player.setHealth(player.getMaxHealth()); player.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 600, 3)); - player.addEffect(new MobEffectInstance(MobEffects.DAMAGE_RESISTANCE, 600, 0)); + player.addEffect(new MobEffectInstance(MobEffects.RESISTANCE, 600, 0)); player.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, 600, 0)); } @@ -125,67 +134,118 @@ private static boolean handleCharmOfLife(Player player) { private static void handleCharmOfKeeping(Player player) { //create a fake inventory to organize our kept inventory in - Inventory keepInventory = new Inventory(player); - ListTag tagList = new ListTag(); + Inventory keepInventory = new Inventory(player, new EntityEquipment()); + + List allItems = new ArrayList<>(); + for (int i = 0; i < 36; i++) { + allItems.add(player.getInventory().getItem(i)); + } - if (!applyCharm(TFItems.CHARM_OF_KEEPING_3, keepInventory, player, player.getInventory().items)) { - if (!applyCharm(TFItems.CHARM_OF_KEEPING_2, keepInventory, player, player.getInventory().items.subList(0, 9))) { - int i = player.getInventory().selected; + if (!applyCharm(TFItems.CHARM_OF_KEEPING_3, keepInventory, player, allItems)) { + List hotbarItems = allItems.subList(0, 9); + if (!applyCharm(TFItems.CHARM_OF_KEEPING_2, keepInventory, player, hotbarItems)) { + int i = player.getInventory().getSelectedSlot(); if (Inventory.isHotbarSlot(i)) { - applyCharm(TFItems.CHARM_OF_KEEPING_1, keepInventory, player, NonNullList.of(player.getInventory().items.get(i))); + applyCharm(TFItems.CHARM_OF_KEEPING_1, keepInventory, player, NonNullList.of(player.getInventory().getItem(i))); } } } //keep all items in the kept_on_death tag. This allows modpacks to support other items to keep on death - for (int i = 0; i < player.getInventory().items.size(); i++) { - ItemStack stack = player.getInventory().items.get(i); - if (stack.is(ItemTagGenerator.KEPT_ON_DEATH)) { - keepInventory.items.set(i, stack.copy()); - player.getInventory().items.set(i, ItemStack.EMPTY); + for (int i = 0; i < player.getInventory().getContainerSize(); i++) { + ItemStack stack = player.getInventory().getItem(i); + if (stack.is(TFItemTags.KEPT_ON_DEATH)) { + keepInventory.setItem(i, stack.copy()); + player.getInventory().setItem(i, ItemStack.EMPTY); } } - for (int i = 0; i < player.getInventory().armor.size(); i++) { - ItemStack armor = player.getInventory().armor.get(i); - if (armor.is(ItemTagGenerator.KEPT_ON_DEATH)) { - keepInventory.armor.set(i, armor.copy()); - player.getInventory().armor.set(i, ItemStack.EMPTY); + for (int slot = 36; slot <= 39; slot++) { + ItemStack armor = player.getInventory().getItem(slot); + if (armor.is(TFItemTags.KEPT_ON_DEATH)) { + int localIndex = slot - 36; + keepInventory.setItem(localIndex, armor.copy()); + player.getInventory().setItem(slot, ItemStack.EMPTY); } } - if (player.getInventory().offhand.getFirst().is(ItemTagGenerator.KEPT_ON_DEATH)) { - keepInventory.offhand.set(0, player.getInventory().offhand.getFirst().copy()); - player.getInventory().offhand.set(0, ItemStack.EMPTY); + ItemStack offhand = player.getInventory().getItem(40); + if (offhand.is(TFItemTags.KEPT_ON_DEATH)) { + keepInventory.setItem(40, offhand.copy()); + player.getInventory().setItem(40, ItemStack.EMPTY); } //take our fake inventory and save it to the persistent player data. //by saving it there we can guarantee we will always get all of our items back, even if the player logs out and back in. if (!keepInventory.isEmpty()) { - keepInventory.save(tagList); - getPlayerData(player).put(CHARM_INV_TAG, tagList); + ProblemReporter.Collector reporter = new ProblemReporter.Collector(); + TagValueOutput valueOutput = TagValueOutput.createWithContext(reporter, player.level().registryAccess()); + var typedList = valueOutput.list("items", ItemStackWithSlot.CODEC); + keepInventory.save(typedList); + CompoundTag resultTag = valueOutput.buildResult(); + ListTag finalTagList = resultTag.getList("items").get(); + getPlayerData(player).put(CHARM_INV_TAG, finalTagList); } + } - private static boolean applyCharm(DeferredItem charm, Inventory keptInventory, Player player, List inventorySlots) { + private static boolean applyCharm(DeferredItem<@NotNull Item> charm, Inventory keptInventory, Player player, List inventorySlots) { + List playerArmor = new java.util.AbstractList<>() { + @Override public ItemStack get(int index) { return player.getInventory().getItem(36 + index); } + @Override public int size() { return 4; } + @Override public ItemStack set(int index, ItemStack element) { + ItemStack old = player.getInventory().getItem(36 + index); + player.getInventory().setItem(36 + index, element); + return old; + } + }; + + List playerOffhand = new java.util.AbstractList<>() { + @Override public ItemStack get(int index) { return player.getInventory().getItem(40); } + @Override public int size() { return 1; } + @Override public ItemStack set(int index, ItemStack element) { + ItemStack old = player.getInventory().getItem(40); + player.getInventory().setItem(40, element); + return old; + } + }; + List mergedCheck = new ArrayList<>(inventorySlots); - //merge armor and offhand into check slots since theyll always be kept by a charm - mergedCheck.addAll(player.getInventory().armor); - mergedCheck.addAll(player.getInventory().offhand); - //first, check all affected slots to make sure they arent empty. - //filter out the charm so it doesnt count towards keeping items if its the only thing we are holding - if (mergedCheck.stream().filter(stack -> !stack.is(charm)).allMatch(ItemStack::isEmpty)) return false; + mergedCheck.addAll(playerArmor); + mergedCheck.addAll(playerOffhand); - //do we even have a charm? No? Then stop operation + if (mergedCheck.stream().filter(stack -> !stack.is(charm)).allMatch(ItemStack::isEmpty)) return false; if (!TFItemStackUtils.consumeInventoryItem(player, charm, getPlayerData(player), true) && !hasCharmCurio(charm.value(), player)) return false; - boolean keptACasket = keepWholeListAndCheckCasket(keptInventory.items, inventorySlots, charm == TFItems.CHARM_OF_KEEPING_3); - keptACasket = keepWholeListAndCheckCasket(keptInventory.armor, player.getInventory().armor, keptACasket); - keepWholeListAndCheckCasket(keptInventory.offhand, player.getInventory().offhand, keptACasket); + NonNullList<@NotNull ItemStack> keptItemsList = NonNullList.create(); + for (int i = 0; i < inventorySlots.size(); i++) { + keptItemsList.add(keptInventory.getItem(i)); + } + + boolean keptACasket = keepWholeListAndCheckCasket(keptItemsList, inventorySlots, charm == TFItems.CHARM_OF_KEEPING_3); + for (int i = 0; i < inventorySlots.size(); i++) { + keptInventory.setItem(i, keptItemsList.get(i)); + } + + NonNullList<@NotNull ItemStack> keptArmorList = NonNullList.create(); + for (int i = 0; i < 4; i++) { + keptArmorList.add(keptInventory.getItem(i + 36)); + } + + keptACasket = keepWholeListAndCheckCasket(keptArmorList, playerArmor, keptACasket); + for (int i = 0; i < 4; i++) { + keptInventory.setItem(i + 36, keptArmorList.get(i)); + } + + NonNullList<@NotNull ItemStack> keptOffhandList = NonNullList.create(); + keptOffhandList.add(keptInventory.getItem(40)); + keepWholeListAndCheckCasket(keptOffhandList, playerOffhand, keptACasket); + keptInventory.setItem(40, keptOffhandList.get(0)); return true; } + private static void stockKeepsakeCasket(Player player) { //make sure we are still actually holding onto items before trying to place a casket if (player.getInventory().hasAnyMatching(stack -> !stack.isEmpty() && !stack.is(TFItems.KEEPSAKE_CASKET))) { @@ -216,7 +276,7 @@ private static void stockKeepsakeCasket(Player player) { BlockPos immutablePos = pos.immutable(); FluidState fluidState = level.getFluidState(immutablePos); - int damage = getPlayerData(player).contains(CASKET_DAMAGE_TAG) ? getPlayerData(player).getInt(CASKET_DAMAGE_TAG) : 0; + int damage = getPlayerData(player).contains(CASKET_DAMAGE_TAG) ? getPlayerData(player).getInt(CASKET_DAMAGE_TAG).get() : 0; BlockState setState = TFBlocks.KEEPSAKE_CASKET.get().defaultBlockState() .setValue(BlockLoggingEnum.MULTILOGGED, BlockLoggingEnum.getFromFluid(fluidState.getType())) .setValue(KeepsakeCasketBlock.BREAKAGE, damage) @@ -245,7 +305,7 @@ private static void stockKeepsakeCasket(Player player) { if (TFConfig.casketUUIDLocking) { //make it so only the player who died can open the chest if our config allows us - casket.owner = new ResolvableProfile(player.getGameProfile()); + casket.owner = ResolvableProfile.createResolved(player.getGameProfile()); } else { casket.owner = null; } @@ -256,28 +316,52 @@ private static void stockKeepsakeCasket(Player player) { int casketCapacity = casket.getContainerSize(); List list = new ArrayList<>(casketCapacity); - NonNullList filler = NonNullList.withSize(4, ItemStack.EMPTY); + NonNullList<@NotNull ItemStack> filler = NonNullList.withSize(4, ItemStack.EMPTY); // lets add our inventory exactly how it was on us list.addAll(TFItemStackUtils.sortArmorForCasket(player)); - player.getInventory().armor.clear(); + for (int slot = 36; slot <= 39; slot++) { + player.getInventory().setItem(slot, net.minecraft.world.item.ItemStack.EMPTY); + } list.addAll(filler); - list.addAll(player.getInventory().offhand); - player.getInventory().offhand.clear(); + list.add(player.getInventory().getItem(40)); + player.getInventory().setItem(40, net.minecraft.world.item.ItemStack.EMPTY); list.addAll(TFItemStackUtils.sortInvForCasket(player)); - player.getInventory().items.clear(); + for (int slot = 0; slot < 36; slot++) { + player.getInventory().setItem(slot, net.minecraft.world.item.ItemStack.EMPTY); + } + casket.setItems(NonNullList.of(ItemStack.EMPTY, list.toArray(new ItemStack[casketCapacity]))); getPlayerData(player).remove(CASKET_DAMAGE_TAG); } else { //inventory is empty minus the casket: put the casket into the kept inventory for (int i = 0; i < player.getInventory().getContainerSize(); i++) { - if (player.getInventory().getItem(i).is(TFItems.KEEPSAKE_CASKET)) { - Inventory tmp = new Inventory(player); - tmp.load(getPlayerData(player).getList(CHARM_INV_TAG, 10)); + if (player.getInventory().getItem(i).is(TFItems.KEEPSAKE_CASKET.get())) { + Inventory tmp = new Inventory(player, new EntityEquipment()); + ListTag savedListTag = getPlayerData(player).getList(CHARM_INV_TAG).get(); + CompoundTag rootLoadTag = new CompoundTag(); + + rootLoadTag.put("items", savedListTag); + + ProblemReporter.Collector loadReporter = new ProblemReporter.Collector(); + var valueInput = TagValueInput.create(loadReporter, player.level().registryAccess(), rootLoadTag); + var typedInputList = valueInput.list("items", ItemStackWithSlot.CODEC); + + tmp.load(typedInputList.get()); tmp.add(player.getInventory().getItem(i).copy()); player.getInventory().setItem(i, ItemStack.EMPTY); - getPlayerData(player).put(CHARM_INV_TAG, tmp.save(new ListTag())); + + ProblemReporter.Collector saveReporter = new ProblemReporter.Collector(); + TagValueOutput valueOutput = TagValueOutput.createWithContext(saveReporter, player.level().registryAccess()); + + var typedOutputList = valueOutput.list("items", net.minecraft.world.ItemStackWithSlot.CODEC); + tmp.save(typedOutputList); + + CompoundTag resultTag = valueOutput.buildResult(); + ListTag finalTagList = resultTag.getList("items").get(); + + getPlayerData(player).put(CHARM_INV_TAG, finalTagList); } } } @@ -293,15 +377,19 @@ private static void returnStoredItems(Player player) { //check if our tag is in the persistent player data. If so, copy that inventory over to our own. Cloud storage at its finest! CompoundTag playerData = getPlayerData(player); if (!player.level().isClientSide() && playerData.contains(CHARM_INV_TAG)) { - ListTag tagList = playerData.getList(CHARM_INV_TAG, 10); + ListTag tagList = playerData.getList(CHARM_INV_TAG).get(); TFItemStackUtils.loadNoClear(player.registryAccess(), tagList, player.getInventory()); - getPlayerData(player).getList(CHARM_INV_TAG, 10).clear(); + getPlayerData(player).getList(CHARM_INV_TAG).get().clear(); getPlayerData(player).remove(CHARM_INV_TAG); } // spawn effect thingers if (getPlayerData(player).contains(CONSUMED_CHARM_TAG)) { - ItemStack stack = ItemStack.parseOptional(player.registryAccess(), (CompoundTag) getPlayerData(player).get(CONSUMED_CHARM_TAG)); + Tag charmTag = getPlayerData(player).get(CONSUMED_CHARM_TAG); + ItemStack stack = charmTag == null ? ItemStack.EMPTY : ItemStack.OPTIONAL_CODEC.parse( + RegistryOps.create(NbtOps.INSTANCE, player.registryAccess()), + charmTag + ).result().orElse(ItemStack.EMPTY); if (player instanceof ServerPlayer serverPlayer) { PacketDistributor.sendToPlayer(serverPlayer, new SpawnCharmPacket(stack, TFSounds.CHARM_KEEP.getKey())); @@ -315,11 +403,11 @@ public static CompoundTag getPlayerData(Player player) { if (!player.getPersistentData().contains(Player.PERSISTED_NBT_TAG)) { player.getPersistentData().put(Player.PERSISTED_NBT_TAG, new CompoundTag()); } - return player.getPersistentData().getCompound(Player.PERSISTED_NBT_TAG); + return player.getPersistentData().getCompound(Player.PERSISTED_NBT_TAG).get(); } //transfers a list of items to another - private static boolean keepWholeListAndCheckCasket(NonNullList transferTo, List transferFrom, boolean skipCasketCheck) { + private static boolean keepWholeListAndCheckCasket(NonNullList<@NotNull ItemStack> transferTo, List transferFrom, boolean skipCasketCheck) { boolean keptCasket = false; for (int i = 0; i < transferFrom.size(); i++) { var item = transferFrom.get(i).copy(); diff --git a/src/main/java/twilightforest/events/EntityEvents.java b/src/main/java/twilightforest/events/EntityEvents.java index e42ae86c65..77767a7450 100644 --- a/src/main/java/twilightforest/events/EntityEvents.java +++ b/src/main/java/twilightforest/events/EntityEvents.java @@ -4,6 +4,8 @@ import net.minecraft.commands.Commands; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; @@ -12,6 +14,7 @@ import net.minecraft.sounds.SoundSource; import net.minecraft.tags.DamageTypeTags; import net.minecraft.tags.ItemTags; +import net.minecraft.util.ProblemReporter; import net.minecraft.util.random.WeightedList; import net.minecraft.world.Difficulty; import net.minecraft.world.InteractionResult; @@ -20,9 +23,10 @@ import net.minecraft.world.entity.*; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.EnderMan; -import net.minecraft.world.entity.monster.Zombie; +import net.minecraft.world.entity.monster.zombie.Zombie; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ProjectileDeflection; @@ -34,18 +38,18 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.biome.MobSpawnSettings; -import net.minecraft.world.level.block.AbstractSkullBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.SkullBlock; -import net.minecraft.world.level.block.WallSkullBlock; +import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.structure.StructurePiece; import net.minecraft.world.level.levelgen.structure.StructureStart; +import net.minecraft.world.level.storage.TagValueInput; +import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.EntityHitResult; import net.neoforged.fml.ModList; +import net.neoforged.neoforge.client.network.ClientPacketDistributor; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.damagesource.DamageContainer; import net.neoforged.neoforge.event.EventHooks; @@ -55,13 +59,12 @@ import net.neoforged.neoforge.event.entity.living.*; import net.neoforged.neoforge.event.entity.player.AdvancementEvent; import net.neoforged.neoforge.event.entity.player.AttackEntityEvent; -import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import net.neoforged.neoforge.event.level.BlockEvent; import net.neoforged.neoforge.event.level.ExplosionEvent; import net.neoforged.neoforge.event.level.LevelEvent; -import net.neoforged.neoforge.items.ItemHandlerHelper; import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import tamaized.beanification.PostConstruct; import twilightforest.TwilightForestMod; @@ -71,7 +74,6 @@ import twilightforest.block.entity.SkullCandleBlockEntity; import twilightforest.block.entity.SkullChestBlockEntity; import twilightforest.config.TFConfig; -import twilightforest.data.tags.EntityTagGenerator; import twilightforest.enchantment.ApplyFrostedEffect; import twilightforest.entity.passive.quest.ram.QuestingRamCurrentContext; import twilightforest.entity.projectile.ITFProjectile; @@ -81,6 +83,7 @@ import twilightforest.item.YetiArmorItem; import twilightforest.network.SyncQuestsPacket; import twilightforest.network.WipeOreMeterPacket; +import twilightforest.tags.TFEntityTypeTags; import twilightforest.util.datamaps.EntityTransformation; import twilightforest.util.entities.EntityUtil; import twilightforest.util.entities.OminousFireDamageSource; @@ -90,6 +93,7 @@ import twilightforest.world.components.structures.type.HollowHillStructure; import twilightforest.world.components.structures.util.ControlledSpawns; +import java.net.URI; import java.util.List; import java.util.Optional; @@ -100,6 +104,7 @@ public class EntityEvents { private QuestingRamCurrentContext questingRamCurrentContext; private static final boolean SHIELD_PARRY_MOD_LOADED = ModList.get().isLoaded("parry"); + private static final EquipmentSlot[] ARMOR_SLOTS = new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET}; @PostConstruct private void setup() { @@ -131,13 +136,13 @@ private void ominousFireConversion(LivingDeathEvent event) { EntityTransformation dataMap = event.getEntity().getType().builtInRegistryHolder().getData(TFDataMaps.OMINOUS_FIRE); if (event.getEntity() instanceof ServerPlayer player) { - var zombie = EntityType.ZOMBIE.create(player.level()); + var zombie = EntityType.ZOMBIE.create(player.level(), EntitySpawnReason.CONVERSION); zombie.setData(TFDataAttachments.ZOMBIFIED_PLAYER, player.getGameProfile()); zombie.setCustomName(player.getName()); zombie.copyPosition(player); zombie.setCanPickUpLoot(true); zombie.setBaby(false); - EventHooks.finalizeMobSpawn(zombie, player.serverLevel(), player.level().getCurrentDifficultyAt(player.blockPosition()), EntitySpawnReason.CONVERSION, null); + EventHooks.finalizeMobSpawn(zombie, player.level(), player.level().getCurrentDifficultyAt(player.blockPosition()), EntitySpawnReason.CONVERSION, null); player.level().addFreshEntity(zombie); } else if (dataMap != null && event.getEntity().level() instanceof ServerLevel) { EntityUtil.convertEntity(event.getEntity(), dataMap.result()); @@ -155,7 +160,7 @@ private void zombifiedPlayerAttacks(LivingIncomingDamageEvent event) { private void alertPlayerCastleIsWIP(AdvancementEvent.AdvancementEarnEvent event) { if (event.getAdvancement().id().equals(TwilightForestMod.prefix("progression_end"))) { - event.getEntity().sendSystemMessage(Component.translatable("gui.twilightforest.progression_end.message", Component.translatable("gui.twilightforest.progression_end.discord").withStyle(style -> style.withColor(ChatFormatting.BLUE).applyFormat(ChatFormatting.UNDERLINE).withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://discord.experiment115.com/"))))); + event.getEntity().sendSystemMessage(Component.translatable("gui.twilightforest.progression_end.message", Component.translatable("gui.twilightforest.progression_end.discord").withStyle(style -> style.withColor(ChatFormatting.BLUE).applyFormat(ChatFormatting.UNDERLINE).withClickEvent(new ClickEvent.OpenUrl(URI.create("https://discord.experiment115.com/")))))); } } @@ -178,13 +183,24 @@ private void attachLeadToWroughtFence(PlayerInteractEvent.RightClickBlock event) private void wipeOreMeterOnLeftClick(PlayerInteractEvent.LeftClickEmpty event) { ItemStack item = event.getItemStack(); if (item.is(TFItems.ORE_METER.get()) && (item.has(TFDataComponents.ORE_DATA) || item.has(TFDataComponents.ORE_FILTER))) { - PacketDistributor.sendToServer(new WipeOreMeterPacket(event.getHand())); + + ClientPacketDistributor.sendToServer(new WipeOreMeterPacket(event.getHand())); + item.remove(TFDataComponents.ORE_DATA); item.remove(TFDataComponents.ORE_FILTER); - event.getLevel().playSound(event.getEntity(), event.getEntity().blockPosition(), TFSounds.ORE_METER_CLEAR.get(), SoundSource.PLAYERS, 1.25F, event.getLevel().getRandom().nextFloat() * 0.2F + 0.6F); + + event.getLevel().playSound( + event.getEntity(), + event.getEntity().blockPosition(), + TFSounds.ORE_METER_CLEAR.get(), + SoundSource.PLAYERS, + 1.25F, + event.getLevel().getRandom().nextFloat() * 0.2F + 0.6F + ); } } + private void entityHurts(LivingDamageEvent.Post event) { LivingEntity living = event.getEntity(); DamageSource source = event.getSource(); @@ -196,7 +212,7 @@ private void entityHurts(LivingDamageEvent.Post event) { int chillLevel = getGearCoverage(living, true); if (fireLevel > 0 && living.getRandom().nextInt(25) < fireLevel && !trueSource.fireImmune()) { - trueSource.igniteForSeconds(fireLevel / 2); + trueSource.igniteForSeconds((float) fireLevel / 2); } if (trueSource instanceof LivingEntity target) { @@ -221,7 +237,7 @@ private void onCasketBreak(BlockEvent.BreakEvent event) { if (te instanceof SkullChestBlockEntity casket) { ResolvableProfile checker = casket.owner; if (checker != null && !casket.isEmpty()) { - if (!Commands.LEVEL_ADMINS.check(player.permissions()) || !player.getGameProfile().equals(checker.gameProfile())) { + if (!Commands.LEVEL_ADMINS.check(player.permissions()) || !player.getGameProfile().equals(checker.partialProfile())) { event.setCanceled(true); } } @@ -249,13 +265,12 @@ private void reduceFrostedEffectIfOnFire(LivingIncomingDamageEvent event) { private void onParryProjectile(ProjectileImpactEvent event) { final Projectile projectile = event.getProjectile(); - if (!projectile.getCommandSenderWorld().isClientSide() && !SHIELD_PARRY_MOD_LOADED && (TFConfig.parryNonTwilightAttacks || projectile instanceof ITFProjectile)) { + if (!projectile.level().isClientSide() && !SHIELD_PARRY_MOD_LOADED && (TFConfig.parryNonTwilightAttacks || projectile instanceof ITFProjectile)) { if (event.getRayTraceResult() instanceof EntityHitResult result) { Entity entity = result.getEntity(); - if (entity instanceof LivingEntity entityBlocking) { if (entityBlocking.isBlocking() && entityBlocking.getUseItem().getUseDuration(entityBlocking) - entityBlocking.getUseItemRemainingTicks() <= TFConfig.shieldParryTicks) { - projectile.deflect(ProjectileDeflection.AIM_DEFLECT, entityBlocking, entityBlocking, true); + projectile.deflect(ProjectileDeflection.AIM_DEFLECT, entityBlocking, EntityReference.of(projectile.getOwner()), true); event.setCanceled(true); } } @@ -263,6 +278,7 @@ private void onParryProjectile(ProjectileImpactEvent event) { } } + /** * Checks if the player is attempting to create a skull candle */ @@ -321,16 +337,27 @@ private void createSkullCandle(PlayerInteractEvent.RightClickBlock event) { private static void makeSkullCandle(PlayerInteractEvent.RightClickBlock event, Block newBlock) { ResolvableProfile profile = null; Level level = event.getLevel(); - if (level.getBlockEntity(event.getPos()) instanceof SkullBlockEntity skull) + + if (level.getBlockEntity(event.getPos()) instanceof SkullBlockEntity skull) { profile = skull.getOwnerProfile(); + } + level.playSound(null, event.getPos(), SoundEvents.CANDLE_PLACE, SoundSource.BLOCKS, 1.0F, 1.0F); - level.setBlockAndUpdate(event.getPos(), newBlock.withPropertiesOf(level.getBlockState(event.getPos())) - .setValue(AbstractSkullCandleBlock.LIGHTING, LightableBlock.Lighting.NONE)); - level.setBlockEntity(new SkullCandleBlockEntity(event.getPos(), - newBlock.withPropertiesOf(level.getBlockState(event.getPos())) - .setValue(AbstractSkullCandleBlock.LIGHTING, LightableBlock.Lighting.NONE), - AbstractSkullCandleBlock.candleToCandleColor(event.getItemStack().getItem()).getValue())); - if (level.getBlockEntity(event.getPos()) instanceof SkullCandleBlockEntity sc) sc.setOwner(profile); + + BlockState state = newBlock.withPropertiesOf(level.getBlockState(event.getPos())).setValue(AbstractSkullCandleBlock.LIGHTING, LightableBlock.Lighting.NONE); + + level.setBlockAndUpdate(event.getPos(), state); + + if (profile != null && level.getBlockEntity(event.getPos()) instanceof SkullCandleBlockEntity sc) { + CompoundTag tag = new CompoundTag(); + + ResolvableProfile.CODEC.encodeStart(NbtOps.INSTANCE, profile).result().ifPresent(nbt -> tag.put("profile", nbt)); + + ValueInput valueInput = TagValueInput.create(ProblemReporter.DISCARDING, level.registryAccess(),tag); + + sc.loadCustomOnly(valueInput); + sc.setChanged(); + } } /** @@ -339,7 +366,8 @@ private static void makeSkullCandle(PlayerInteractEvent.RightClickBlock event, B public static int getGearCoverage(LivingEntity entity, boolean yeti) { int amount = 0; - for (ItemStack armor : entity.getArmorSlots()) { + for (int i = 0; i < 4; i++) { + ItemStack armor = entity.getItemBySlot(ARMOR_SLOTS[i]); if (!armor.isEmpty() && (yeti ? armor.getItem() instanceof YetiArmorItem : armor.getItem() instanceof FieryArmorItem)) { amount++; } @@ -367,7 +395,7 @@ private static int getSpawnListIndexAt(StructureStart start, BlockPos pos) { } @Nullable - public static WeightedList gatherPotentialSpawns(StructureManager structureManager, MobCategory classification, BlockPos pos) { + public static WeightedList gatherPotentialSpawns(StructureManager structureManager, MobCategory classification, BlockPos pos) { List structureStarts = structureManager.startsForStructure(ChunkPos.containing(pos), s -> s instanceof ControlledSpawns); // This is wretched FIXME make this method return void instead, make one of parameters the SpawnerData consumer (eg LevelEvent.PotentialSpawns::addSpawnerData or List::add) @@ -401,7 +429,7 @@ private void structureSpecialSpawns(LevelEvent.PotentialSpawns event) { if (!(event.getLevel() instanceof ServerLevel serverLevel)) return; - WeightedList potentialStructureSpawns = gatherPotentialSpawns(serverLevel.structureManager(), event.getMobCategory(), event.getPos()); + WeightedList potentialStructureSpawns = gatherPotentialSpawns(serverLevel.structureManager(), event.getMobCategory(), event.getPos()); if (potentialStructureSpawns != null) { List.copyOf(event.getSpawnerDataList()).forEach(event::removeSpawnerData); potentialStructureSpawns.unwrap().forEach(event::addSpawnerData); @@ -412,7 +440,7 @@ private void removeCastleTextIfAttacked(AttackEntityEvent event) { // For clearing our Display text entities at the Final Castle Gazebo, there's no other way to remove them otherwise // The tag distinguishes our Interaction entities from other Mods' utilization if (event.getTarget().level() instanceof ServerLevel level && event.getTarget() instanceof Interaction interaction - && interaction.getTags().contains(FinalCastleBossGazeboComponent.INTERACTION_TAG)) { + && interaction.entityTags().contains(FinalCastleBossGazeboComponent.INTERACTION_TAG)) { AABB bounds = interaction.getBoundingBox(); level.getEntities(interaction, bounds, e -> e instanceof Display).forEach(Entity::discard); interaction.discard(); @@ -420,7 +448,7 @@ private void removeCastleTextIfAttacked(AttackEntityEvent event) { } private void adjustEntityHealthInMultiplayerFights(FinalizeSpawnEvent event) { - if (event.getEntity().getType().is(EntityTagGenerator.MULTIPLAYER_INCLUSIVE_ENTITIES)) { + if (event.getEntity().is(TFEntityTypeTags.MULTIPLAYER_INCLUSIVE_ENTITIES)) { if (TFConfig.multiplayerFightAdjuster.adjustsHealth()) { List nearbyPlayers = event.getLevel().getEntitiesOfClass(ServerPlayer.class, event.getEntity().getBoundingBox().inflate(32, 10, 32), player -> EntitySelector.NO_CREATIVE_OR_SPECTATOR.and(EntitySelector.ENTITY_STILL_ALIVE).test(player)); if (nearbyPlayers.size() > 1 && event.getEntity().getAttribute(Attributes.MAX_HEALTH) != null) { @@ -440,7 +468,7 @@ private static double getHealthBasedOnDifficulty(Difficulty difficulty) { } private void addQualifiedGroupPlayerIfNeeded(LivingDamageEvent.Post event) { - if (event.getEntity().getType().is(EntityTagGenerator.MULTIPLAYER_INCLUSIVE_ENTITIES)) { + if (event.getEntity().is(TFEntityTypeTags.MULTIPLAYER_INCLUSIVE_ENTITIES)) { var data = event.getEntity().getData(TFDataAttachments.MULTIPLAYER_FIGHT); if (event.getSource().getEntity() != null) { data.maybeAddQualifiedPlayer(event.getSource().getEntity()); @@ -490,29 +518,51 @@ private void handleLeashPathingOverrides(EntityJoinLevelEvent event) { private void stopEndermenFromGrabbingBlocksInTF(EntityJoinLevelEvent event) { if (event.getEntity() instanceof EnderMan enderMan) { enderMan.goalSelector.getAvailableGoals().stream() - .filter(g -> g.getGoal() instanceof EnderMan.EndermanTakeBlockGoal) + .filter(g -> g.getGoal().getClass().getName().endsWith("EndermanTakeBlockGoal")) .findAny() .ifPresent(g -> { enderMan.goalSelector.removeGoal(g.getGoal()); - enderMan.goalSelector.addGoal(g.getPriority(), new ExtendedEndermanTakeBlockGoal((EnderMan.EndermanTakeBlockGoal) g.getGoal(), enderMan)); + enderMan.goalSelector.addGoal(g.getPriority(), new ExtendedEndermanTakeBlockGoal(g.getGoal(), enderMan)); }); } } - static class ExtendedEndermanTakeBlockGoal extends EnderMan.EndermanTakeBlockGoal { - - private final EnderMan.EndermanTakeBlockGoal delegate; + static class ExtendedEndermanTakeBlockGoal extends Goal { + private final Goal delegate; private final EnderMan enderman; - public ExtendedEndermanTakeBlockGoal(EnderMan.EndermanTakeBlockGoal delegate, EnderMan enderman) { - super(enderman); + public ExtendedEndermanTakeBlockGoal(Goal delegate, EnderMan enderman) { this.delegate = delegate; this.enderman = enderman; + this.setFlags(delegate.getFlags()); } @Override public boolean canUse() { - return this.delegate.canUse() && !this.enderman.level().dimensionTypeRegistration().is(TFDimensionData.TWILIGHT_DIM_TYPE); + if (this.enderman.level().dimensionTypeRegistration().is(TFDimensionData.TWILIGHT_DIM_TYPE)) { + return false; + } + return this.delegate.canUse(); + } + + @Override + public boolean canContinueToUse() { + return this.delegate.canContinueToUse(); + } + + @Override + public boolean isInterruptable() { + return this.delegate.isInterruptable(); + } + + @Override + public void start() { + this.delegate.start(); + } + + @Override + public void stop() { + this.delegate.stop(); } @Override diff --git a/src/main/java/twilightforest/events/MiscEvents.java b/src/main/java/twilightforest/events/MiscEvents.java index 224fe77dd3..e233e53f48 100644 --- a/src/main/java/twilightforest/events/MiscEvents.java +++ b/src/main/java/twilightforest/events/MiscEvents.java @@ -40,7 +40,6 @@ @Component public class MiscEvents { - @PostConstruct private void setup() { NeoForge.EVENT_BUS.addListener(this::addPrey); @@ -91,7 +90,7 @@ private void addTomesToLecterns(PlayerInteractEvent.RightClickBlock event) { Player player = event.getEntity(); ItemStack stack = player.getItemInHand(event.getHand()); - if (!(stack.getItem() instanceof SpawnEggItem spawnEggItem) || spawnEggItem.getType(stack) != TFEntities.DEATH_TOME.get()) + if (!(stack.getItem() instanceof SpawnEggItem) || SpawnEggItem.getType(stack) != TFEntities.DEATH_TOME.get()) return; BlockPos pos = event.getPos(); @@ -103,7 +102,7 @@ private void addTomesToLecterns(PlayerInteractEvent.RightClickBlock event) { level.playSound(null, pos, SoundEvents.BOOK_PUT, SoundSource.BLOCKS, 1.0F, 1.0F); if (level instanceof ServerLevel serverLevel) { - DeathTome tome = TFEntities.DEATH_TOME.get().spawn(serverLevel, stack, player, pos.below(), EntitySpawnReason.SPAWN_EGG, true, false); + DeathTome tome = TFEntities.DEATH_TOME.get().spawn(serverLevel, stack, player, pos.below(), EntitySpawnReason.SPAWN_ITEM_USE, true, false); if (tome != null) { stack.consume(1, player); serverLevel.gameEvent(player, GameEvent.ENTITY_PLACE, pos); From c1de0650b2efc3d8e4af530331b8124aff8b3921 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Sun, 5 Jul 2026 03:48:48 +0300 Subject: [PATCH 02/21] Removed every @NotNull and unnecessary line change --- .../java/twilightforest/asmhooks/PlayerHooks.java | 2 -- .../client/event/LockedBiomeToastHandler.java | 1 + .../twilightforest/client/event/OverlayHandler.java | 9 ++++----- .../client/event/TravellersClientEvents.java | 7 ++----- .../{client/event => events}/CapabilityEvents.java | 2 +- .../java/twilightforest/events/CharmEvents.java | 13 ++++++------- .../java/twilightforest/events/EntityEvents.java | 5 ++--- src/main/java/twilightforest/events/MiscEvents.java | 1 + .../{client/event => events}/ProgressionEvents.java | 5 ++--- .../event => events}/RegistrationEvents.java | 5 ++--- .../{client/event => events}/ToolEvents.java | 5 ++--- .../event => events}/TravellersGearEvents.java | 13 ++++++------- 12 files changed, 29 insertions(+), 39 deletions(-) rename src/main/java/twilightforest/{client/event => events}/CapabilityEvents.java (99%) rename src/main/java/twilightforest/{client/event => events}/ProgressionEvents.java (98%) rename src/main/java/twilightforest/{client/event => events}/RegistrationEvents.java (99%) rename src/main/java/twilightforest/{client/event => events}/ToolEvents.java (98%) rename src/main/java/twilightforest/{client/event => events}/TravellersGearEvents.java (96%) diff --git a/src/main/java/twilightforest/asmhooks/PlayerHooks.java b/src/main/java/twilightforest/asmhooks/PlayerHooks.java index b717a3f5e9..1398796700 100644 --- a/src/main/java/twilightforest/asmhooks/PlayerHooks.java +++ b/src/main/java/twilightforest/asmhooks/PlayerHooks.java @@ -8,8 +8,6 @@ import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -//import twilightforest.asm.transformers.player.GetFieldOfViewModifierTransformer; -//import twilightforest.asm.transformers.player.ReduceMovementFoodExhaustionTransformer; import twilightforest.init.TFAttributeModifiers; import twilightforest.init.TFDataAttachments; import twilightforest.init.TFDataComponents; diff --git a/src/main/java/twilightforest/client/event/LockedBiomeToastHandler.java b/src/main/java/twilightforest/client/event/LockedBiomeToastHandler.java index 6cc72b0fb5..6a9b45af90 100644 --- a/src/main/java/twilightforest/client/event/LockedBiomeToastHandler.java +++ b/src/main/java/twilightforest/client/event/LockedBiomeToastHandler.java @@ -12,6 +12,7 @@ import java.util.Optional; public class LockedBiomeToastHandler { + private static boolean shownToast = false; private static int timeUntilToast = 60; diff --git a/src/main/java/twilightforest/client/event/OverlayHandler.java b/src/main/java/twilightforest/client/event/OverlayHandler.java index a787d8c507..6db6fe96bd 100644 --- a/src/main/java/twilightforest/client/event/OverlayHandler.java +++ b/src/main/java/twilightforest/client/event/OverlayHandler.java @@ -25,7 +25,6 @@ import net.neoforged.api.distmarker.Dist; import net.neoforged.neoforge.client.event.RegisterGuiLayersEvent; import net.neoforged.neoforge.client.gui.VanillaGuiLayers; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import twilightforest.TwilightForestMod; import tamaized.beanification.Autowired; @@ -278,7 +277,7 @@ private static ComponentColumn withoutPrettyPrinting(int totalScanned, List oreNameKeys) { - ImmutableList.Builder<@NotNull Component> toList = ImmutableList.builder(); + ImmutableList.Builder toList = ImmutableList.builder(); toList.add(Component.translatable("misc.twilightforest.ore_meter_header_block").withStyle(ChatFormatting.GRAY)); @@ -291,7 +290,7 @@ private static ComponentColumn nameColumn(List oreNameKeys) { } private static ComponentColumn dashColumn(int size) { - ImmutableList.Builder<@NotNull Component> toList = ImmutableList.builder(); + ImmutableList.Builder toList = ImmutableList.builder(); toList.add(Component.empty()); @@ -303,7 +302,7 @@ private static ComponentColumn dashColumn(int size) { } private static ComponentColumn countColumn(List oreCounts) { - ImmutableList.Builder<@NotNull Component> toList = ImmutableList.builder(); + ImmutableList.Builder toList = ImmutableList.builder(); toList.add(Component.translatable("misc.twilightforest.ore_meter_header_count").withStyle(ChatFormatting.GRAY)); @@ -313,7 +312,7 @@ private static ComponentColumn countColumn(List oreCounts) { } private static ComponentColumn ratioColumn(int totalScanned, List oreCounts) { - ImmutableList.Builder<@NotNull Component> toList = ImmutableList.builder(); + ImmutableList.Builder toList = ImmutableList.builder(); toList.add(Component.translatable("misc.twilightforest.ore_meter_header_ratio").withStyle(ChatFormatting.GRAY)); diff --git a/src/main/java/twilightforest/client/event/TravellersClientEvents.java b/src/main/java/twilightforest/client/event/TravellersClientEvents.java index 9ec55bfd32..77613322e3 100644 --- a/src/main/java/twilightforest/client/event/TravellersClientEvents.java +++ b/src/main/java/twilightforest/client/event/TravellersClientEvents.java @@ -20,10 +20,8 @@ import net.minecraft.core.component.DataComponents; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; -import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; @@ -38,7 +36,6 @@ import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.registries.DeferredHolder; -import org.jetbrains.annotations.NotNull; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; import twilightforest.TwilightForestMod; @@ -334,10 +331,10 @@ private void renderGlovesInFirstPerson(RenderArmEvent event) { if (entityRenderDispatcher.getRenderer(player) instanceof AvatarRenderer avatarRenderer) { var playerModel = avatarRenderer.getModel(); - Model<@NotNull HumanoidRenderState> armorModel = IClientItemExtensions.of(TFItems.TRAVELLERS_GLOVES.get()) + Model armorModel = IClientItemExtensions.of(TFItems.TRAVELLERS_GLOVES.get()) .getHumanoidArmorModel(chestStack, EquipmentClientInfo.LayerType.HUMANOID, playerModel); - if (armorModel instanceof HumanoidModel<@NotNull HumanoidRenderState> humanoidArmorModel) { + if (armorModel instanceof HumanoidModel humanoidArmorModel) { ModelPart armPart = humanoidArmorModel.getArm(event.getArm()); var dummyState = new HumanoidRenderState(); diff --git a/src/main/java/twilightforest/client/event/CapabilityEvents.java b/src/main/java/twilightforest/events/CapabilityEvents.java similarity index 99% rename from src/main/java/twilightforest/client/event/CapabilityEvents.java rename to src/main/java/twilightforest/events/CapabilityEvents.java index 045500ba76..ba33dae638 100644 --- a/src/main/java/twilightforest/client/event/CapabilityEvents.java +++ b/src/main/java/twilightforest/events/CapabilityEvents.java @@ -1,4 +1,4 @@ -package twilightforest.client.event; +package twilightforest.events; import net.minecraft.core.BlockPos; import net.minecraft.core.GlobalPos; diff --git a/src/main/java/twilightforest/events/CharmEvents.java b/src/main/java/twilightforest/events/CharmEvents.java index d8d301b425..db17bdcad8 100644 --- a/src/main/java/twilightforest/events/CharmEvents.java +++ b/src/main/java/twilightforest/events/CharmEvents.java @@ -36,7 +36,6 @@ import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.registries.DeferredItem; -import org.jetbrains.annotations.NotNull; import tamaized.beanification.PostConstruct; import twilightforest.TwilightForestMod; import twilightforest.block.KeepsakeCasketBlock; @@ -189,7 +188,7 @@ private static void handleCharmOfKeeping(Player player) { } - private static boolean applyCharm(DeferredItem<@NotNull Item> charm, Inventory keptInventory, Player player, List inventorySlots) { + private static boolean applyCharm(DeferredItem charm, Inventory keptInventory, Player player, List inventorySlots) { List playerArmor = new java.util.AbstractList<>() { @Override public ItemStack get(int index) { return player.getInventory().getItem(36 + index); } @Override public int size() { return 4; } @@ -217,7 +216,7 @@ private static boolean applyCharm(DeferredItem<@NotNull Item> charm, Inventory k if (mergedCheck.stream().filter(stack -> !stack.is(charm)).allMatch(ItemStack::isEmpty)) return false; if (!TFItemStackUtils.consumeInventoryItem(player, charm, getPlayerData(player), true) && !hasCharmCurio(charm.value(), player)) return false; - NonNullList<@NotNull ItemStack> keptItemsList = NonNullList.create(); + NonNullList keptItemsList = NonNullList.create(); for (int i = 0; i < inventorySlots.size(); i++) { keptItemsList.add(keptInventory.getItem(i)); } @@ -227,7 +226,7 @@ private static boolean applyCharm(DeferredItem<@NotNull Item> charm, Inventory k keptInventory.setItem(i, keptItemsList.get(i)); } - NonNullList<@NotNull ItemStack> keptArmorList = NonNullList.create(); + NonNullList keptArmorList = NonNullList.create(); for (int i = 0; i < 4; i++) { keptArmorList.add(keptInventory.getItem(i + 36)); } @@ -237,7 +236,7 @@ private static boolean applyCharm(DeferredItem<@NotNull Item> charm, Inventory k keptInventory.setItem(i + 36, keptArmorList.get(i)); } - NonNullList<@NotNull ItemStack> keptOffhandList = NonNullList.create(); + NonNullList keptOffhandList = NonNullList.create(); keptOffhandList.add(keptInventory.getItem(40)); keepWholeListAndCheckCasket(keptOffhandList, playerOffhand, keptACasket); keptInventory.setItem(40, keptOffhandList.get(0)); @@ -316,7 +315,7 @@ private static void stockKeepsakeCasket(Player player) { int casketCapacity = casket.getContainerSize(); List list = new ArrayList<>(casketCapacity); - NonNullList<@NotNull ItemStack> filler = NonNullList.withSize(4, ItemStack.EMPTY); + NonNullList filler = NonNullList.withSize(4, ItemStack.EMPTY); // lets add our inventory exactly how it was on us list.addAll(TFItemStackUtils.sortArmorForCasket(player)); @@ -407,7 +406,7 @@ public static CompoundTag getPlayerData(Player player) { } //transfers a list of items to another - private static boolean keepWholeListAndCheckCasket(NonNullList<@NotNull ItemStack> transferTo, List transferFrom, boolean skipCasketCheck) { + private static boolean keepWholeListAndCheckCasket(NonNullList transferTo, List transferFrom, boolean skipCasketCheck) { boolean keptCasket = false; for (int i = 0; i < transferFrom.size(); i++) { var item = transferFrom.get(i).copy(); diff --git a/src/main/java/twilightforest/events/EntityEvents.java b/src/main/java/twilightforest/events/EntityEvents.java index 77767a7450..500d414d52 100644 --- a/src/main/java/twilightforest/events/EntityEvents.java +++ b/src/main/java/twilightforest/events/EntityEvents.java @@ -64,7 +64,6 @@ import net.neoforged.neoforge.event.level.ExplosionEvent; import net.neoforged.neoforge.event.level.LevelEvent; import net.neoforged.neoforge.network.PacketDistributor; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import tamaized.beanification.PostConstruct; import twilightforest.TwilightForestMod; @@ -395,7 +394,7 @@ private static int getSpawnListIndexAt(StructureStart start, BlockPos pos) { } @Nullable - public static WeightedList gatherPotentialSpawns(StructureManager structureManager, MobCategory classification, BlockPos pos) { + public static WeightedList gatherPotentialSpawns(StructureManager structureManager, MobCategory classification, BlockPos pos) { List structureStarts = structureManager.startsForStructure(ChunkPos.containing(pos), s -> s instanceof ControlledSpawns); // This is wretched FIXME make this method return void instead, make one of parameters the SpawnerData consumer (eg LevelEvent.PotentialSpawns::addSpawnerData or List::add) @@ -429,7 +428,7 @@ private void structureSpecialSpawns(LevelEvent.PotentialSpawns event) { if (!(event.getLevel() instanceof ServerLevel serverLevel)) return; - WeightedList potentialStructureSpawns = gatherPotentialSpawns(serverLevel.structureManager(), event.getMobCategory(), event.getPos()); + WeightedList potentialStructureSpawns = gatherPotentialSpawns(serverLevel.structureManager(), event.getMobCategory(), event.getPos()); if (potentialStructureSpawns != null) { List.copyOf(event.getSpawnerDataList()).forEach(event::removeSpawnerData); potentialStructureSpawns.unwrap().forEach(event::addSpawnerData); diff --git a/src/main/java/twilightforest/events/MiscEvents.java b/src/main/java/twilightforest/events/MiscEvents.java index e233e53f48..8a955e8690 100644 --- a/src/main/java/twilightforest/events/MiscEvents.java +++ b/src/main/java/twilightforest/events/MiscEvents.java @@ -40,6 +40,7 @@ @Component public class MiscEvents { + @PostConstruct private void setup() { NeoForge.EVENT_BUS.addListener(this::addPrey); diff --git a/src/main/java/twilightforest/client/event/ProgressionEvents.java b/src/main/java/twilightforest/events/ProgressionEvents.java similarity index 98% rename from src/main/java/twilightforest/client/event/ProgressionEvents.java rename to src/main/java/twilightforest/events/ProgressionEvents.java index 20fced2967..481a167f50 100644 --- a/src/main/java/twilightforest/client/event/ProgressionEvents.java +++ b/src/main/java/twilightforest/events/ProgressionEvents.java @@ -1,4 +1,4 @@ -package twilightforest.client.event; +package twilightforest.events; import com.mojang.datafixers.util.Pair; import net.minecraft.advancements.AdvancementHolder; @@ -6,8 +6,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.network.chat.ChatType; -import net.minecraft.network.chat.OutgoingChatMessage; import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -60,6 +58,7 @@ */ @Component public class ProgressionEvents { + @PostConstruct private void setup() { NeoForge.EVENT_BUS.addListener(this::preventLockedAreaBlockBreaking); diff --git a/src/main/java/twilightforest/client/event/RegistrationEvents.java b/src/main/java/twilightforest/events/RegistrationEvents.java similarity index 99% rename from src/main/java/twilightforest/client/event/RegistrationEvents.java rename to src/main/java/twilightforest/events/RegistrationEvents.java index 1f95d22472..53bf5af3b2 100644 --- a/src/main/java/twilightforest/client/event/RegistrationEvents.java +++ b/src/main/java/twilightforest/events/RegistrationEvents.java @@ -1,4 +1,4 @@ -package twilightforest.client.event; +package twilightforest.events; import com.google.common.collect.Maps; import net.minecraft.core.Direction; @@ -43,7 +43,6 @@ import net.neoforged.neoforge.transfer.item.ItemResource; import net.neoforged.neoforge.transfer.item.ItemStackResourceHandler; import net.neoforged.neoforge.transfer.item.VanillaContainerWrapper; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import tamaized.beanification.Autowired; import tamaized.beanification.Component; @@ -114,7 +113,7 @@ private void setup(IEventBus bus) { } private void registerGenericItemHandlers(RegisterCapabilitiesEvent event) { - IBlockCapabilityProvider<@NotNull ResourceHandler<@NotNull ItemResource>, @Nullable Direction> itemHandlerProvider = (level, pos, state, blockEntity, side) -> level.getBlockEntity(pos) instanceof ChestBlockEntity tfChestBlock ? VanillaContainerWrapper.of(tfChestBlock) : null; + IBlockCapabilityProvider, @Nullable Direction> itemHandlerProvider = (level, pos, state, blockEntity, side) -> level.getBlockEntity(pos) instanceof ChestBlockEntity tfChestBlock ? VanillaContainerWrapper.of(tfChestBlock) : null; event.registerBlock( Capabilities.Item.BLOCK, itemHandlerProvider, diff --git a/src/main/java/twilightforest/client/event/ToolEvents.java b/src/main/java/twilightforest/events/ToolEvents.java similarity index 98% rename from src/main/java/twilightforest/client/event/ToolEvents.java rename to src/main/java/twilightforest/events/ToolEvents.java index 3e492c37c6..a48b48d1a0 100644 --- a/src/main/java/twilightforest/client/event/ToolEvents.java +++ b/src/main/java/twilightforest/events/ToolEvents.java @@ -1,4 +1,4 @@ -package twilightforest.client.event; +package twilightforest.events; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderSet; @@ -35,7 +35,6 @@ import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; import net.neoforged.neoforge.event.entity.living.MobEffectEvent; import net.neoforged.neoforge.event.level.BlockEvent; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; @@ -226,7 +225,7 @@ private void refreshOreMagnetCache(TagsUpdatedEvent event) { OreMagnetItem.TREE_ORE_TO_BLOCK_REPLACEMENTS.clear(); //collect all tags - for (TagKey<@NotNull Block> tag : BuiltInRegistries.BLOCK.getTags().filter(location -> location.key().location().getNamespace().equals("c")).map(HolderSet.Named::key).toList()) { + for (TagKey tag : BuiltInRegistries.BLOCK.getTags().filter(location -> location.key().location().getNamespace().equals("c")).map(HolderSet.Named::key).toList()) { //check if the tag is a valid ore tag if (tag.location().getPath().contains("ores_in_ground/")) { //grab the part after the slash for use later diff --git a/src/main/java/twilightforest/client/event/TravellersGearEvents.java b/src/main/java/twilightforest/events/TravellersGearEvents.java similarity index 96% rename from src/main/java/twilightforest/client/event/TravellersGearEvents.java rename to src/main/java/twilightforest/events/TravellersGearEvents.java index e1fa75f97c..d8c2e4fcf7 100644 --- a/src/main/java/twilightforest/client/event/TravellersGearEvents.java +++ b/src/main/java/twilightforest/events/TravellersGearEvents.java @@ -1,4 +1,4 @@ -package twilightforest.client.event; +package twilightforest.events; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; @@ -42,7 +42,6 @@ import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.server.ServerLifecycleHooks; -import org.jetbrains.annotations.NotNull; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; import twilightforest.components.entity.SlimySolesAttachment; @@ -61,7 +60,7 @@ @Component public class TravellersGearEvents { - private static final List, ? extends @NotNull AttachmentType>> ATTACHMENTS_TO_PRESERVE_ON_DEATH = List.of( + private static final List, ? extends AttachmentType>> ATTACHMENTS_TO_PRESERVE_ON_DEATH = List.of( TFDataAttachments.TRAVELLERS_GOGGLES_RED_THREAD_VISION ); @@ -310,7 +309,7 @@ private void removeModifiersFromTravellersGear(GrindstoneEvent.OnPlaceItem event return; } ItemStack inputStack = travellersItemStacks.getFirst(); - List> modifiers = TravellersModifiersManager.findAllInsertableModifiers(access, inputStack); + List> modifiers = TravellersModifiersManager.findAllInsertableModifiers(access, inputStack); if (modifiers.isEmpty()) { event.setCanceled(true); return; @@ -335,7 +334,7 @@ private void extractItemsFromSwapHotbarModifier(GrindstoneEvent.OnTakeItem event ); } - private void returnModifierItems(GrindstoneEvent.OnTakeItem event, ResourceKey<@NotNull TravellersModifier> modifierKey, DataComponentType<@NotNull T> componentType, Function> itemStreamExtractor) { + private void returnModifierItems(GrindstoneEvent.OnTakeItem event, ResourceKey modifierKey, DataComponentType componentType, Function> itemStreamExtractor) { if (event.getPlayer() == null) return; @@ -380,13 +379,13 @@ private void fireCraftingModifierTrigger(PlayerEvent.ItemCraftedEvent event) { public void keepAttachmentsOnDeath(PlayerEvent.Clone event) { if (event.isWasDeath()) { - for (DeferredHolder<@NotNull AttachmentType, ? extends @NotNull AttachmentType> attachmentHolder : ATTACHMENTS_TO_PRESERVE_ON_DEATH) { + for (DeferredHolder, ? extends AttachmentType> attachmentHolder : ATTACHMENTS_TO_PRESERVE_ON_DEATH) { copyAttachmentData(event.getOriginal(), event.getEntity(), attachmentHolder.get()); } } } - private void copyAttachmentData(Player source, Player target, AttachmentType<@NotNull T> type) { + private void copyAttachmentData(Player source, Player target, AttachmentType type) { if (source.hasData(type)) { target.setData(type, source.getData(type)); } From 72a945a8d583eabd91ea88f812c013d8b28f43ed Mon Sep 17 00:00:00 2001 From: Albazavr Date: Thu, 16 Jul 2026 02:53:36 +0300 Subject: [PATCH 03/21] Fixed files --- .../twilightforest/asmhooks/EntityHooks.java | 3 --- .../client/event/ClientGameEvents.java | 20 +++++-------------- .../event/ClientRegistrationEvents.java | 11 +++++----- 3 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/main/java/twilightforest/asmhooks/EntityHooks.java b/src/main/java/twilightforest/asmhooks/EntityHooks.java index 76b2c4a403..11fba04e1b 100644 --- a/src/main/java/twilightforest/asmhooks/EntityHooks.java +++ b/src/main/java/twilightforest/asmhooks/EntityHooks.java @@ -33,10 +33,8 @@ public static boolean processWaterWalking(boolean o, LivingEntity livingEntity, return o; boolean isWaterWalking = TravellersGearLogic.isBelowMaxWaterWalkingSubmergedHeight(livingEntity) && !livingEntity.isShiftKeyDown(); - if (livingEntity.getFluidHeight(FluidTags.WATER) > 0.0D && isWaterWalking && livingEntity.level().getGameTime() % 3 == 1) TravellersGearLogic.waterWalkingSplashEffect(livingEntity); - return isWaterWalking; } @@ -102,7 +100,6 @@ public static float resetFactorWithUnrestrained(float o, Entity entity) { public static Entity resetStuckUnrestrained(Entity entity) { if (!(entity instanceof LivingEntity living) || !TravellersModifiersManager.isModifierActive(entity, TravellersModifiersManager.UNRESTRAINED_MODIFIER)) return entity; - living.makeStuckInBlock(Blocks.AIR.defaultBlockState(), new Vec3(1.0D, 1.0D, 1.0D)); return entity; diff --git a/src/main/java/twilightforest/client/event/ClientGameEvents.java b/src/main/java/twilightforest/client/event/ClientGameEvents.java index 6ab2eed0b6..3d98cc2fe7 100644 --- a/src/main/java/twilightforest/client/event/ClientGameEvents.java +++ b/src/main/java/twilightforest/client/event/ClientGameEvents.java @@ -21,6 +21,8 @@ import net.minecraft.sounds.Music; import net.minecraft.sounds.Musics; import net.minecraft.util.Mth; +import net.minecraft.world.attribute.BackgroundMusic; +import net.minecraft.world.attribute.EnvironmentAttributes; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; @@ -103,8 +105,8 @@ private void setup() { NeoForge.EVENT_BUS.addListener(this::unrenderHeadWithTrophies); NeoForge.EVENT_BUS.addListener(this::updateBowFOV); -// NeoForge.EVENT_BUS.addListener(CloudEvents::renderPrecipitation); -// NeoForge.EVENT_BUS.addListener(CloudEvents::tickWeatherEffects); + NeoForge.EVENT_BUS.addListener(CloudEvents::renderPrecipitation); + NeoForge.EVENT_BUS.addListener(CloudEvents::tickWeatherEffects); NeoForge.EVENT_BUS.addListener(FogHandler::renderFog); NeoForge.EVENT_BUS.addListener(FogHandler::unloadFog); @@ -144,19 +146,7 @@ private void clearEntityRenderUtilMap(ScreenEvent.Closing event) { private void setMusicInDimension(SelectMusicEvent event) { Music music = event.getOriginalMusic(); if (Minecraft.getInstance().level != null && Minecraft.getInstance().player != null && (music == Musics.CREATIVE || music == Musics.UNDER_WATER) && TFDimension.isTwilightWorldOnClient(Minecraft.getInstance().level)) { - var biomeHolder = Minecraft.getInstance().level.getBiomeManager().getNoiseBiomeAtPosition(Minecraft.getInstance().player.blockPosition()); - var biomeKeyOpt = biomeHolder.unwrapKey(); - if (biomeKeyOpt.isPresent()) { - var biomeKey = biomeKeyOpt.get(); - Music selectedMusic = Musics.GAME; // Default fallback - String keyString = biomeKey.toString(); - if (keyString.contains("lake") || keyString.contains("swamp")) { - selectedMusic = Musics.UNDER_WATER; - } else if (keyString.contains("enchanted")) { - selectedMusic = Musics.CREDITS; - } - event.setMusic(selectedMusic); - } + event.setMusic(Minecraft.getInstance().level.getBiomeManager().getNoiseBiomeAtPosition(Minecraft.getInstance().player.blockPosition()).value().getAttributes().applyModifier(EnvironmentAttributes.BACKGROUND_MUSIC, new BackgroundMusic(Musics.GAME)).defaultMusic().orElse(Musics.GAME)); } } diff --git a/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java b/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java index 2529099b34..ead79957ce 100644 --- a/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java +++ b/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java @@ -16,19 +16,19 @@ import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleEngine; import net.minecraft.client.particle.SpriteSet; -import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.renderer.Sheets; +import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.LivingEntityRenderer; import net.minecraft.client.renderer.entity.NoopRenderer; import net.minecraft.client.renderer.entity.ThrownItemRenderer; -import net.minecraft.client.renderer.entity.player.AvatarRenderer; -import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; import net.minecraft.client.resources.model.sprite.AtlasManager; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; @@ -453,7 +453,6 @@ private void registerSpecialModelRenderers(RegisterSpecialModelRendererEvent eve event.register(TwilightForestMod.prefix("item/moonworm"), MoonwormSpecialRenderer.Unbaked.MAP_CODEC); } - private void registerClientExtensions(RegisterClientExtensionsEvent event) { event.registerBlock(new IClientBlockExtensions() { @Override @@ -573,13 +572,13 @@ private void registerMapDecorators(RegisterMapDecorationRenderersEvent event) { private void attachRenderLayers(EntityRenderersEvent.AddLayers event) { BakedMultiPartRenderers.bakeMultiPartRenderers(event.getContext()); for (EntityType type : event.getEntityTypes()) { - var renderer = event.getRenderer(type); + EntityRenderer renderer = event.getRenderer(type); if (renderer instanceof LivingEntityRenderer living) { attachToLiving(living); } } event.getSkins().forEach(skinName -> { - var skinRenderer = event.getPlayerRenderer(skinName); + LivingEntityRenderer skinRenderer = event.getPlayerRenderer(skinName); if (skinRenderer != null) { attachToLiving(skinRenderer); } From aad53bdd9ce64d103c6f150c424024ff8fbd5663 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Mon, 20 Jul 2026 03:32:42 +0300 Subject: [PATCH 04/21] Fixed review comment for EntityHooks --- src/main/java/twilightforest/asmhooks/EntityHooks.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/twilightforest/asmhooks/EntityHooks.java b/src/main/java/twilightforest/asmhooks/EntityHooks.java index 11fba04e1b..820f57a75e 100644 --- a/src/main/java/twilightforest/asmhooks/EntityHooks.java +++ b/src/main/java/twilightforest/asmhooks/EntityHooks.java @@ -100,7 +100,7 @@ public static float resetFactorWithUnrestrained(float o, Entity entity) { public static Entity resetStuckUnrestrained(Entity entity) { if (!(entity instanceof LivingEntity living) || !TravellersModifiersManager.isModifierActive(entity, TravellersModifiersManager.UNRESTRAINED_MODIFIER)) return entity; - living.makeStuckInBlock(Blocks.AIR.defaultBlockState(), new Vec3(1.0D, 1.0D, 1.0D)); + living.makeStuckInBlock(Blocks.AIR.defaultBlockState(), Vec3.ZERO); return entity; } From dc54f61ec0d1683b6d4d657f3dbd07698d96525c Mon Sep 17 00:00:00 2001 From: Albazavr Date: Tue, 21 Jul 2026 19:19:22 +0300 Subject: [PATCH 05/21] Fixed ClientGameEvents --- .../client/event/ClientGameEvents.java | 169 +++++++++--------- 1 file changed, 83 insertions(+), 86 deletions(-) diff --git a/src/main/java/twilightforest/client/event/ClientGameEvents.java b/src/main/java/twilightforest/client/event/ClientGameEvents.java index 3d98cc2fe7..a65ca5a634 100644 --- a/src/main/java/twilightforest/client/event/ClientGameEvents.java +++ b/src/main/java/twilightforest/client/event/ClientGameEvents.java @@ -6,11 +6,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.SplashRenderer; import net.minecraft.client.gui.screens.TitleScreen; -import net.minecraft.client.model.HeadedModel; -import net.minecraft.client.model.HumanoidModel; -import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.ShapeRenderer; import net.minecraft.client.renderer.rendertype.RenderTypes; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -23,8 +19,6 @@ import net.minecraft.util.Mth; import net.minecraft.world.attribute.BackgroundMusic; import net.minecraft.world.attribute.EnvironmentAttributes; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.*; import net.minecraft.world.level.ChunkPos; @@ -36,8 +30,6 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.neoforged.api.distmarker.Dist; -import net.neoforged.fml.ModList; -import net.neoforged.fml.util.ObfuscationReflectionHelper; import net.neoforged.neoforge.client.event.*; import net.neoforged.neoforge.client.gui.VanillaGuiLayers; import net.neoforged.neoforge.common.NeoForge; @@ -51,7 +43,6 @@ import twilightforest.client.*; import twilightforest.client.renderer.TFSkyRenderer; import twilightforest.client.renderer.entity.MagicPaintingRenderer; -import twilightforest.compat.curios.CuriosCompat; import twilightforest.config.TFConfig; import twilightforest.entity.boss.bar.ClientTFBossBar; import twilightforest.events.HostileMountEvents; @@ -60,8 +51,6 @@ import twilightforest.tags.TFItemTags; import twilightforest.util.HolderMatcher; -import java.awt.*; -import java.lang.reflect.Field; import java.time.LocalDate; import java.time.Month; import java.util.HashSet; @@ -82,8 +71,6 @@ public class ClientGameEvents { private int aurora = 0; private int lastAurora = 0; - private static Field skyFlashTimeField; - @Autowired(dist = Dist.CLIENT) private HolderMatcher holderMatcher; @@ -102,7 +89,8 @@ private void setup() { NeoForge.EVENT_BUS.addListener(this::setMusicInDimension); NeoForge.EVENT_BUS.addListener(this::shakeCamera); NeoForge.EVENT_BUS.addListener(this::translateBookAuthor); - NeoForge.EVENT_BUS.addListener(this::unrenderHeadWithTrophies); + // Uncomment this when Curios compat is ready +// NeoForge.EVENT_BUS.addListener(this::unrenderHeadWithTrophies); NeoForge.EVENT_BUS.addListener(this::updateBowFOV); NeoForge.EVENT_BUS.addListener(CloudEvents::renderPrecipitation); @@ -167,30 +155,21 @@ private void removeHostileMountHealth(RenderGuiLayerEvent.Pre event) { private void renderAurora(RenderLevelStageEvent.AfterWeather event) { if (Minecraft.getInstance().level == null) return; - if (aurora > 0 || lastAurora > 0) { - var bufferSource = Minecraft.getInstance().renderBuffers().bufferSource(); - - VertexConsumer consumer = bufferSource.getBuffer(TFShaders.AURORA); + if ((aurora > 0 || lastAurora > 0) && TFShaders.AURORA != null) { + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder buffer = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - float partialTicks = Minecraft.getInstance().getDeltaTracker().getGameTimeDeltaTicks(); - float alpha = (Mth.lerp(partialTicks, lastAurora, aurora)) / 60F * 0.5F; - - final float scale = 2048F * (Minecraft.getInstance().options.renderDistance().get() / 32F); + final float scale = 2048F * (Minecraft.getInstance().options.getEffectiveRenderDistance() / 2F); Vec3 pos = Minecraft.getInstance().gameRenderer.getMainCamera().position(); float y = (float) (256F - pos.y()); - - PoseStack poseStack = event.getPoseStack(); - poseStack.pushPose(); - - PoseStack.Pose lastPose = poseStack.last(); - - consumer.addVertex(lastPose.pose(), -scale, y, scale).setColor(1F, 1F, 1F, alpha); - consumer.addVertex(lastPose.pose(), -scale, y, -scale).setColor(1F, 1F, 1F, alpha); - consumer.addVertex(lastPose.pose(), scale, y, -scale).setColor(1F, 1F, 1F, alpha); - consumer.addVertex(lastPose.pose(), scale, y, scale).setColor(1F, 1F, 1F, alpha); - - poseStack.popPose(); - bufferSource.endBatch(TFShaders.AURORA); + buffer.addVertex(-scale, y, scale).setColor(1F, 1F, 1F, 1F); + buffer.addVertex(-scale, y, -scale).setColor(1F, 1F, 1F, 1F); + buffer.addVertex(scale, y, -scale).setColor(1F, 1F, 1F, 1F); + buffer.addVertex(scale, y, scale).setColor(1F, 1F, 1F, 1F); + + TFShaders.AURORA.invokeThenEndTesselator( + Minecraft.getInstance().level == null ? 0 : Mth.abs((int) Minecraft.getInstance().level.getBiomeManager().biomeZoomSeed), + (float) pos.x(), (float) pos.y(), (float) pos.z(), buffer); } } @@ -228,9 +207,7 @@ private void clientTick(ClientTickEvent.Post event) { BugModelAnimationHelper.animate(); if (mc.level != null) { - Integer flashTime = ObfuscationReflectionHelper.getPrivateValue(ClientLevel.class, mc.level, "skyFlashTime"); - - if (flashTime != null && flashTime > 0) { + if (mc.level.getSkyFlashTime() > 0) { MagicPaintingRenderer.lastLightning = mc.level.getGameTime(); } @@ -251,7 +228,7 @@ private void clientTick(ClientTickEvent.Post event) { Player player = mc.player; shakeIntensity = (float) (1.0F - mc.player.distanceToSqr(Vec3.atCenterOf(beanstalk.getBlockPos())) / Math.pow(16, 2)); if (shakeIntensity > 0) { - player.absSnapTo(player.getX(), player.getY(), player.getZ(), + player.snapTo(player.getX(), player.getY(), player.getZ(), player.getYRot() + (player.getRandom().nextFloat() - 0.5F) * shakeIntensity, player.getXRot() + (player.getRandom().nextFloat() * 2.5F - 1.25F) * shakeIntensity); shakeIntensity = 0.0F; @@ -301,28 +278,25 @@ private void updateBowFOV(ComputeFovModifierEvent event) { } } - private void unrenderHeadWithTrophies(RenderLivingEvent.Pre event) { - LocalPlayer localPlayer = Minecraft.getInstance().player; - if (localPlayer == null) { - return; - } - ItemStack stack = localPlayer.getItemBySlot(EquipmentSlot.HEAD); - boolean visible = !(stack.is(TFItemTags.TROPHIES)) && !areCuriosEquipped(localPlayer); - boolean isPlayer = true; - if (event.getRenderer().getModel() instanceof HeadedModel headedModel) { - headedModel.getHead().visible = visible && (!isPlayer || headedModel.getHead().visible); - if (event.getRenderer().getModel() instanceof HumanoidModel humanoidModel) { - humanoidModel.hat.visible = visible && (!isPlayer || humanoidModel.hat.visible); - } - } - } - - private boolean areCuriosEquipped(LivingEntity entity) { - if (ModList.get().isLoaded("curios")) { - return CuriosCompat.isCurioEquippedAndVisible(entity, stack -> stack.is(TFItemTags.TROPHIES)); - } - return false; - } + // Uncomment this when Curios compat is ready +// private void unrenderHeadWithTrophies(RenderLivingEvent.Pre event) { +// ItemStack stack = event.getEntity().getItemBySlot(EquipmentSlot.HEAD); +// boolean visible = !(stack.getItem() instanceof TrophyItem) && !areCuriosEquipped(event.getEntity()); +// boolean isPlayer = event.getEntity() instanceof Player; +// if (event.getRenderer().getModel() instanceof HeadedModel headedModel) { +// headedModel.getHead().visible = visible && (!isPlayer || headedModel.getHead().visible); // some mods like Better Combat can move player's head and hide it in the first person view +// if (event.getRenderer().getModel() instanceof HumanoidModel humanoidModel) { +// humanoidModel.hat.visible = visible && (!isPlayer || humanoidModel.hat.visible); +// } +// } +// } +// +// private boolean areCuriosEquipped(LivingEntity entity) { +// if (ModList.get().isLoaded("curios")) { +// return CuriosCompat.isCurioEquippedAndVisible(entity, stack -> stack.getItem() instanceof TrophyItem); +// } +// return false; +// } private void translateBookAuthor(ItemTooltipEvent event) { ItemStack stack = event.getItemStack(); @@ -339,37 +313,60 @@ private void translateBookAuthor(ItemTooltipEvent event) { } } + // [VanillaCopy] the copy of removed LevelRenderer.renderShape + private void renderShape( + PoseStack poseStack, + VertexConsumer consumer, + VoxelShape shape, + double x, + double y, + double z, + float red, + float green, + float blue, + float alpha + ) { + PoseStack.Pose posestack$pose = poseStack.last(); + shape.forAllEdges( + (p_323073_, p_323074_, p_323075_, p_323076_, p_323077_, p_323078_) -> { + float f = (float)(p_323076_ - p_323073_); + float f1 = (float)(p_323077_ - p_323074_); + float f2 = (float)(p_323078_ - p_323075_); + float f3 = Mth.sqrt(f * f + f1 * f1 + f2 * f2); + f /= f3; + f1 /= f3; + f2 /= f3; + consumer.addVertex(posestack$pose, (float)(p_323073_ + x), (float)(p_323074_ + y), (float)(p_323075_ + z)) + .setColor(red, green, blue, alpha) + .setNormal(posestack$pose, f, f1, f2); + consumer.addVertex(posestack$pose, (float)(p_323076_ + x), (float)(p_323077_ + y), (float)(p_323078_ + z)) + .setColor(red, green, blue, alpha) + .setNormal(posestack$pose, f, f1, f2); + } + ); + } + private void renderGiantBlockOutlines(ExtractBlockOutlineRenderStateEvent event) { BlockPos pos = event.getBlockPos(); BlockState state = event.getBlockState(); - boolean isMiniature = state.getBlock() instanceof MiniatureStructureBlock; + if (state.getBlock() instanceof MiniatureStructureBlock) { + event.setCanceled(true); + return; + } LocalPlayer player = Minecraft.getInstance().player; - boolean isHoldingGiantTool = player != null && (player.getMainHandItem().getItem() instanceof GiantPickItem - || (player.getMainHandItem().getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof GiantBlock)); - - if (isMiniature || (isHoldingGiantTool && !state.isAir() && player.level().getWorldBorder().isWithinBounds(pos))) { - Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().position(); - BlockPos offsetPos = new BlockPos(pos.getX() & ~0b11, pos.getY() & ~0b11, pos.getZ() & ~0b11); - Vec3 xyz = Vec3.atLowerCornerOf(offsetPos).subtract(cameraPos); - - int outlineColor = 0x7F000000; - event.addCustomRenderer((_, bufferSource, poseStack, _, _) -> { - - if (isHoldingGiantTool && !isMiniature) { - VertexConsumer consumer = bufferSource.getBuffer(RenderTypes.lines()); - ShapeRenderer.renderShape( - poseStack, - consumer, - GIANT_BLOCK, - xyz.x(), xyz.y(), xyz.z(), - outlineColor, 1.0F - ); - } - - return false; - }); + if (player != null && (player.getMainHandItem().getItem() instanceof GiantPickItem || (player.getMainHandItem().getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof GiantBlock))) { + event.setCanceled(true); + if (!state.isAir() && player.level().getWorldBorder().isWithinBounds(pos)) { + event.addCustomRenderer((renderState, buffer, stack, translucentPass, levelRenderState) -> { + BlockPos offsetPos = new BlockPos(renderState.pos().getX() & ~0b11, renderState.pos().getY() & ~0b11, renderState.pos().getZ() & ~0b11); + Vec3 xyz = Vec3.atLowerCornerOf(offsetPos).subtract(event.getCamera().position()); + VertexConsumer consumer = buffer.getBuffer(RenderTypes.lines()); + renderShape(stack, consumer, GIANT_BLOCK, xyz.x(), xyz.y(), xyz.z(), 0.0F, 0.0F, 0.0F, 0.45F); + return true; + }); + } } } From 1d6b091f359e1769a0e216465812fd05162b1a37 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Thu, 23 Jul 2026 18:26:02 +0300 Subject: [PATCH 06/21] Fixed ClientRegistrationEvents --- .../event/ClientRegistrationEvents.java | 96 +++++++++---------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java b/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java index ead79957ce..858f2e2211 100644 --- a/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java +++ b/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java @@ -4,11 +4,8 @@ import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.model.animal.wolf.AdultWolfModel; import net.minecraft.client.model.geom.LayerDefinitions; -import net.minecraft.client.model.geom.PartPose; import net.minecraft.client.model.geom.builders.CubeDeformation; -import net.minecraft.client.model.geom.builders.CubeListBuilder; import net.minecraft.client.model.geom.builders.LayerDefinition; -import net.minecraft.client.model.geom.builders.MeshDefinition; import net.minecraft.client.model.monster.silverfish.SilverfishModel; import net.minecraft.client.model.monster.slime.SlimeModel; import net.minecraft.client.model.monster.spider.SpiderModel; @@ -16,19 +13,18 @@ import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleEngine; import net.minecraft.client.particle.SpriteSet; +import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.renderer.Sheets; -import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.LivingEntityRenderer; import net.minecraft.client.renderer.entity.NoopRenderer; import net.minecraft.client.renderer.entity.ThrownItemRenderer; +import net.minecraft.client.renderer.entity.player.AvatarRenderer; import net.minecraft.client.resources.model.sprite.AtlasManager; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; -import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; @@ -68,18 +64,18 @@ import twilightforest.client.renderer.entity.layers.ShieldLayer; import twilightforest.client.renderer.map.ConqueredMapIconRenderer; import twilightforest.client.renderer.map.MagicMapPlayerIconRenderer; -import twilightforest.client.renderer.special.*; import twilightforest.client.renderer.tooltip.ItemDisplayTooltipComponent; import twilightforest.client.renderer.tooltip.PotionFlaskTooltipComponent; import twilightforest.client.renderer.tooltip.TravellersBeltTooltipComponent; import twilightforest.init.*; -import twilightforest.item.ArcticArmorItem; -import twilightforest.item.PotionFlaskItem; +import twilightforest.item.*; import twilightforest.item.travellers_gear.TravellersArmorBeltItem; import twilightforest.item.travellers_gear.TravellersArmorItem; import twilightforest.item.travellers_gear.TravellersGogglesItem; import twilightforest.util.woods.TFWoodTypes; +import java.util.Objects; + @Component(dist = Dist.CLIENT) public class ClientRegistrationEvents { @@ -88,7 +84,9 @@ public class ClientRegistrationEvents { @PostConstruct private void setup(IEventBus bus) { bus.addListener(EntityRenderersEvent.AddLayers.class, this::attachRenderLayers); + bus.addListener(this::cacheJarLids); bus.addListener(this::clientSetup); + bus.addListener(this::registerAdditionalModels); bus.addListener(this::registerClientReloadListeners); bus.addListener(this::registerAtlases); bus.addListener(this::registerEntityRenderers); @@ -103,13 +101,14 @@ private void setup(IEventBus bus) { bus.addListener(this::registerRangeProperties); bus.addListener(this::registerSelectProperties); bus.addListener(this::registerItemModels); - bus.addListener(this::registerSpecialModelRenderers); bus.addListener(RegisterKeyMappingsEvent.class, event -> TFKeyBinds.KEY_MAPPINGS.forEach(event::register)); bus.addListener(ColorHandler::registerBlockColors); bus.addListener(ColorHandler::registerItemColors); + bus.addListener(TFShaders::registerShaders); + bus.addListener(OverlayHandler::registerOverlays); bus.addListener(RegisterClientTooltipComponentFactoriesEvent.class, event -> { @@ -149,6 +148,25 @@ private void registerSelectProperties(RegisterSelectItemModelPropertyEvent event event.register(TwilightForestMod.prefix("experiment_115_variant"), Experiment115Type.TYPE); } + // FIXME should be fixed after JarRenderer + private void registerAdditionalModels(RegisterBlockModelsEvent event) { + for (JarRenderer.LidResource lid : JarRenderer.LID_LOCATION_LIST.get()) { + Identifier location = lid.identifier(); + String name = location.getPath(); + if (lid.customPath() != null) name = lid.customPath(); + event.register(ModelResourceLocation.standalone(TwilightForestMod.prefix("block/lid/" + name))); + } + } + + // FIXME should be fixed after JarRenderer + private void cacheJarLids(ModelEvent.BakingCompleted event) { + JarRenderer.LID_LOCATION_LIST.get().forEach((lid) -> { + String name = lid.identifier().getPath(); + if (lid.customPath() != null) name = lid.customPath(); + JarRenderer.LIDS.put(lid.lid(), event.getModelManager().getBlockModelSet().get(ModelResourceLocation.standalone(TwilightForestMod.prefix("block/lid/" + name)))); + }); + } + private void clientSetup(FMLClientSetupEvent evt) { try { Class.forName("net.optifine.Config"); @@ -174,7 +192,7 @@ private void registerAtlases(RegisterTextureAtlasesEvent event) { } private void registerClientReloadListeners(AddClientReloadListenersEvent event) { -// event.addListener(TwilightForestMod.prefix("texture_generator"), TextureGeneratorReloadListener.INSTANCE); + event.addListener(TwilightForestMod.prefix("texture_generator"), TextureGeneratorReloadListener.INSTANCE); event.addListener(TwilightForestMod.prefix("armor_cache"), new TFArmorRenderer.ResourceReloadListener()); } @@ -288,11 +306,6 @@ private void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers even event.registerBlockEntityRenderer(TFBlockEntities.DRYING_RACK.get(), DryingRackRenderer::new); } - private static LayerDefinition createFixedLayer(MeshDefinition mesh, int texWidth, int texHeight) { - mesh.getRoot().addOrReplaceChild("hat", CubeListBuilder.create(), PartPose.ZERO); - return LayerDefinition.create(mesh, texWidth, texHeight); - } - private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefinitions event) { event.registerLayerDefinition(TFModelLayers.ARCTIC_ARMOR_INNER, () -> LayerDefinition.create(ArcticArmorModel.addPieces(LayerDefinitions.INNER_ARMOR_DEFORMATION), 64, 32)); event.registerLayerDefinition(TFModelLayers.ARCTIC_ARMOR_OUTER, () -> LayerDefinition.create(ArcticArmorModel.addPieces(LayerDefinitions.OUTER_ARMOR_DEFORMATION), 64, 32)); @@ -322,6 +335,7 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.ADHERENT, AdherentModel::create); event.registerLayerDefinition(TFModelLayers.ALPHA_YETI, AlphaYetiModel::create); + event.registerLayerDefinition(TFModelLayers.ARMORED_GIANT, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 32)); event.registerLayerDefinition(TFModelLayers.BIGHORN_SHEEP, BighornModel::create); event.registerLayerDefinition(TFModelLayers.BIGHORN_SHEEP_BABY, () -> BighornModel.create().apply(BighornModel.BABY_TRANSFORMER)); event.registerLayerDefinition(TFModelLayers.BLOCKCHAIN_GOBLIN, BlockChainGoblinModel::create); @@ -338,6 +352,7 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.DEER, DeerModel::create); event.registerLayerDefinition(TFModelLayers.DEER_BABY, () -> DeerModel.create().apply(DeerModel.BABY_TRANSFORMER)); event.registerLayerDefinition(TFModelLayers.FIRE_BEETLE, FireBeetleModel::create); + event.registerLayerDefinition(TFModelLayers.GIANT_MINER, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 32)); event.registerLayerDefinition(TFModelLayers.HARBINGER_CUBE, HarbingerCubeModel::create); event.registerLayerDefinition(TFModelLayers.HEDGE_SPIDER, SpiderModel::createSpiderBodyLayer); event.registerLayerDefinition(TFModelLayers.HELMET_CRAB, HelmetCrabModel::create); @@ -350,8 +365,10 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.KING_SPIDER, SpiderModel::createSpiderBodyLayer); event.registerLayerDefinition(TFModelLayers.KNIGHT_PHANTOM, KnightPhantomModel::create); event.registerLayerDefinition(TFModelLayers.KOBOLD, KoboldModel::create); + event.registerLayerDefinition(TFModelLayers.LICH_MINION, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); event.registerLayerDefinition(TFModelLayers.LICH, LichModel::create); event.registerLayerDefinition(TFModelLayers.LOWER_GOBLIN_KNIGHT, LowerGoblinKnightModel::create); + event.registerLayerDefinition(TFModelLayers.LOYAL_ZOMBIE, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); event.registerLayerDefinition(TFModelLayers.MAZE_SLIME, SlimeModel::createInnerBodyLayer); event.registerLayerDefinition(TFModelLayers.MAZE_SLIME_OUTER, SlimeModel::createOuterBodyLayer); event.registerLayerDefinition(TFModelLayers.MINOSHROOM, MinoshroomModel::create); @@ -360,6 +377,7 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.MOSQUITO_SWARM, MosquitoSwarmModel::create); event.registerLayerDefinition(TFModelLayers.NAGA, NagaModel::create); event.registerLayerDefinition(TFModelLayers.NAGA_BODY, NagaModel::create); + event.registerLayerDefinition(TFModelLayers.NOOP, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 0, 0)); event.registerLayerDefinition(TFModelLayers.PENGUIN, PenguinModel::create); event.registerLayerDefinition(TFModelLayers.PENGUIN_BABY, () -> PenguinModel.create().apply(PenguinModel.BABY_TRANSFORMER)); event.registerLayerDefinition(TFModelLayers.PINCH_BEETLE, PinchBeetleModel::create); @@ -367,6 +385,9 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.QUEST_RAM, QuestRamModel::create); event.registerLayerDefinition(TFModelLayers.RAVEN, RavenModel::create); event.registerLayerDefinition(TFModelLayers.REDCAP, RedcapModel::create); + event.registerLayerDefinition(TFModelLayers.REDCAP_ARMOR_INNER, () -> LayerDefinition.create(HumanoidModel.createMesh(new CubeDeformation(0.25F), 0.7F), 64, 32)); + event.registerLayerDefinition(TFModelLayers.REDCAP_ARMOR_OUTER, () -> LayerDefinition.create(HumanoidModel.createMesh(new CubeDeformation(0.65F), 0.7F), 64, 32)); + event.registerLayerDefinition(TFModelLayers.RISING_ZOMBIE, () -> LayerDefinition.create(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); event.registerLayerDefinition(TFModelLayers.ROVING_CUBE, CubeOfAnnihilationModel::create); event.registerLayerDefinition(TFModelLayers.SKELETON_DRUID, SkeletonDruidModel::create); event.registerLayerDefinition(TFModelLayers.SLIME_BEETLE, SlimeBeetleModel::create); @@ -396,15 +417,6 @@ private void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefiniti event.registerLayerDefinition(TFModelLayers.RED_THREAD, RedThreadModel::create); event.registerLayerDefinition(TFModelLayers.KNIGHTMETAL_SHIELD, KnightmetalShieldModel::create); - - event.registerLayerDefinition(TFModelLayers.ARMORED_GIANT, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 32)); - event.registerLayerDefinition(TFModelLayers.GIANT_MINER, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 32)); - event.registerLayerDefinition(TFModelLayers.LICH_MINION, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); - event.registerLayerDefinition(TFModelLayers.LOYAL_ZOMBIE, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); - event.registerLayerDefinition(TFModelLayers.NOOP, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 0, 0)); - event.registerLayerDefinition(TFModelLayers.REDCAP_ARMOR_INNER, () -> createFixedLayer(HumanoidModel.createMesh(new CubeDeformation(0.25F), 0.7F), 64, 32)); - event.registerLayerDefinition(TFModelLayers.REDCAP_ARMOR_OUTER, () -> createFixedLayer(HumanoidModel.createMesh(new CubeDeformation(0.65F), 0.7F), 64, 32)); - event.registerLayerDefinition(TFModelLayers.RISING_ZOMBIE, () -> createFixedLayer(HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F), 64, 64)); } private void registerParticleFactories(RegisterParticleProvidersEvent event) { @@ -439,20 +451,6 @@ private void registerParticleFactories(RegisterParticleProvidersEvent event) { event.registerSpriteSet(TFParticleType.SHIELD_BREAK.get(), CustomTextureParticle.ShieldBreak::new); } - private void registerSpecialModelRenderers(RegisterSpecialModelRendererEvent event) { - event.register(TwilightForestMod.prefix("item/template_skull_candle"), SkullCandleSpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("item/template_trophy"), TrophySpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("block/mason_jar"), MasonJarSpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("block/candelabra"), CandelabraSpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("item/mystic_crown"), MysticCrownSpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("item/knightmetal_shield"), KnightmetalShieldSpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("item/keepsake_casket"), KeepsakeCasketSpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("item/skull_chest"), SkullChestSpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("item/cicada"), CicadaSpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("item/firefly"), FireflySpecialRenderer.Unbaked.MAP_CODEC); - event.register(TwilightForestMod.prefix("item/moonworm"), MoonwormSpecialRenderer.Unbaked.MAP_CODEC); - } - private void registerClientExtensions(RegisterClientExtensionsEvent event) { event.registerBlock(new IClientBlockExtensions() { @Override @@ -572,23 +570,21 @@ private void registerMapDecorators(RegisterMapDecorationRenderersEvent event) { private void attachRenderLayers(EntityRenderersEvent.AddLayers event) { BakedMultiPartRenderers.bakeMultiPartRenderers(event.getContext()); for (EntityType type : event.getEntityTypes()) { - EntityRenderer renderer = event.getRenderer(type); + var renderer = event.getRenderer(type); if (renderer instanceof LivingEntityRenderer living) { - attachToLiving(living); + attachRenderLayers(living); } } - event.getSkins().forEach(skinName -> { - LivingEntityRenderer skinRenderer = event.getPlayerRenderer(skinName); - if (skinRenderer != null) { - attachToLiving(skinRenderer); - } + + event.getSkins().forEach(modelType -> { + AvatarRenderer skin = event.getPlayerRenderer(modelType); + attachRenderLayers(Objects.requireNonNull(skin)); }); } - @SuppressWarnings({"unchecked", "rawtypes"}) - private static void attachToLiving(net.minecraft.client.renderer.entity.LivingEntityRenderer renderer) { - renderer.addLayer(new ShieldLayer(renderer)); - renderer.addLayer(new IceLayer(renderer)); + private void attachRenderLayers(AvatarRenderer renderer) { + renderer.addLayer(new ShieldLayer<>(renderer)); + renderer.addLayer(new IceLayer<>(renderer)); } public static boolean isOptifinePresent() { From 148624a8028fbc665670a06e446f98de853bcc5b Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 00:07:36 +0300 Subject: [PATCH 07/21] Fixed ClientRegistrationEvents --- .../client/event/ClientRegistrationEvents.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java b/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java index 858f2e2211..2f85fa0ab7 100644 --- a/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java +++ b/src/main/java/twilightforest/client/event/ClientRegistrationEvents.java @@ -1,6 +1,7 @@ package twilightforest.client.event; import net.minecraft.client.Minecraft; +import net.minecraft.client.model.EntityModel; import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.model.animal.wolf.AdultWolfModel; import net.minecraft.client.model.geom.LayerDefinitions; @@ -19,12 +20,16 @@ import net.minecraft.client.renderer.entity.NoopRenderer; import net.minecraft.client.renderer.entity.ThrownItemRenderer; import net.minecraft.client.renderer.entity.player.AvatarRenderer; +import net.minecraft.client.renderer.entity.state.AvatarRenderState; +import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; import net.minecraft.client.resources.model.sprite.AtlasManager; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; @@ -572,7 +577,7 @@ private void attachRenderLayers(EntityRenderersEvent.AddLayers event) { for (EntityType type : event.getEntityTypes()) { var renderer = event.getRenderer(type); if (renderer instanceof LivingEntityRenderer living) { - attachRenderLayers(living); + attachRenderLayers((LivingEntityRenderer) living); // FIXME find the better way to avoid raw types } } @@ -582,7 +587,7 @@ private void attachRenderLayers(EntityRenderersEvent.AddLayers event) { }); } - private void attachRenderLayers(AvatarRenderer renderer) { + private > void attachRenderLayers(LivingEntityRenderer renderer) { renderer.addLayer(new ShieldLayer<>(renderer)); renderer.addLayer(new IceLayer<>(renderer)); } From bb8ed59cec9bebf94acb8091fa27c9c6260c6ba6 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 00:08:51 +0300 Subject: [PATCH 08/21] Excluded CloudEvents --- .../client/event/CloudEvents.java | 96 ++++++++++++------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/src/main/java/twilightforest/client/event/CloudEvents.java b/src/main/java/twilightforest/client/event/CloudEvents.java index 1f1548d8eb..7c17e4ab1b 100644 --- a/src/main/java/twilightforest/client/event/CloudEvents.java +++ b/src/main/java/twilightforest/client/event/CloudEvents.java @@ -1,17 +1,17 @@ package twilightforest.client.event; +import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; -import net.minecraft.client.GraphicsPreset; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.rendertype.RenderType; -import net.minecraft.client.renderer.rendertype.RenderTypes; +import net.minecraft.client.ParticleStatus; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.LightTexture; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.server.level.ParticleStatus; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.FluidTags; @@ -38,7 +38,6 @@ public class CloudEvents { private static final List RENDER_HELPER = new ArrayList<>(); - private static int twilightRainSoundTime = 0; record PrecipitationRenderHelper(BlockPos cloudPos, Biome.Precipitation precipitation, float precipitationLevel, int rainOnY) { @@ -49,7 +48,7 @@ protected static void tickWeatherEffects(ClientTickEvent.Post event) { if (!mc.isPaused()) { if (mc.level != null && TFConfig.getClientCloudBlockPrecipitationDistance() > 0) { // Semi vanilla copy of the weather tick, but made to work with cloud blocks instead - Vec3 vec3 = mc.gameRenderer.getMainCamera().position(); + Vec3 vec3 = mc.gameRenderer.getMainCamera().getPosition(); if (mc.level.getGameTime() % 10L == 0L) { RENDER_HELPER.clear(); @@ -61,7 +60,7 @@ protected static void tickWeatherEffects(ClientTickEvent.Post event) { int floorY = Mth.floor(camY); int floorZ = Mth.floor(camZ); - int renderDistance = Minecraft.getInstance().options.graphicsPreset().get() != GraphicsPreset.FAST ? 10 : 5; + int renderDistance = Minecraft.useFancyGraphics() ? 10 : 5; int precipitationDistance = TFConfig.getClientCloudBlockPrecipitationDistance(); BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); @@ -116,8 +115,8 @@ protected static void tickWeatherEffects(ClientTickEvent.Post event) { if (!Heightmap.Types.MOTION_BLOCKING.isOpaque().test(mc.level.getBlockState(highestRainyPos.below()))) continue; - if (yetToMakeASound && particlePos != null && randomsource.nextInt(3) < twilightRainSoundTime++) { - twilightRainSoundTime = 0; + if (yetToMakeASound && particlePos != null && randomsource.nextInt(3) < mc.levelRenderer.rainSoundTime++) { + mc.levelRenderer.rainSoundTime = 0; if (particlePos.getY() > camPos.getY() + 1 && mc.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, camPos).getY() > Mth.floor((float) camPos.getY())) { mc.level.playLocalSound(particlePos, SoundEvents.WEATHER_RAIN_ABOVE, SoundSource.WEATHER, 0.1F, 0.5F, false); } else { @@ -150,14 +149,16 @@ protected static void tickWeatherEffects(ClientTickEvent.Post event) { } } - protected static void renderPrecipitation(RenderLevelStageEvent.AfterWeather event) { - if (TFConfig.getClientCloudBlockPrecipitationDistance() > 0 && !RENDER_HELPER.isEmpty()) { + protected static void renderPrecipitation(RenderLevelStageEvent event) { + if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_WEATHER && TFConfig.getClientCloudBlockPrecipitationDistance() > 0 && !RENDER_HELPER.isEmpty()) { Minecraft minecraft = Minecraft.getInstance(); if (minecraft.level == null) return; - float partialTick = minecraft.getDeltaTracker().getGameTimeDeltaPartialTick(false); + float partialTick = minecraft.getTimer().getGameTimeDeltaPartialTick(false); + LightTexture lightTexture = minecraft.gameRenderer.lightTexture(); int ticks = minecraft.levelRenderer.getTicks(); + lightTexture.turnOnLightLayer(); - Vec3 vec3 = minecraft.gameRenderer.getMainCamera().position(); + Vec3 vec3 = event.getCamera().getPosition(); double camX = vec3.x(); double camY = vec3.y(); double camZ = vec3.z(); @@ -166,9 +167,18 @@ protected static void renderPrecipitation(RenderLevelStageEvent.AfterWeather eve int floorY = Mth.floor(camY); int floorZ = Mth.floor(camZ); - int renderDistance = minecraft.options.graphicsPreset().get() != GraphicsPreset.FAST ? 10 : 5; + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder bufferbuilder = null; + RenderSystem.disableCull(); + RenderSystem.enableBlend(); + RenderSystem.enableDepthTest(); + RenderSystem.depthMask(Minecraft.useShaderTransparency()); + int renderDistance = Minecraft.useFancyGraphics() ? 10 : 5; + + int tesselatorCheck = -1; float fullTick = (float) ticks + partialTick; + RenderSystem.setShader(GameRenderer::getParticleShader); BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); for (PrecipitationRenderHelper helper : RENDER_HELPER) { @@ -188,7 +198,13 @@ protected static void renderPrecipitation(RenderLevelStageEvent.AfterWeather eve RandomSource random = RandomSource.create((long) roofX * roofX * 3121 + roofX * 45238971L ^ (long) roofZ * roofZ * 418711 + roofZ * 13761L); if (helper.precipitation() == Biome.Precipitation.RAIN) { - RenderType renderType = RenderTypes.entityTranslucent(TFWeatherRenderer.RAIN_TEXTURES); + if (tesselatorCheck != 0) { + if (tesselatorCheck >= 0) BufferUploader.drawWithShader(bufferbuilder.buildOrThrow()); + + tesselatorCheck = 0; + RenderSystem.setShaderTexture(0, TFWeatherRenderer.RAIN_TEXTURES); + bufferbuilder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE); + } int offset = ticks + roofX * roofX * 3121 + roofX * 45238971 + roofZ * roofZ * 418711 + roofZ * 13761 & 31; float uvOffset = -((float) offset + partialTick) / 32.0F * (3.0F + random.nextFloat()); @@ -196,17 +212,21 @@ protected static void renderPrecipitation(RenderLevelStageEvent.AfterWeather eve double zDiff = (double) roofZ + 0.5D - camZ; float distance = (float) Math.sqrt(xDiff * xDiff + zDiff * zDiff) / (float) renderDistance; float alpha = ((1.0F - distance * distance) * 0.5F + 0.5F) * helper.precipitationLevel(); + mutableBlockPos.set(roofX, Math.max(helper.rainOnY(), floorY), roofZ); + int lightColor = LevelRenderer.getLightColor(minecraft.level, mutableBlockPos); - MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); - VertexConsumer vC = bufferSource.getBuffer(renderType); - - vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX - rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F, (float) botY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(0xF000F0); - vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX + rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F, (float) botY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(0xF000F0); - vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX + rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F, (float) topY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(0xF000F0); - vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX - rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F, (float) topY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(0xF000F0); - + bufferbuilder.addVertex((float) (roofX - camX - rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F, (float) botY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(lightColor); + bufferbuilder.addVertex((float) (roofX - camX + rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F, (float) botY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(lightColor); + bufferbuilder.addVertex((float) (roofX - camX + rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F, (float) topY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(lightColor); + bufferbuilder.addVertex((float) (roofX - camX - rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F, (float) topY * 0.25F + uvOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(lightColor); } else if (helper.precipitation() == Biome.Precipitation.SNOW) { - RenderType renderType = RenderTypes.entityTranslucent(TFWeatherRenderer.SNOW_TEXTURES); + if (tesselatorCheck != 1) { + if (tesselatorCheck == 0) BufferUploader.drawWithShader(bufferbuilder.buildOrThrow()); + + tesselatorCheck = 1; + RenderSystem.setShaderTexture(0, TFWeatherRenderer.SNOW_TEXTURES); + bufferbuilder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE); + } float offset = -((float) (ticks & 511) + partialTick) / 512.0F; float uOffset = (float) (random.nextDouble() + (double) fullTick * 0.01D * (double) ((float) random.nextGaussian())); @@ -215,24 +235,26 @@ protected static void renderPrecipitation(RenderLevelStageEvent.AfterWeather eve double zDiff = (double) roofZ + 0.5D - camZ; float distance = (float) Math.sqrt(xDiff * xDiff + zDiff * zDiff) / (float) renderDistance; float alpha = ((1.0F - distance * distance) * 0.3F + 0.5F) * helper.precipitationLevel(); + mutableBlockPos.set(roofX, Math.max(helper.rainOnY(), floorY), roofZ); - int defaultLight = 0xF000F0; - int v = defaultLight >> 16 & '\uffff'; - int u = defaultLight & '\uffff'; + int lightColor = LevelRenderer.getLightColor(minecraft.level, mutableBlockPos); + int v = lightColor >> 16 & '\uffff'; + int u = lightColor & '\uffff'; v = (v * 3 + 240) / 4; u = (u * 3 + 240) / 4; - int packedWeatherLight = u | v << 16; - - MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); - VertexConsumer vC = bufferSource.getBuffer(renderType); - vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX - rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F + uOffset, (float) botY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(packedWeatherLight); - vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX + rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F + uOffset, (float) botY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(packedWeatherLight); - vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX + rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F + uOffset, (float) topY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(packedWeatherLight); - vC.addVertex(event.getPoseStack().last(), (float) (roofX - camX - rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F + uOffset, (float) topY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setLight(packedWeatherLight); + bufferbuilder.addVertex((float) (roofX - camX - rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F + uOffset, (float) botY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setUv2(u, v); + bufferbuilder.addVertex((float) (roofX - camX + rainX + 0.5D), (float) (topY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F + uOffset, (float) botY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setUv2(u, v); + bufferbuilder.addVertex((float) (roofX - camX + rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ + rainZ + 0.5D)).setUv(1.0F + uOffset, (float) topY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setUv2(u, v); + bufferbuilder.addVertex((float) (roofX - camX - rainX + 0.5D), (float) (botY - camY), (float) (roofZ - camZ - rainZ + 0.5D)).setUv(0.0F + uOffset, (float) topY * 0.25F + offset + vOffset).setColor(1.0F, 1.0F, 1.0F, alpha).setUv2(u, v); } - } + + if (tesselatorCheck >= 0) BufferUploader.drawWithShader(bufferbuilder.buildOrThrow());; + + RenderSystem.enableCull(); + RenderSystem.disableBlend(); + lightTexture.turnOffLightLayer(); } } } From 7f18e763da7ecb9f50fde153b2fbb57bd50a0705 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 01:25:49 +0300 Subject: [PATCH 09/21] Fixed OverlayHandler --- .../client/event/OverlayHandler.java | 74 ++----------------- .../resources/META-INF/accesstransformer.cfg | 2 +- 2 files changed, 7 insertions(+), 69 deletions(-) diff --git a/src/main/java/twilightforest/client/event/OverlayHandler.java b/src/main/java/twilightforest/client/event/OverlayHandler.java index 6db6fe96bd..0b22c90494 100644 --- a/src/main/java/twilightforest/client/event/OverlayHandler.java +++ b/src/main/java/twilightforest/client/event/OverlayHandler.java @@ -1,7 +1,6 @@ package twilightforest.client.event; import com.google.common.collect.ImmutableList; -import com.mojang.blaze3d.platform.Window; import com.mojang.datafixers.util.Pair; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; @@ -9,15 +8,10 @@ import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.sprite.SpriteId; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.Identifier; import net.minecraft.world.InteractionHand; -import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ChunkPos; @@ -29,13 +23,12 @@ import twilightforest.TwilightForestMod; import tamaized.beanification.Autowired; import twilightforest.client.overlay.ItemDisplayOverlay; -import twilightforest.components.entity.TFPortalAttachment; +import twilightforest.client.overlay.PortalOverlay; import twilightforest.components.item.OreScannerData; import twilightforest.config.TFConfig; import twilightforest.entity.passive.QuestRam; import twilightforest.entity.passive.quest.ram.QuestingRamCurrentContext; import twilightforest.events.HostileMountEvents; -import twilightforest.init.TFBlocks; import twilightforest.init.TFDataAttachments; import twilightforest.init.TFDataComponents; import twilightforest.init.TFItems; @@ -70,11 +63,11 @@ protected static void registerOverlays(RegisterGuiLayersEvent event) { if (!minecraft.options.hideGui && minecraft.gameMode.canHurtPlayer() && player != null && HostileMountEvents.isRidingUnfriendly(player)) { int xPos = graphics.guiWidth() / 2 + 91; int yPos = graphics.guiHeight() - gui.rightHeight; - renderModdedFoodBar(graphics, player, xPos, yPos); + gui.extractFood(graphics, player, yPos, xPos); gui.rightHeight += 10; } }); - event.registerAboveAll(TwilightForestMod.prefix("ore_meter_stats"), (graphics, _) -> { + event.registerAboveAll(TwilightForestMod.prefix("ore_meter_stats"), (graphics, partialTicks) -> { Minecraft minecraft = Minecraft.getInstance(); LocalPlayer player = minecraft.player; Gui gui = minecraft.gui; @@ -94,32 +87,10 @@ protected static void registerOverlays(RegisterGuiLayersEvent event) { event.registerAboveAll(TwilightForestMod.prefix("portal_overlay"), (graphics, partialTick) -> { Minecraft minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); LocalPlayer player = minecraft.player; if (player != null) { - TFPortalAttachment portal = player.getData(TFDataAttachments.TF_PORTAL_COOLDOWN); - if (portal.getPortalTimer() > 0) { - float alpha = (float) portal.getPortalTimer() / (float) TFPortalAttachment.MAX_TICKS; - int packedColor = ((int)(alpha * 255.0F) << 24) | 0xFFFFFF; - - Identifier portalTextureLocation = BuiltInRegistries.BLOCK.getKey(TFBlocks.TWILIGHT_PORTAL.get()).withPrefix("block/"); - - TextureAtlasSprite sprite = minecraft.getAtlasManager().get(new SpriteId(TextureAtlas.LOCATION_BLOCKS, portalTextureLocation)); - - graphics.blit( - RenderPipelines.GUI_TEXTURED, - TextureAtlas.LOCATION_BLOCKS, - 0, 0, - sprite.getU0() * sprite.contents().width(), - sprite.getV0() * sprite.contents().height(), - window.getGuiScaledWidth(), - window.getGuiScaledHeight(), - sprite.contents().width(), - sprite.contents().height(), - packedColor - ); - } + PortalOverlay.render(graphics, minecraft, player); } }); @@ -130,39 +101,6 @@ protected static void registerOverlays(RegisterGuiLayersEvent event) { }); } - private static void renderModdedFoodBar(GuiGraphicsExtractor graphics, Player player, int xPos, int yPos) { - Identifier guiIcons = Identifier.fromNamespaceAndPath("minecraft", "textures/gui/icons.png"); - - int foodLevel = player.getFoodData().getFoodLevel(); - boolean hasHungerEffect = player.hasEffect(MobEffects.HUNGER); - - int uOffset = hasHungerEffect ? 88 : 16; - int uIconOffset = hasHungerEffect ? 115 : 52; - - int textureWidth = 256; - int textureHeight = 256; - - for (int i = 0; i < 10; ++i) { - int idx = i * 2 + 1; - int x = xPos - i * 8; - - graphics.blit( - RenderPipelines.GUI_TEXTURED, - guiIcons, - x, yPos, - uOffset, 27, - 9, 9, - textureWidth, textureHeight - ); - - if (idx < foodLevel) { - graphics.blit(RenderPipelines.GUI_TEXTURED, guiIcons, x, yPos, uIconOffset, 27, 9, 9, textureWidth, textureHeight); - } else if (idx == foodLevel) { - graphics.blit(RenderPipelines.GUI_TEXTURED, guiIcons, x, yPos, uIconOffset + 9, 27, 9, 9, textureWidth, textureHeight); - } - } - } - private static void renderIndicator(Minecraft minecraft, GuiGraphicsExtractor graphics, Gui gui, Player player, int screenWidth, int screenHeight) { if (minecraft.options.getCameraType().isFirstPerson() && (minecraft.gameMode.getPlayerMode() != GameType.SPECTATOR || gui.canRenderCrosshairForSpectator(minecraft.hitResult)) && minecraft.crosshairPickEntity instanceof QuestRam ram) { ItemStack stack = player.getInventory().getItem(player.getInventory().getSelectedSlot()); @@ -172,9 +110,9 @@ private static void renderIndicator(Minecraft minecraft, GuiGraphicsExtractor gr int j = ((screenHeight - 1) / 2) - 11; int k = ((screenWidth - 1) / 2) - 3; if (!ram.isColorPresent(questEntry.getKey())) { - graphics.blitSprite(RenderPipelines.GUI_INVERT, QUESTING_RAM_X_SPRITE, k, j, 7, 7); + graphics.blitSprite(RenderPipelines.CROSSHAIR, QUESTING_RAM_X_SPRITE, k, j, 7, 7); } else { - graphics.blitSprite(RenderPipelines.GUI_INVERT, QUESTING_RAM_CHECK_SPRITE, k, j, 7, 7); + graphics.blitSprite(RenderPipelines.CROSSHAIR, QUESTING_RAM_CHECK_SPRITE, k, j, 7, 7); } break; } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 6d28fdf800..1393526658 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -116,7 +116,7 @@ public-f net.minecraft.world.level.saveddata.maps.MapItemSavedData trackedDecora # TFOverlays (partially determines if the quest ram indicator should render below the crosshair, and allows the food bar to be rendered when riding hostile mounts) public net.minecraft.client.gui.Gui canRenderCrosshairForSpectator(Lnet/minecraft/world/phys/HitResult;)Z -#public net.minecraft.client.gui.Gui renderFood(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/world/entity/player/Player;II)V +public net.minecraft.client.gui.Gui extractFood(Lnet/minecraft/client/gui/GuiGraphicsExtractor;Lnet/minecraft/world/entity/player/Player;II)V # TFSkyRenderer (allows for rendering the sky) public net.minecraft.client.renderer.LevelRenderer skyRenderer From bfce2b68f83bab381883ab8711f9c5cc77d6de5a Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 01:59:01 +0300 Subject: [PATCH 10/21] Fixed TravellersClientEvents --- .../client/event/TravellersClientEvents.java | 162 ++++++------------ 1 file changed, 52 insertions(+), 110 deletions(-) diff --git a/src/main/java/twilightforest/client/event/TravellersClientEvents.java b/src/main/java/twilightforest/client/event/TravellersClientEvents.java index 0f2f166dd8..c533136ecf 100644 --- a/src/main/java/twilightforest/client/event/TravellersClientEvents.java +++ b/src/main/java/twilightforest/client/event/TravellersClientEvents.java @@ -3,32 +3,32 @@ import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; -import net.minecraft.client.input.KeyEvent; import net.minecraft.client.model.HumanoidModel; -import net.minecraft.client.model.Model; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.player.ClientInput; import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.entity.EntityRenderDispatcher; import net.minecraft.client.renderer.entity.player.AvatarRenderer; -import net.minecraft.client.renderer.entity.state.HumanoidRenderState; +import net.minecraft.client.renderer.entity.state.AvatarRenderState; import net.minecraft.client.renderer.rendertype.RenderTypes; import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.EquipmentClientInfo; import net.minecraft.core.component.DataComponents; +import net.minecraft.data.AtlasIds; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.player.Input; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ProjectileWeaponItem; import net.minecraft.world.item.component.ItemContainerContents; -import net.minecraft.world.phys.Vec3; import net.neoforged.api.distmarker.Dist; import net.neoforged.neoforge.attachment.AttachmentType; import net.neoforged.neoforge.client.event.*; @@ -71,20 +71,14 @@ private void setup() { NeoForge.EVENT_BUS.addListener(this::renderGlovesInFirstPerson); } - private static final Identifier AGILE_RANGER_ID = Identifier.fromNamespaceAndPath("twilightforest", "agile_ranger_speed"); - + // FIXME try to find another way to modify the input data and implement it for the three methods listed below private void handleAgileRanger(MovementInputUpdateEvent event) { if (!(event.getEntity() instanceof LocalPlayer localPlayer)) return; - - var speedAttribute = localPlayer.getAttribute(Attributes.MOVEMENT_SPEED); - if (speedAttribute == null) - return; - ItemStack leggingsStack = localPlayer.getItemBySlot(EquipmentSlot.LEGS); Float agileRangerModifier = leggingsStack.get(TFDataComponents.AGILE_RANGER_MODIFIER); - - boolean isModifierActive = TravellersModifiersManager.isModifierActive(localPlayer, leggingsStack, TravellersModifiersManager.AGILE_RANGER_MODIFIER) && agileRangerModifier != null; + if (!TravellersModifiersManager.isModifierActive(localPlayer, leggingsStack, TravellersModifiersManager.AGILE_RANGER_MODIFIER) || agileRangerModifier == null) + return; ItemStack stack = localPlayer.getUseItem(); boolean isLegalItem = (stack.getItem() instanceof ProjectileWeaponItem || stack.is(TFItemTags.TRAVELLERS_AGILE_RANGER_WHITELISTED)) && !stack.is(TFItemTags.TRAVELLERS_AGILE_RANGER_BLACKLISTED); if (localPlayer.isUsingItem() && !localPlayer.isPassenger() && isLegalItem) { @@ -94,79 +88,27 @@ private void handleAgileRanger(MovementInputUpdateEvent event) { } } - private static final Identifier STRAIGHT_AHEAD_ID = Identifier.fromNamespaceAndPath("twilightforest", "straight_ahead_speed"); - private void handleStraightAhead(MovementInputUpdateEvent event) { if (!(event.getEntity() instanceof LocalPlayer localPlayer)) return; - - AttributeInstance attributeInstance = localPlayer.getAttribute(Attributes.MOVEMENT_SPEED); - if (attributeInstance == null) - return; - ItemStack bootsStack = localPlayer.getItemBySlot(EquipmentSlot.FEET); Double multiplier = bootsStack.get(TFDataComponents.STRAIGHT_AHEAD_MULTIPLIER); + AttributeInstance attributeInstance = localPlayer.getAttributes().getInstance(Attributes.MOVEMENT_SPEED); + if (attributeInstance == null) + return; - ClientInput input = event.getInput(); - - boolean isMovingForward = input.keyPresses.forward(); - boolean isStrafing = input.keyPresses.left() || input.keyPresses.right(); - boolean isModifierActive = TravellersModifiersManager.isModifierActive(localPlayer, bootsStack, TravellersModifiersManager.STRAIGHT_AHEAD_MODIFIER) && multiplier != null; - - if (isModifierActive && isMovingForward) { - if (!attributeInstance.hasModifier(STRAIGHT_AHEAD_ID)) { - attributeInstance.addTransientModifier(new AttributeModifier( - STRAIGHT_AHEAD_ID, - multiplier - 1.0, - AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL - )); - } - - if (isStrafing) { - Vec3 currentVelocity = localPlayer.getDeltaMovement(); - - Vec3 lookDirection = localPlayer.getLookAngle(); - Vec3 rightVector = new Vec3(-lookDirection.z, 0, lookDirection.x).normalize(); - - double sideVelocity = currentVelocity.dot(rightVector); - - double reductionFactor = 1.0 - (1.0 / multiplier); - Vec3 counterImpulse = rightVector.scale(-sideVelocity * reductionFactor); - - localPlayer.setDeltaMovement(currentVelocity.add(counterImpulse)); - } - } else { - if (attributeInstance.hasModifier(STRAIGHT_AHEAD_ID)) { - attributeInstance.removeModifier(STRAIGHT_AHEAD_ID); - } - } + Input input = localPlayer.input; + if (!TravellersModifiersManager.isModifierActive(localPlayer, bootsStack, TravellersModifiersManager.STRAIGHT_AHEAD_MODIFIER) || multiplier == null || input.forwardImpulse <= 0) + multiplier = 1D; + attributeInstance.addOrUpdateTransientModifier(new AttributeModifier(TFAttributeModifiers.STRAIGHT_AHEAD_ATTRIBUTE_MODIFIER_LOCATION, multiplier - 1, AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL)); + input.leftImpulse /= multiplier; } - private static final Identifier GLIDE_SNEAK_SPEED_ID = Identifier.fromNamespaceAndPath("twilightforest", "glide_sneak_speed"); - private void speedUpControlledWhileSneaking(MovementInputUpdateEvent event) { - if (!(event.getEntity() instanceof LocalPlayer localPlayer)) - return; - - var sneakSpeedAttribute = localPlayer.getAttribute(Attributes.SNEAKING_SPEED); - if (sneakSpeedAttribute == null) + if (!(event.getEntity() instanceof LocalPlayer localPlayer) || !localPlayer.getData(TFDataAttachments.IS_GRADUALLY_GLIDING) || !localPlayer.isShiftKeyDown()) return; - - boolean isGlidingAndSneaking = localPlayer.getData(TFDataAttachments.IS_GRADUALLY_GLIDING) && localPlayer.isShiftKeyDown(); - - if (isGlidingAndSneaking) { - if (!sneakSpeedAttribute.hasModifier(GLIDE_SNEAK_SPEED_ID)) { - sneakSpeedAttribute.addTransientModifier(new AttributeModifier( - GLIDE_SNEAK_SPEED_ID, - 2.333333F, - AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL - )); - } - } else { - if (sneakSpeedAttribute.hasModifier(GLIDE_SNEAK_SPEED_ID)) { - sneakSpeedAttribute.removeModifier(GLIDE_SNEAK_SPEED_ID); - } - } + localPlayer.input.forwardImpulse /= 0.2F; + localPlayer.input.leftImpulse /= 0.2F; } private void handleSidestep(MovementInputUpdateEvent event) { @@ -303,40 +245,40 @@ private void slowZoomSensitivity(CalculatePlayerTurnEvent event) { } private boolean ignoreKeyEvent(InputEvent.Key event, KeyMapping key) { - return !key.matches(new KeyEvent(event.getKey(), event.getScanCode(), event.getModifiers())) || event.getAction() != InputConstants.PRESS || Minecraft.getInstance().screen != null; + return !key.matches(event.getKeyEvent()) || event.getAction() != InputConstants.PRESS || Minecraft.getInstance().screen != null; } - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") //meh private void renderGlovesInFirstPerson(RenderArmEvent event) { - if (!TFConfig.firstPersonGloveOverlay) - return; - - AbstractClientPlayer player = event.getPlayer(); - ItemStack chestStack = player.getItemBySlot(EquipmentSlot.CHEST); - if (!chestStack.has(TFDataComponents.TRAVELLERS_HAS_GLOVES) || chestStack.has(TFDataComponents.EMPERORS_CLOTH)) - return; - - Minecraft minecraft = Minecraft.getInstance(); - EntityRenderDispatcher renderDispatcher = minecraft.getEntityRenderDispatcher(); - - if (!(renderDispatcher.getRenderer(player) instanceof AvatarRenderer avatarRenderer)) - return; - - if (!(IClientItemExtensions.of(TFItems.TRAVELLERS_GLOVES.get()).getHumanoidArmorModel(chestStack, EquipmentClientInfo.LayerType.HUMANOID, avatarRenderer.getModel()) instanceof HumanoidModel model)) - return; - - if (!(avatarRenderer.createRenderState(player, minecraft.getDeltaTracker().getGameTimeDeltaPartialTick(false)) instanceof AvatarRenderState renderState)) - return; - - renderState.attackTime = 0.0F; - renderState.isCrouching = false; - renderState.swimAmount = 0.0F; - model.setupAnim(renderState); - - ModelPart armPart = event.getArm() == HumanoidArm.RIGHT ? model.rightArm : model.leftArm; - armPart.xRot = 0.0F; - - Identifier gloveLocation = TwilightForestMod.prefix("textures/models/armor/travellers_layer_1.png"); - event.getSubmitNodeCollector().submitModelPart(armPart, event.getPoseStack(), RenderTypes.armorCutoutNoCull(gloveLocation), event.getPackedLight(), OverlayTexture.NO_OVERLAY, null); - } + if (TFConfig.firstPersonGloveOverlay) { + AbstractClientPlayer player = event.getPlayer(); + ItemStack chestStack = player.getItemBySlot(EquipmentSlot.CHEST); + if (chestStack.has(TFDataComponents.TRAVELLERS_HAS_GLOVES) && !chestStack.has(TFDataComponents.EMPERORS_CLOTH)) { + AvatarRenderer renderer = (AvatarRenderer) Minecraft.getInstance().getEntityRenderDispatcher().getRenderer(player); + HumanoidModel model = (HumanoidModel) IClientItemExtensions.of(TFItems.TRAVELLERS_GLOVES.get()).getHumanoidArmorModel(chestStack, EquipmentClientInfo.LayerType.HUMANOID, renderer.getModel()); + ModelPart armPart = event.getArm() == HumanoidArm.RIGHT ? model.rightArm : model.leftArm; + AvatarRenderState renderState = renderer.createRenderState(); + renderState.attackTime = 0.0F; + renderState.isCrouching = false; + renderState.swimAmount = 0.0F; + renderState.walkAnimationPos = 0.0F; + renderState.walkAnimationSpeed = 0.0F; + renderState.ageInTicks = 0.0F; + renderState.yRot = 0.0F; + renderState.xRot = 0.0F; + model.setupAnim(renderState); + armPart.xRot = 0.0F; + Identifier gloveLocation = TwilightForestMod.prefix("textures/models/armor/travellers_layer_1.png"); + TextureAtlasSprite glove = Minecraft.getInstance().getAtlasManager().getAtlasOrThrow(AtlasIds.ARMOR_TRIMS).getSprite(gloveLocation); + event.getSubmitNodeCollector().submitModelPart( + armPart, + event.getPoseStack(), + RenderTypes.armorCutoutNoCull(gloveLocation), + event.getPackedLight(), + OverlayTexture.NO_OVERLAY, + glove + ); + } + } + } } From 3f552eb787c9fed588796463296d512556553884 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 02:02:56 +0300 Subject: [PATCH 11/21] Fixed CapabilityEvents.java --- .../events/CapabilityEvents.java | 328 +++++++++++++----- 1 file changed, 249 insertions(+), 79 deletions(-) diff --git a/src/main/java/twilightforest/events/CapabilityEvents.java b/src/main/java/twilightforest/events/CapabilityEvents.java index ba33dae638..ca63ad0ecb 100644 --- a/src/main/java/twilightforest/events/CapabilityEvents.java +++ b/src/main/java/twilightforest/events/CapabilityEvents.java @@ -1,130 +1,300 @@ package twilightforest.events; +import com.mojang.datafixers.util.Pair; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.DisplayInfo; import net.minecraft.core.BlockPos; -import net.minecraft.core.GlobalPos; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.core.SectionPos; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.DamageTypeTags; -import net.minecraft.util.Unit; +import net.minecraft.server.permissions.PermissionLevel; +import net.minecraft.util.TriState; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.monster.Enemy; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.storage.LevelData; -import net.minecraft.world.phys.Vec3; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.StructurePiece; +import net.minecraft.world.level.levelgen.structure.StructureStart; import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.common.util.BlockSnapshot; +import net.neoforged.neoforge.common.util.FakePlayer; import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; -import net.neoforged.neoforge.event.entity.player.PlayerEvent; -import net.neoforged.neoforge.event.tick.EntityTickEvent; +import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; +import net.neoforged.neoforge.event.level.BlockEvent; import net.neoforged.neoforge.event.tick.PlayerTickEvent; +import net.neoforged.neoforge.network.PacketDistributor; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; -import twilightforest.components.entity.FortificationShieldAttachment; +import twilightforest.block.TFPortalBlock; import twilightforest.config.TFConfig; -import twilightforest.init.TFDataAttachments; +import twilightforest.entity.monster.Kobold; +import twilightforest.init.TFAdvancements; +import twilightforest.init.TFBlocks; import twilightforest.init.TFDimension; -import twilightforest.world.NoReturnTeleporter; -import twilightforest.world.TFTeleporter; +import twilightforest.network.AreaProtectionPacket; +import twilightforest.network.MissingAdvancementToastPacket; +import twilightforest.network.StructureProtectionPacket; +import twilightforest.tags.TFBlockTags; +import twilightforest.tags.TFItemTags; +import twilightforest.util.Enforcement; +import twilightforest.util.PlayerHelper; +import twilightforest.util.landmarks.LandmarkUtil; +import twilightforest.world.components.structures.util.AdvancementLockedStructure; +import twilightforest.world.components.structures.util.ProgressionPiece; +import twilightforest.world.components.structures.util.ProgressionStructure; +import java.util.*; + +/** + * A class to store events relating to progression + */ @Component -public class CapabilityEvents { - private static final int COMMON_INVULNERABLE_DURATION = 20; +public class ProgressionEvents { @PostConstruct private void setup() { - NeoForge.EVENT_BUS.addListener(this::updateShields); - NeoForge.EVENT_BUS.addListener(this::updatePlayerCaps); - NeoForge.EVENT_BUS.addListener(this::absorbShieldHits); - NeoForge.EVENT_BUS.addListener(this::spawnInTFIfNecessary); - NeoForge.EVENT_BUS.addListener(this::playerLogsIn); + NeoForge.EVENT_BUS.addListener(this::preventLockedAreaBlockBreaking); + NeoForge.EVENT_BUS.addListener(this::preventLockedAreaBlockPlacing); + NeoForge.EVENT_BUS.addListener(this::preventLockedAreaBlockInteracting); + NeoForge.EVENT_BUS.addListener(this::preventLockedAreaMultiblocks); + NeoForge.EVENT_BUS.addListener(this::preventLockedAreaEntityDamage); + NeoForge.EVENT_BUS.addListener(this::performProtectionAndPortalChecks); } - private void updateShields(EntityTickEvent.Post event) { - if (event.getEntity() instanceof LivingEntity living && !living.level().isClientSide() && living.hasData(TFDataAttachments.FORTIFICATION_SHIELDS)) { - event.getEntity().getData(TFDataAttachments.FORTIFICATION_SHIELDS).tick(living); + /** + * Check if the player is trying to break a block in a structure that's considered unbreakable for progression reasons + */ + private void preventLockedAreaBlockBreaking(BlockEvent.BreakEvent event) { + if (!(event.getLevel() instanceof ServerLevel level) || event.isCanceled()) return; + + BlockPos pos = event.getPos(); + if (isBlockProtectedFromBreaking(level, pos) && isAreaProtected(level, event.getPlayer(), pos)) { + event.setCanceled(true); } } - private void updatePlayerCaps(PlayerTickEvent.Post event) { - if (event.getEntity().getData(TFDataAttachments.FEATHER_FAN)) { - event.getEntity().setIgnoreFallDamageFromCurrentImpulse(true, Vec3.ZERO); - event.getEntity().currentImpulseImpactPos = event.getEntity().position(); + /** + * Check if the player is trying to place a block in a structure that's considered inaccessible for progression reasons + */ + private void preventLockedAreaBlockPlacing(PlayerInteractEvent.RightClickBlock event) { + if (!(event.getLevel() instanceof ServerLevel level) || event.isCanceled()) return; - if (event.getEntity().onGround() || event.getEntity().isSwimming() || event.getEntity().isInWater()) { - event.getEntity().setData(TFDataAttachments.FEATHER_FAN, false); - } + BlockPos pos = event.getPos(); + if (isAreaProtected(level, event.getEntity(), pos)) { + event.setCanceled(true); + event.getEntity().inventoryMenu.sendAllDataToRemote(); } - event.getEntity().getData(TFDataAttachments.YETI_THROWING).tick(event.getEntity()); - event.getEntity().getData(TFDataAttachments.TF_PORTAL_COOLDOWN).tick(event.getEntity()); } - private void absorbShieldHits(LivingIncomingDamageEvent event) { - LivingEntity living = event.getEntity(); - // shields - if (!living.level().isClientSide() && !event.getSource().is(DamageTypeTags.BYPASSES_ARMOR)) { - FortificationShieldAttachment attachment = living.getData(TFDataAttachments.FORTIFICATION_SHIELDS); - if (attachment.shieldsLeft() > 0) { - if (living.invulnerableTime <= 0) { - attachment.breakShield(living, false); - FortificationShieldAttachment.addShieldBreakParticles(event.getSource(), living); - living.invulnerableTime = COMMON_INVULNERABLE_DURATION; - } + /** + * Check if the player is trying to break a multi-block that intersects a structure that's considered inaccessible for progression reasons + */ + private void preventLockedAreaMultiblocks(BlockEvent.EntityMultiPlaceEvent event) { + Entity entity = event.getEntity(); + + if (!(event.getLevel() instanceof ServerLevel level) || !(entity instanceof Player player) || event.isCanceled()) return; + + for (BlockSnapshot snapshot : event.getReplacedBlockSnapshots()) { + BlockPos pos = snapshot.getPos(); + + if (isBlockProtectedFromBreaking(level, pos) && isAreaProtected(level, player, pos)) { event.setCanceled(true); + player.inventoryMenu.sendAllDataToRemote(); + break; } } } - private void spawnInTFIfNecessary(PlayerEvent.PlayerRespawnEvent event) { - if (!(event.getEntity() instanceof ServerPlayer serverPlayer)) return; + /** + * Stop the player from interacting with blocks that could produce treasure or open doors in a protected area + */ + private void preventLockedAreaBlockInteracting(PlayerInteractEvent.RightClickBlock event) { + Player player = event.getEntity(); + Level level = player.level(); - if (serverPlayer.getRespawnConfig() == null) { - newSpawnInTwilightForest(serverPlayer); + if (!level.isClientSide() && level instanceof ServerLevel serverLevel && isBlockProtectedFromInteraction(level, event.getPos()) && isAreaProtected(serverLevel, player, event.getPos())) { + event.setUseBlock(TriState.FALSE); } } /** - * When player logs in, report conflict status, set progression status + * Return if the area at the coordinates is considered protected for that player. + * Currently, if we return true, we also send the area protection packet here. */ - public void playerLogsIn(PlayerEvent.PlayerLoggedInEvent event) { - if (event.getEntity().level().isClientSide() || !(event.getEntity() instanceof ServerPlayer player)) - return; - dataFixLegacyBanish(player); - if (!player.hasData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST)) - newSpawnInTwilightForest(player); + private static boolean isAreaProtected(ServerLevel level, Player player, BlockPos pos) { + if (player.getAbilities().instabuild || player.isSpectator() || + !LandmarkUtil.isProgressionEnforced(level) || player instanceof FakePlayer) { + return false; + } + + Optional struct = LandmarkUtil.locateNearestLandmarkStart(level, SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); + if (struct.isPresent()) { + StructureStart structureStart = struct.get(); + if (structureStart.getPieces().stream().anyMatch(structurePiece -> structurePiece.getBoundingBox().isInside(pos) && isPieceProtected(structurePiece)) && structureStart.getStructure() instanceof ProgressionStructure structureHints) { + if (!structureHints.doesPlayerHaveRequiredAdvancements(player)/* && chunkGenerator.isBlockProtected(pos)*/) { + // send protection packet + List boxes = new ArrayList<>(); + structureStart.getPieces().forEach(piece -> { + if (piece.getBoundingBox().isInside(pos)) + boxes.add(piece.getBoundingBox()); + }); + + sendAreaProtectionPacket(level, pos, boxes); + + // send a hint monster? + structureHints.trySpawnHintMonster(level, player, pos); + + return true; + } + } + } + return false; } - private static void newSpawnInTwilightForest(ServerPlayer player) { - if (!TFConfig.newPlayersSpawnInTF) - return; - ServerLevel level = player.level().getServer().getLevel(TFDimension.DIMENSION_KEY); - if (level == null) - return; + //TODO make ignored entities into a tag + private void preventLockedAreaEntityDamage(LivingIncomingDamageEvent event) { + LivingEntity living = event.getEntity(); + // cancel attacks in protected areas + if (living.level() instanceof ServerLevel serverLevel && living instanceof Enemy && event.getSource().getEntity() instanceof Player && !(living instanceof Kobold) + && isAreaProtected(serverLevel, (Player) event.getSource().getEntity(), new BlockPos(living.blockPosition()))) { - BlockPos newDefaultSpawn = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, player.blockPosition()); + event.setCanceled(true); + } + } - player.teleport(TFConfig.portalForNewPlayerSpawn ? - TFTeleporter.createTransition(player, level, newDefaultSpawn, true) : - NoReturnTeleporter.createNoPortalTransition(level, player, newDefaultSpawn)); - ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig(new LevelData.RespawnData(new GlobalPos(TFDimension.DIMENSION_KEY, newDefaultSpawn), player.getYRot(), player.getXRot()), true); - player.setRespawnPosition(respawnConfig, false); + private void performProtectionAndPortalChecks(PlayerTickEvent.Post event) { + Player eventPlayer = event.getEntity(); - player.setData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST, Unit.INSTANCE); + if (!(eventPlayer instanceof ServerPlayer player)) return; + ServerLevel level = player.level(); + + // check for portal creation, at least if it's not disabled + if (!TFConfig.disablePortalCreation && player.tickCount % (!TFConfig.checkPortalPlacement ? 100 : 20) == 0) { + // skip non admin players when the option is on + if (level.getServer().getProfilePermissions(player.nameAndId()).level().isEqualOrHigherThan(TFConfig.portalCreationPermission)) { + // reduce range to 4.0 if config is set to admins/owners only + checkForPortalCreation(player, level, TFConfig.portalCreationPermission.isEqualOrHigherThan(PermissionLevel.ADMINS) ? 4.0F : 32.0F); + } + } + + // check the player for being in a forbidden progression area, only every 20 ticks + if (player.tickCount % 20 == 0 && LandmarkUtil.isProgressionEnforced(level) && !player.isCreative() && !player.isSpectator()) { + Enforcement.enforceBiomeProgression(player, level); + } + + // check and send nearby forbidden structures, every 100 ticks or so + if (player.tickCount % 100 == 0 && LandmarkUtil.isProgressionEnforced(level)) { + if (player.isCreative() || player.isSpectator()) { + sendAllClearPacket(player); + } else { + checkForLockedStructuresSendPacket(player, level); + } + } } - private static void dataFixLegacyBanish(ServerPlayer player) { - CompoundTag tagCompound = player.getPersistentData(); - if (!tagCompound.contains(Player.PERSISTED_NBT_TAG)) - return; - CompoundTag playerData = tagCompound.getCompound(Player.PERSISTED_NBT_TAG).get(); - if (!playerData.contains("twilightforest_banished")) - return; + @SuppressWarnings("UnusedReturnValue") + private static boolean checkForLockedStructuresSendPacket(Player player, ServerLevel world) { + ChunkPos chunkPlayer = player.chunkPosition(); + return LandmarkUtil.locateNearestLandmarkStart(world, chunkPlayer.x(), chunkPlayer.z()).map(structureStart -> { + if (structureStart.getStructure() instanceof AdvancementLockedStructure advancementLockedStructure && !advancementLockedStructure.doesPlayerHaveRequiredAdvancements(player)) { + List> boundingBoxesData = structureStart.getPieces().stream() + .map(piece -> Pair.of(isPieceProtected(piece) ? piece.getBoundingBox().inflatedBy(4) : piece.getBoundingBox(), isPieceProtected(piece))) + .toList(); - playerData.remove("twilightforest_banished"); - tagCompound.put(Player.PERSISTED_NBT_TAG, playerData); + sendStructureProtectionPacket(player, boundingBoxesData); + return true; + } + + sendAllClearPacket(player); + return false; + }).orElse(false); + } + + private static void checkForPortalCreation(ServerPlayer player, ServerLevel level, float rangeToCheck) { + if (level.dimension().identifier().equals(Identifier.parse(TFConfig.originDimension)) + || TFDimension.isTwilightPortalDestination(level) + || TFConfig.allowPortalsInOtherDimensions) { + + List itemList = level.getEntitiesOfClass(ItemEntity.class, player.getBoundingBox().inflate(rangeToCheck)); + ItemEntity qualified = null; + + for (ItemEntity entityItem : itemList) { + if (entityItem.getItem().is(TFItemTags.PORTAL_ACTIVATOR) && + TFBlocks.TWILIGHT_PORTAL.get().canFormPortal(level.getBlockState(entityItem.blockPosition())) && + Objects.equals(entityItem.getOwner(), player)) { + + qualified = entityItem; + break; + } + } + + if (qualified == null) return; + + if (!player.isCreative() && !player.isSpectator() && TFConfig.getPortalLockingAdvancement(player) != null) { + AdvancementHolder requirement = PlayerHelper.getAdvancement(player, Objects.requireNonNull(TFConfig.getPortalLockingAdvancement(player))); + if (requirement != null && !PlayerHelper.doesPlayerHaveRequiredAdvancement(player, requirement)) { + player.sendOverlayMessage(TFPortalBlock.PORTAL_UNWORTHY); + + if (!TFPortalBlock.isPlayerNotifiedOfRequirement(player)) { + // .doesPlayerHaveRequiredAdvancement null-checks already, so we can skip null-checking the `requirement` + DisplayInfo info = requirement.value().display().orElse(null); + PacketDistributor.sendToPlayer(player, info == null ? new MissingAdvancementToastPacket(net.minecraft.network.chat.Component.translatable("twilightforest.ui.advancement.no_title"), new ItemStack(TFBlocks.TWILIGHT_PORTAL_MINIATURE_STRUCTURE.get())) : new MissingAdvancementToastPacket(info.getTitle(), info.getIcon())); + + TFPortalBlock.playerNotifiedOfRequirement(player); + } + + return; // Item qualifies, but the player doesn't + } + } + + Random rand = new Random(); + for (int i = 0; i < 2; i++) { + double vx = rand.nextGaussian() * 0.02D; + double vy = rand.nextGaussian() * 0.02D; + double vz = rand.nextGaussian() * 0.02D; - if (player.hasData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST)) - return; + level.addParticle(() -> ParticleTypes.EFFECT, qualified.getX(), qualified.getY() + 0.2, qualified.getZ(), vx, vy, vz); + } + + if (TFBlocks.TWILIGHT_PORTAL.get().tryToCreatePortal(level, qualified.blockPosition(), qualified, player)) + TFAdvancements.MADE_TF_PORTAL.get().trigger(player); + + } + } + + private static boolean isPieceProtected(StructurePiece piece) { + return !(piece instanceof ProgressionPiece progressionPiece) || progressionPiece.isComponentProtected(); + } + + private static boolean isBlockProtectedFromInteraction(BlockGetter level, BlockPos pos) { + return level.getBlockState(pos).is(TFBlockTags.STRUCTURE_BANNED_INTERACTIONS); + } - player.setData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST, Unit.INSTANCE); + private static boolean isBlockProtectedFromBreaking(BlockGetter level, BlockPos pos) { + return !level.getBlockState(pos).is(TFBlockTags.PROGRESSION_ALLOW_BREAKING); + } + + private static void sendAreaProtectionPacket(ServerLevel level, BlockPos pos, List sbb) { + PacketDistributor.sendToPlayersNear(level, null, pos.getX(), pos.getY(), pos.getZ(), 64, new AreaProtectionPacket(sbb, pos)); + } + + private static void sendStructureProtectionPacket(Player player, List> sbbData) { + if (player instanceof ServerPlayer sp) { + PacketDistributor.sendToPlayer(sp, new StructureProtectionPacket(Optional.of(sbbData))); + } + } + + private static void sendAllClearPacket(Player player) { + if (player instanceof ServerPlayer sp) { + PacketDistributor.sendToPlayer(sp, new StructureProtectionPacket(Optional.empty())); + } } } From 782d0f96f40b576ef49e04c55bd52f79a419a41f Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 12:37:03 +0300 Subject: [PATCH 12/21] Fixed CharmEvents --- src/main/java/twilightforest/events/CharmEvents.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/twilightforest/events/CharmEvents.java b/src/main/java/twilightforest/events/CharmEvents.java index 7e00d91b8d..b287f9dfef 100644 --- a/src/main/java/twilightforest/events/CharmEvents.java +++ b/src/main/java/twilightforest/events/CharmEvents.java @@ -89,7 +89,10 @@ private void applyKeepingAndCasket(LivingDeathEvent event) { if (living.level() instanceof ServerLevel serverLevel) { if (!serverLevel.getGameRules().get(GameRules.KEEP_INVENTORY)) { + // Did the player recover? No? Let's give them their stuff based on the keeping charms handleCharmOfKeeping(player); + + // Then let's store the rest of their stuff in the casket stockKeepsakeCasket(player); } } From 6cd6dac3e5b4009db32e8f9e94b497e409460b73 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 13:12:16 +0300 Subject: [PATCH 13/21] Fixed CapabilityEvents --- src/main/java/twilightforest/events/CapabilityEvents.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/twilightforest/events/CapabilityEvents.java b/src/main/java/twilightforest/events/CapabilityEvents.java index ca63ad0ecb..bb41206bbd 100644 --- a/src/main/java/twilightforest/events/CapabilityEvents.java +++ b/src/main/java/twilightforest/events/CapabilityEvents.java @@ -29,6 +29,7 @@ import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import net.neoforged.neoforge.event.level.BlockEvent; +import net.neoforged.neoforge.event.level.block.BreakBlockEvent; import net.neoforged.neoforge.event.tick.PlayerTickEvent; import net.neoforged.neoforge.network.PacketDistributor; import tamaized.beanification.Component; @@ -72,7 +73,7 @@ private void setup() { /** * Check if the player is trying to break a block in a structure that's considered unbreakable for progression reasons */ - private void preventLockedAreaBlockBreaking(BlockEvent.BreakEvent event) { + private void preventLockedAreaBlockBreaking(BreakBlockEvent event) { if (!(event.getLevel() instanceof ServerLevel level) || event.isCanceled()) return; BlockPos pos = event.getPos(); From 147d3a918f72dc2f6f3883a08aedd6f2b2895def Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 13:14:03 +0300 Subject: [PATCH 14/21] Fixed ProgressionEvents --- src/main/java/twilightforest/events/ProgressionEvents.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/twilightforest/events/ProgressionEvents.java b/src/main/java/twilightforest/events/ProgressionEvents.java index be27f0e082..3a699606a7 100644 --- a/src/main/java/twilightforest/events/ProgressionEvents.java +++ b/src/main/java/twilightforest/events/ProgressionEvents.java @@ -31,6 +31,7 @@ import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import net.neoforged.neoforge.event.level.BlockEvent; import net.neoforged.neoforge.event.level.GameRuleChangedEvent; +import net.neoforged.neoforge.event.level.block.BreakBlockEvent; import net.neoforged.neoforge.event.tick.PlayerTickEvent; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.server.ServerLifecycleHooks; @@ -90,7 +91,7 @@ private void gameRuleChanged(GameRuleChangedEvent event) { /** * Check if the player is trying to break a block in a structure that's considered unbreakable for progression reasons */ - private void preventLockedAreaBlockBreaking(BlockEvent.BreakEvent event) { + private void preventLockedAreaBlockBreaking(BreakBlockEvent event) { if (!(event.getLevel() instanceof ServerLevel level) || event.isCanceled()) return; BlockPos pos = event.getPos(); From c34a2a8e4df5f03aaa062dce6e3d6e031dbbad79 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 13:23:49 +0300 Subject: [PATCH 15/21] Fixed CapabilityEvents --- .../events/CapabilityEvents.java | 331 +++++------------- 1 file changed, 81 insertions(+), 250 deletions(-) diff --git a/src/main/java/twilightforest/events/CapabilityEvents.java b/src/main/java/twilightforest/events/CapabilityEvents.java index bb41206bbd..3eb553e588 100644 --- a/src/main/java/twilightforest/events/CapabilityEvents.java +++ b/src/main/java/twilightforest/events/CapabilityEvents.java @@ -1,301 +1,132 @@ package twilightforest.events; -import com.mojang.datafixers.util.Pair; -import net.minecraft.advancements.AdvancementHolder; -import net.minecraft.advancements.DisplayInfo; import net.minecraft.core.BlockPos; -import net.minecraft.core.SectionPos; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.resources.Identifier; +import net.minecraft.core.GlobalPos; +import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.permissions.PermissionLevel; -import net.minecraft.util.TriState; -import net.minecraft.world.entity.Entity; +import net.minecraft.tags.DamageTypeTags; +import net.minecraft.util.Unit; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.monster.Enemy; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.common.util.BlockSnapshot; -import net.neoforged.neoforge.common.util.FakePlayer; import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; -import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; -import net.neoforged.neoforge.event.level.BlockEvent; -import net.neoforged.neoforge.event.level.block.BreakBlockEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.event.tick.EntityTickEvent; import net.neoforged.neoforge.event.tick.PlayerTickEvent; -import net.neoforged.neoforge.network.PacketDistributor; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; -import twilightforest.block.TFPortalBlock; +import twilightforest.components.entity.FortificationShieldAttachment; import twilightforest.config.TFConfig; -import twilightforest.entity.monster.Kobold; -import twilightforest.init.TFAdvancements; -import twilightforest.init.TFBlocks; +import twilightforest.init.TFDataAttachments; import twilightforest.init.TFDimension; -import twilightforest.network.AreaProtectionPacket; -import twilightforest.network.MissingAdvancementToastPacket; -import twilightforest.network.StructureProtectionPacket; -import twilightforest.tags.TFBlockTags; -import twilightforest.tags.TFItemTags; -import twilightforest.util.Enforcement; -import twilightforest.util.PlayerHelper; -import twilightforest.util.landmarks.LandmarkUtil; -import twilightforest.world.components.structures.util.AdvancementLockedStructure; -import twilightforest.world.components.structures.util.ProgressionPiece; -import twilightforest.world.components.structures.util.ProgressionStructure; +import twilightforest.world.NoReturnTeleporter; +import twilightforest.world.TFTeleporter; -import java.util.*; - -/** - * A class to store events relating to progression - */ @Component -public class ProgressionEvents { +public class CapabilityEvents { + + private static final int COMMON_INVULNERABLE_DURATION = 20; @PostConstruct private void setup() { - NeoForge.EVENT_BUS.addListener(this::preventLockedAreaBlockBreaking); - NeoForge.EVENT_BUS.addListener(this::preventLockedAreaBlockPlacing); - NeoForge.EVENT_BUS.addListener(this::preventLockedAreaBlockInteracting); - NeoForge.EVENT_BUS.addListener(this::preventLockedAreaMultiblocks); - NeoForge.EVENT_BUS.addListener(this::preventLockedAreaEntityDamage); - NeoForge.EVENT_BUS.addListener(this::performProtectionAndPortalChecks); + NeoForge.EVENT_BUS.addListener(this::updateShields); + NeoForge.EVENT_BUS.addListener(this::updatePlayerCaps); + NeoForge.EVENT_BUS.addListener(this::absorbShieldHits); + NeoForge.EVENT_BUS.addListener(this::spawnInTFIfNecessary); + NeoForge.EVENT_BUS.addListener(this::playerLogsIn); } - /** - * Check if the player is trying to break a block in a structure that's considered unbreakable for progression reasons - */ - private void preventLockedAreaBlockBreaking(BreakBlockEvent event) { - if (!(event.getLevel() instanceof ServerLevel level) || event.isCanceled()) return; - - BlockPos pos = event.getPos(); - if (isBlockProtectedFromBreaking(level, pos) && isAreaProtected(level, event.getPlayer(), pos)) { - event.setCanceled(true); + private void updateShields(EntityTickEvent.Post event) { + if (event.getEntity() instanceof LivingEntity living && !living.level().isClientSide() && living.hasData(TFDataAttachments.FORTIFICATION_SHIELDS)) { + event.getEntity().getData(TFDataAttachments.FORTIFICATION_SHIELDS).tick(living); } } - /** - * Check if the player is trying to place a block in a structure that's considered inaccessible for progression reasons - */ - private void preventLockedAreaBlockPlacing(PlayerInteractEvent.RightClickBlock event) { - if (!(event.getLevel() instanceof ServerLevel level) || event.isCanceled()) return; + private void updatePlayerCaps(PlayerTickEvent.Post event) { + if (event.getEntity().getData(TFDataAttachments.FEATHER_FAN)) { + event.getEntity().setIgnoreFallDamageFromCurrentImpulse(true, Vec3.ZERO); + event.getEntity().currentImpulseImpactPos = event.getEntity().position(); - BlockPos pos = event.getPos(); - if (isAreaProtected(level, event.getEntity(), pos)) { - event.setCanceled(true); - event.getEntity().inventoryMenu.sendAllDataToRemote(); + if (event.getEntity().onGround() || event.getEntity().isSwimming() || event.getEntity().isInWater()) { + event.getEntity().setData(TFDataAttachments.FEATHER_FAN, false); + } } + event.getEntity().getData(TFDataAttachments.YETI_THROWING).tick(event.getEntity()); + event.getEntity().getData(TFDataAttachments.TF_PORTAL_COOLDOWN).tick(event.getEntity()); } - /** - * Check if the player is trying to break a multi-block that intersects a structure that's considered inaccessible for progression reasons - */ - private void preventLockedAreaMultiblocks(BlockEvent.EntityMultiPlaceEvent event) { - Entity entity = event.getEntity(); - - if (!(event.getLevel() instanceof ServerLevel level) || !(entity instanceof Player player) || event.isCanceled()) return; - - for (BlockSnapshot snapshot : event.getReplacedBlockSnapshots()) { - BlockPos pos = snapshot.getPos(); - - if (isBlockProtectedFromBreaking(level, pos) && isAreaProtected(level, player, pos)) { + private void absorbShieldHits(LivingIncomingDamageEvent event) { + LivingEntity living = event.getEntity(); + // shields + if (!living.level().isClientSide() && !event.getSource().is(DamageTypeTags.BYPASSES_ARMOR)) { + FortificationShieldAttachment attachment = living.getData(TFDataAttachments.FORTIFICATION_SHIELDS); + if (attachment.shieldsLeft() > 0) { + if (living.invulnerableTime <= 0) { + attachment.breakShield(living, false); + FortificationShieldAttachment.addShieldBreakParticles(event.getSource(), living); + living.invulnerableTime = COMMON_INVULNERABLE_DURATION; + } event.setCanceled(true); - player.inventoryMenu.sendAllDataToRemote(); - break; } } } - /** - * Stop the player from interacting with blocks that could produce treasure or open doors in a protected area - */ - private void preventLockedAreaBlockInteracting(PlayerInteractEvent.RightClickBlock event) { - Player player = event.getEntity(); - Level level = player.level(); + private void spawnInTFIfNecessary(PlayerEvent.PlayerRespawnEvent event) { + if (!(event.getEntity() instanceof ServerPlayer serverPlayer)) return; - if (!level.isClientSide() && level instanceof ServerLevel serverLevel && isBlockProtectedFromInteraction(level, event.getPos()) && isAreaProtected(serverLevel, player, event.getPos())) { - event.setUseBlock(TriState.FALSE); + if (serverPlayer.getRespawnConfig() == null) { + newSpawnInTwilightForest(serverPlayer); } } /** - * Return if the area at the coordinates is considered protected for that player. - * Currently, if we return true, we also send the area protection packet here. + * When player logs in, report conflict status, set progression status */ - private static boolean isAreaProtected(ServerLevel level, Player player, BlockPos pos) { - if (player.getAbilities().instabuild || player.isSpectator() || - !LandmarkUtil.isProgressionEnforced(level) || player instanceof FakePlayer) { - return false; - } - - Optional struct = LandmarkUtil.locateNearestLandmarkStart(level, SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); - if (struct.isPresent()) { - StructureStart structureStart = struct.get(); - if (structureStart.getPieces().stream().anyMatch(structurePiece -> structurePiece.getBoundingBox().isInside(pos) && isPieceProtected(structurePiece)) && structureStart.getStructure() instanceof ProgressionStructure structureHints) { - if (!structureHints.doesPlayerHaveRequiredAdvancements(player)/* && chunkGenerator.isBlockProtected(pos)*/) { - // send protection packet - List boxes = new ArrayList<>(); - structureStart.getPieces().forEach(piece -> { - if (piece.getBoundingBox().isInside(pos)) - boxes.add(piece.getBoundingBox()); - }); - - sendAreaProtectionPacket(level, pos, boxes); - - // send a hint monster? - structureHints.trySpawnHintMonster(level, player, pos); - - return true; - } - } - } - return false; - } - - //TODO make ignored entities into a tag - private void preventLockedAreaEntityDamage(LivingIncomingDamageEvent event) { - LivingEntity living = event.getEntity(); - // cancel attacks in protected areas - if (living.level() instanceof ServerLevel serverLevel && living instanceof Enemy && event.getSource().getEntity() instanceof Player && !(living instanceof Kobold) - && isAreaProtected(serverLevel, (Player) event.getSource().getEntity(), new BlockPos(living.blockPosition()))) { - - event.setCanceled(true); - } - } - - private void performProtectionAndPortalChecks(PlayerTickEvent.Post event) { - Player eventPlayer = event.getEntity(); - - if (!(eventPlayer instanceof ServerPlayer player)) return; - ServerLevel level = player.level(); - - // check for portal creation, at least if it's not disabled - if (!TFConfig.disablePortalCreation && player.tickCount % (!TFConfig.checkPortalPlacement ? 100 : 20) == 0) { - // skip non admin players when the option is on - if (level.getServer().getProfilePermissions(player.nameAndId()).level().isEqualOrHigherThan(TFConfig.portalCreationPermission)) { - // reduce range to 4.0 if config is set to admins/owners only - checkForPortalCreation(player, level, TFConfig.portalCreationPermission.isEqualOrHigherThan(PermissionLevel.ADMINS) ? 4.0F : 32.0F); - } - } - - // check the player for being in a forbidden progression area, only every 20 ticks - if (player.tickCount % 20 == 0 && LandmarkUtil.isProgressionEnforced(level) && !player.isCreative() && !player.isSpectator()) { - Enforcement.enforceBiomeProgression(player, level); - } - - // check and send nearby forbidden structures, every 100 ticks or so - if (player.tickCount % 100 == 0 && LandmarkUtil.isProgressionEnforced(level)) { - if (player.isCreative() || player.isSpectator()) { - sendAllClearPacket(player); - } else { - checkForLockedStructuresSendPacket(player, level); - } - } - } - - @SuppressWarnings("UnusedReturnValue") - private static boolean checkForLockedStructuresSendPacket(Player player, ServerLevel world) { - ChunkPos chunkPlayer = player.chunkPosition(); - return LandmarkUtil.locateNearestLandmarkStart(world, chunkPlayer.x(), chunkPlayer.z()).map(structureStart -> { - if (structureStart.getStructure() instanceof AdvancementLockedStructure advancementLockedStructure && !advancementLockedStructure.doesPlayerHaveRequiredAdvancements(player)) { - List> boundingBoxesData = structureStart.getPieces().stream() - .map(piece -> Pair.of(isPieceProtected(piece) ? piece.getBoundingBox().inflatedBy(4) : piece.getBoundingBox(), isPieceProtected(piece))) - .toList(); - - sendStructureProtectionPacket(player, boundingBoxesData); - return true; - } - - sendAllClearPacket(player); - return false; - }).orElse(false); + public void playerLogsIn(PlayerEvent.PlayerLoggedInEvent event) { + if (event.getEntity().level().isClientSide() || !(event.getEntity() instanceof ServerPlayer player)) + return; + dataFixLegacyBanish(player); + if (!player.hasData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST)) + newSpawnInTwilightForest(player); } - private static void checkForPortalCreation(ServerPlayer player, ServerLevel level, float rangeToCheck) { - if (level.dimension().identifier().equals(Identifier.parse(TFConfig.originDimension)) - || TFDimension.isTwilightPortalDestination(level) - || TFConfig.allowPortalsInOtherDimensions) { - - List itemList = level.getEntitiesOfClass(ItemEntity.class, player.getBoundingBox().inflate(rangeToCheck)); - ItemEntity qualified = null; - - for (ItemEntity entityItem : itemList) { - if (entityItem.getItem().is(TFItemTags.PORTAL_ACTIVATOR) && - TFBlocks.TWILIGHT_PORTAL.get().canFormPortal(level.getBlockState(entityItem.blockPosition())) && - Objects.equals(entityItem.getOwner(), player)) { - - qualified = entityItem; - break; - } - } - - if (qualified == null) return; - - if (!player.isCreative() && !player.isSpectator() && TFConfig.getPortalLockingAdvancement(player) != null) { - AdvancementHolder requirement = PlayerHelper.getAdvancement(player, Objects.requireNonNull(TFConfig.getPortalLockingAdvancement(player))); - if (requirement != null && !PlayerHelper.doesPlayerHaveRequiredAdvancement(player, requirement)) { - player.sendOverlayMessage(TFPortalBlock.PORTAL_UNWORTHY); + private static void newSpawnInTwilightForest(ServerPlayer player) { + if (!TFConfig.newPlayersSpawnInTF) + return; + ServerLevel level = player.level().getServer().getLevel(TFDimension.DIMENSION_KEY); + if (level == null) + return; - if (!TFPortalBlock.isPlayerNotifiedOfRequirement(player)) { - // .doesPlayerHaveRequiredAdvancement null-checks already, so we can skip null-checking the `requirement` - DisplayInfo info = requirement.value().display().orElse(null); - PacketDistributor.sendToPlayer(player, info == null ? new MissingAdvancementToastPacket(net.minecraft.network.chat.Component.translatable("twilightforest.ui.advancement.no_title"), new ItemStack(TFBlocks.TWILIGHT_PORTAL_MINIATURE_STRUCTURE.get())) : new MissingAdvancementToastPacket(info.getTitle(), info.getIcon())); + BlockPos newDefaultSpawn = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, player.blockPosition()); - TFPortalBlock.playerNotifiedOfRequirement(player); - } + player.teleport(TFConfig.portalForNewPlayerSpawn ? + TFTeleporter.createTransition(player, level, newDefaultSpawn, true) : + NoReturnTeleporter.createNoPortalTransition(level, player, newDefaultSpawn)); + LevelData.RespawnData respawnData = new LevelData.RespawnData(new GlobalPos(TFDimension.DIMENSION_KEY, newDefaultSpawn), player.getYRot(), player.getXRot()); + ServerPlayer.RespawnConfig respawnConfig = new ServerPlayer.RespawnConfig(respawnData, true); + player.setRespawnPosition(respawnConfig, false); - return; // Item qualifies, but the player doesn't - } - } - - Random rand = new Random(); - for (int i = 0; i < 2; i++) { - double vx = rand.nextGaussian() * 0.02D; - double vy = rand.nextGaussian() * 0.02D; - double vz = rand.nextGaussian() * 0.02D; - - level.addParticle(() -> ParticleTypes.EFFECT, qualified.getX(), qualified.getY() + 0.2, qualified.getZ(), vx, vy, vz); - } - - if (TFBlocks.TWILIGHT_PORTAL.get().tryToCreatePortal(level, qualified.blockPosition(), qualified, player)) - TFAdvancements.MADE_TF_PORTAL.get().trigger(player); - - } + player.setData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST, Unit.INSTANCE); } - private static boolean isPieceProtected(StructurePiece piece) { - return !(piece instanceof ProgressionPiece progressionPiece) || progressionPiece.isComponentProtected(); - } + private static void dataFixLegacyBanish(ServerPlayer player) { + CompoundTag tagCompound = player.getPersistentData(); + if (!tagCompound.contains(Player.PERSISTED_NBT_TAG)) + return; + CompoundTag playerData = tagCompound.getCompoundOrEmpty(Player.PERSISTED_NBT_TAG); + if (!playerData.contains("twilightforest_banished")) + return; - private static boolean isBlockProtectedFromInteraction(BlockGetter level, BlockPos pos) { - return level.getBlockState(pos).is(TFBlockTags.STRUCTURE_BANNED_INTERACTIONS); - } - - private static boolean isBlockProtectedFromBreaking(BlockGetter level, BlockPos pos) { - return !level.getBlockState(pos).is(TFBlockTags.PROGRESSION_ALLOW_BREAKING); - } - - private static void sendAreaProtectionPacket(ServerLevel level, BlockPos pos, List sbb) { - PacketDistributor.sendToPlayersNear(level, null, pos.getX(), pos.getY(), pos.getZ(), 64, new AreaProtectionPacket(sbb, pos)); - } + playerData.remove("twilightforest_banished"); + tagCompound.put(Player.PERSISTED_NBT_TAG, playerData); - private static void sendStructureProtectionPacket(Player player, List> sbbData) { - if (player instanceof ServerPlayer sp) { - PacketDistributor.sendToPlayer(sp, new StructureProtectionPacket(Optional.of(sbbData))); - } - } + if (player.hasData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST)) + return; - private static void sendAllClearPacket(Player player) { - if (player instanceof ServerPlayer sp) { - PacketDistributor.sendToPlayer(sp, new StructureProtectionPacket(Optional.empty())); - } + player.setData(TFDataAttachments.BANISHED_TO_TWILIGHT_FOREST, Unit.INSTANCE); } } From dcec00ea710ef725814cd2c9164a2a26b5f9df98 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 13:27:02 +0300 Subject: [PATCH 16/21] Fixed EntityEvents --- .../twilightforest/events/EntityEvents.java | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/main/java/twilightforest/events/EntityEvents.java b/src/main/java/twilightforest/events/EntityEvents.java index cd672af20d..bfaa7f4328 100644 --- a/src/main/java/twilightforest/events/EntityEvents.java +++ b/src/main/java/twilightforest/events/EntityEvents.java @@ -4,8 +4,6 @@ import net.minecraft.commands.Commands; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; @@ -43,8 +41,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.structure.StructurePiece; import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.storage.TagValueInput; -import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.EntityHitResult; import net.neoforged.fml.ModList; @@ -59,9 +55,9 @@ import net.neoforged.neoforge.event.entity.player.AdvancementEvent; import net.neoforged.neoforge.event.entity.player.AttackEntityEvent; import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; -import net.neoforged.neoforge.event.level.BlockEvent; import net.neoforged.neoforge.event.level.ExplosionEvent; import net.neoforged.neoforge.event.level.LevelEvent; +import net.neoforged.neoforge.event.level.block.BreakBlockEvent; import net.neoforged.neoforge.network.PacketDistributor; import tamaized.beanification.PostConstruct; import twilightforest.TwilightForestMod; @@ -183,12 +179,9 @@ private void attachLeadToWroughtFence(PlayerInteractEvent.RightClickBlock event) private void wipeOreMeterOnLeftClick(PlayerInteractEvent.LeftClickEmpty event) { ItemStack item = event.getItemStack(); if (item.is(TFItems.ORE_METER.get()) && (item.has(TFDataComponents.ORE_DATA) || item.has(TFDataComponents.ORE_FILTER))) { - ClientPacketDistributor.sendToServer(new WipeOreMeterPacket(event.getHand())); - item.remove(TFDataComponents.ORE_DATA); item.remove(TFDataComponents.ORE_FILTER); - event.getLevel().playSound( event.getEntity(), event.getEntity().blockPosition(), @@ -200,7 +193,6 @@ private void wipeOreMeterOnLeftClick(PlayerInteractEvent.LeftClickEmpty event) { } } - private void entityHurts(LivingDamageEvent.Post event) { LivingEntity living = event.getEntity(); DamageSource source = event.getSource(); @@ -230,7 +222,7 @@ private void entityHurts(LivingDamageEvent.Post event) { } //if our casket is owned by someone and that player isnt the one breaking it, stop them - private void onCasketBreak(BlockEvent.BreakEvent event) { + private void onCasketBreak(BreakBlockEvent event) { Player player = event.getPlayer(); if (event.getState().getBlock() instanceof SkullChestBlock) { BlockEntity te = event.getLevel().getBlockEntity(event.getPos()); @@ -268,9 +260,10 @@ private void onParryProjectile(ProjectileImpactEvent event) { if (!projectile.level().isClientSide() && !SHIELD_PARRY_MOD_LOADED && (TFConfig.parryNonTwilightAttacks || projectile instanceof ITFProjectile)) { if (event.getRayTraceResult() instanceof EntityHitResult result) { Entity entity = result.getEntity(); + if (entity instanceof LivingEntity entityBlocking) { if (entityBlocking.isBlocking() && entityBlocking.getUseItem().getUseDuration(entityBlocking) - entityBlocking.getUseItemRemainingTicks() <= TFConfig.shieldParryTicks) { - projectile.deflect(ProjectileDeflection.AIM_DEFLECT, entityBlocking, EntityReference.of(projectile.getOwner()), true); + projectile.deflect(ProjectileDeflection.AIM_DEFLECT, entityBlocking, EntityReference.of(entityBlocking), true); event.setCanceled(true); } } @@ -278,7 +271,6 @@ private void onParryProjectile(ProjectileImpactEvent event) { } } - /** * Checks if the player is attempting to create a skull candle */ @@ -337,11 +329,9 @@ private void createSkullCandle(PlayerInteractEvent.RightClickBlock event) { private static void makeSkullCandle(PlayerInteractEvent.RightClickBlock event, Block newBlock) { ResolvableProfile profile = null; Level level = event.getLevel(); - if (level.getBlockEntity(event.getPos()) instanceof SkullBlockEntity skull) { profile = skull.getOwnerProfile(); } - level.playSound(null, event.getPos(), SoundEvents.CANDLE_PLACE, SoundSource.BLOCKS, 1.0F, 1.0F); level.setBlockAndUpdate(event.getPos(), newBlock.withPropertiesOf(level.getBlockState(event.getPos())) .setValue(AbstractSkullCandleBlock.LIGHTING, LightableBlock.Lighting.NONE)); @@ -360,8 +350,8 @@ private static void makeSkullCandle(PlayerInteractEvent.RightClickBlock event, B public static int getGearCoverage(LivingEntity entity, boolean yeti) { int amount = 0; - for (int i = 0; i < 4; i++) { - ItemStack armor = entity.getItemBySlot(ARMOR_SLOTS[i]); + for (EquipmentSlot armorSlot : ARMOR_SLOTS) { + ItemStack armor = entity.getItemBySlot(armorSlot); if (!armor.isEmpty() && (yeti ? armor.getItem() instanceof YetiArmorItem : armor.getItem() instanceof FieryArmorItem)) { amount++; } From 87773d251b38ba17febefe0cf0d321205a057ff5 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 14:09:54 +0300 Subject: [PATCH 17/21] Fixed CharmEvents --- .../twilightforest/events/CharmEvents.java | 188 ++++++++---------- 1 file changed, 78 insertions(+), 110 deletions(-) diff --git a/src/main/java/twilightforest/events/CharmEvents.java b/src/main/java/twilightforest/events/CharmEvents.java index b287f9dfef..b60c31c381 100644 --- a/src/main/java/twilightforest/events/CharmEvents.java +++ b/src/main/java/twilightforest/events/CharmEvents.java @@ -2,13 +2,12 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; -import net.minecraft.resources.RegistryOps; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.ProblemReporter; @@ -16,9 +15,11 @@ import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.EntityEquipment; +import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.PlayerEquipment; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.ResolvableProfile; @@ -28,6 +29,8 @@ import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.storage.TagValueInput; import net.minecraft.world.level.storage.TagValueOutput; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.neoforged.bus.api.EventPriority; import net.neoforged.fml.ModList; import net.neoforged.neoforge.common.NeoForge; @@ -60,6 +63,7 @@ public class CharmEvents { public static final String CHARM_INV_TAG = "TFCharmInventory"; public static final String CASKET_DAMAGE_TAG = "CasketDamage"; public static final String CONSUMED_CHARM_TAG = "CharmStack"; + public static final EquipmentSlot[] ARMOR_SLOTS = new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET}; @PostConstruct private void setup() { @@ -74,7 +78,7 @@ private void applyCharmOfLife(LivingDeathEvent event) { //ensure our player is real and in survival before attempting anything if (event.isCanceled() || living.level().isClientSide() || !(living instanceof Player player) || living instanceof FakePlayer || - player.isCreative() || player.isSpectator()) return; + player.isCreative() || player.isSpectator()) return; if (handleCharmOfLife(player)) event.setCanceled(true); // Executes if the player had charms } @@ -85,10 +89,10 @@ private void applyKeepingAndCasket(LivingDeathEvent event) { //ensure our player is real and in survival before attempting anything if (event.isCanceled() || living.level().isClientSide() || !(living instanceof Player player) || living instanceof FakePlayer || - player.isCreative() || player.isSpectator()) return; + player.isCreative() || player.isSpectator()) return; if (living.level() instanceof ServerLevel serverLevel) { - if (!serverLevel.getGameRules().get(GameRules.KEEP_INVENTORY)) { + if (serverLevel.getGameRules().get(GameRules.KEEP_INVENTORY)) { // Did the player recover? No? Let's give them their stuff based on the keeping charms handleCharmOfKeeping(player); @@ -136,43 +140,38 @@ private static boolean handleCharmOfLife(Player player) { private static void handleCharmOfKeeping(Player player) { //create a fake inventory to organize our kept inventory in - Inventory keepInventory = new Inventory(player, new EntityEquipment()); + Inventory keepInventory = new Inventory(player, new PlayerEquipment(player)); - List allItems = new ArrayList<>(); - for (int i = 0; i < 36; i++) { - allItems.add(player.getInventory().getItem(i)); - } - - if (!applyCharm(TFItems.CHARM_OF_KEEPING_3, keepInventory, player, allItems)) { - List hotbarItems = allItems.subList(0, 9); - if (!applyCharm(TFItems.CHARM_OF_KEEPING_2, keepInventory, player, hotbarItems)) { + if (!applyCharm(TFItems.CHARM_OF_KEEPING_3, keepInventory, player, player.getInventory().getNonEquipmentItems())) { + if (!applyCharm(TFItems.CHARM_OF_KEEPING_2, keepInventory, player, player.getInventory().getNonEquipmentItems().subList(0, 9))) { int i = player.getInventory().getSelectedSlot(); if (Inventory.isHotbarSlot(i)) { - applyCharm(TFItems.CHARM_OF_KEEPING_1, keepInventory, player, NonNullList.of(player.getInventory().getItem(i))); + applyCharm(TFItems.CHARM_OF_KEEPING_1, keepInventory, player, NonNullList.of(player.getInventory().getNonEquipmentItems().get(i))); } } } //keep all items in the kept_on_death tag. This allows modpacks to support other items to keep on death - for (int i = 0; i < player.getInventory().items.size(); i++) { - ItemStack stack = player.getInventory().items.get(i); + for (int i = 0; i < player.getInventory().getNonEquipmentItems().size(); i++) { + ItemStack stack = player.getInventory().getNonEquipmentItems().get(i); if (stack.is(TFItemTags.KEPT_ON_DEATH)) { - keepInventory.items.set(i, stack.copy()); - player.getInventory().items.set(i, ItemStack.EMPTY); + keepInventory.getNonEquipmentItems().set(i, stack.copy()); + player.getInventory().getNonEquipmentItems().set(i, ItemStack.EMPTY); } } - for (int i = 0; i < player.getInventory().armor.size(); i++) { - ItemStack armor = player.getInventory().armor.get(i); + for (int i = 0; i < CharmEvents.ARMOR_SLOTS.length; i++) { + int slotIndex = i + Inventory.INVENTORY_SIZE; + ItemStack armor = player.getInventory().getItem(slotIndex); if (armor.is(TFItemTags.KEPT_ON_DEATH)) { - keepInventory.armor.set(i, armor.copy()); - player.getInventory().armor.set(i, ItemStack.EMPTY); + keepInventory.setItem(slotIndex, armor.copy()); + player.getInventory().setItem(slotIndex, ItemStack.EMPTY); } } - if (player.getInventory().offhand.getFirst().is(TFItemTags.KEPT_ON_DEATH)) { - keepInventory.offhand.set(0, player.getInventory().offhand.getFirst().copy()); - player.getInventory().offhand.set(0, ItemStack.EMPTY); + if (player.getInventory().getItem(Inventory.SLOT_OFFHAND).is(TFItemTags.KEPT_ON_DEATH)) { + keepInventory.setItem(Inventory.SLOT_OFFHAND, player.getInventory().getItem(Inventory.SLOT_OFFHAND)); + player.getInventory().setItem(Inventory.SLOT_OFFHAND, ItemStack.EMPTY); } //take our fake inventory and save it to the persistent player data. @@ -180,72 +179,36 @@ private static void handleCharmOfKeeping(Player player) { if (!keepInventory.isEmpty()) { ProblemReporter.Collector reporter = new ProblemReporter.Collector(); TagValueOutput valueOutput = TagValueOutput.createWithContext(reporter, player.level().registryAccess()); - var typedList = valueOutput.list("items", ItemStackWithSlot.CODEC); + ValueOutput.TypedOutputList typedList = valueOutput.list("items", ItemStackWithSlot.CODEC); keepInventory.save(typedList); CompoundTag resultTag = valueOutput.buildResult(); - ListTag finalTagList = resultTag.getList("items").get(); - getPlayerData(player).put(CHARM_INV_TAG, finalTagList); + ListTag tagList = resultTag.getListOrEmpty("items"); + getPlayerData(player).put(CHARM_INV_TAG, tagList); + getPlayerData(player).put(CHARM_INV_TAG, tagList); } - } private static boolean applyCharm(DeferredItem charm, Inventory keptInventory, Player player, List inventorySlots) { - List playerArmor = new java.util.AbstractList<>() { - @Override public ItemStack get(int index) { return player.getInventory().getItem(36 + index); } - @Override public int size() { return 4; } - @Override public ItemStack set(int index, ItemStack element) { - ItemStack old = player.getInventory().getItem(36 + index); - player.getInventory().setItem(36 + index, element); - return old; - } - }; - - List playerOffhand = new java.util.AbstractList<>() { - @Override public ItemStack get(int index) { return player.getInventory().getItem(40); } - @Override public int size() { return 1; } - @Override public ItemStack set(int index, ItemStack element) { - ItemStack old = player.getInventory().getItem(40); - player.getInventory().setItem(40, element); - return old; - } - }; - List mergedCheck = new ArrayList<>(inventorySlots); - mergedCheck.addAll(playerArmor); - mergedCheck.addAll(playerOffhand); - - if (mergedCheck.stream().filter(stack -> !stack.is(charm)).allMatch(ItemStack::isEmpty)) return false; - if (!TFItemStackUtils.consumeInventoryItem(player, charm, getPlayerData(player), true) && !hasCharmCurio(charm.value(), player)) return false; - - NonNullList keptItemsList = NonNullList.create(); - for (int i = 0; i < inventorySlots.size(); i++) { - keptItemsList.add(keptInventory.getItem(i)); - } - - boolean keptACasket = keepWholeListAndCheckCasket(keptItemsList, inventorySlots, charm == TFItems.CHARM_OF_KEEPING_3); - for (int i = 0; i < inventorySlots.size(); i++) { - keptInventory.setItem(i, keptItemsList.get(i)); - } - - NonNullList keptArmorList = NonNullList.create(); - for (int i = 0; i < 4; i++) { - keptArmorList.add(keptInventory.getItem(i + 36)); + //merge armor and offhand into check slots since theyll always be kept by a charm + for (int i = 0; i < ARMOR_SLOTS.length; i++) { + mergedCheck.add(player.getInventory().getItem(Inventory.INVENTORY_SIZE + i)); } + mergedCheck.add(player.getInventory().getItem(Inventory.SLOT_OFFHAND)); + //first, check all affected slots to make sure they arent empty. + //filter out the charm so it doesnt count towards keeping items if its the only thing we are holding + if (mergedCheck.stream().filter(stack -> !stack.is(charm)).allMatch(ItemStack::isEmpty)) return false; - keptACasket = keepWholeListAndCheckCasket(keptArmorList, playerArmor, keptACasket); - for (int i = 0; i < 4; i++) { - keptInventory.setItem(i + 36, keptArmorList.get(i)); - } + //do we even have a charm? No? Then stop operation + if (!TFItemStackUtils.consumeInventoryItem(player, charm, getPlayerData(player), true) && !hasCharmCurio(charm.value(), player)) return false; - NonNullList keptOffhandList = NonNullList.create(); - keptOffhandList.add(keptInventory.getItem(40)); - keepWholeListAndCheckCasket(keptOffhandList, playerOffhand, keptACasket); - keptInventory.setItem(40, keptOffhandList.get(0)); + boolean keptACasket = keepWholeListAndCheckCasket(keptInventory.getNonEquipmentItems(), inventorySlots, charm == TFItems.CHARM_OF_KEEPING_3); + keptACasket = keepWholeListAndCheckCasket(keptInventory, player.getInventory(), 0, Inventory.INVENTORY_SIZE, keptACasket); + keepWholeListAndCheckCasket(keptInventory, player.getInventory(), Inventory.SLOT_OFFHAND, Inventory.SLOT_OFFHAND + 1, keptACasket); return true; } - private static void stockKeepsakeCasket(Player player) { //make sure we are still actually holding onto items before trying to place a casket if (player.getInventory().hasAnyMatching(stack -> !stack.isEmpty() && !stack.is(TFItems.KEEPSAKE_CASKET))) { @@ -276,7 +239,7 @@ private static void stockKeepsakeCasket(Player player) { BlockPos immutablePos = pos.immutable(); FluidState fluidState = level.getFluidState(immutablePos); - int damage = getPlayerData(player).contains(CASKET_DAMAGE_TAG) ? getPlayerData(player).getInt(CASKET_DAMAGE_TAG).get() : 0; + int damage = getPlayerData(player).getIntOr(CASKET_DAMAGE_TAG, 0); BlockState setState = TFBlocks.KEEPSAKE_CASKET.get().defaultBlockState() .setValue(BlockLoggingEnum.MULTILOGGED, BlockLoggingEnum.getFromFluid(fluidState.getType())) .setValue(KeepsakeCasketBlock.BREAKAGE, damage) @@ -320,17 +283,10 @@ private static void stockKeepsakeCasket(Player player) { // lets add our inventory exactly how it was on us list.addAll(TFItemStackUtils.sortArmorForCasket(player)); - for (int slot = 36; slot <= 39; slot++) { - player.getInventory().setItem(slot, net.minecraft.world.item.ItemStack.EMPTY); - } list.addAll(filler); - list.add(player.getInventory().getItem(40)); - player.getInventory().setItem(40, net.minecraft.world.item.ItemStack.EMPTY); + list.add(player.getInventory().getItem(Inventory.SLOT_OFFHAND)); list.addAll(TFItemStackUtils.sortInvForCasket(player)); - for (int slot = 0; slot < 36; slot++) { - player.getInventory().setItem(slot, net.minecraft.world.item.ItemStack.EMPTY); - } - + player.getInventory().clearContent(); casket.setItems(NonNullList.of(ItemStack.EMPTY, list.toArray(new ItemStack[casketCapacity]))); getPlayerData(player).remove(CASKET_DAMAGE_TAG); @@ -338,29 +294,22 @@ private static void stockKeepsakeCasket(Player player) { //inventory is empty minus the casket: put the casket into the kept inventory for (int i = 0; i < player.getInventory().getContainerSize(); i++) { if (player.getInventory().getItem(i).is(TFItems.KEEPSAKE_CASKET.get())) { - Inventory tmp = new Inventory(player, new EntityEquipment()); - ListTag savedListTag = getPlayerData(player).getList(CHARM_INV_TAG).get(); + Inventory tmp = new Inventory(player, new PlayerEquipment(player)); + ListTag savedListTag = getPlayerData(player).getListOrEmpty(CHARM_INV_TAG); CompoundTag rootLoadTag = new CompoundTag(); - rootLoadTag.put("items", savedListTag); - ProblemReporter.Collector loadReporter = new ProblemReporter.Collector(); - var valueInput = TagValueInput.create(loadReporter, player.level().registryAccess(), rootLoadTag); - var typedInputList = valueInput.list("items", ItemStackWithSlot.CODEC); - - tmp.load(typedInputList.get()); + ValueInput valueInput = TagValueInput.create(loadReporter, player.level().registryAccess(), rootLoadTag); + ValueInput.TypedInputList typedInputList = valueInput.listOrEmpty("items", ItemStackWithSlot.CODEC); + tmp.load(typedInputList); tmp.add(player.getInventory().getItem(i).copy()); player.getInventory().setItem(i, ItemStack.EMPTY); - ProblemReporter.Collector saveReporter = new ProblemReporter.Collector(); TagValueOutput valueOutput = TagValueOutput.createWithContext(saveReporter, player.level().registryAccess()); - - var typedOutputList = valueOutput.list("items", net.minecraft.world.ItemStackWithSlot.CODEC); + ValueOutput.TypedOutputList typedOutputList = valueOutput.list("items", ItemStackWithSlot.CODEC); tmp.save(typedOutputList); - CompoundTag resultTag = valueOutput.buildResult(); - ListTag finalTagList = resultTag.getList("items").get(); - + ListTag finalTagList = resultTag.getListOrEmpty("items"); getPlayerData(player).put(CHARM_INV_TAG, finalTagList); } } @@ -377,19 +326,18 @@ private static void returnStoredItems(Player player) { //check if our tag is in the persistent player data. If so, copy that inventory over to our own. Cloud storage at its finest! CompoundTag playerData = getPlayerData(player); if (!player.level().isClientSide() && playerData.contains(CHARM_INV_TAG)) { - ListTag tagList = playerData.getList(CHARM_INV_TAG).get(); + ListTag tagList = playerData.getListOrEmpty(CHARM_INV_TAG); TFItemStackUtils.loadNoClear(player.registryAccess(), tagList, player.getInventory()); - getPlayerData(player).getList(CHARM_INV_TAG).get().clear(); + getPlayerData(player).getListOrEmpty(CHARM_INV_TAG).clear(); getPlayerData(player).remove(CHARM_INV_TAG); } // spawn effect thingers if (getPlayerData(player).contains(CONSUMED_CHARM_TAG)) { - Tag charmTag = getPlayerData(player).get(CONSUMED_CHARM_TAG); - ItemStack stack = charmTag == null ? ItemStack.EMPTY : ItemStack.OPTIONAL_CODEC.parse( - RegistryOps.create(NbtOps.INSTANCE, player.registryAccess()), - charmTag - ).result().orElse(ItemStack.EMPTY); + CompoundTag tag = (CompoundTag) getPlayerData(player).get(CONSUMED_CHARM_TAG); + HolderLookup.Provider lookupProvider = player.registryAccess(); + ItemStack parsedStack = ItemStack.CODEC.parse(lookupProvider.createSerializationContext(NbtOps.INSTANCE), tag).resultOrPartial().orElse(ItemStack.EMPTY); + ItemStack stack = tag.isEmpty() ? ItemStack.EMPTY : parsedStack; if (player instanceof ServerPlayer serverPlayer) { PacketDistributor.sendToPlayer(serverPlayer, new SpawnCharmPacket(stack, TFSounds.CHARM_KEEP.getKey())); @@ -403,7 +351,7 @@ public static CompoundTag getPlayerData(Player player) { if (!player.getPersistentData().contains(Player.PERSISTED_NBT_TAG)) { player.getPersistentData().put(Player.PERSISTED_NBT_TAG, new CompoundTag()); } - return player.getPersistentData().getCompound(Player.PERSISTED_NBT_TAG).get(); + return player.getPersistentData().getCompoundOrEmpty(Player.PERSISTED_NBT_TAG); } //transfers a list of items to another @@ -426,6 +374,26 @@ private static boolean keepWholeListAndCheckCasket(NonNullList transf return keptCasket || skipCasketCheck; } + //transfers content from some slots to another slots + private static boolean keepWholeListAndCheckCasket(Inventory transferTo, Inventory transferFrom, int fromSlot, int toSlot, boolean skipCasketCheck) { + boolean keptCasket = false; + for (int i = fromSlot; i < toSlot; i++) { + var item = transferFrom.getItem(i).copy(); + if (skipCasketCheck || (!item.is(TFItems.KEEPSAKE_CASKET) || keptCasket)) { + transferTo.setItem(i, item); + transferFrom.setItem(i, ItemStack.EMPTY); + } else { + keptCasket = true; + if (item.getCount() > 1) { + item.shrink(1); + transferTo.setItem(i, item); + transferFrom.setItem(i, item.copyWithCount(1)); + } + } + } + return keptCasket || skipCasketCheck; + } + private static boolean hasCharmCurio(Item item, Player player) { if (ModList.get().isLoaded("curios")) { return CuriosCompat.findAndConsumeCurio(item, player); From 6a97e5b612e73054e7448a2d30da11935d2a1f46 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 14:18:34 +0300 Subject: [PATCH 18/21] Fixed ProgressionEvents --- src/main/java/twilightforest/events/ProgressionEvents.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/twilightforest/events/ProgressionEvents.java b/src/main/java/twilightforest/events/ProgressionEvents.java index 3a699606a7..d8f001408d 100644 --- a/src/main/java/twilightforest/events/ProgressionEvents.java +++ b/src/main/java/twilightforest/events/ProgressionEvents.java @@ -280,11 +280,12 @@ private static void checkForPortalCreation(ServerPlayer player, ServerLevel leve double vy = rand.nextGaussian() * 0.02D; double vz = rand.nextGaussian() * 0.02D; - level.addParticle(ParticleTypes.PORTAL, qualified.getX(), qualified.getY() + 0.2, qualified.getZ(), vx, vy, vz); + level.addParticle(() -> ParticleTypes.EFFECT, qualified.getX(), qualified.getY() + 0.2, qualified.getZ(), vx, vy, vz); } if (TFBlocks.TWILIGHT_PORTAL.get().tryToCreatePortal(level, qualified.blockPosition(), qualified, player)) TFAdvancements.MADE_TF_PORTAL.get().trigger(player); + } } From d662f9ab03957cd860e9ba44c14386169ecddbe0 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 14:23:41 +0300 Subject: [PATCH 19/21] Fixed RegistrationEvents --- src/main/java/twilightforest/events/RegistrationEvents.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/twilightforest/events/RegistrationEvents.java b/src/main/java/twilightforest/events/RegistrationEvents.java index b078adc719..771553cff1 100644 --- a/src/main/java/twilightforest/events/RegistrationEvents.java +++ b/src/main/java/twilightforest/events/RegistrationEvents.java @@ -14,7 +14,6 @@ import net.minecraft.world.entity.monster.spider.Spider; import net.minecraft.world.entity.monster.zombie.Zombie; import net.minecraft.world.item.AxeItem; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.FireBlock; @@ -60,10 +59,12 @@ import twilightforest.entity.boss.*; import twilightforest.entity.monster.*; import twilightforest.entity.passive.*; +import twilightforest.entity.passive.quest.QuestReloadListener; import twilightforest.init.*; import twilightforest.init.custom.BiomeLayerStack; import twilightforest.init.custom.ChunkBlanketProcessors; import twilightforest.init.custom.TemplateMarkerHandlers; +import twilightforest.init.custom.TravellersModifiersManager; import twilightforest.item.travellers_gear.modifiers.TravellersModifier; import twilightforest.loot.modifiers.GiantToolGroupingModifier; import twilightforest.network.*; @@ -210,7 +211,7 @@ public void sendIMCs(InterModEnqueueEvent event) { } public void setupPackets(RegisterPayloadHandlersEvent event) { - PayloadRegistrar registrar = event.registrar(TwilightForestMod.ID); + PayloadRegistrar registrar = event.registrar(TwilightForestMod.ID).versioned("1.0.0").optional(); registrar.playToClient(AreaProtectionPacket.TYPE, AreaProtectionPacket.STREAM_CODEC, AreaProtectionPacket::handle); registrar.playToClient(CreateMovingCicadaSoundPacket.TYPE, CreateMovingCicadaSoundPacket.STREAM_CODEC, CreateMovingCicadaSoundPacket::handle); registrar.playToClient(EnforceProgressionStatusPacket.TYPE, EnforceProgressionStatusPacket.STREAM_CODEC, EnforceProgressionStatusPacket::handle); From dadbddd8d7d4f79ba92432a3b4034b0458be8e96 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 14:27:40 +0300 Subject: [PATCH 20/21] Fixed ToolEvents --- src/main/java/twilightforest/events/ToolEvents.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/twilightforest/events/ToolEvents.java b/src/main/java/twilightforest/events/ToolEvents.java index a48b48d1a0..a019bf4eea 100644 --- a/src/main/java/twilightforest/events/ToolEvents.java +++ b/src/main/java/twilightforest/events/ToolEvents.java @@ -35,6 +35,7 @@ import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; import net.neoforged.neoforge.event.entity.living.MobEffectEvent; import net.neoforged.neoforge.event.level.BlockEvent; +import net.neoforged.neoforge.event.level.block.BreakBlockEvent; import org.jetbrains.annotations.Nullable; import tamaized.beanification.Component; import tamaized.beanification.PostConstruct; @@ -150,7 +151,7 @@ private void addExtraAxeChargingDamage(LivingIncomingDamageEvent event) { } } - private void damageNonMazebreakerToolsMore(BlockEvent.BreakEvent event) { + private void damageNonMazebreakerToolsMore(BreakBlockEvent event) { ItemStack stack = event.getPlayer().getMainHandItem(); if (event.getState().is(TFBlockTags.MAZEBREAKER_ACCELERATED)) { if (stack.isDamageableItem() && !(stack.getItem() instanceof MazebreakerPickItem)) { @@ -165,7 +166,7 @@ private void preventFatigueWithPocketWatch(MobEffectEvent.Applicable event) { } } - private void handleGiantPickaxeMining(BlockEvent.BreakEvent event) { + private void handleGiantPickaxeMining(BreakBlockEvent event) { BlockPos pos = event.getPos(); BlockState state = event.getState(); @@ -175,7 +176,7 @@ private void handleGiantPickaxeMining(BlockEvent.BreakEvent event) { if (shouldBreakGiantBlock(player, attachment)) { attachment.setBreaking(true); // Tell the capability that a block breaking loop is happening, so it knows to fail the if check above. Otherwise, this would go on forever - LootParams.Builder builder = new LootParams.Builder(player.level()) + LootParams.Builder builder = new LootParams.Builder(player.level().getLevel()) .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos)) .withParameter(LootContextParams.BLOCK_STATE, state) .withOptionalParameter(LootContextParams.THIS_ENTITY, player) From 7bf3324913e226fb55c66109e0a6b31cfca390c5 Mon Sep 17 00:00:00 2001 From: Albazavr Date: Fri, 24 Jul 2026 14:34:33 +0300 Subject: [PATCH 21/21] Fixed FogHandler --- .../client/event/FogHandler.java | 79 +++++++++++-------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/src/main/java/twilightforest/client/event/FogHandler.java b/src/main/java/twilightforest/client/event/FogHandler.java index 14c01491ff..6512a831b6 100644 --- a/src/main/java/twilightforest/client/event/FogHandler.java +++ b/src/main/java/twilightforest/client/event/FogHandler.java @@ -1,59 +1,74 @@ package twilightforest.client.event; -import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import net.minecraft.util.Mth; import net.minecraft.world.level.material.FogType; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.client.event.ViewportEvent; import net.neoforged.neoforge.event.level.LevelEvent; import twilightforest.init.TFBiomes; -import twilightforest.init.TFDimension; import javax.annotation.Nullable; public class FogHandler { - private static boolean FOG_CHUNK_LOADED = false; + + private static boolean SKY_CHUNK_LOADED = false; + + private static float SKY_FAR = 0.0F; + private static float SKY_NEAR = 0.0F; + + private static boolean TERRAIN_CHUNK_LOADED = false; private static float TERRAIN_FAR = 0.0F; private static float TERRAIN_NEAR = 0.0F; - @SubscribeEvent - public static void renderFog(ViewportEvent.RenderFog event) { - Camera camera = event.getCamera(); - - if (camera.getFluidInCamera() == FogType.NONE) { - if (camera.entity() instanceof LocalPlayer player && player.level() instanceof ClientLevel clientLevel) { - if (clientLevel.dimension() == TFDimension.DIMENSION_KEY) { - if (FOG_CHUNK_LOADED) { - boolean spooky = isSpooky(clientLevel, player); - float far = spooky ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); - float near = spooky ? far * 0.75F : event.getNearPlaneDistance(); - - TERRAIN_FAR = Mth.lerp(0.003F, TERRAIN_FAR, far); - TERRAIN_NEAR = Mth.lerp(0.003F * (TERRAIN_NEAR < near ? 0.5F : 2.0F), TERRAIN_NEAR, near); - event.setFarPlaneDistance(TERRAIN_FAR); - event.setNearPlaneDistance(TERRAIN_NEAR); - - } else if (clientLevel.isLoaded(player.blockPosition())) { - FOG_CHUNK_LOADED = true; - - TERRAIN_FAR = isSpooky(clientLevel, player) ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); - TERRAIN_NEAR = isSpooky(clientLevel, player) ? TERRAIN_FAR * 0.75F : event.getNearPlaneDistance(); - } + protected static void renderFog(ViewportEvent.RenderFog event) { + if (event.getType().equals(FogType.NONE) && Minecraft.getInstance().getCameraEntity() instanceof LocalPlayer player && player.level() instanceof ClientLevel clientLevel) { + if (event.getType().equals(FogType.ATMOSPHERIC)) { + if (SKY_CHUNK_LOADED) { + boolean spooky = isSpooky(clientLevel, player); + + float far = spooky ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); + float near = spooky ? 0.0F : event.getNearPlaneDistance(); + + SKY_FAR = Mth.lerp(0.003F, SKY_FAR, far); + SKY_NEAR = Mth.lerp(0.003F * (SKY_NEAR < near ? 0.5F : 2.0F), SKY_NEAR, near); + + event.setFarPlaneDistance(SKY_FAR); + event.setNearPlaneDistance(SKY_NEAR); + } else if (clientLevel.isLoaded(player.blockPosition())) { //We do a first-time set up after the chunk the player is in is loaded + SKY_CHUNK_LOADED = true; + SKY_FAR = isSpooky(clientLevel, player) ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); + SKY_NEAR = isSpooky(clientLevel, player) ? 0.0F : event.getNearPlaneDistance(); + } + } else { + if (TERRAIN_CHUNK_LOADED) { + boolean spooky = isSpooky(clientLevel, player); + + float far = spooky ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); + float near = spooky ? far * 0.75F : event.getNearPlaneDistance(); + + TERRAIN_FAR = Mth.lerp(0.003F, TERRAIN_FAR, far); + TERRAIN_NEAR = Mth.lerp(0.003F * (TERRAIN_NEAR < near ? 0.5F : 2.0F), TERRAIN_NEAR, near); + + event.setFarPlaneDistance(TERRAIN_FAR); + event.setNearPlaneDistance(TERRAIN_NEAR); + } else if (SKY_CHUNK_LOADED || clientLevel.isLoaded(player.blockPosition())) { //SKY is always called first in vanilla, so we only need to check if the SKY flag is true, but just in case + TERRAIN_CHUNK_LOADED = true; + TERRAIN_FAR = isSpooky(clientLevel, player) ? event.getFarPlaneDistance() * 0.5F : event.getFarPlaneDistance(); + TERRAIN_NEAR = isSpooky(clientLevel, player) ? TERRAIN_FAR * 0.75F : event.getNearPlaneDistance(); } } } } - @SubscribeEvent - public static void unloadFog(LevelEvent.Unload event) { - FOG_CHUNK_LOADED = false; + protected static void unloadFog(LevelEvent.Unload event) { //As supernatural as the fog is, it shouldn't follow the player between worlds + SKY_CHUNK_LOADED = false; + TERRAIN_CHUNK_LOADED = false; } private static boolean isSpooky(@Nullable ClientLevel level, @Nullable LocalPlayer player) { - if (level == null || player == null) return false; - return level.getBiome(player.blockPosition()).is(TFBiomes.SPOOKY_FOREST); + return level != null && player != null && level.getBiome(player.blockPosition()).is(TFBiomes.SPOOKY_FOREST); } }