From d810c8eb0a765e376d1554132fd98bf40da41ea9 Mon Sep 17 00:00:00 2001 From: Fretux Date: Fri, 16 Jan 2026 21:15:55 +0100 Subject: [PATCH] Add config for mob execution and knocked effects --- .../fretux/knockedback/KnockedManager.java | 62 +++++++++++++++++++ .../fretux/knockedback/MobKillHandler.java | 15 ++++- .../net/fretux/knockedback/config/Config.java | 16 +++++ 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/fretux/knockedback/KnockedManager.java b/src/main/java/net/fretux/knockedback/KnockedManager.java index 6ced95a..33031c5 100644 --- a/src/main/java/net/fretux/knockedback/KnockedManager.java +++ b/src/main/java/net/fretux/knockedback/KnockedManager.java @@ -3,16 +3,20 @@ package net.fretux.knockedback; import net.fretux.knockedback.config.Config; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Items; +import net.minecraft.resources.ResourceLocation; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.registries.ForgeRegistries; import java.util.*; @@ -42,6 +46,7 @@ public class KnockedManager { if (!isKnocked(entity)) { knockedEntities.put(entity.getUUID(), getKnockedDuration()); entity.setHealth(1.0F); + applyKnockedEffects((Player) entity); } } @@ -107,6 +112,7 @@ public class KnockedManager { || CarryManager.isBeingCarried(playerId)) { ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId); if (p != null) { + applyKnockedEffects(p); NetworkHandler.CHANNEL.send( PacketDistributor.PLAYER.with(() -> p), new KnockedTimePacket(timeLeft) @@ -118,6 +124,7 @@ public class KnockedManager { || PlayerExecutionHandler.isBeingPlayerExecuted(playerId)) { ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId); if (p != null) { + applyKnockedEffects(p); NetworkHandler.CHANNEL.send( PacketDistributor.PLAYER.with(() -> p), new KnockedTimePacket(timeLeft) @@ -139,6 +146,7 @@ public class KnockedManager { entry.setValue(timeLeft); ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId); if (p != null) { + applyKnockedEffects(p); NetworkHandler.CHANNEL.send( PacketDistributor.PLAYER.with(() -> p), new KnockedTimePacket(timeLeft) @@ -186,6 +194,7 @@ public class KnockedManager { return; } knockedEntities.put(player.getUUID(), getKnockedDuration()); + applyKnockedEffects(player); event.setCanceled(true); } @@ -209,4 +218,57 @@ public class KnockedManager { new GrippedStatePacket(playerId, false) ); } + + private static void applyKnockedEffects(Player player) { + List configured = Config.COMMON.knockedPotionEffects.get(); + if (configured.isEmpty()) { + return; + } + for (String entry : configured) { + MobEffectInstance instance = parseEffect(entry); + if (instance == null) { + continue; + } + MobEffect effect = instance.getEffect(); + MobEffectInstance existing = player.getEffect(effect); + if (existing == null || existing.getDuration() < 20 || existing.getAmplifier() < instance.getAmplifier()) { + player.addEffect(instance); + } + } + } + + private static MobEffectInstance parseEffect(String entry) { + if (entry == null || entry.isBlank()) { + return null; + } + String[] parts = entry.split(","); + if (parts.length < 3) { + return null; + } + ResourceLocation effectId = ResourceLocation.tryParse(parts[0].trim()); + if (effectId == null) { + return null; + } + MobEffect effect = ForgeRegistries.MOB_EFFECTS.getValue(effectId); + if (effect == null) { + return null; + } + Integer duration = parseInt(parts[1].trim()); + Integer amplifier = parseInt(parts[2].trim()); + if (duration == null || amplifier == null) { + return null; + } + boolean ambient = parts.length > 3 ? Boolean.parseBoolean(parts[3].trim()) : false; + boolean visible = parts.length > 4 ? Boolean.parseBoolean(parts[4].trim()) : true; + boolean showIcon = parts.length > 5 ? Boolean.parseBoolean(parts[5].trim()) : true; + return new MobEffectInstance(effect, duration, amplifier, ambient, visible, showIcon); + } + + private static Integer parseInt(String value) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return null; + } + } } diff --git a/src/main/java/net/fretux/knockedback/MobKillHandler.java b/src/main/java/net/fretux/knockedback/MobKillHandler.java index 13dceff..4cd2137 100644 --- a/src/main/java/net/fretux/knockedback/MobKillHandler.java +++ b/src/main/java/net/fretux/knockedback/MobKillHandler.java @@ -7,11 +7,13 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.phys.AABB; import net.minecraft.server.level.ServerLevel; +import net.minecraft.resources.ResourceLocation; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.network.PacketDistributor; +import net.minecraftforge.registries.ForgeRegistries; import javax.annotation.Nullable; import java.util.*; @@ -156,7 +158,7 @@ public class MobKillHandler { p.getX() + r, p.getY() + r, p.getZ() + r ); return world.getEntitiesOfClass(Mob.class, box).stream() - .filter(m -> isHostile(m) || isAggressiveNeutral(m, p)) + .filter(m -> (isHostile(m) || isAggressiveNeutral(m, p)) && isMobAllowedToExecute(m)) .findAny().orElse(null); } @@ -169,10 +171,19 @@ public class MobKillHandler { p.getX() + r, p.getY() + r, p.getZ() + r ); return world.getEntitiesOfClass(Mob.class, box).stream() - .filter(m -> m.getUUID().equals(mobId)) + .filter(m -> m.getUUID().equals(mobId) && isMobAllowedToExecute(m)) .findAny().orElse(null); } + private boolean isMobAllowedToExecute(Mob mob) { + List allowlist = Config.COMMON.mobExecutionAllowlist.get(); + if (allowlist.isEmpty()) { + return true; + } + ResourceLocation id = ForgeRegistries.ENTITY_TYPES.getKey(mob.getType()); + return id != null && allowlist.contains(id.toString()); + } + private boolean isHostile(Mob m) { return m instanceof net.minecraft.world.entity.monster.Monster; diff --git a/src/main/java/net/fretux/knockedback/config/Config.java b/src/main/java/net/fretux/knockedback/config/Config.java index ceac068..a022ba7 100644 --- a/src/main/java/net/fretux/knockedback/config/Config.java +++ b/src/main/java/net/fretux/knockedback/config/Config.java @@ -2,6 +2,8 @@ package net.fretux.knockedback.config; import net.minecraftforge.common.ForgeConfigSpec; +import java.util.List; + public class Config { public static final ForgeConfigSpec COMMON_SPEC; public static final Common COMMON; @@ -16,6 +18,8 @@ public class Config { public final ForgeConfigSpec.IntValue knockedDuration; public final ForgeConfigSpec.IntValue executionTime; public final ForgeConfigSpec.BooleanValue totemPreventsKnockdown; + public final ForgeConfigSpec.ConfigValue> mobExecutionAllowlist; + public final ForgeConfigSpec.ConfigValue> knockedPotionEffects; public Common(ForgeConfigSpec.Builder builder) { builder.push("general"); @@ -28,6 +32,18 @@ public class Config { .comment("How long (in ticks) it takes to execute a knocked player. Default: 60 (3 seconds).") .defineInRange("executionTime", 60, 20, 200); + mobExecutionAllowlist = builder + .comment("List of entity ids allowed to execute knocked players.", + "If empty, any hostile/aggro mob can execute.", + "Example: [\"minecraft:zombie\", \"modid:custom_mob\"]") + .defineListAllowEmpty("mobExecutionAllowlist", List.of(), o -> o instanceof String); + + knockedPotionEffects = builder + .comment("Potion effects to apply while a player is knocked.", + "Format: effect_id,duration,amplifier[,ambient][,visible][,showIcon]", + "Example: [\"minecraft:slowness,200,1\", \"minecraft:weakness,200,0,true,false,false\"]") + .defineListAllowEmpty("knockedPotionEffects", List.of(), o -> o instanceof String); + builder.pop().push("damage_behavior"); totemPreventsKnockdown = builder