forked from SteamWar/SteamWar
Move Hull and generic HullHider into HiderCore
Hull no longer depends on FightSystem: plugin, world, region, ground visibility and hidden blocks are passed in. HullRegion is the common block box hulls work on. The generic HullHider<K> manages one hull per key and does the block, entity and REntity tracking; FightHullHider keeps the fight specific parts (team hulls, viewer rules, state dependent registration). Entities are now checked against the hull with block coordinates, so entities in the bottom layer and at the max z edge are handled correctly.
This commit is contained in:
@@ -32,8 +32,8 @@ import de.steamwar.fightsystem.record.GlobalRecorder;
|
||||
import de.steamwar.fightsystem.states.FightState;
|
||||
import de.steamwar.fightsystem.states.OneShotStateDependent;
|
||||
import de.steamwar.fightsystem.states.StateDependentListener;
|
||||
import de.steamwar.fightsystem.utils.FightHullHider;
|
||||
import de.steamwar.fightsystem.utils.FightUI;
|
||||
import de.steamwar.fightsystem.utils.HullHider;
|
||||
import de.steamwar.fightsystem.utils.SWSound;
|
||||
import de.steamwar.fightsystem.utils.TechHiderWrapper;
|
||||
import de.steamwar.linkage.AbstractLinker;
|
||||
@@ -58,7 +58,7 @@ public class FightSystem extends JavaPlugin {
|
||||
private FightTeam lastWinner;
|
||||
private String lastWinreason;
|
||||
private TechHiderWrapper techHider;
|
||||
private HullHider hullHider;
|
||||
private FightHullHider hullHider;
|
||||
|
||||
private SpigotLinker spigotLinker;
|
||||
|
||||
@@ -111,9 +111,9 @@ public class FightSystem extends JavaPlugin {
|
||||
new OneShotStateDependent(ArenaMode.Test, FightState.All, SteamEdit::init);
|
||||
Config.world.setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode));
|
||||
|
||||
hullHider = new HullHider();
|
||||
hullHider = new FightHullHider();
|
||||
techHider = new TechHiderWrapper(hullHider);
|
||||
FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true));
|
||||
hullHider.fillAll(true);
|
||||
|
||||
|
||||
FileSource.startReplay();
|
||||
@@ -165,7 +165,7 @@ public class FightSystem extends JavaPlugin {
|
||||
return plugin.techHider;
|
||||
}
|
||||
|
||||
public static HullHider getHullHider() {
|
||||
public static FightHullHider getHullHider() {
|
||||
return plugin.hullHider;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ public class TechhiderbugCommand extends FightSWCommand {
|
||||
writer.append('\n');
|
||||
|
||||
writer.append("HullHider regions: ");
|
||||
FightSystem.getHullHider().getHullMap().forEach((t, h) -> writer.append(t.getName()).append(':').append(h.getRegion().toString()).append(' '));
|
||||
FightSystem.getHullHider().forEachHull((t, h) -> writer.append(t.getName()).append(':').append(h.getRegion().toString()).append(' '));
|
||||
writer.append('\n');
|
||||
|
||||
writer.append("Hidden regions: ");
|
||||
|
||||
@@ -84,7 +84,7 @@ public class FightWorld extends StateDependent {
|
||||
entities.forEach(Entity::remove);
|
||||
entities.clear();
|
||||
|
||||
FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true));
|
||||
FightSystem.getHullHider().fillAll(true);
|
||||
|
||||
Config.ArenaRegion.forEachChunk((x, z) -> {
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
|
||||
+34
-85
@@ -30,7 +30,8 @@ import de.steamwar.fightsystem.listener.Recording;
|
||||
import de.steamwar.fightsystem.states.FightState;
|
||||
import de.steamwar.fightsystem.states.StateDependentListener;
|
||||
import de.steamwar.fightsystem.states.StateDependentTask;
|
||||
import lombok.Getter;
|
||||
import de.steamwar.hullhider.Hull;
|
||||
import de.steamwar.hullhider.HullHider;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
@@ -38,43 +39,38 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
import org.bukkit.event.entity.EntitySpawnEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class HullHider implements Listener {
|
||||
/**
|
||||
* Fight specific part of the hull hider: one hull per team schematic region, and who may view which hull.
|
||||
*/
|
||||
public class FightHullHider {
|
||||
|
||||
@Getter
|
||||
private final Map<FightTeam, Hull> hullMap = new HashMap<>();
|
||||
private final Hull[] hulls;
|
||||
private final HullHider<FightTeam> hullHider = new HullHider<>();
|
||||
|
||||
public HullHider() {
|
||||
if (!TechHiderWrapper.ENABLED) {
|
||||
hulls = new Hull[0];
|
||||
return;
|
||||
}
|
||||
|
||||
Fight.teams().forEach(team -> hullMap.put(team, new Hull(team)));
|
||||
hulls = hullMap.values().toArray(new Hull[0]);
|
||||
public FightHullHider() {
|
||||
if (!TechHiderWrapper.ENABLED) return;
|
||||
|
||||
Fight.teams().forEach(team -> {
|
||||
Region region = team.getSchemRegion();
|
||||
hullHider.addHull(team, new Hull(FightSystem.getPlugin(), Config.world, region.toHullRegion(), region.getMinY() != Config.PlayerRegion.getMinY(), Config.GameModeConfig.Techhider.HiddenBlocks));
|
||||
});
|
||||
|
||||
new StateDependentListener(TechHiderWrapper.ENABLED, FightState.All, new MembershipListener());
|
||||
new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, this);
|
||||
new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, hullHider);
|
||||
new StateDependentTask(TechHiderWrapper.ENABLED, FightState.Schem, this::onTick, 0, 1);
|
||||
}
|
||||
|
||||
public void initialize(FightTeam team) {
|
||||
if (!TechHiderWrapper.ENABLED) return;
|
||||
Hull hull = hullMap.get(team);
|
||||
hull.initialize();
|
||||
hullHider.initialize(team);
|
||||
|
||||
// Chunks encoded while the hull was filled as hidden contain obfuscated blocks that are visible now, but no block update is generated for them
|
||||
Hull hull = hullHider.getHull(team);
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!hull.isViewer(player)) {
|
||||
FightSystem.getTechHider().reloadChunks(player, team.getExtendRegion(), Region.EMPTY);
|
||||
@@ -84,7 +80,15 @@ public class HullHider implements Listener {
|
||||
|
||||
public void fill(FightTeam team, boolean visible) {
|
||||
if (!TechHiderWrapper.ENABLED) return;
|
||||
hullMap.get(team).fill(visible);
|
||||
hullHider.fill(team, visible);
|
||||
}
|
||||
|
||||
public void fillAll(boolean visible) {
|
||||
hullHider.fillAll(visible);
|
||||
}
|
||||
|
||||
public void forEachHull(BiConsumer<FightTeam, Hull> consumer) {
|
||||
hullHider.forEach(consumer);
|
||||
}
|
||||
|
||||
// Registered in every state: players must be hidden from the hulls by default, no matter when they join.
|
||||
@@ -96,7 +100,7 @@ public class HullHider implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onLeave(PlayerQuitEvent e) {
|
||||
forgetViewer(e.getPlayer());
|
||||
hullHider.forgetViewer(e.getPlayer());
|
||||
}
|
||||
|
||||
// A dead player loses the right to see the enemy hull again
|
||||
@@ -114,82 +118,27 @@ public class HullHider implements Listener {
|
||||
|
||||
FightTeam team = Fight.getPlayerTeam(player);
|
||||
FightPlayer fp = Fight.getFightPlayer(player);
|
||||
for (Map.Entry<FightTeam, Hull> hull : hullMap.entrySet()) {
|
||||
if (Config.isReferee(player) || hull.getKey() == team || (fp != null && fp.canEntern())) {
|
||||
hull.getValue().addViewer(player);
|
||||
} else {
|
||||
hull.getValue().removeViewer(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void forgetViewer(Player player) {
|
||||
for (Hull hull : hulls) {
|
||||
hull.forgetViewer(player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onBlockPhysic(BlockPhysicsEvent e) {
|
||||
if (e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR) {
|
||||
blockUpdate(e.getBlock(), e.getChangedType());
|
||||
}
|
||||
hullHider.forEach((hullTeam, hull) -> hullHider.setViewer(player, hullTeam, Config.isReferee(player) || hullTeam == team || (fp != null && fp.canEntern())));
|
||||
}
|
||||
|
||||
public void blockUpdate(Block block, Material changedType) {
|
||||
for (Hull hull : hulls) {
|
||||
hull.updateBlockVisibility(block, changedType);
|
||||
}
|
||||
hullHider.blockUpdate(block, changedType);
|
||||
}
|
||||
|
||||
public boolean isBlockHidden(Player player, int x, int y, int z) {
|
||||
if (!TechHiderWrapper.ENABLED) return false;
|
||||
|
||||
for (Hull hull : hulls) {
|
||||
if (hull.isBlockHidden(player, x, y, z)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onSpawn(EntitySpawnEvent e) {
|
||||
for (Hull hull : hulls) {
|
||||
hull.checkEntity(e.getEntity());
|
||||
}
|
||||
return hullHider.isBlockHidden(player, x, y, z);
|
||||
}
|
||||
|
||||
private void onTick() {
|
||||
Recording.iterateOverEntities(Objects::nonNull, entity -> {
|
||||
for (Hull hull : hulls) {
|
||||
hull.checkEntity(entity);
|
||||
}
|
||||
});
|
||||
|
||||
for (Hull hull : hulls) {
|
||||
hull.sendUncoveredBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onDeath(EntityDeathEvent e) {
|
||||
for (Hull hull : hulls) {
|
||||
hull.removeEntity(e.getEntity());
|
||||
}
|
||||
Recording.iterateOverEntities(Objects::nonNull, hullHider::checkEntity);
|
||||
hullHider.sendUncoveredBlocks();
|
||||
}
|
||||
|
||||
public void updateREntity(REntity e) {
|
||||
for (Hull hull : hulls) {
|
||||
hull.checkREntity(e);
|
||||
}
|
||||
hullHider.updateREntity(e);
|
||||
}
|
||||
|
||||
public void despawnREntity(REntity e) {
|
||||
for (Hull hull : hulls) {
|
||||
hull.removeREntity(e);
|
||||
}
|
||||
hullHider.despawnREntity(e);
|
||||
}
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2025 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.fightsystem.utils;
|
||||
|
||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import de.steamwar.core.ProtocolWrapper;
|
||||
import de.steamwar.entity.REntity;
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import de.steamwar.fightsystem.FightSystem;
|
||||
import de.steamwar.fightsystem.fight.FightTeam;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class Hull {
|
||||
|
||||
private static final IntVector[] NO_BRANCHES = new IntVector[0];
|
||||
|
||||
private static boolean isOccluding(Material material) {
|
||||
return material.isOccluding() || Config.GameModeConfig.Techhider.HiddenBlocks.contains(material);
|
||||
}
|
||||
|
||||
@Getter
|
||||
private final Region region;
|
||||
private final boolean groundVisible;
|
||||
private final IntVector[] directions;
|
||||
|
||||
private final BitSet occluding;
|
||||
private final BitSet visibility;
|
||||
private final HashMap<IntVector, BitSet> visibilityDirections = new HashMap<>(); // Contains the visible directions of each occluding visible block
|
||||
private final HashSet<IntVector> uncoveredSurface = new HashSet<>();
|
||||
|
||||
// Players that may see the hull contents (viewers). Everybody else is hidden from it (default deny). Concurrent as it is read on netty threads.
|
||||
private final Set<UUID> viewers = ConcurrentHashMap.newKeySet();
|
||||
private final HashSet<Entity> entities = new HashSet<>();
|
||||
private final HashSet<REntity> rentities = new HashSet<>();
|
||||
|
||||
public Hull(FightTeam team) {
|
||||
this.region = team.getSchemRegion();
|
||||
this.groundVisible = region.getMinY() != Config.PlayerRegion.getMinY();
|
||||
this.occluding = new BitSet(region.volume());
|
||||
this.visibility = new BitSet(region.volume());
|
||||
|
||||
directions = IntStream.range(0, 27)
|
||||
.mapToObj(v -> new IntVector(v % 3 - 1, (v / 3) % 3 - 1, v / 9 - 1))
|
||||
.toArray(IntVector[]::new);
|
||||
|
||||
for (IntVector direction : directions) {
|
||||
visibilityDirections.put(direction, new BitSet(region.volume()));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBlockHidden(Player player, int x, int y, int z) {
|
||||
return region.inRegion(x, y, z) && !isViewer(player) && !visibility.get(((y - region.getMinY()) * region.getSizeZ() + (z - region.getMinZ())) * region.getSizeX() + (x - region.getMinX()));
|
||||
}
|
||||
|
||||
public boolean isViewer(Player player) {
|
||||
return player != null && viewers.contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the hull from this player (removes the viewer), also hiding all entities currently inside it.
|
||||
*/
|
||||
public void removeViewer(Player player) {
|
||||
viewers.remove(player.getUniqueId());
|
||||
for (Entity entity : entities) {
|
||||
if (!player.equals(entity)) untrackEntity(player, entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows this player to see the hull (adds a viewer).
|
||||
*/
|
||||
public void addViewer(Player player) {
|
||||
if (viewers.add(player.getUniqueId())) {
|
||||
for (Entity entity : entities) {
|
||||
trackEntity(player, entity);
|
||||
}
|
||||
// techhider triggers block change sending
|
||||
}
|
||||
}
|
||||
|
||||
public void forgetViewer(Player player) {
|
||||
viewers.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void checkEntity(Entity entity) {
|
||||
Location location = entity.getLocation();
|
||||
if (region.inRegion(location) && !visibility.get(new IntVector(location).toId(region))) {
|
||||
if (entities.add(entity)) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!isViewer(player) && !player.equals(entity)) untrackEntity(player, entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (entities.remove(entity)) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!isViewer(player) && !player.equals(entity)) trackEntity(player, entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void trackEntity(Player player, Entity entity) {
|
||||
if (entity instanceof Player) {
|
||||
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE));
|
||||
}
|
||||
|
||||
player.showEntity(FightSystem.getPlugin(), entity);
|
||||
}
|
||||
|
||||
public void untrackEntity(Player player, Entity entity) {
|
||||
player.hideEntity(FightSystem.getPlugin(), entity);
|
||||
|
||||
if (entity instanceof Player) {
|
||||
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE));
|
||||
}
|
||||
}
|
||||
|
||||
public void removeEntity(Entity entity) {
|
||||
entities.remove(entity);
|
||||
}
|
||||
|
||||
public void checkREntity(REntity entity) {
|
||||
Location location = new Location(Config.world, entity.getX(), entity.getY(), entity.getZ());
|
||||
if (region.inRegion(location) && !visibility.get(new IntVector(location).toId(region))) {
|
||||
if (rentities.add(entity)) entity.hide(true);
|
||||
} else {
|
||||
if (rentities.remove(entity)) entity.hide(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeREntity(REntity entity) {
|
||||
rentities.remove(entity);
|
||||
}
|
||||
|
||||
public void fill(boolean visible) {
|
||||
visibility.set(0, visibility.size(), visible);
|
||||
occluding.set(0, occluding.size(), !visible);
|
||||
uncoveredSurface.clear();
|
||||
for (BitSet directionalVisibility : visibilityDirections.values()) {
|
||||
directionalVisibility.set(0, directionalVisibility.size(), visible);
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
visibility.clear();
|
||||
occluding.clear();
|
||||
uncoveredSurface.clear();
|
||||
for (BitSet directionalVisibility : visibilityDirections.values()) {
|
||||
directionalVisibility.clear();
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
region.forEach((x, y, z) -> {
|
||||
IntVector block = new IntVector(x, y, z);
|
||||
if (isOccluding(Config.world.getBlockAt(x, y, z).getType())) {
|
||||
occluding.set(block.toId(region));
|
||||
}
|
||||
});
|
||||
forEachBorder((root, direction) -> uncoverBlocks(new NullList<>(), root, direction));
|
||||
FightSystem.getPlugin().getLogger().log(Level.INFO, () -> "[HullHider] initialisation finished: " + (System.currentTimeMillis() - start) + " ms, visible blocks: " + visibility.cardinality());
|
||||
}
|
||||
|
||||
public void updateBlockVisibility(Block b, Material changedType) {
|
||||
IntVector root = new IntVector(b.getX(), b.getY(), b.getZ());
|
||||
if (root.notInRegion(region)) return;
|
||||
|
||||
int id = root.toId(region);
|
||||
if (!occluding.get(id) || isOccluding(changedType)) return;
|
||||
|
||||
occluding.clear(id);
|
||||
if (!visibility.get(id)) return;
|
||||
|
||||
List<IntVector> uncovered = new ArrayList<>();
|
||||
for (Map.Entry<IntVector, BitSet> directionalVisibility : visibilityDirections.entrySet()) {
|
||||
if (directionalVisibility.getValue().get(id)) {
|
||||
directionalVisibility.getValue().clear(id);
|
||||
uncoverBlocks(uncovered, root, directionalVisibility.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
uncoveredSurface.addAll(new HashSet<>(uncovered));
|
||||
uncoveredSurface.remove(root);
|
||||
}
|
||||
|
||||
public void sendUncoveredBlocks() {
|
||||
Map<IntVector, List<IntVector>> sectionWise = new HashMap<>();
|
||||
|
||||
for (IntVector uncovered : uncoveredSurface) {
|
||||
sectionWise.computeIfAbsent(new IntVector(uncovered.x >> 4, uncovered.y >> 4, uncovered.z >> 4), section -> new ArrayList<>()).add(uncovered);
|
||||
}
|
||||
uncoveredSurface.clear();
|
||||
|
||||
for (Map.Entry<IntVector, List<IntVector>> entry : sectionWise.entrySet()) {
|
||||
Object result;
|
||||
List<IntVector> changes = entry.getValue();
|
||||
Object[] blockdata = new Object[changes.size()];
|
||||
for (int i = 0; i < blockdata.length; i++) {
|
||||
IntVector change = changes.get(i);
|
||||
blockdata[i] = ((CraftBlockData) Config.world.getBlockData(change.getX(), change.getY(), change.getZ())).getState();
|
||||
}
|
||||
|
||||
if (changes.size() > 1) {
|
||||
IntVector section = changes.get(0);
|
||||
section = new IntVector(section.getX() >> 4, section.getY() >> 4, section.getZ() >> 4);
|
||||
int xOffset = 16 * section.getX();
|
||||
int yOffset = 16 * section.getY();
|
||||
int zOffset = 16 * section.getZ();
|
||||
|
||||
short[] pos = new short[changes.size()];
|
||||
for (int i = 0; i < changes.size(); i++) {
|
||||
IntVector change = changes.get(i);
|
||||
|
||||
pos[i] = (short) (((change.getX() - xOffset) << 8) + ((change.getZ() - zOffset) << 4) + (change.getY() - yOffset));
|
||||
}
|
||||
|
||||
result = new ClientboundSectionBlocksUpdatePacket(SectionPos.of(section.getX(), section.getY(), section.getZ()), new Short2ObjectArrayMap<>(pos, blockdata, blockdata.length));
|
||||
} else {
|
||||
IntVector pos = changes.get(0);
|
||||
result = new ClientboundBlockUpdatePacket(new BlockPos(pos.getX(), pos.getY(), pos.getZ()), (BlockState) blockdata[0]);
|
||||
}
|
||||
Object packet = result;
|
||||
Bukkit.getOnlinePlayers().stream().filter(player -> !isViewer(player)).forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
|
||||
}
|
||||
}
|
||||
|
||||
private void forEachBorder(BiConsumer<IntVector, IntVector> f) {
|
||||
for (int x = region.getMinX(); x < region.getMaxX(); x++) {
|
||||
for (int z = region.getMinZ(); z < region.getMaxZ(); z++) {
|
||||
if (groundVisible) {
|
||||
f.accept(new IntVector(x, region.getMinY(), z), new IntVector(0, 1, 0));
|
||||
}
|
||||
f.accept(new IntVector(x, region.getMaxY() - 1, z), new IntVector(0, -1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = region.getMinX(); x < region.getMaxX(); x++) {
|
||||
for (int y = region.getMinY(); y < region.getMaxY(); y++) {
|
||||
f.accept(new IntVector(x, y, region.getMinZ()), new IntVector(0, 0, 1));
|
||||
f.accept(new IntVector(x, y, region.getMaxZ() - 1), new IntVector(0, 0, -1));
|
||||
}
|
||||
}
|
||||
|
||||
for (int z = region.getMinZ(); z < region.getMaxZ(); z++) {
|
||||
for (int y = region.getMinY(); y < region.getMaxY(); y++) {
|
||||
f.accept(new IntVector(region.getMinX(), y, z), new IntVector(1, 0, 0));
|
||||
f.accept(new IntVector(region.getMaxX() - 1, y, z), new IntVector(-1, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void uncoverBlocks(List<IntVector> uncovered, IntVector block, IntVector direction) {
|
||||
uncoverBlocks(
|
||||
uncovered, block, direction, visibilityDirections.get(direction),
|
||||
direction.sqLength() == 1
|
||||
? Arrays.stream(directions)
|
||||
.filter(v -> v.sqLength() >= 2 && v.x * direction.x + v.y * direction.y + v.z * direction.z == 1)
|
||||
.toArray(IntVector[]::new)
|
||||
: NO_BRANCHES
|
||||
);
|
||||
}
|
||||
|
||||
private void uncoverBlocks(List<IntVector> uncovered, IntVector block, IntVector direction, BitSet directionalVisibility, IntVector[] branchDirections) {
|
||||
if (block.notInRegion(region)) return;
|
||||
|
||||
int id = block.toId(region);
|
||||
if (directionalVisibility.get(id)) return;
|
||||
directionalVisibility.set(id);
|
||||
|
||||
if (!visibility.get(id)) {
|
||||
visibility.set(id);
|
||||
uncovered.add(block);
|
||||
}
|
||||
|
||||
if (occluding.get(id)) return;
|
||||
|
||||
uncoverBlocks(uncovered, block.add(direction), direction, directionalVisibility, branchDirections);
|
||||
for (IntVector branchDirection : branchDirections) {
|
||||
uncoverBlocks(uncovered, block.add(branchDirection), branchDirection, visibilityDirections.get(branchDirection), NO_BRANCHES);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class IntVector {
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int z;
|
||||
|
||||
public IntVector(Location location) {
|
||||
this.x = location.getBlockX();
|
||||
this.y = location.getBlockY();
|
||||
this.z = location.getBlockZ();
|
||||
}
|
||||
|
||||
public boolean notInRegion(Region region) {
|
||||
return !region.inRegion(x, y, z);
|
||||
}
|
||||
|
||||
public int toId(Region region) {
|
||||
return ((y - region.getMinY()) * region.getSizeZ() + (z - region.getMinZ())) * region.getSizeX() + (x - region.getMinX());
|
||||
}
|
||||
|
||||
public IntVector add(int x, int y, int z) {
|
||||
return new IntVector(this.x + x, this.y + y, this.z + z);
|
||||
}
|
||||
|
||||
public IntVector add(IntVector v) {
|
||||
return add(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
public int sqLength() {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return y << 24 ^ x << 12 ^ z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || this.getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IntVector v = (IntVector) o;
|
||||
return x == v.x && y == v.y && z == v.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return x + "," + y + "," + z;
|
||||
}
|
||||
}
|
||||
|
||||
private static class NullList<E> extends AbstractList<E> {
|
||||
@Override
|
||||
public void add(int index, E element) {
|
||||
// Straight to /dev/null!
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
package de.steamwar.fightsystem.utils;
|
||||
|
||||
import de.steamwar.core.ProtocolUtils;
|
||||
import de.steamwar.hullhider.HullRegion;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Location;
|
||||
@@ -155,6 +156,10 @@ public class Region {
|
||||
return in2dRegion(x, z) && minY <= y && y < maxY;
|
||||
}
|
||||
|
||||
public HullRegion toHullRegion() {
|
||||
return new HullRegion(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
public Region intersection(Region other) {
|
||||
int x = Math.max(minX, other.minX);
|
||||
int y = Math.max(minY, other.minY);
|
||||
|
||||
+2
-2
@@ -67,9 +67,9 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
|
||||
@Getter
|
||||
private final ConcurrentHashMap<Player, Region[]> hiddenRegion = new ConcurrentHashMap<>();
|
||||
private final HullHider hullHider;
|
||||
private final FightHullHider hullHider;
|
||||
|
||||
public TechHiderWrapper(HullHider hullHider) {
|
||||
public TechHiderWrapper(FightHullHider hullHider) {
|
||||
super(ENABLED, FightState.All);
|
||||
|
||||
this.hullHider = hullHider;
|
||||
|
||||
Reference in New Issue
Block a user