Add config for mob execution and knocked effects

This commit is contained in:
2026-01-16 21:15:55 +01:00
parent 29feda6868
commit d810c8eb0a
3 changed files with 91 additions and 2 deletions
@@ -3,16 +3,20 @@ package net.fretux.knockedback;
import net.fretux.knockedback.config.Config; import net.fretux.knockedback.config.Config;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.damagesource.DamageSource; 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.LivingEntity;
import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.*; import java.util.*;
@@ -42,6 +46,7 @@ public class KnockedManager {
if (!isKnocked(entity)) { if (!isKnocked(entity)) {
knockedEntities.put(entity.getUUID(), getKnockedDuration()); knockedEntities.put(entity.getUUID(), getKnockedDuration());
entity.setHealth(1.0F); entity.setHealth(1.0F);
applyKnockedEffects((Player) entity);
} }
} }
@@ -107,6 +112,7 @@ public class KnockedManager {
|| CarryManager.isBeingCarried(playerId)) { || CarryManager.isBeingCarried(playerId)) {
ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId); ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId);
if (p != null) { if (p != null) {
applyKnockedEffects(p);
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> p), PacketDistributor.PLAYER.with(() -> p),
new KnockedTimePacket(timeLeft) new KnockedTimePacket(timeLeft)
@@ -118,6 +124,7 @@ public class KnockedManager {
|| PlayerExecutionHandler.isBeingPlayerExecuted(playerId)) { || PlayerExecutionHandler.isBeingPlayerExecuted(playerId)) {
ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId); ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId);
if (p != null) { if (p != null) {
applyKnockedEffects(p);
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> p), PacketDistributor.PLAYER.with(() -> p),
new KnockedTimePacket(timeLeft) new KnockedTimePacket(timeLeft)
@@ -139,6 +146,7 @@ public class KnockedManager {
entry.setValue(timeLeft); entry.setValue(timeLeft);
ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId); ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId);
if (p != null) { if (p != null) {
applyKnockedEffects(p);
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> p), PacketDistributor.PLAYER.with(() -> p),
new KnockedTimePacket(timeLeft) new KnockedTimePacket(timeLeft)
@@ -186,6 +194,7 @@ public class KnockedManager {
return; return;
} }
knockedEntities.put(player.getUUID(), getKnockedDuration()); knockedEntities.put(player.getUUID(), getKnockedDuration());
applyKnockedEffects(player);
event.setCanceled(true); event.setCanceled(true);
} }
@@ -209,4 +218,57 @@ public class KnockedManager {
new GrippedStatePacket(playerId, false) new GrippedStatePacket(playerId, false)
); );
} }
private static void applyKnockedEffects(Player player) {
List<? extends String> 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;
}
}
} }
@@ -7,11 +7,13 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
@@ -156,7 +158,7 @@ public class MobKillHandler {
p.getX() + r, p.getY() + r, p.getZ() + r p.getX() + r, p.getY() + r, p.getZ() + r
); );
return world.getEntitiesOfClass(Mob.class, box).stream() 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); .findAny().orElse(null);
} }
@@ -169,10 +171,19 @@ public class MobKillHandler {
p.getX() + r, p.getY() + r, p.getZ() + r p.getX() + r, p.getY() + r, p.getZ() + r
); );
return world.getEntitiesOfClass(Mob.class, box).stream() return world.getEntitiesOfClass(Mob.class, box).stream()
.filter(m -> m.getUUID().equals(mobId)) .filter(m -> m.getUUID().equals(mobId) && isMobAllowedToExecute(m))
.findAny().orElse(null); .findAny().orElse(null);
} }
private boolean isMobAllowedToExecute(Mob mob) {
List<? extends String> 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) { private boolean isHostile(Mob m) {
return m instanceof net.minecraft.world.entity.monster.Monster; return m instanceof net.minecraft.world.entity.monster.Monster;
@@ -2,6 +2,8 @@ package net.fretux.knockedback.config;
import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.ForgeConfigSpec;
import java.util.List;
public class Config { public class Config {
public static final ForgeConfigSpec COMMON_SPEC; public static final ForgeConfigSpec COMMON_SPEC;
public static final Common COMMON; public static final Common COMMON;
@@ -16,6 +18,8 @@ public class Config {
public final ForgeConfigSpec.IntValue knockedDuration; public final ForgeConfigSpec.IntValue knockedDuration;
public final ForgeConfigSpec.IntValue executionTime; public final ForgeConfigSpec.IntValue executionTime;
public final ForgeConfigSpec.BooleanValue totemPreventsKnockdown; public final ForgeConfigSpec.BooleanValue totemPreventsKnockdown;
public final ForgeConfigSpec.ConfigValue<List<? extends String>> mobExecutionAllowlist;
public final ForgeConfigSpec.ConfigValue<List<? extends String>> knockedPotionEffects;
public Common(ForgeConfigSpec.Builder builder) { public Common(ForgeConfigSpec.Builder builder) {
builder.push("general"); 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).") .comment("How long (in ticks) it takes to execute a knocked player. Default: 60 (3 seconds).")
.defineInRange("executionTime", 60, 20, 200); .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"); builder.pop().push("damage_behavior");
totemPreventsKnockdown = builder totemPreventsKnockdown = builder