Merge pull request #2 from Fretuks/fix-mob-kill-timer-reset-issue

Fix mob execution timer resets, clear respawn knockdown, and sync grip animation
This commit is contained in:
2026-01-07 11:07:27 +01:00
committed by GitHub
6 changed files with 153 additions and 6 deletions
@@ -0,0 +1,32 @@
package net.fretux.knockedback;
import net.fretux.knockedback.client.ClientGrippedState;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent;
import java.util.UUID;
import java.util.function.Supplier;
public class GrippedStatePacket {
private final UUID playerId;
private final boolean gripped;
public GrippedStatePacket(UUID playerId, boolean gripped) {
this.playerId = playerId;
this.gripped = gripped;
}
public static void encode(GrippedStatePacket packet, FriendlyByteBuf buf) {
buf.writeUUID(packet.playerId);
buf.writeBoolean(packet.gripped);
}
public static GrippedStatePacket decode(FriendlyByteBuf buf) {
return new GrippedStatePacket(buf.readUUID(), buf.readBoolean());
}
public static void handle(GrippedStatePacket packet, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> ClientGrippedState.setGripped(packet.playerId, packet.gripped));
ctx.get().setPacketHandled(true);
}
}
@@ -11,6 +11,7 @@ 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 java.util.*; import java.util.*;
@@ -63,7 +64,7 @@ public class KnockedManager {
public static void removeKnockedState(LivingEntity entity) { public static void removeKnockedState(LivingEntity entity) {
knockedEntities.remove(entity.getUUID()); knockedEntities.remove(entity.getUUID());
grippedEntities.remove(entity.getUUID()); setGripped(entity, false);
MobKillHandler.clearKillAttempt(entity.getUUID()); MobKillHandler.clearKillAttempt(entity.getUUID());
entity.setPose(null); entity.setPose(null);
if (entity instanceof ServerPlayer sp) { if (entity instanceof ServerPlayer sp) {
@@ -81,6 +82,12 @@ public class KnockedManager {
} else { } else {
grippedEntities.remove(entity.getUUID()); grippedEntities.remove(entity.getUUID());
} }
if (entity instanceof ServerPlayer sp) {
NetworkHandler.CHANNEL.send(
PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> sp),
new GrippedStatePacket(sp.getUUID(), isGripped)
);
}
} }
public static Collection<UUID> getKnockedUuids() { public static Collection<UUID> getKnockedUuids() {
@@ -193,4 +200,25 @@ public class KnockedManager {
knockedEntities.put(player.getUUID(), getKnockedDuration()); knockedEntities.put(player.getUUID(), getKnockedDuration());
event.setCanceled(true); event.setCanceled(true);
} }
}
@SubscribeEvent
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
if (!(event.getEntity() instanceof ServerPlayer sp)) return;
UUID playerId = sp.getUUID();
knockedEntities.remove(playerId);
grippedEntities.remove(playerId);
MobKillHandler.clearKillAttempt(playerId);
NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> sp),
new KnockedTimePacket(0)
);
NetworkHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> sp),
new ExecutionProgressPacket(0)
);
NetworkHandler.CHANNEL.send(
PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> sp),
new GrippedStatePacket(playerId, false)
);
}
}
@@ -91,7 +91,11 @@ public class MobKillHandler {
setGripped(knocked, false); setGripped(knocked, false);
continue; continue;
} }
Mob mob = getMobInRange(knocked); KillAttempt attempt = killAttempts.get(knockedId);
Mob mob = attempt != null ? getMobInRangeById(knocked, attempt.mobUuid) : null;
if (mob == null) {
mob = getMobInRange(knocked);
}
if (mob == null) { if (mob == null) {
if (knocked instanceof ServerPlayer sp) { if (knocked instanceof ServerPlayer sp) {
NetworkHandler.CHANNEL.send( NetworkHandler.CHANNEL.send(
@@ -103,7 +107,6 @@ public class MobKillHandler {
continue; continue;
} }
setGripped(knocked, true); setGripped(knocked, true);
KillAttempt attempt = killAttempts.get(knockedId);
if (attempt == null || !attempt.mobUuid.equals(mob.getUUID())) { if (attempt == null || !attempt.mobUuid.equals(mob.getUUID())) {
attempt = new KillAttempt(mob.getUUID(), getExecutionTime()); attempt = new KillAttempt(mob.getUUID(), getExecutionTime());
} else { } else {
@@ -157,6 +160,19 @@ public class MobKillHandler {
.findAny().orElse(null); .findAny().orElse(null);
} }
@Nullable
private Mob getMobInRangeById(Player p, UUID mobId) {
if (!(p.level() instanceof ServerLevel world)) return null;
double r = 2.5;
AABB box = new AABB(
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()
.filter(m -> m.getUUID().equals(mobId))
.findAny().orElse(null);
}
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;
@@ -184,4 +200,4 @@ public class MobKillHandler {
public static void clearKillAttempt(UUID playerUuid) { public static void clearKillAttempt(UUID playerUuid) {
killAttempts.remove(playerUuid); killAttempts.remove(playerUuid);
} }
} }
@@ -14,5 +14,6 @@ public class NetworkHandler {
CHANNEL.registerMessage(id++, KnockedTimePacket.class, KnockedTimePacket::encode, KnockedTimePacket::decode, KnockedTimePacket::handle); CHANNEL.registerMessage(id++, KnockedTimePacket.class, KnockedTimePacket::encode, KnockedTimePacket::decode, KnockedTimePacket::handle);
CHANNEL.registerMessage(id++, ExecutionProgressPacket.class, ExecutionProgressPacket::encode, ExecutionProgressPacket::decode, ExecutionProgressPacket::handle); CHANNEL.registerMessage(id++, ExecutionProgressPacket.class, ExecutionProgressPacket::encode, ExecutionProgressPacket::decode, ExecutionProgressPacket::handle);
CHANNEL.registerMessage(id++, CarryTogglePacket.class, CarryTogglePacket::encode, CarryTogglePacket::decode, CarryTogglePacket::handle); CHANNEL.registerMessage(id++, CarryTogglePacket.class, CarryTogglePacket::encode, CarryTogglePacket::decode, CarryTogglePacket::handle);
CHANNEL.registerMessage(id++, GrippedStatePacket.class, GrippedStatePacket::encode, GrippedStatePacket::decode, GrippedStatePacket::handle);
} }
} }
@@ -0,0 +1,21 @@
package net.fretux.knockedback.client;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class ClientGrippedState {
private static final Set<UUID> grippedPlayers = new HashSet<>();
public static void setGripped(UUID playerId, boolean gripped) {
if (gripped) {
grippedPlayers.add(playerId);
} else {
grippedPlayers.remove(playerId);
}
}
public static boolean isGripped(UUID playerId) {
return grippedPlayers.contains(playerId);
}
}
@@ -0,0 +1,49 @@
package net.fretux.knockedback.client;
import dev.kosmx.playerAnim.api.layered.IAnimation;
import dev.kosmx.playerAnim.api.layered.ModifierLayer;
import dev.kosmx.playerAnim.core.player.KeyframeAnimationPlayer;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
import net.fretux.knockedback.KnockedBack;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Mod.EventBusSubscriber(modid = KnockedBack.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class PlayerAnimationHandler {
private static final ResourceLocation GRIP_ANIMATION_ID = new ResourceLocation(KnockedBack.MOD_ID, "animation.model.gripping");
private static final Map<UUID, ModifierLayer<IAnimation>> layers = new HashMap<>();
private static final Map<UUID, Boolean> activeStates = new HashMap<>();
@SubscribeEvent
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
if (event.phase != TickEvent.Phase.END) return;
if (!(event.player instanceof AbstractClientPlayer player)) return;
UUID playerId = player.getUUID();
boolean shouldGrip = ClientGrippedState.isGripped(playerId);
boolean wasGripping = activeStates.getOrDefault(playerId, false);
if (shouldGrip == wasGripping) return;
activeStates.put(playerId, shouldGrip);
ModifierLayer<IAnimation> layer = layers.computeIfAbsent(playerId, id -> {
ModifierLayer<IAnimation> newLayer = new ModifierLayer<>();
PlayerAnimationAccess.getPlayerAnimLayer(player).addAnimLayer(newLayer);
return newLayer;
});
if (shouldGrip) {
var animation = PlayerAnimationRegistry.getAnimation(GRIP_ANIMATION_ID);
if (animation != null) {
layer.setAnimation(new KeyframeAnimationPlayer(animation));
}
} else {
layer.setAnimation(null);
}
}
}