diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStone.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStone.java index ad4afc56..e34dce68 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStone.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStone.java @@ -22,6 +22,7 @@ package de.steamwar.bausystem.features.design.endstone; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.region.Region; import de.steamwar.entity.REntity; +import de.steamwar.entity.REntityAction; import de.steamwar.entity.REntityServer; import de.steamwar.entity.RFallingBlockEntity; import net.md_5.bungee.api.ChatMessageType; @@ -61,7 +62,7 @@ public class DesignEndStone { calculateFromBottom = region.getGameModeConfig().Arena.NoFloor; entityServer.setCallback((player, rEntity, entityAction) -> { - if (entityAction != REntityServer.EntityAction.ATTACK) return; + if (entityAction != REntityAction.ATTACK) return; Location location = new Location(WORLD, rEntity.getX(), rEntity.getY(), rEntity.getZ()); Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { location.getBlock().breakNaturally(); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java index c55c5135..5179eead 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java @@ -25,6 +25,7 @@ import de.steamwar.bausystem.features.tracer.rendering.TraceEntity; import de.steamwar.bausystem.features.tracer.rendering.ViewFlag; import de.steamwar.bausystem.region.Region; import de.steamwar.entity.REntity; +import de.steamwar.entity.REntityAction; import de.steamwar.entity.REntityServer; import lombok.Getter; import lombok.Setter; @@ -144,7 +145,7 @@ public class Trace { entityServer = new REntityServer(); entityServer.addPlayer(player); entityServer.setCallback((p, rEntity, entityAction) -> { - if (entityAction != REntityServer.EntityAction.INTERACT) { + if (entityAction != REntityAction.INTERACT) { return; } if (rEntity instanceof TraceEntity) { @@ -168,7 +169,7 @@ public class Trace { REntityServer newEntityServer = new REntityServer(); newEntityServer.addPlayer(k); newEntityServer.setCallback((p, rEntity, entityAction) -> { - if (entityAction != REntityServer.EntityAction.INTERACT) { + if (entityAction != REntityAction.INTERACT) { return; } if (rEntity instanceof TraceEntity) { diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java index e6b0c64e..3bf98bda 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java @@ -46,6 +46,7 @@ public class ArrowStopper { private void run() { Recording.iterateOverEntities(AbstractArrow.class::isInstance, entity -> { Projectile arrow = (Projectile) entity; + if(!(arrow.getShooter() instanceof Player)) return; if (invalidEntity(arrow)) return; Location prevLocation = arrow.getLocation().toVector().subtract(arrow.getVelocity()).toLocation(arrow.getWorld()); diff --git a/LobbySystem/src/de/steamwar/lobby/boatrace/BoatRace.java b/LobbySystem/src/de/steamwar/lobby/boatrace/BoatRace.java index 81d99dc2..ea8575b9 100644 --- a/LobbySystem/src/de/steamwar/lobby/boatrace/BoatRace.java +++ b/LobbySystem/src/de/steamwar/lobby/boatrace/BoatRace.java @@ -20,6 +20,7 @@ package de.steamwar.lobby.boatrace; import de.steamwar.entity.REntity; +import de.steamwar.entity.REntityAction; import de.steamwar.entity.REntityServer; import de.steamwar.lobby.LobbySystem; import de.steamwar.lobby.util.LeaderboardManager; @@ -62,7 +63,7 @@ public class BoatRace implements EventListener, Listener { boatNpcServer.setCallback((player, rEntity, entityAction) -> { if (rEntity != starter) return; Bukkit.getWorlds().get(0).getEntities().stream().filter(entity -> entity.getType() == EntityType.END_CRYSTAL).forEach(Entity::remove); - if (entityAction == REntityServer.EntityAction.INTERACT && !oneNotStarted) { + if (entityAction == REntityAction.INTERACT && !oneNotStarted) { oneNotStarted = true; new BoatRace(player); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityAction.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityAction.java new file mode 100644 index 00000000..d4046cf0 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityAction.java @@ -0,0 +1,25 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.entity; + +public enum REntityAction { + INTERACT, + ATTACK, +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityActionListener.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityActionListener.java new file mode 100644 index 00000000..8a364642 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityActionListener.java @@ -0,0 +1,26 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.entity; + +import org.bukkit.entity.Player; + +public interface REntityActionListener { + void onAction(Player player, E entity, REntityAction action); +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java index 051e8f9b..b6e3d2d4 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java @@ -21,6 +21,7 @@ package de.steamwar.entity; import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.core.Core; +import lombok.Setter; import net.minecraft.network.protocol.game.ServerboundInteractPacket; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -51,7 +52,8 @@ public class REntityServer implements Listener { private final HashMap lastLocation = new HashMap<>(); private final HashMap viewDistance = new HashMap<>(); - private EntityActionListener callback = null; + @Setter + private REntityActionListener callback = null; private final Set playersThatClicked = Collections.synchronizedSet(new HashSet<>()); private final BiFunction filter = (player, packet) -> { @@ -62,26 +64,22 @@ public class REntityServer implements Listener { if (playersThatClicked.contains(player)) return null; playersThatClicked.add(player); - EntityAction action = packet.isAttack() ? EntityAction.ATTACK : EntityAction.INTERACT; - + REntityAction action = packet.isAttack() ? REntityAction.ATTACK : REntityAction.INTERACT; Bukkit.getScheduler().runTask(Core.getInstance(), () -> { playersThatClicked.remove(player); - callback.onAction(player, targetEntity, action); + if (entity instanceof RInteraction interaction && interaction.callback != null) { + interaction.callback.onAction(player, interaction, action); + } + if (callback != null) { + callback.onAction(player, entity, action); + } }); return null; }; public REntityServer() { Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance()); - } - - public void setCallback(EntityActionListener callback) { - boolean uninitialized = this.callback == null; - this.callback = callback; - - if (uninitialized) { - TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, filter); - } + TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, filter); } public void addPlayer(Player player) { @@ -295,13 +293,4 @@ public class REntityServer implements Listener { private long chunkToId(int x, int z) { return ((long) x << 32) + z; } - - public enum EntityAction { - INTERACT, - ATTACK, - } - - public interface EntityActionListener { - void onAction(Player player, REntity entity, EntityAction action); - } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RInteraction.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RInteraction.java new file mode 100644 index 00000000..b43d02ca --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RInteraction.java @@ -0,0 +1,125 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.entity; + +import de.steamwar.core.BountifulWrapper; +import lombok.AccessLevel; +import lombok.Getter; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * !! This class cannot be used in Versions lower than or equal to 1.19.4 !! + */ +@Getter +public class RInteraction extends REntity { + + protected final Consumer updatePacketSink = o -> server.updateEntity(this, o); + + @Getter(AccessLevel.PRIVATE) + protected REntityActionListener callback = null; + + private float interactionWidth = 1.0f; + private float interactionHeight = 1.0f; + private boolean responsive = false; + + public RInteraction(REntityServer server, Location location) { + super(server, EntityType.INTERACTION, location); + server.addEntity(this); + } + + @Override + protected void postSpawn(Consumer packetSink) { + super.postSpawn(packetSink); + sendPacket(packetSink, + this::getInteractionWidthData, + this::getInteractionHeightData, + this::getResponsiveData + ); + } + + @SafeVarargs + protected final void sendPacket(Consumer packetSink, BiConsumer>... dataSinkSinks) { + List keyValueData = new ArrayList<>(); + boolean ignoreDefault = packetSink == updatePacketSink; + for (BiConsumer> dataSinkSink : dataSinkSinks) { + dataSinkSink.accept(ignoreDefault, (dataWatcher, value) -> { + keyValueData.add(dataWatcher); + keyValueData.add(value); + }); + } + if (!keyValueData.isEmpty()) { + packetSink.accept(getDataWatcherPacket(keyValueData.toArray())); + } + } + + public void setInteractionWidth(float interactionWidth) { + this.interactionWidth = interactionWidth; + sendPacket(updatePacketSink, this::getInteractionWidthData); + } + + private static final Object interactionWidthWatcher = BountifulWrapper.impl.getDataWatcherObject(8, Float.class); + + private void getInteractionWidthData(boolean ignoreDefault, BiConsumer dataSink) { + if (ignoreDefault || interactionWidth != 1.0) { + dataSink.accept(interactionWidthWatcher, interactionWidth); + } + } + + public void setInteractionHeight(float interactionHeight) { + this.interactionHeight = interactionHeight; + sendPacket(updatePacketSink, this::getInteractionHeightData); + } + + private static final Object interactionHeightWatcher = BountifulWrapper.impl.getDataWatcherObject(9, Float.class); + + private void getInteractionHeightData(boolean ignoreDefault, BiConsumer dataSink) { + if (ignoreDefault || interactionHeight != 1.0) { + dataSink.accept(interactionHeightWatcher, interactionHeight); + } + } + + public void setResponsive(boolean responsive) { + this.responsive = responsive; + sendPacket(updatePacketSink, this::getResponsiveData); + } + + private static final Object responsiveWatcher = BountifulWrapper.impl.getDataWatcherObject(10, Boolean.class); + + private void getResponsiveData(boolean ignoreDefault, BiConsumer dataSink) { + if (ignoreDefault || !responsive) { + dataSink.accept(responsiveWatcher, responsive); + } + } + + public void setCallback(REntityActionListener callback) { + this.callback = callback; + } + + public void setCallback(BiConsumer callback) { + this.callback = (player, interaction, entityAction) -> callback.accept(player, entityAction); + } +} \ No newline at end of file