Add RInteraction Entity

This commit is contained in:
2026-05-30 13:17:49 +02:00
parent 9f18644763
commit c5f5be7d58
7 changed files with 194 additions and 25 deletions
@@ -22,6 +22,7 @@ package de.steamwar.bausystem.features.design.endstone;
import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.Region; import de.steamwar.bausystem.region.Region;
import de.steamwar.entity.REntity; import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityAction;
import de.steamwar.entity.REntityServer; import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity; import de.steamwar.entity.RFallingBlockEntity;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
@@ -61,7 +62,7 @@ public class DesignEndStone {
calculateFromBottom = region.getGameModeConfig().Arena.NoFloor; calculateFromBottom = region.getGameModeConfig().Arena.NoFloor;
entityServer.setCallback((player, rEntity, entityAction) -> { 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()); Location location = new Location(WORLD, rEntity.getX(), rEntity.getY(), rEntity.getZ());
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
location.getBlock().breakNaturally(); location.getBlock().breakNaturally();
@@ -25,6 +25,7 @@ import de.steamwar.bausystem.features.tracer.rendering.TraceEntity;
import de.steamwar.bausystem.features.tracer.rendering.ViewFlag; import de.steamwar.bausystem.features.tracer.rendering.ViewFlag;
import de.steamwar.bausystem.region.Region; import de.steamwar.bausystem.region.Region;
import de.steamwar.entity.REntity; import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityAction;
import de.steamwar.entity.REntityServer; import de.steamwar.entity.REntityServer;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -144,7 +145,7 @@ public class Trace {
entityServer = new REntityServer(); entityServer = new REntityServer();
entityServer.addPlayer(player); entityServer.addPlayer(player);
entityServer.setCallback((p, rEntity, entityAction) -> { entityServer.setCallback((p, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.INTERACT) { if (entityAction != REntityAction.INTERACT) {
return; return;
} }
if (rEntity instanceof TraceEntity) { if (rEntity instanceof TraceEntity) {
@@ -168,7 +169,7 @@ public class Trace {
REntityServer newEntityServer = new REntityServer(); REntityServer newEntityServer = new REntityServer();
newEntityServer.addPlayer(k); newEntityServer.addPlayer(k);
newEntityServer.setCallback((p, rEntity, entityAction) -> { newEntityServer.setCallback((p, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.INTERACT) { if (entityAction != REntityAction.INTERACT) {
return; return;
} }
if (rEntity instanceof TraceEntity) { if (rEntity instanceof TraceEntity) {
@@ -20,6 +20,7 @@
package de.steamwar.lobby.boatrace; package de.steamwar.lobby.boatrace;
import de.steamwar.entity.REntity; import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityAction;
import de.steamwar.entity.REntityServer; import de.steamwar.entity.REntityServer;
import de.steamwar.lobby.LobbySystem; import de.steamwar.lobby.LobbySystem;
import de.steamwar.lobby.util.LeaderboardManager; import de.steamwar.lobby.util.LeaderboardManager;
@@ -62,7 +63,7 @@ public class BoatRace implements EventListener, Listener {
boatNpcServer.setCallback((player, rEntity, entityAction) -> { boatNpcServer.setCallback((player, rEntity, entityAction) -> {
if (rEntity != starter) return; if (rEntity != starter) return;
Bukkit.getWorlds().get(0).getEntities().stream().filter(entity -> entity.getType() == EntityType.END_CRYSTAL).forEach(Entity::remove); 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; oneNotStarted = true;
new BoatRace(player); new BoatRace(player);
} }
@@ -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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.entity;
public enum REntityAction {
INTERACT,
ATTACK,
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.entity;
import org.bukkit.entity.Player;
public interface REntityActionListener<E extends REntity> {
void onAction(Player player, E entity, REntityAction action);
}
@@ -21,6 +21,7 @@ package de.steamwar.entity;
import com.comphenix.tinyprotocol.TinyProtocol; import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.core.Core; import de.steamwar.core.Core;
import lombok.Setter;
import net.minecraft.network.protocol.game.ServerboundInteractPacket; import net.minecraft.network.protocol.game.ServerboundInteractPacket;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
@@ -51,7 +52,8 @@ public class REntityServer implements Listener {
private final HashMap<Player, Location> lastLocation = new HashMap<>(); private final HashMap<Player, Location> lastLocation = new HashMap<>();
private final HashMap<Player, Integer> viewDistance = new HashMap<>(); private final HashMap<Player, Integer> viewDistance = new HashMap<>();
private EntityActionListener callback = null; @Setter
private REntityActionListener<REntity> callback = null;
private final Set<Player> playersThatClicked = Collections.synchronizedSet(new HashSet<>()); private final Set<Player> playersThatClicked = Collections.synchronizedSet(new HashSet<>());
private final BiFunction<Player, ServerboundInteractPacket, Object> filter = (player, packet) -> { private final BiFunction<Player, ServerboundInteractPacket, Object> filter = (player, packet) -> {
@@ -61,26 +63,23 @@ public class REntityServer implements Listener {
if (playersThatClicked.contains(player)) return null; if (playersThatClicked.contains(player)) return null;
playersThatClicked.add(player); playersThatClicked.add(player);
EntityAction action = packet.isAttack() ? EntityAction.ATTACK : EntityAction.INTERACT; REntityAction action = packet.isAttack() ? REntityAction.ATTACK : REntityAction.INTERACT;
Bukkit.getScheduler().runTask(Core.getInstance(), () -> { Bukkit.getScheduler().runTask(Core.getInstance(), () -> {
playersThatClicked.remove(player); playersThatClicked.remove(player);
if (entity instanceof RInteraction interaction && interaction.callback != null) {
interaction.callback.onAction(player, interaction, action);
}
if (callback != null) {
callback.onAction(player, entity, action); callback.onAction(player, entity, action);
}
}); });
return null; return null;
}; };
public REntityServer() { public REntityServer() {
Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance()); 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) { public void addPlayer(Player player) {
Location location = player.getLocation(); Location location = player.getLocation();
@@ -293,13 +292,4 @@ public class REntityServer implements Listener {
private long chunkToId(int x, int z) { private long chunkToId(int x, int z) {
return ((long) x << 32) + z; return ((long) x << 32) + z;
} }
public enum EntityAction {
INTERACT,
ATTACK,
}
public interface EntityActionListener {
void onAction(Player player, REntity entity, EntityAction action);
}
} }
@@ -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 <https://www.gnu.org/licenses/>.
*/
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<Object> updatePacketSink = o -> server.updateEntity(this, o);
@Getter(AccessLevel.PRIVATE)
protected REntityActionListener<RInteraction> 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<Object> packetSink) {
super.postSpawn(packetSink);
sendPacket(packetSink,
this::getInteractionWidthData,
this::getInteractionHeightData,
this::getResponsiveData
);
}
@SafeVarargs
protected final void sendPacket(Consumer<Object> packetSink, BiConsumer<Boolean, BiConsumer<Object, Object>>... dataSinkSinks) {
List<Object> keyValueData = new ArrayList<>();
boolean ignoreDefault = packetSink == updatePacketSink;
for (BiConsumer<Boolean, BiConsumer<Object, Object>> 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<Object, Object> 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<Object, Object> 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<Object, Object> dataSink) {
if (ignoreDefault || !responsive) {
dataSink.accept(responsiveWatcher, responsive);
}
}
public void setCallback(REntityActionListener<RInteraction> callback) {
this.callback = callback;
}
public void setCallback(BiConsumer<Player, REntityAction> callback) {
this.callback = (player, interaction, entityAction) -> callback.accept(player, entityAction);
}
}