24package com.dreamfirestudios.dreamcore.DreamBlockMask;
26import com.dreamfirestudios.dreamcore.DreamCore;
28import org.bukkit.Location;
29import org.bukkit.Material;
30import org.bukkit.block.BlockState;
31import org.bukkit.entity.Player;
32import org.bukkit.util.Vector;
34import java.util.Collections;
35import java.util.HashMap;
62 private Player player;
64 @Getter
private boolean deleteMaskOnNull;
65 @Getter
private boolean ignoreAir;
66 @Getter
private boolean resetLastFrames;
67 @Getter
private boolean keepTrailTheSame;
68 @Getter
private double minDistance;
69 @Getter
private double maxX;
70 @Getter
private double maxY;
71 @Getter
private double maxZ;
73 @Getter
private Map<Material, Material> blockExceptions;
75 private Map<Vector, BlockState> lastFrameBlockStates =
new HashMap<>();
76 private final Map<Vector, BlockState> visitedTrailLocations =
new HashMap<>();
82 private volatile boolean actionBarPaused =
true;
94 if (blockExceptions ==
null)
throw new IllegalArgumentException(
"Block exceptions cannot be null.");
95 this.blockExceptions = mergeExceptions(this.blockExceptions, blockExceptions);
110 if (player ==
null || !player.isOnline() || actionBarPaused)
return true;
112 final Location playerLoc = player.getLocation();
113 final double px = playerLoc.getX();
114 final double py = playerLoc.getY();
115 final double pz = playerLoc.getZ();
117 final Map<Vector, BlockState> previousFrameStates =
new HashMap<>();
118 final Map<Vector, BlockState> newFrameStates =
new HashMap<>();
121 for (
double x = px - maxX; x < px + maxX; x++){
122 for (
double y = py - maxY; y < py + maxY; y++){
123 for (
double z = pz - maxZ; z < pz + maxZ; z++){
125 final Location loc =
new Location(player.getWorld(), x, y, z);
126 if (loc.distance(playerLoc) < minDistance)
continue;
128 final var block = loc.getBlock();
129 if (block ==
null)
continue;
130 if (block.getType() == Material.AIR && ignoreAir)
continue;
132 final Material viewMat = blockExceptions.getOrDefault(block.getType(), Material.BARRIER);
133 if (viewMat ==
null)
continue;
135 final Vector key = block.getLocation().toVector();
136 previousFrameStates.put(key, block.getState());
138 final BlockState newState = block.getState();
139 newState.setType(viewMat);
140 newFrameStates.put(key, newState);
142 if (keepTrailTheSame && !visitedTrailLocations.containsKey(key)) {
143 visitedTrailLocations.put(key, newState);
150 if (resetLastFrames) {
151 for (Map.Entry<Vector, BlockState> e : lastFrameBlockStates.entrySet()) {
152 if (!newFrameStates.containsKey(e.getKey()) && !visitedTrailLocations.containsKey(e.getKey())) {
153 newFrameStates.put(e.getKey(), e.getValue());
160 Collections.unmodifiableMap(newFrameStates),
161 Collections.unmodifiableMap(previousFrameStates));
164 player.sendBlockChanges(newFrameStates.values());
165 lastFrameBlockStates = previousFrameStates;
169 Collections.unmodifiableMap(newFrameStates));
178 if (!actionBarPaused) {
179 actionBarPaused =
true;
181 player.sendBlockChanges(lastFrameBlockStates.values());
182 player.sendBlockChanges(visitedTrailLocations.values());
191 if (actionBarPaused) {
192 actionBarPaused =
false;
207 actionBarPaused =
true;
208 player.sendBlockChanges(lastFrameBlockStates.values());
209 player.sendBlockChanges(visitedTrailLocations.values());
220 return Collections.unmodifiableMap(visitedTrailLocations);
223 private static Map<Material, Material> mergeExceptions(Map<Material, Material> base, Map<Material, Material> add) {
224 Map<Material, Material> out =
new HashMap<>(base);
226 return Collections.unmodifiableMap(out);
236 private final Map<Material, Material> blockExceptions =
new HashMap<>();
237 private boolean deleteMaskOnNull =
false;
238 private boolean ignoreAir =
true;
239 private boolean resetLastFrames =
true;
240 private boolean keepTrailTheSame =
false;
241 private double minDistance = 0.0d;
242 private double maxX = 5.0d;
243 private double maxY = 5.0d;
244 private double maxZ = 5.0d;
252 if (blockExceptions ==
null)
throw new IllegalArgumentException(
"Block exceptions cannot be null.");
253 this.blockExceptions.putAll(blockExceptions);
264 if (target ==
null || view ==
null)
throw new IllegalArgumentException(
"Block type cannot be null.");
265 this.blockExceptions.put(target, view);
275 this.deleteMaskOnNull = deleteMaskOnNull;
285 this.ignoreAir = ignoreAir;
295 this.resetLastFrames = resetLastFrames;
305 this.keepTrailTheSame = keepTrailTheSame;
315 if (minDistance < 0)
throw new IllegalArgumentException(
"minDistance cannot be negative.");
316 this.minDistance = minDistance;
324 if (maxX < 0)
throw new IllegalArgumentException(
"Max X cannot be negative.");
333 if (maxY < 0)
throw new IllegalArgumentException(
"Max Y cannot be negative.");
342 if (maxZ < 0)
throw new IllegalArgumentException(
"Max Z cannot be negative.");
357 if (player ==
null)
throw new IllegalArgumentException(
"Player cannot be null.");
360 stored.addToExceptions(this.blockExceptions);
365 mask.player = player;
366 mask.deleteMaskOnNull = deleteMaskOnNull;
367 mask.resetLastFrames = resetLastFrames;
368 mask.keepTrailTheSame = keepTrailTheSame;
369 mask.minDistance = minDistance;
373 mask.ignoreAir = ignoreAir;
374 mask.blockExceptions = Collections.unmodifiableMap(
new HashMap<>(this.blockExceptions));
Fired when a DreamBlockMask is created and registered for a player.
Fired after a frame’s block changes are sent to the player.
Fired after a frame is computed but before it is sent to the player.
Fired when a DreamBlockMask is paused.
Fired when a DreamBlockMask transitions from paused to playing.
Fired when a DreamBlockMask is stopped and unregistered.
Fluent builder for DreamBlockMask.
Builder maxZ(double maxZ)
Set Z axis bound around the player for the mask region.
Builder keepTrailTheSame(boolean keepTrailTheSame)
Keep a persistent trail of masked blocks across frames.
Builder minDistance(double minDistance)
Minimum distance from the player under which blocks are not affected.
Builder resetLastFrames(boolean resetLastFrames)
Restore blocks from the previous frame if they are not in the new frame (unless retained by trail).
Builder deleteMaskOnNull(boolean deleteMaskOnNull)
Delete mask automatically when the player reference becomes invalid.
Builder blockExceptions(Map< Material, Material > blockExceptions)
Bulk add block exceptions (actual → view).
Builder blockExceptions(Material target, Material view)
Add a single block exception mapping.
Builder maxY(double maxY)
Set Y axis bound around the player for the mask region.
Builder maxX(double maxX)
Set X axis bound around the player for the mask region.
Builder ignoreAir(boolean ignoreAir)
Ignore AIR blocks when producing the mask.
DreamBlockMask CreateMask(Player player)
Creates (or merges with) the player's mask.
Renders a per-player “block mask” viewport by sending ephemeral block changes around the player.
DreamBlockMask stop()
Stops the mask, restores visible changes, fires stopped event, and removes it from the core registry.
boolean displayNextFrame()
Computes and applies the next frame.
Map< Vector, BlockState > getVisitedTrailLocationsView()
Returns an immutable view of the persistent trail entries.
void addToExceptions(Map< Material, Material > blockExceptions)
Adds/merges block type exceptions into this mask.
void play()
Resumes the mask if paused.
void pause()
Pauses the mask and restores any pending last/trail states to the player.
static final LinkedHashMap< UUID, DreamBlockMask > DreamBlockMasks