Allow removing hulls and resend their chunks

HullHider#removeHull stops hiding a hull: it shows the entities and REntities
the hull hid and resends its chunks to everybody who could not view it. A hull
is enabled again by adding and initialising it.

Hull#resendChunks only resends chunks the player has loaded. HullHider now
resends the chunks to non-viewers after initialising a hull itself, so
FightHullHider no longer does it.
This commit is contained in:
2026-10-31 15:09:04 +01:00
parent ce2a23f4b8
commit d1fb4ca67e
4 changed files with 82 additions and 10 deletions
@@ -32,7 +32,6 @@ import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.states.StateDependentTask;
import de.steamwar.hullhider.Hull;
import de.steamwar.hullhider.HullHider;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
@@ -68,14 +67,6 @@ public class FightHullHider {
public void initialize(FightTeam team) {
if (!TechHiderWrapper.ENABLED) return;
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);
}
}
}
public void fill(FightTeam team, boolean visible) {
@@ -21,6 +21,7 @@ package de.steamwar.hullhider;
import com.comphenix.tinyprotocol.TinyProtocol;
import com.mojang.authlib.GameProfile;
import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.core.ProtocolWrapper;
import de.steamwar.entity.REntity;
import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap;
@@ -32,6 +33,7 @@ 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.Chunk;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -180,6 +182,38 @@ public class Hull {
rentities.remove(entity);
}
/**
* Shows all entities hidden by this hull again, used when the hull is removed.
*/
public void reveal() {
for (Player player : Bukkit.getOnlinePlayers()) {
if (isViewer(player)) continue;
for (Entity entity : entities) {
if (!player.equals(entity)) trackEntity(player, entity);
}
}
entities.clear();
for (REntity entity : rentities) {
entity.hide(false);
}
rentities.clear();
}
/**
* Resends the chunks of this hull the player has loaded, so they are encoded with the current visibility.
*/
public void resendChunks(Player player) {
if (player.getWorld() != world) return;
region.forEachChunk((chunkX, chunkZ) -> {
if (player.isChunkSent(Chunk.getChunkKey(chunkX, chunkZ))) {
CraftbukkitWrapper.sendChunk(player, chunkX, chunkZ);
}
});
}
public void fill(boolean visible) {
visibility.set(0, visibility.size(), visible);
occluding.set(0, occluding.size(), !visible);
@@ -20,6 +20,7 @@
package de.steamwar.hullhider;
import de.steamwar.entity.REntity;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
@@ -45,11 +46,30 @@ public class HullHider<K> implements Listener {
// Read on netty threads, replaced on every change
private volatile Hull[] hulls = new Hull[0];
/**
* The hull hides everything from non-viewers until it is initialised.
*/
public void addHull(K key, Hull hull) {
hullMap.put(key, hull);
hulls = hullMap.values().toArray(new Hull[0]);
}
/**
* Stops hiding the hull: shows the entities it hid and resends its chunks to everybody who could not view it.
*/
public void removeHull(K key) {
Hull hull = hullMap.remove(key);
if (hull == null) return;
hulls = hullMap.values().toArray(new Hull[0]);
hull.reveal();
for (Player player : Bukkit.getOnlinePlayers()) {
if (!hull.isViewer(player)) {
hull.resendChunks(player);
}
}
}
public Hull getHull(K key) {
return hullMap.get(key);
}
@@ -59,7 +79,22 @@ public class HullHider<K> implements Listener {
}
public void initialize(K key) {
hullMap.get(key).initialize();
Hull hull = hullMap.get(key);
hull.initialize();
// Chunks encoded while the hull was filled as hidden contain obfuscated blocks that are visible now, but no block update is generated for them
for (Player player : Bukkit.getOnlinePlayers()) {
if (!hull.isViewer(player)) {
hull.resendChunks(player);
}
}
}
/**
* Has to be called after the player became a viewer of the hull or stopped being one.
*/
public void resendChunks(Player player, K key) {
hullMap.get(key).resendChunks(player);
}
public void fill(K key, boolean visible) {
@@ -90,10 +90,22 @@ public final class HullRegion {
}
}
public void forEachChunk(ChunkConsumer executor) {
for (int chunkX = minX >> 4; chunkX <= (maxX - 1) >> 4; chunkX++) {
for (int chunkZ = minZ >> 4; chunkZ <= (maxZ - 1) >> 4; chunkZ++) {
executor.accept(chunkX, chunkZ);
}
}
}
public interface BlockConsumer {
void accept(int x, int y, int z);
}
public interface ChunkConsumer {
void accept(int chunkX, int chunkZ);
}
@Override
public String toString() {
return minX + "," + minY + "," + minZ + "->" + maxX + "," + maxY + "," + maxZ;