forked from SteamWar/SteamWar
+237
-71
@@ -21,13 +21,13 @@ package de.steamwar.bausystem.features.util;
|
||||
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.Permission;
|
||||
import de.steamwar.entity.CWireframe;
|
||||
import de.steamwar.entity.REntity;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import de.steamwar.entity.RFallingBlockEntity;
|
||||
import de.steamwar.entity.RTextDisplay;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import de.steamwar.linkage.MinVersion;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@@ -37,101 +37,276 @@ import org.bukkit.block.PistonMoveReaction;
|
||||
import org.bukkit.block.TileState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Piston;
|
||||
import org.bukkit.entity.Display;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.TextDisplay;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.*;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@Linked
|
||||
@MinVersion(20)
|
||||
public class PistonCalculator implements Listener {
|
||||
|
||||
private final Map<Player, Long> DEBOUNCE = new HashMap<>();
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
|
||||
if (!event.hasItem() || event.getItem().getType() != Material.SLIME_BALL) return;
|
||||
if (event.hasItem() && event.getItem().getType() != Material.SLIME_BALL) return;
|
||||
if (event.getClickedBlock() == null) return;
|
||||
Block clickedBlock = event.getClickedBlock();
|
||||
Material blockType = clickedBlock.getType();
|
||||
if (!(blockType == Material.PISTON || blockType == Material.STICKY_PISTON)) return;
|
||||
Piston piston = (Piston) clickedBlock.getBlockData();
|
||||
if (System.currentTimeMillis() - DEBOUNCE.getOrDefault(event.getPlayer(), 0L) <= 200) return;
|
||||
DEBOUNCE.put(event.getPlayer(), System.currentTimeMillis());
|
||||
|
||||
if (blockType == Material.PISTON && piston.isExtended()) {
|
||||
BauSystem.MESSAGE.sendPrefixless("PISTON_INFO", event.getPlayer(), ChatMessageType.ACTION_BAR, "§a", 0);
|
||||
Location location = event.getClickedBlock().getLocation();
|
||||
if (pistOrders.containsKey(location)) {
|
||||
PistOrder pistOrder = pistOrders.get(location);
|
||||
if (pistOrder.server.getPlayers().contains(event.getPlayer())) {
|
||||
pistOrder.server.removePlayer(event.getPlayer());
|
||||
} else {
|
||||
pistOrder.server.addPlayer(event.getPlayer());
|
||||
}
|
||||
if (pistOrder.server.getPlayers().isEmpty()) {
|
||||
pistOrders.remove(location);
|
||||
pistOrder.server.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boolean pulling = blockType == Material.STICKY_PISTON && (clickedBlock.getRelative(piston.getFacing()).getType() == Material.AIR || piston.isExtended());
|
||||
PistOrder pistOrder = new PistOrder(clickedBlock);
|
||||
pistOrder.calculate();
|
||||
pistOrder.server.addPlayer(event.getPlayer());
|
||||
pistOrders.put(location, pistOrder);
|
||||
}
|
||||
|
||||
CalculationResult result = calc(clickedBlock, piston.getFacing(), (pulling ? piston.getFacing().getOppositeFace() : piston.getFacing()));
|
||||
result.entityServer.addPlayer(event.getPlayer());
|
||||
BauSystem.MESSAGE.sendPrefixless("PISTON_INFO", event.getPlayer(), ChatMessageType.ACTION_BAR, result.unmovable ? "§c" : (result.tooMany ? "§e" : "§a"), result.amount);
|
||||
@EventHandler
|
||||
public void onBlockPistonExtend(BlockPistonExtendEvent event) {
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
pistOrders.values().forEach(PistOrder::calculate);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockPistonRetract(BlockPistonRetractEvent event) {
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
pistOrders.values().forEach(PistOrder::calculate);
|
||||
}, 3);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockPlace(BlockPlaceEvent event) {
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
pistOrders.values().forEach(PistOrder::calculate);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
if (pistOrders.containsKey(event.getBlock().getLocation())) {
|
||||
PistOrder pistOrder = pistOrders.remove(event.getBlock().getLocation());
|
||||
pistOrder.server.close();
|
||||
}
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
pistOrders.values().forEach(PistOrder::calculate);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
DEBOUNCE.remove(event.getPlayer());
|
||||
|
||||
Set<Location> toRemove = new HashSet<>();
|
||||
pistOrders.forEach((location, pistOrder) -> {
|
||||
pistOrder.server.removePlayer(event.getPlayer());
|
||||
if (pistOrder.server.getPlayers().isEmpty()) {
|
||||
toRemove.add(location);
|
||||
pistOrder.server.close();
|
||||
}
|
||||
});
|
||||
toRemove.forEach(pistOrders::remove);
|
||||
}
|
||||
|
||||
private final BlockFace[] FACES = new BlockFace[]{BlockFace.UP, BlockFace.DOWN, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST};
|
||||
|
||||
private CalculationResult calc(Block origin, BlockFace facing, BlockFace direction) {
|
||||
Set<Block> blockSet = new HashSet<>();
|
||||
Set<Location> unmovable = new HashSet<>();
|
||||
private final Map<Location, PistOrder> pistOrders = new HashMap<>();
|
||||
|
||||
Block calcOrigin = origin;
|
||||
if (facing != direction) calcOrigin = origin.getRelative(facing, 3);
|
||||
@RequiredArgsConstructor
|
||||
private final class PistOrder {
|
||||
private final Block piston;
|
||||
private REntityServer server = new REntityServer();
|
||||
|
||||
List<Block> toCalc = new LinkedList<>();
|
||||
calcDirection(origin, null, calcOrigin, facing != direction ? origin.getRelative(facing) : null, facing, direction, blockSet, toCalc, unmovable);
|
||||
private boolean pulling = false;
|
||||
private List<Location> movedBlocks = new ArrayList<>();
|
||||
private List<Location> brokenBlocks = new ArrayList<>();
|
||||
private List<Location> immovableBlocks = new ArrayList<>();
|
||||
|
||||
while (!toCalc.isEmpty()) {
|
||||
Block current = toCalc.remove(0);
|
||||
blockSet.add(current);
|
||||
public void calculate() {
|
||||
movedBlocks.clear();
|
||||
brokenBlocks.clear();
|
||||
immovableBlocks.clear();
|
||||
|
||||
Material type = current.getType();
|
||||
if (type != Material.SLIME_BLOCK && type != Material.HONEY_BLOCK) continue;
|
||||
Material oppositeType = type == Material.SLIME_BLOCK ? Material.HONEY_BLOCK : Material.SLIME_BLOCK;
|
||||
Piston pistonData = (Piston) piston.getBlockData();
|
||||
if (piston.getType() == Material.PISTON && pistonData.isExtended()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (BlockFace face : FACES) {
|
||||
Block block = current.getRelative(face);
|
||||
if (block.getType().isAir()) continue;
|
||||
if (!isPiston(block) && (block.getPistonMoveReaction() == PistonMoveReaction.BLOCK || block.getPistonMoveReaction() == PistonMoveReaction.IGNORE || block.getPistonMoveReaction() == PistonMoveReaction.PUSH_ONLY || block.getState() instanceof TileState || block.getPistonMoveReaction() == PistonMoveReaction.BREAK)) continue;
|
||||
if (block.getType() != oppositeType) {
|
||||
if (!blockSet.contains(block)) toCalc.add(block);
|
||||
calcDirection(null, origin, block, null, facing, direction, blockSet, toCalc, unmovable);
|
||||
pulling = piston.getType() == Material.STICKY_PISTON && (piston.getRelative(pistonData.getFacing()).getType() == Material.AIR || pistonData.isExtended());
|
||||
|
||||
calculate(piston, pistonData.getFacing(), (pulling ? pistonData.getFacing().getOppositeFace() : pistonData.getFacing()));
|
||||
|
||||
Collections.reverse(movedBlocks);
|
||||
Collections.reverse(brokenBlocks);
|
||||
Collections.reverse(immovableBlocks);
|
||||
|
||||
visualize();
|
||||
}
|
||||
|
||||
private void calculate(Block origin, BlockFace facing, BlockFace direction) {
|
||||
Block calcOrigin = origin;
|
||||
if (facing != direction) calcOrigin = origin.getRelative(facing, 3);
|
||||
|
||||
List<Block> toCalc = new LinkedList<>();
|
||||
calcDirection(origin, null, calcOrigin, facing != direction ? origin.getRelative(facing) : null, facing, direction, toCalc);
|
||||
|
||||
while (!toCalc.isEmpty()) {
|
||||
Block current = toCalc.remove(0);
|
||||
if (!movedBlocks.contains(current.getLocation())) {
|
||||
movedBlocks.add(current.getLocation());
|
||||
}
|
||||
|
||||
Material type = current.getType();
|
||||
if (type != Material.SLIME_BLOCK && type != Material.HONEY_BLOCK) continue;
|
||||
Material oppositeType = type == Material.SLIME_BLOCK ? Material.HONEY_BLOCK : Material.SLIME_BLOCK;
|
||||
|
||||
for (BlockFace face : FACES) {
|
||||
Block block = current.getRelative(face);
|
||||
if (block.getType().isAir()) continue;
|
||||
if (isImmovable(block) || block.getPistonMoveReaction() == PistonMoveReaction.BREAK) continue;
|
||||
if (block.getType() != oppositeType) {
|
||||
if (!movedBlocks.contains(block.getLocation())) toCalc.add(block);
|
||||
calcDirection(null, origin, block, null, facing, direction, toCalc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
movedBlocks.remove(origin.getLocation());
|
||||
if (facing != direction) movedBlocks.remove(origin.getRelative(facing, 1).getLocation());
|
||||
}
|
||||
|
||||
blockSet.remove(origin);
|
||||
if (facing != direction) blockSet.remove(origin.getRelative(facing, 1));
|
||||
|
||||
REntityServer entityServer = new REntityServer();
|
||||
for (Location loc : unmovable) {
|
||||
RFallingBlockEntity rFallingBlockEntity = new RFallingBlockEntity(entityServer, loc.clone().add(0.5, 0, 0.5), Material.RED_STAINED_GLASS);
|
||||
rFallingBlockEntity.setGlowing(true);
|
||||
rFallingBlockEntity.setNoGravity(true);
|
||||
rFallingBlockEntity.setInvisible(true);
|
||||
private void calcDirection(Block origin, Block blockOrigin, Block calcOrigin, Block ignore, BlockFace facing, BlockFace direction, List<Block> toCalc) {
|
||||
for (int i = 1; i < 24; i++) {
|
||||
Block block = calcOrigin.getRelative(direction, i);
|
||||
if (block.equals(ignore)) return;
|
||||
if (block.getPistonMoveReaction() == PistonMoveReaction.BREAK) {
|
||||
brokenBlocks.add(block.getLocation());
|
||||
return;
|
||||
}
|
||||
if (isImmovable(block)) {
|
||||
immovableBlocks.add(block.getLocation());
|
||||
return;
|
||||
}
|
||||
if (block.getType().isAir()) return;
|
||||
if (facing == direction && block.equals(blockOrigin)) {
|
||||
immovableBlocks.add(block.getLocation());
|
||||
return;
|
||||
}
|
||||
if (facing != direction && (block.equals(origin) || block.getRelative(facing.getOppositeFace()).equals(origin))) return;
|
||||
if (!movedBlocks.contains(block.getLocation())) toCalc.add(block);
|
||||
}
|
||||
}
|
||||
|
||||
private void visualize() {
|
||||
server.getEntities().forEach(REntity::die);
|
||||
|
||||
for (int i = 0; i < movedBlocks.size(); i++) {
|
||||
Location location = movedBlocks.get(i);
|
||||
int order = i + 1;
|
||||
|
||||
RTextDisplay textDisplay = new RTextDisplay(server, location.clone().add(0.5, 0.4, 0.5));
|
||||
textDisplay.setText("§e" + order);
|
||||
textDisplay.setBillboard(Display.Billboard.CENTER);
|
||||
textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER);
|
||||
textDisplay.setSeeThrough(true);
|
||||
textDisplay.setBackgroundColor(0);
|
||||
textDisplay.setShadowed(false);
|
||||
textDisplay.setBrightness(new Display.Brightness(15, 15));
|
||||
}
|
||||
|
||||
for (int i = 0; i < brokenBlocks.size(); i++) {
|
||||
Location location = brokenBlocks.get(i);
|
||||
int order = i + 1;
|
||||
|
||||
RTextDisplay textDisplay = new RTextDisplay(server, location.clone().add(0.5, 0.4, 0.5));
|
||||
textDisplay.setText("§c" + order);
|
||||
textDisplay.setBillboard(Display.Billboard.CENTER);
|
||||
textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER);
|
||||
textDisplay.setSeeThrough(true);
|
||||
textDisplay.setBackgroundColor(0);
|
||||
textDisplay.setShadowed(false);
|
||||
textDisplay.setBrightness(new Display.Brightness(15, 15));
|
||||
}
|
||||
|
||||
for (Location location : immovableBlocks) {
|
||||
CWireframe wireframe = new CWireframe(server);
|
||||
wireframe.setPos1(location);
|
||||
wireframe.setPos2(location);
|
||||
wireframe.setWidth(1 / 32f);
|
||||
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
|
||||
}
|
||||
|
||||
CWireframe wireframe = new CWireframe(server);
|
||||
wireframe.setPos1(piston.getLocation());
|
||||
wireframe.setPos2(piston.getLocation());
|
||||
if (!immovableBlocks.isEmpty()) {
|
||||
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
|
||||
} else if (movedBlocks.size() > 12) {
|
||||
wireframe.setBlock(Material.YELLOW_CONCRETE.createBlockData());
|
||||
} else {
|
||||
wireframe.setBlock(Material.LIME_CONCRETE.createBlockData());
|
||||
}
|
||||
wireframe.setWidth(1 / 32f);
|
||||
|
||||
RTextDisplay textDisplay = new RTextDisplay(server, piston.getLocation().clone().add(0.5, 0.3, 0.5));
|
||||
StringBuilder text = new StringBuilder();
|
||||
if (pulling) {
|
||||
text.append("§ePull\n");
|
||||
} else {
|
||||
text.append("§ePush\n");
|
||||
}
|
||||
text.append("§f").append(movedBlocks.size()).append("§eB");
|
||||
textDisplay.setBillboard(Display.Billboard.CENTER);
|
||||
textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER);
|
||||
textDisplay.setSeeThrough(true);
|
||||
textDisplay.setBackgroundColor(0);
|
||||
textDisplay.setShadowed(false);
|
||||
textDisplay.setBrightness(new Display.Brightness(15, 15));
|
||||
}
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), entityServer::close, 20);
|
||||
return new CalculationResult(blockSet.size(), blockSet.size() > 12, !unmovable.isEmpty(), entityServer);
|
||||
}
|
||||
|
||||
private void calcDirection(Block origin, Block blockOrigin, Block calcOrigin, Block ignore, BlockFace facing, BlockFace direction, Set<Block> blockSet, List<Block> toCalc, Set<Location> unmovable) {
|
||||
for (int i = 1; i < 24; i++) {
|
||||
Block block = calcOrigin.getRelative(direction, i);
|
||||
if (block.equals(ignore)) return;
|
||||
if (block.getPistonMoveReaction() == PistonMoveReaction.BREAK) return;
|
||||
if (!isPiston(block) && (block.getPistonMoveReaction() == PistonMoveReaction.BLOCK || block.getPistonMoveReaction() == PistonMoveReaction.IGNORE || block.getState() instanceof TileState)) {
|
||||
unmovable.add(block.getLocation());
|
||||
return;
|
||||
}
|
||||
if (block.getType().isAir()) return;
|
||||
if (facing == direction && block.equals(blockOrigin)) {
|
||||
unmovable.add(block.getLocation());
|
||||
return;
|
||||
}
|
||||
if (facing != direction && (block.equals(origin) || block.getRelative(facing.getOppositeFace()).equals(origin))) return;
|
||||
if (!blockSet.contains(block)) toCalc.add(block);
|
||||
private boolean isImmovable(Block block) {
|
||||
BlockData blockData = block.getBlockData();
|
||||
if (blockData instanceof Piston) {
|
||||
return ((Piston) blockData).isExtended();
|
||||
}
|
||||
if (block.getState() instanceof TileState) {
|
||||
return true;
|
||||
}
|
||||
PistonMoveReaction reaction = block.getPistonMoveReaction();
|
||||
if (reaction == PistonMoveReaction.IGNORE) return true;
|
||||
if (reaction == PistonMoveReaction.BLOCK) return true;
|
||||
switch (block.getType()) {
|
||||
case OBSIDIAN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,13 +317,4 @@ public class PistonCalculator implements Listener {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
private static class CalculationResult {
|
||||
private int amount;
|
||||
private boolean tooMany;
|
||||
private boolean unmovable;
|
||||
private REntityServer entityServer;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -22,9 +22,11 @@ package de.steamwar.bausystem.features.util;
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.command.SWCommand;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.linkage.MinVersion;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@Linked
|
||||
@MinVersion(20)
|
||||
public class PistonCalculatorCommand extends SWCommand {
|
||||
|
||||
public PistonCalculatorCommand() {
|
||||
|
||||
@@ -189,6 +189,7 @@ public class REntityServer implements Listener {
|
||||
private void removeEntityFromChunk(REntity entity) {
|
||||
long id = entityToId(entity);
|
||||
HashSet<REntity> entitiesInChunk = entities.get(id);
|
||||
if (entitiesInChunk == null) return;
|
||||
entitiesInChunk.remove(entity);
|
||||
if(entitiesInChunk.isEmpty())
|
||||
entities.remove(id);
|
||||
|
||||
@@ -47,7 +47,7 @@ public class RTextDisplay extends RDisplay {
|
||||
|
||||
private boolean seeThrough;
|
||||
|
||||
private boolean defaultBackground;
|
||||
private Integer backgroundColor;
|
||||
|
||||
private TextDisplay.TextAlignment alignment;
|
||||
|
||||
@@ -58,7 +58,7 @@ public class RTextDisplay extends RDisplay {
|
||||
this.textOpacity = (byte) -1;
|
||||
this.shadowed = false;
|
||||
this.seeThrough = false;
|
||||
this.defaultBackground = false;
|
||||
this.backgroundColor = null;
|
||||
this.alignment = TextDisplay.TextAlignment.CENTER;
|
||||
server.addEntity(this);
|
||||
}
|
||||
@@ -116,8 +116,22 @@ public class RTextDisplay extends RDisplay {
|
||||
sendPacket(updatePacketSink, this::getTextStatus);
|
||||
}
|
||||
|
||||
public void setBackgroundColor(int color) {
|
||||
this.backgroundColor = color;
|
||||
sendPacket(updatePacketSink, this::getTextStatus, this::getBackgroundColor);
|
||||
}
|
||||
|
||||
private static final Object backgroundColorWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 25 : 24, Integer.class);
|
||||
private void getBackgroundColor(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
|
||||
if (ignoreDefault || backgroundColor != null) {
|
||||
packetSink.accept(backgroundColorWatcher, backgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultBackground(boolean defaultBackground) {
|
||||
this.defaultBackground = defaultBackground;
|
||||
if (defaultBackground) {
|
||||
this.backgroundColor = null;
|
||||
}
|
||||
sendPacket(updatePacketSink, this::getTextStatus);
|
||||
}
|
||||
|
||||
@@ -136,7 +150,7 @@ public class RTextDisplay extends RDisplay {
|
||||
if (seeThrough) {
|
||||
status |= 0x02;
|
||||
}
|
||||
if (defaultBackground) {
|
||||
if (backgroundColor == null) {
|
||||
status |= 0x04;
|
||||
}
|
||||
if (alignment == TextDisplay.TextAlignment.CENTER) {
|
||||
|
||||
Reference in New Issue
Block a user