diff --git a/src/main/java/net/fretux/knockedback/CarryManager.java b/src/main/java/net/fretux/knockedback/CarryManager.java new file mode 100644 index 0000000..319aac6 --- /dev/null +++ b/src/main/java/net/fretux/knockedback/CarryManager.java @@ -0,0 +1,129 @@ +package net.fretux.knockedback; + +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.AABB; +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.event.entity.EntityMountEvent; +import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket; + +import java.util.*; + +@Mod.EventBusSubscriber(modid = KnockedBack.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE) +public class CarryManager { + private static final Map carrying = new HashMap<>(); + private static final double CARRY_RANGE = 2.0; + + public static void toggleCarry(ServerPlayer carrier) { + Optional existing = carrier.getPassengers().stream() + .filter(p -> p instanceof Player) + .map(p -> (Player)p) + .findFirst(); + if (existing.isPresent()) { + stopCarry(existing.get(), carrier); + return; + } + Player near = findNearestKnocked(carrier); + if (near != null) { + startCarry(carrier, near); + } + } + + + private static Player findNearestKnocked(ServerPlayer carrier) { + AABB box = carrier.getBoundingBox().inflate(CARRY_RANGE); + return carrier + .getCommandSenderWorld() + .getEntitiesOfClass(Player.class, box).stream() + .filter(p -> KnockedManager.isKnocked(p) + && !p.getUUID().equals(carrier.getUUID())) + .findFirst() + .orElse(null); + } + + + private static void startCarry(ServerPlayer carrier, Player knocked) { + // mount + knocked.startRiding(carrier, true); + carrying.put(knocked.getUUID(), carrier.getUUID()); + carrier.sendSystemMessage(Component.literal("Picked up " + knocked.getName().getString())); + knocked.sendSystemMessage(Component.literal("You are being carried by " + carrier.getName().getString())); + knocked.startRiding(carrier, true); + carrying.put(knocked.getUUID(), carrier.getUUID()); + carrier.connection.send(new ClientboundSetPassengersPacket(carrier)); + } + + private static void stopCarry(Player knocked, ServerPlayer carrier) { + knocked.stopRiding(); + carrying.remove(knocked.getUUID()); + carrier.connection.send(new ClientboundSetPassengersPacket(carrier)); + carrier.sendSystemMessage(Component.literal("Dropped " + knocked.getName().getString())); + knocked.sendSystemMessage(Component.literal("You have been dropped")); + } + + /** + * Called each server tick to drop if carrier hurt or out of range + */ + @SubscribeEvent + public static void onServerTick(TickEvent.ServerTickEvent ev) { + if (ev.phase != TickEvent.Phase.END || !ev.side.isServer()) return; + var server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); + + List toDrop = new ArrayList<>(); + for (var entry : carrying.entrySet()) { + UUID knockedId = entry.getKey(); + UUID carrierId = entry.getValue(); + ServerPlayer carrier = NetworkHandlerHelper.getPlayerByUuid(server, carrierId); + ServerPlayer knocked = NetworkHandlerHelper.getPlayerByUuid(server, knockedId); + + if (carrier == null || knocked == null + || !KnockedManager.isKnocked(knocked) + || carrier.distanceTo(knocked) > CARRY_RANGE) { + toDrop.add(knockedId); + } + } + for (UUID kid : toDrop) { + UUID cid = carrying.get(kid); + ServerPlayer carrier = NetworkHandlerHelper.getPlayerByUuid( + net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(), cid); + ServerPlayer knocked = NetworkHandlerHelper.getPlayerByUuid( + net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(), kid); + if (carrier != null && knocked != null) stopCarry(knocked, carrier); + } + } + + @SubscribeEvent + public static void onEntityMount(EntityMountEvent evt) { + if (!evt.isMounting()) { + if (evt.getEntityMounting() instanceof Player knocked + && carrying.containsKey(knocked.getUUID())) { + evt.setCanceled(true); + } + } + } + + /** + * Drop if carrier is hurt + */ + @SubscribeEvent + public static void onCarrierHurt(LivingHurtEvent ev) { + if (!(ev.getEntity() instanceof ServerPlayer carrier)) return; + carrying.entrySet().stream() + .filter(e -> e.getValue().equals(carrier.getUUID())) + .map(Map.Entry::getKey) + .findFirst() + .ifPresent(knockedId -> { + ServerPlayer knocked = NetworkHandlerHelper.getPlayerByUuid( + net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(), knockedId); + if (knocked != null) stopCarry(knocked, carrier); + }); + } + + public static boolean isBeingCarried(UUID knockedId) { + return carrying.containsKey(knockedId); + } +} \ No newline at end of file diff --git a/src/main/java/net/fretux/knockedback/CarryTogglePacket.java b/src/main/java/net/fretux/knockedback/CarryTogglePacket.java new file mode 100644 index 0000000..24073f5 --- /dev/null +++ b/src/main/java/net/fretux/knockedback/CarryTogglePacket.java @@ -0,0 +1,20 @@ +package net.fretux.knockedback; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.network.NetworkEvent; +import java.util.function.Supplier; + +public class CarryTogglePacket { + public static void encode(CarryTogglePacket msg, FriendlyByteBuf buf) {} + public static CarryTogglePacket decode(FriendlyByteBuf buf) { return new CarryTogglePacket(); } + + public static void handle(CarryTogglePacket msg, Supplier ctx) { + ctx.get().enqueueWork(() -> { + ServerPlayer carrier = ctx.get().getSender(); + if (carrier == null) return; + CarryManager.toggleCarry(carrier); + }); + ctx.get().setPacketHandled(true); + } +} diff --git a/src/main/java/net/fretux/knockedback/KnockedManager.java b/src/main/java/net/fretux/knockedback/KnockedManager.java index 61e5b4d..34dd0ae 100644 --- a/src/main/java/net/fretux/knockedback/KnockedManager.java +++ b/src/main/java/net/fretux/knockedback/KnockedManager.java @@ -65,12 +65,8 @@ public class KnockedManager { Map.Entry entry = it.next(); UUID playerId = entry.getKey(); int timeLeft = entry.getValue(); - - // ① If a mob *or* a player is in the middle of executing them, pause: if (MobKillHandler.isBeingMobExecuted(playerId) || PlayerExecutionHandler.isBeingPlayerExecuted(playerId)) { - - // just re‑send the *current* timeLeft, do NOT decrement ServerPlayer p = NetworkHandlerHelper.getPlayerByUuid(server, playerId); if (p != null) { NetworkHandler.CHANNEL.send( @@ -80,7 +76,6 @@ public class KnockedManager { } continue; } - // ② Otherwise count down as normal: timeLeft--; if (timeLeft <= 0) { diff --git a/src/main/java/net/fretux/knockedback/NetworkHandler.java b/src/main/java/net/fretux/knockedback/NetworkHandler.java index 3a945d9..081efa1 100644 --- a/src/main/java/net/fretux/knockedback/NetworkHandler.java +++ b/src/main/java/net/fretux/knockedback/NetworkHandler.java @@ -15,5 +15,6 @@ public class NetworkHandler { CHANNEL.registerMessage(id++, ExecuteKnockedPacket.class, ExecuteKnockedPacket::encode, ExecuteKnockedPacket::decode, ExecuteKnockedPacket::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++, CarryTogglePacket.class, CarryTogglePacket::encode, CarryTogglePacket::decode, CarryTogglePacket::handle); } } \ No newline at end of file diff --git a/src/main/java/net/fretux/knockedback/PlayerExecutionHandler.java b/src/main/java/net/fretux/knockedback/PlayerExecutionHandler.java index 68d9cc8..a8d1e6e 100644 --- a/src/main/java/net/fretux/knockedback/PlayerExecutionHandler.java +++ b/src/main/java/net/fretux/knockedback/PlayerExecutionHandler.java @@ -50,6 +50,7 @@ public class PlayerExecutionHandler { private static class PlayerExecutionAttempt { private final UUID executorUuid; private int timeLeft; + public PlayerExecutionAttempt(UUID executorUuid) { this.executorUuid = executorUuid; this.timeLeft = EXECUTION_DELAY_TICKS; @@ -64,6 +65,10 @@ public class PlayerExecutionHandler { public static void startExecution(ServerPlayer executor, Player target) { UUID knockedId = target.getUUID(); if (!KnockedManager.isKnocked(target)) return; + if (CarryManager.isBeingCarried(target.getUUID())) { + executor.sendSystemMessage(Component.literal("Cannot execute someone you're carrying!")); + return; + } if (executionAttempts.containsKey(knockedId)) return; executionAttempts.put(knockedId, new PlayerExecutionAttempt(executor.getUUID())); executor.sendSystemMessage(Component.literal("Execution started...")); diff --git a/src/main/java/net/fretux/knockedback/client/ClientKeyHandler.java b/src/main/java/net/fretux/knockedback/client/ClientKeyHandler.java index d2996e1..46845bf 100644 --- a/src/main/java/net/fretux/knockedback/client/ClientKeyHandler.java +++ b/src/main/java/net/fretux/knockedback/client/ClientKeyHandler.java @@ -1,5 +1,6 @@ package net.fretux.knockedback.client; +import net.fretux.knockedback.CarryTogglePacket; import net.fretux.knockedback.ExecuteKnockedPacket; import net.fretux.knockedback.NetworkHandler; import net.minecraft.client.KeyMapping; @@ -29,6 +30,17 @@ public class ClientKeyHandler { } } + public static final KeyMapping CARRY_KEY = new KeyMapping( + "key.knockedback.carry", + GLFW.GLFW_KEY_V, + "key.categories.knockedback" + ); + + @SubscribeEvent + public static void registerCarryKey(RegisterKeyMappingsEvent e) { + e.register(CARRY_KEY); + } + @SubscribeEvent public static void onClientTick(TickEvent.ClientTickEvent event) { if (Minecraft.getInstance().screen != null) return; @@ -41,5 +53,11 @@ public class ClientKeyHandler { NetworkHandler.CHANNEL.sendToServer(new ExecuteKnockedPacket()); } } + if (event.phase == TickEvent.Phase.END && CARRY_KEY.consumeClick()) { + Minecraft mc = Minecraft.getInstance(); + if (mc.player != null && mc.screen == null) { + NetworkHandler.CHANNEL.sendToServer(new CarryTogglePacket()); + } + } } } \ No newline at end of file