forked from SteamWar/SteamWar
Add /hullhider to BauSystem
/hullhider hides the inside of the build area hull of a region from the players who toggle it, like the hull hider of a fight. Each region gets its own hull while somebody uses it; it is rebuilt every time somebody enables it, as WorldEdit and Axiom changes do not update it. /techhider, /xray and /hullhider exclude each other per player and region. Which players may see the hull contents is now decided by Viewers, a sealed class with HiddenByDefault (fight) and VisibleByDefault (bau). Added players are stored in a set; isViewer alone defines what being added means.
This commit is contained in:
@@ -37,6 +37,7 @@ SCOREBOARD_TPS_FROZEN = §eFrozen
|
|||||||
SCOREBOARD_TRACE_TICKS = Ticks
|
SCOREBOARD_TRACE_TICKS = Ticks
|
||||||
SCOREBOARD_TECHHIDER = TechHider§8: §aOn
|
SCOREBOARD_TECHHIDER = TechHider§8: §aOn
|
||||||
SCOREBOARD_XRAY = XRay§8: §aOn
|
SCOREBOARD_XRAY = XRay§8: §aOn
|
||||||
|
SCOREBOARD_HULLHIDER = HullHider§8: §aOn
|
||||||
SCOREBOARD_LOCK_TEAM = Bau Lock§8: §eTeam
|
SCOREBOARD_LOCK_TEAM = Bau Lock§8: §eTeam
|
||||||
SCOREBOARD_LOCK_SUPERVISOR = Bau Lock§8: §eSupervisor
|
SCOREBOARD_LOCK_SUPERVISOR = Bau Lock§8: §eSupervisor
|
||||||
SCOREBOARD_LOCK_TEAM_AND_SERVERTEAM = Bau Lock§8: §e(Server) Team
|
SCOREBOARD_LOCK_TEAM_AND_SERVERTEAM = Bau Lock§8: §e(Server) Team
|
||||||
@@ -1033,6 +1034,11 @@ XRAY_HELP = §8/§exray §8- §7Toggle Xray
|
|||||||
XRAY_GLOBAL = §cNo xray in global region
|
XRAY_GLOBAL = §cNo xray in global region
|
||||||
XRAY_ON = §aXray activated
|
XRAY_ON = §aXray activated
|
||||||
XRAY_OFF = §cXray deactivated
|
XRAY_OFF = §cXray deactivated
|
||||||
|
# HULLHIDER
|
||||||
|
HULLHIDER_HELP = §8/§ehullhider §8- §7Toggle Hullhider
|
||||||
|
HULLHIDER_GLOBAL = §cNo hullhider in global region
|
||||||
|
HULLHIDER_ON = §aHullhider activated
|
||||||
|
HULLHIDER_OFF = §cHullhider deactivated
|
||||||
# WorldEdit
|
# WorldEdit
|
||||||
COLORREPLACE_HELP = §8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §7Replace all blocks of one color with another
|
COLORREPLACE_HELP = §8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §7Replace all blocks of one color with another
|
||||||
TYPEREPLACE_HELP = §8//§etypereplace §8[§7type§8] §8[§7type§8] §8- §7Replace all blocks of one type with another
|
TYPEREPLACE_HELP = §8//§etypereplace §8[§7type§8] §8[§7type§8] §8- §7Replace all blocks of one type with another
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ SCOREBOARD_TPS_FROZEN = §eEingefroren
|
|||||||
SCOREBOARD_TRACE_TICKS = Ticks
|
SCOREBOARD_TRACE_TICKS = Ticks
|
||||||
SCOREBOARD_TECHHIDER = TechHider§8: §aAn
|
SCOREBOARD_TECHHIDER = TechHider§8: §aAn
|
||||||
SCOREBOARD_XRAY = XRay§8: §aAn
|
SCOREBOARD_XRAY = XRay§8: §aAn
|
||||||
|
SCOREBOARD_HULLHIDER = HullHider§8: §aAn
|
||||||
SCOREBOARD_LOCK_TEAM = Bau Lock§8: §eTeam
|
SCOREBOARD_LOCK_TEAM = Bau Lock§8: §eTeam
|
||||||
SCOREBOARD_LOCK_TEAM_AND_SERVERTEAM = Bau Lock§8: §e(Server-) Team
|
SCOREBOARD_LOCK_TEAM_AND_SERVERTEAM = Bau Lock§8: §e(Server-) Team
|
||||||
SCOREBOARD_LOCK_SERVERTEAM = Bau Lock§8: §eServerteam
|
SCOREBOARD_LOCK_SERVERTEAM = Bau Lock§8: §eServerteam
|
||||||
@@ -966,6 +967,11 @@ XRAY_HELP = §8/§exray §8- §7Xray umschalten
|
|||||||
XRAY_GLOBAL = §cKein Xray in der globalen region
|
XRAY_GLOBAL = §cKein Xray in der globalen region
|
||||||
XRAY_ON = §aXray aktiviert
|
XRAY_ON = §aXray aktiviert
|
||||||
XRAY_OFF = §cXray deaktiviert
|
XRAY_OFF = §cXray deaktiviert
|
||||||
|
# HULLHIDER
|
||||||
|
HULLHIDER_HELP = §8/§ehullhider §8- §7Hullhider umschalten
|
||||||
|
HULLHIDER_GLOBAL = §cKein Hullhider in der globalen region
|
||||||
|
HULLHIDER_ON = §aHullhider aktiviert
|
||||||
|
HULLHIDER_OFF = §cHullhider deaktiviert
|
||||||
# WorldEdit
|
# WorldEdit
|
||||||
COLORREPLACE_HELP = §8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §7Ersetzt eine Farbe mit einer anderen
|
COLORREPLACE_HELP = §8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §7Ersetzt eine Farbe mit einer anderen
|
||||||
TYPEREPLACE_HELP = §8//§etyreplace §8[§7type§8] §8[§7type§8] §8- §7Ersetzt einen Blockgruppe mit einer anderen
|
TYPEREPLACE_HELP = §8//§etyreplace §8[§7type§8] §8[§7type§8] §8- §7Ersetzt einen Blockgruppe mit einer anderen
|
||||||
|
|||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* 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.bausystem.features.hullhider;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.BauSystem;
|
||||||
|
import de.steamwar.bausystem.features.techhider.HiddenHull;
|
||||||
|
import de.steamwar.bausystem.features.techhider.TechHiderCommand;
|
||||||
|
import de.steamwar.bausystem.features.xray.XrayCommand;
|
||||||
|
import de.steamwar.bausystem.region.Point;
|
||||||
|
import de.steamwar.bausystem.region.Region;
|
||||||
|
import de.steamwar.bausystem.utils.ScoreboardElement;
|
||||||
|
import de.steamwar.command.SWCommand;
|
||||||
|
import de.steamwar.hullhider.Hull;
|
||||||
|
import de.steamwar.hullhider.HullHider;
|
||||||
|
import de.steamwar.hullhider.HullRegion;
|
||||||
|
import de.steamwar.hullhider.Viewers;
|
||||||
|
import de.steamwar.linkage.Linked;
|
||||||
|
import de.steamwar.linkage.LinkedInstance;
|
||||||
|
import de.steamwar.techhider.ObfuscationConfig;
|
||||||
|
import net.md_5.bungee.api.ChatMessageType;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the inside of the build area hull of a region from the players who toggle it, as the hull hider of a fight would.
|
||||||
|
*/
|
||||||
|
@Linked
|
||||||
|
public class HullHiderCommand extends SWCommand implements Listener, ScoreboardElement {
|
||||||
|
|
||||||
|
public HullHiderCommand() {
|
||||||
|
super("hullhider");
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().registerEvents(hullHider, BauSystem.getInstance());
|
||||||
|
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), this::onTick, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final HullHider<Region> hullHider = new HullHider<>();
|
||||||
|
// Read on netty threads by the BauTechHider
|
||||||
|
private final Map<Region, HiddenHull> hulls = new ConcurrentHashMap<>();
|
||||||
|
private final Map<Region, Set<Player>> hidden = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@LinkedInstance
|
||||||
|
public TechHiderCommand techHiderCommand;
|
||||||
|
|
||||||
|
@LinkedInstance
|
||||||
|
public XrayCommand xrayCommand;
|
||||||
|
|
||||||
|
@Register(description = "HULLHIDER_HELP")
|
||||||
|
public void toggleHider(@Validator Player player) {
|
||||||
|
Region region = Region.getRegion(player.getLocation());
|
||||||
|
if (region.getType().isGlobal()) {
|
||||||
|
BauSystem.MESSAGE.send("HULLHIDER_GLOBAL", player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!region.getGameModeConfig().Techhider.Active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
techHiderCommand.disable(region, player);
|
||||||
|
xrayCommand.disable(region, player);
|
||||||
|
if (hidden.getOrDefault(region, Collections.emptySet()).contains(player)) {
|
||||||
|
disable(region, player);
|
||||||
|
BauSystem.MESSAGE.sendPrefixless("HULLHIDER_OFF", player, ChatMessageType.ACTION_BAR);
|
||||||
|
} else {
|
||||||
|
enable(region, player);
|
||||||
|
BauSystem.MESSAGE.sendPrefixless("HULLHIDER_ON", player, ChatMessageType.ACTION_BAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enable(Region region, Player player) {
|
||||||
|
if (!hulls.containsKey(region)) {
|
||||||
|
Point min = region.getBuildArea().getMinPoint(true);
|
||||||
|
Point max = region.getBuildArea().getMaxPoint(true);
|
||||||
|
HullRegion hullRegion = HullRegion.fromInclusive(min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ());
|
||||||
|
|
||||||
|
// Only the hidden blocks of an active techhider occlude, the hull hider is used without one
|
||||||
|
Hull hull = new Hull(BauSystem.getInstance(), world(), hullRegion, false, Collections.emptySet(), new Viewers.VisibleByDefault());
|
||||||
|
hullHider.addHull(region, hull);
|
||||||
|
hulls.put(region, new HiddenHull(hull, ObfuscationConfig.of(region.getGameModeConfig().Techhider).getObfuscationState()));
|
||||||
|
}
|
||||||
|
|
||||||
|
hidden.computeIfAbsent(region, rg -> ConcurrentHashMap.newKeySet()).add(player);
|
||||||
|
// Added players see the hull blocks of a hull visible by default
|
||||||
|
hullHider.addViewer(player, region);
|
||||||
|
// Block changes by WorldEdit or Axiom do not update the hull, so it is rebuilt every time somebody enables it.
|
||||||
|
// This also resends the chunks to the player.
|
||||||
|
hullHider.initialize(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops hiding the hull of the region from the player, removing the hull if nobody uses it anymore.
|
||||||
|
*/
|
||||||
|
public void disable(Region region, Player player) {
|
||||||
|
Set<Player> players = hidden.get(region);
|
||||||
|
if (players == null || !players.remove(player)) return;
|
||||||
|
|
||||||
|
if (players.isEmpty()) {
|
||||||
|
hidden.remove(region);
|
||||||
|
hulls.remove(region);
|
||||||
|
hullHider.removeHull(region);
|
||||||
|
} else {
|
||||||
|
hullHider.removeViewer(player, region);
|
||||||
|
hullHider.resendChunks(player, region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHiddenHulls(Player player, List<HiddenHull> hiddenHulls) {
|
||||||
|
hidden.forEach((region, players) -> {
|
||||||
|
HiddenHull hiddenHull = hulls.get(region);
|
||||||
|
if (hiddenHull != null && players.contains(player)) {
|
||||||
|
hiddenHulls.add(hiddenHull);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onTick() {
|
||||||
|
if (hidden.isEmpty()) return;
|
||||||
|
|
||||||
|
for (Entity entity : world().getEntities()) {
|
||||||
|
hullHider.checkEntity(entity);
|
||||||
|
}
|
||||||
|
hullHider.sendUncoveredBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private World world() {
|
||||||
|
return Bukkit.getWorlds().get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
for (Region region : hidden.keySet()) {
|
||||||
|
disable(region, event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScoreboardGroup getGroup() {
|
||||||
|
return ScoreboardGroup.OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int order() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(Region region, Player p) {
|
||||||
|
if (!hidden.getOrDefault(region, Collections.emptySet()).contains(p)) return null;
|
||||||
|
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_HULLHIDER", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
-7
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package de.steamwar.bausystem.features.techhider;
|
package de.steamwar.bausystem.features.techhider;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.features.hullhider.HullHiderCommand;
|
||||||
import de.steamwar.bausystem.features.world.SpectatorListener;
|
import de.steamwar.bausystem.features.world.SpectatorListener;
|
||||||
import de.steamwar.bausystem.features.xray.XrayCommand;
|
import de.steamwar.bausystem.features.xray.XrayCommand;
|
||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.linkage.Linked;
|
||||||
@@ -29,18 +30,20 @@ import net.minecraft.world.level.block.Block;
|
|||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.bukkit.craftbukkit.CraftWorld;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The single TechHider of the server. It combines the hiding rules of the spectator techhider, /techhider and /xray per player.
|
* The single TechHider of the server. It combines the hiding rules of the spectator techhider, /techhider and /xray and the hulls of /hullhider per player.
|
||||||
*/
|
*/
|
||||||
@Linked
|
@Linked
|
||||||
public class BauTechHider implements AccessPrivilegeProvider {
|
public class BauTechHider implements AccessPrivilegeProvider {
|
||||||
|
|
||||||
private static final HidingRule[] NO_RULES = new HidingRule[0];
|
private static final HidingRule[] NO_RULES = new HidingRule[0];
|
||||||
|
private static final HiddenHull[] NO_HULLS = new HiddenHull[0];
|
||||||
|
|
||||||
// Injected after construction, until then nothing is hidden
|
// Injected after construction, until then nothing is hidden
|
||||||
@LinkedInstance
|
@LinkedInstance
|
||||||
@@ -49,6 +52,8 @@ public class BauTechHider implements AccessPrivilegeProvider {
|
|||||||
public TechHiderCommand techHiderCommand;
|
public TechHiderCommand techHiderCommand;
|
||||||
@LinkedInstance
|
@LinkedInstance
|
||||||
public SpectatorListener spectatorListener;
|
public SpectatorListener spectatorListener;
|
||||||
|
@LinkedInstance
|
||||||
|
public HullHiderCommand hullHiderCommand;
|
||||||
|
|
||||||
public BauTechHider() {
|
public BauTechHider() {
|
||||||
new TechHider(this);
|
new TechHider(this);
|
||||||
@@ -56,8 +61,8 @@ public class BauTechHider implements AccessPrivilegeProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ForPlayer forPlayer(Player p) {
|
public ForPlayer forPlayer(Player p) {
|
||||||
if (xrayCommand == null || techHiderCommand == null || spectatorListener == null) {
|
if (xrayCommand == null || techHiderCommand == null || spectatorListener == null || hullHiderCommand == null) {
|
||||||
return new BauPlayerAccess(p, NO_RULES);
|
return new BauPlayerAccess(p, NO_RULES, NO_HULLS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Region rules first: where rules overlap, the first one decides the obfuscation block
|
// Region rules first: where rules overlap, the first one decides the obfuscation block
|
||||||
@@ -65,7 +70,10 @@ public class BauTechHider implements AccessPrivilegeProvider {
|
|||||||
xrayCommand.addHidingRules(p, rules);
|
xrayCommand.addHidingRules(p, rules);
|
||||||
techHiderCommand.addHidingRules(p, rules);
|
techHiderCommand.addHidingRules(p, rules);
|
||||||
spectatorListener.addHidingRules(p, rules);
|
spectatorListener.addHidingRules(p, rules);
|
||||||
return new BauPlayerAccess(p, rules.toArray(NO_RULES));
|
|
||||||
|
List<HiddenHull> hulls = new ArrayList<>();
|
||||||
|
hullHiderCommand.addHiddenHulls(p, hulls);
|
||||||
|
return new BauPlayerAccess(p, rules.toArray(NO_RULES), hulls.toArray(NO_HULLS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,7 +84,7 @@ public class BauTechHider implements AccessPrivilegeProvider {
|
|||||||
/**
|
/**
|
||||||
* Snapshot of the hiding rules of one player, taken once per outgoing packet on the netty thread.
|
* Snapshot of the hiding rules of one player, taken once per outgoing packet on the netty thread.
|
||||||
*/
|
*/
|
||||||
private record BauPlayerAccess(Player player, HidingRule[] rules) implements ForPlayer {
|
private record BauPlayerAccess(Player player, HidingRule[] rules, HiddenHull[] hulls) implements ForPlayer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Player getPlayer() {
|
public Player getPlayer() {
|
||||||
@@ -88,11 +96,17 @@ public class BauTechHider implements AccessPrivilegeProvider {
|
|||||||
for (HidingRule rule : rules) {
|
for (HidingRule rule : rules) {
|
||||||
if (rule.coversChunk(chunkX, chunkZ)) return false;
|
if (rule.coversChunk(chunkX, chunkZ)) return false;
|
||||||
}
|
}
|
||||||
|
for (HiddenHull hull : hulls) {
|
||||||
|
if (!hull.hull().getRegion().chunkOutside(chunkX, chunkZ)) return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canAccessPosition(int blockX, int blockY, int blockZ) {
|
public boolean canAccessPosition(int blockX, int blockY, int blockZ) {
|
||||||
|
for (HiddenHull hull : hulls) {
|
||||||
|
if (hull.hull().isBlockHidden(player, blockX, blockY, blockZ)) return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +128,10 @@ public class BauTechHider implements AccessPrivilegeProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canAccessEntity(int entityId) {
|
public boolean canAccessEntity(int entityId) {
|
||||||
return true;
|
if (hulls.length == 0) return true;
|
||||||
|
|
||||||
|
net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) player.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId);
|
||||||
|
return nmsEntity == null || canAccessPosition(nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,7 +147,10 @@ public class BauTechHider implements AccessPrivilegeProvider {
|
|||||||
for (HidingRule rule : rules) {
|
for (HidingRule rule : rules) {
|
||||||
if (rule.covers(blockX, blockY, blockZ)) return rule.obfuscation().getObfuscationState();
|
if (rule.covers(blockX, blockY, blockZ)) return rule.obfuscation().getObfuscationState();
|
||||||
}
|
}
|
||||||
// Only asked for positions a rule hides
|
for (HiddenHull hull : hulls) {
|
||||||
|
if (hull.hull().getRegion().contains(blockX, blockY, blockZ)) return hull.obfuscation();
|
||||||
|
}
|
||||||
|
// Only asked for positions a rule or hull hides
|
||||||
return Blocks.END_STONE.defaultBlockState();
|
return Blocks.END_STONE.defaultBlockState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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.bausystem.features.techhider;
|
||||||
|
|
||||||
|
import de.steamwar.hullhider.Hull;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A hull a player may not view, and the block sent instead of its hidden blocks.
|
||||||
|
*/
|
||||||
|
public record HiddenHull(Hull hull, BlockState obfuscation) {
|
||||||
|
}
|
||||||
+5
@@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.techhider;
|
|||||||
|
|
||||||
import de.steamwar.bausystem.BauSystem;
|
import de.steamwar.bausystem.BauSystem;
|
||||||
import de.steamwar.bausystem.Permission;
|
import de.steamwar.bausystem.Permission;
|
||||||
|
import de.steamwar.bausystem.features.hullhider.HullHiderCommand;
|
||||||
import de.steamwar.bausystem.features.world.SpectatorListener;
|
import de.steamwar.bausystem.features.world.SpectatorListener;
|
||||||
import de.steamwar.bausystem.features.xray.XrayCommand;
|
import de.steamwar.bausystem.features.xray.XrayCommand;
|
||||||
import de.steamwar.bausystem.region.Region;
|
import de.steamwar.bausystem.region.Region;
|
||||||
@@ -58,6 +59,9 @@ public class TechHiderCommand extends SWCommand implements Listener, ScoreboardE
|
|||||||
@LinkedInstance
|
@LinkedInstance
|
||||||
public XrayCommand xrayCommand;
|
public XrayCommand xrayCommand;
|
||||||
|
|
||||||
|
@LinkedInstance
|
||||||
|
public HullHiderCommand hullHiderCommand;
|
||||||
|
|
||||||
@Register(description = "TECHHIDER_HELP")
|
@Register(description = "TECHHIDER_HELP")
|
||||||
public void toggleHider(@Validator Player player) {
|
public void toggleHider(@Validator Player player) {
|
||||||
Region region = Region.getRegion(player.getLocation());
|
Region region = Region.getRegion(player.getLocation());
|
||||||
@@ -79,6 +83,7 @@ public class TechHiderCommand extends SWCommand implements Listener, ScoreboardE
|
|||||||
}
|
}
|
||||||
|
|
||||||
xrayCommand.disable(region, player);
|
xrayCommand.disable(region, player);
|
||||||
|
hullHiderCommand.disable(region, player);
|
||||||
if (hidden.get(region).contains(player)) {
|
if (hidden.get(region).contains(player)) {
|
||||||
hidden.get(region).remove(player);
|
hidden.get(region).remove(player);
|
||||||
BauSystem.MESSAGE.sendPrefixless("TECHHIDER_OFF", player, ChatMessageType.ACTION_BAR);
|
BauSystem.MESSAGE.sendPrefixless("TECHHIDER_OFF", player, ChatMessageType.ACTION_BAR);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.xray;
|
|||||||
|
|
||||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
import de.steamwar.bausystem.BauSystem;
|
import de.steamwar.bausystem.BauSystem;
|
||||||
|
import de.steamwar.bausystem.features.hullhider.HullHiderCommand;
|
||||||
import de.steamwar.bausystem.features.techhider.HidingRule;
|
import de.steamwar.bausystem.features.techhider.HidingRule;
|
||||||
import de.steamwar.bausystem.features.techhider.TechHiderCommand;
|
import de.steamwar.bausystem.features.techhider.TechHiderCommand;
|
||||||
import de.steamwar.bausystem.region.Region;
|
import de.steamwar.bausystem.region.Region;
|
||||||
@@ -62,6 +63,9 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
|
|||||||
@LinkedInstance
|
@LinkedInstance
|
||||||
public TechHiderCommand techHiderCommand;
|
public TechHiderCommand techHiderCommand;
|
||||||
|
|
||||||
|
@LinkedInstance
|
||||||
|
public HullHiderCommand hullHiderCommand;
|
||||||
|
|
||||||
@Register(description = "XRAY_HELP")
|
@Register(description = "XRAY_HELP")
|
||||||
public void toggleHider(@Validator Player player) {
|
public void toggleHider(@Validator Player player) {
|
||||||
Region region = Region.getRegion(player.getLocation());
|
Region region = Region.getRegion(player.getLocation());
|
||||||
@@ -87,6 +91,7 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
techHiderCommand.disable(region, player);
|
techHiderCommand.disable(region, player);
|
||||||
|
hullHiderCommand.disable(region, player);
|
||||||
if (hidden.get(region).contains(player)) {
|
if (hidden.get(region).contains(player)) {
|
||||||
hidden.get(region).remove(player);
|
hidden.get(region).remove(player);
|
||||||
BauSystem.MESSAGE.sendPrefixless("XRAY_OFF", player, ChatMessageType.ACTION_BAR);
|
BauSystem.MESSAGE.sendPrefixless("XRAY_OFF", player, ChatMessageType.ACTION_BAR);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import de.steamwar.fightsystem.states.StateDependentListener;
|
|||||||
import de.steamwar.fightsystem.states.StateDependentTask;
|
import de.steamwar.fightsystem.states.StateDependentTask;
|
||||||
import de.steamwar.hullhider.Hull;
|
import de.steamwar.hullhider.Hull;
|
||||||
import de.steamwar.hullhider.HullHider;
|
import de.steamwar.hullhider.HullHider;
|
||||||
|
import de.steamwar.hullhider.Viewers;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@@ -56,7 +57,7 @@ public class FightHullHider {
|
|||||||
|
|
||||||
Fight.teams().forEach(team -> {
|
Fight.teams().forEach(team -> {
|
||||||
Region region = team.getSchemRegion();
|
Region region = team.getSchemRegion();
|
||||||
hullHider.addHull(team, new Hull(FightSystem.getPlugin(), Config.world, region.toHullRegion(), region.getMinY() != Config.PlayerRegion.getMinY(), Config.GameModeConfig.Techhider.HiddenBlocks));
|
hullHider.addHull(team, new Hull(FightSystem.getPlugin(), Config.world, region.toHullRegion(), region.getMinY() != Config.PlayerRegion.getMinY(), Config.GameModeConfig.Techhider.HiddenBlocks, new Viewers.HiddenByDefault()));
|
||||||
});
|
});
|
||||||
|
|
||||||
new StateDependentListener(TechHiderWrapper.ENABLED, FightState.All, new MembershipListener());
|
new StateDependentListener(TechHiderWrapper.ENABLED, FightState.All, new MembershipListener());
|
||||||
@@ -109,7 +110,13 @@ public class FightHullHider {
|
|||||||
|
|
||||||
FightTeam team = Fight.getPlayerTeam(player);
|
FightTeam team = Fight.getPlayerTeam(player);
|
||||||
FightPlayer fp = Fight.getFightPlayer(player);
|
FightPlayer fp = Fight.getFightPlayer(player);
|
||||||
hullHider.forEach((hullTeam, hull) -> hullHider.setViewer(player, hullTeam, Config.isReferee(player) || hullTeam == team || (fp != null && fp.canEntern())));
|
hullHider.forEach((hullTeam, hull) -> {
|
||||||
|
if (Config.isReferee(player) || hullTeam == team || (fp != null && fp.canEntern())) {
|
||||||
|
hullHider.addViewer(player, hullTeam);
|
||||||
|
} else {
|
||||||
|
hullHider.removeViewer(player, hullTeam);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void blockUpdate(Block block, Material changedType) {
|
public void blockUpdate(Block block, Material changedType) {
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
@@ -67,21 +66,22 @@ public class Hull {
|
|||||||
private final HashMap<IntVector, BitSet> visibilityDirections = new HashMap<>(); // Contains the visible directions of each occluding visible block
|
private final HashMap<IntVector, BitSet> visibilityDirections = new HashMap<>(); // Contains the visible directions of each occluding visible block
|
||||||
private final HashSet<IntVector> uncoveredSurface = new HashSet<>();
|
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 Viewers viewers;
|
||||||
private final Set<UUID> viewers = ConcurrentHashMap.newKeySet();
|
|
||||||
private final HashSet<Entity> entities = new HashSet<>();
|
private final HashSet<Entity> entities = new HashSet<>();
|
||||||
private final HashSet<REntity> rentities = new HashSet<>();
|
private final HashSet<REntity> rentities = new HashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param groundVisible whether the bottom layer of the region is uncovered (if the region does not reach the ground)
|
* @param groundVisible whether the bottom layer of the region is uncovered (if the region does not reach the ground)
|
||||||
* @param hiddenBlocks blocks that occlude like solid blocks, as the techhider replaces them by an occluding block
|
* @param hiddenBlocks blocks that occlude like solid blocks, as the techhider replaces them by an occluding block
|
||||||
|
* @param viewers which players may see the hull contents, a new instance for every hull
|
||||||
*/
|
*/
|
||||||
public Hull(Plugin plugin, World world, HullRegion region, boolean groundVisible, Set<Material> hiddenBlocks) {
|
public Hull(Plugin plugin, World world, HullRegion region, boolean groundVisible, Set<Material> hiddenBlocks, Viewers viewers) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.region = region;
|
this.region = region;
|
||||||
this.groundVisible = groundVisible;
|
this.groundVisible = groundVisible;
|
||||||
this.hiddenBlocks = hiddenBlocks;
|
this.hiddenBlocks = hiddenBlocks;
|
||||||
|
this.viewers = viewers;
|
||||||
this.occluding = new BitSet(region.volume());
|
this.occluding = new BitSet(region.volume());
|
||||||
this.visibility = new BitSet(region.volume());
|
this.visibility = new BitSet(region.volume());
|
||||||
|
|
||||||
@@ -103,33 +103,48 @@ public class Hull {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isViewer(Player player) {
|
public boolean isViewer(Player player) {
|
||||||
return player != null && viewers.contains(player.getUniqueId());
|
return player != null && viewers.isViewer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the hull from this player (removes the viewer), also hiding all entities currently inside it.
|
* Adds the player to the {@link Viewers}, which decide whether the player may see the hull contents afterwards.
|
||||||
*/
|
|
||||||
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) {
|
public void addViewer(Player player) {
|
||||||
if (viewers.add(player.getUniqueId())) {
|
boolean wasViewer = isViewer(player);
|
||||||
for (Entity entity : entities) {
|
viewers.addViewer(player);
|
||||||
trackEntity(player, entity);
|
updateEntities(player, wasViewer);
|
||||||
}
|
|
||||||
// techhider triggers block change sending
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the player from the {@link Viewers}, which decide whether the player may see the hull contents afterwards.
|
||||||
|
*/
|
||||||
|
public void removeViewer(Player player) {
|
||||||
|
boolean wasViewer = isViewer(player);
|
||||||
|
viewers.removeViewer(player);
|
||||||
|
updateEntities(player, wasViewer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEntities(Player player, boolean wasViewer) {
|
||||||
|
if (isViewer(player)) {
|
||||||
|
if (!wasViewer) {
|
||||||
|
for (Entity entity : entities) {
|
||||||
|
trackEntity(player, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Also hides the entities from players that joined after they were hidden
|
||||||
|
for (Entity entity : entities) {
|
||||||
|
if (!player.equals(entity)) untrackEntity(player, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// techhider triggers block change sending
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the player to the default viewer state, e.g. when leaving.
|
||||||
|
*/
|
||||||
public void forgetViewer(Player player) {
|
public void forgetViewer(Player player) {
|
||||||
viewers.remove(player.getUniqueId());
|
viewers.removeViewer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkEntity(Entity entity) {
|
public void checkEntity(Entity entity) {
|
||||||
|
|||||||
@@ -107,13 +107,18 @@ public class HullHider<K> implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setViewer(Player player, K key, boolean viewer) {
|
/**
|
||||||
Hull hull = hullMap.get(key);
|
* @see Viewers
|
||||||
if (viewer) {
|
*/
|
||||||
hull.addViewer(player);
|
public void addViewer(Player player, K key) {
|
||||||
} else {
|
hullMap.get(key).addViewer(player);
|
||||||
hull.removeViewer(player);
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* @see Viewers
|
||||||
|
*/
|
||||||
|
public void removeViewer(Player player, K key) {
|
||||||
|
hullMap.get(key).removeViewer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forgetViewer(Player player) {
|
public void forgetViewer(Player player) {
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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.hullhider;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The players of a {@link Hull}. Whether an added player may see the hull contents or sees the hull blocks instead is decided by {@link #isViewer(Player)}.
|
||||||
|
* Stores the players, so every hull needs its own instance.
|
||||||
|
*/
|
||||||
|
public abstract sealed class Viewers {
|
||||||
|
|
||||||
|
// Concurrent as it is read on netty threads
|
||||||
|
protected final Set<UUID> players = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether the player may see the hull contents instead of the hull blocks
|
||||||
|
*/
|
||||||
|
public abstract boolean isViewer(Player player);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether the player was not added before
|
||||||
|
*/
|
||||||
|
public boolean addViewer(Player player) {
|
||||||
|
return players.add(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether the player was added before
|
||||||
|
*/
|
||||||
|
public boolean removeViewer(Player player) {
|
||||||
|
return players.remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hidden from everybody, only added players may see the hull contents.
|
||||||
|
*/
|
||||||
|
public static final class HiddenByDefault extends Viewers {
|
||||||
|
@Override
|
||||||
|
public boolean isViewer(Player player) {
|
||||||
|
return players.contains(player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible to everybody, only added players see the hull blocks instead of the hull contents.
|
||||||
|
*/
|
||||||
|
public static final class VisibleByDefault extends Viewers {
|
||||||
|
@Override
|
||||||
|
public boolean isViewer(Player player) {
|
||||||
|
return !players.contains(player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user