mirror of
https://github.com/loganintech/render-region-forcefield.git
synced 2026-05-30 22:31:15 +00:00
Added more
This commit is contained in:
23
README.md
23
README.md
@@ -5,10 +5,14 @@ A Minecraft plugin that renders visible particle forcefields around WorldGuard r
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Automatically detects WorldGuard regions with `entry deny` flag
|
- Automatically detects WorldGuard regions with `entry deny` flag
|
||||||
- Renders particle forcefields only visible to players who cannot enter
|
- Renders **visible glass pane barriers** and particle effects for blocked regions
|
||||||
|
- Only shows forcefields to players who **actually cannot enter** (respects bypass permissions and ops)
|
||||||
|
- Glass panes only placed where there's currently air (doesn't cover existing blocks)
|
||||||
- Configurable particle color, size, spacing, and render distance
|
- Configurable particle color, size, spacing, and render distance
|
||||||
|
- Configurable block material (glass panes, barriers, etc.)
|
||||||
- Supports cuboid and polygonal region types
|
- Supports cuboid and polygonal region types
|
||||||
- Performance-optimized with distance-based rendering
|
- Performance-optimized with distance-based rendering
|
||||||
|
- Automatic cleanup when players move away or disconnect
|
||||||
- Clean, readable, and well-documented code
|
- Clean, readable, and well-documented code
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
@@ -85,14 +89,27 @@ particle-color:
|
|||||||
|
|
||||||
# Particle size (0.5-2.0 recommended)
|
# Particle size (0.5-2.0 recommended)
|
||||||
particle-size: 1.0
|
particle-size: 1.0
|
||||||
|
|
||||||
|
# Block rendering settings
|
||||||
|
# Whether to render actual blocks (glass panes) in addition to particles
|
||||||
|
render-blocks: true
|
||||||
|
|
||||||
|
# Distance between blocks (in blocks)
|
||||||
|
block-spacing: 1.0
|
||||||
|
|
||||||
|
# Block material to use (e.g., PURPLE_STAINED_GLASS_PANE, BARRIER, GLASS)
|
||||||
|
block-material: PURPLE_STAINED_GLASS_PANE
|
||||||
```
|
```
|
||||||
|
|
||||||
## Performance Tips
|
## Performance Tips
|
||||||
|
|
||||||
- Reduce `max-render-distance` for servers with many regions
|
- Reduce `max-render-distance` for servers with many regions
|
||||||
- Increase `particle-spacing` to reduce particle count
|
- Increase `particle-spacing` to reduce particle count
|
||||||
|
- Increase `block-spacing` to reduce block count
|
||||||
|
- Set `render-blocks: false` to disable glass panes and only use particles
|
||||||
- Set `render-walls: false` to only show edges
|
- Set `render-walls: false` to only show edges
|
||||||
- Increase `update-interval-ticks` if you don't need real-time updates
|
- Increase `update-interval-ticks` if you don't need real-time updates
|
||||||
|
- Use `BARRIER` blocks instead of glass panes (less visible but lighter)
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
@@ -102,8 +119,10 @@ particle-size: 1.0
|
|||||||
2. Check the server console for debug messages
|
2. Check the server console for debug messages
|
||||||
3. Use `/forcefield info` to see if regions are being detected
|
3. Use `/forcefield info` to see if regions are being detected
|
||||||
4. Verify the region has `entry deny` set: `/rg info <region>`
|
4. Verify the region has `entry deny` set: `/rg info <region>`
|
||||||
5. Check you're not a member/owner of the region
|
5. **Check you can't actually enter** - ops and members won't see forcefields
|
||||||
6. Ensure you're within render distance of the region (default: 100 blocks)
|
6. Ensure you're within render distance of the region (default: 100 blocks)
|
||||||
|
7. Try setting `render-blocks: true` if you only see particles
|
||||||
|
8. Check that locations aren't already occupied by blocks
|
||||||
|
|
||||||
### Plugin won't load
|
### Plugin won't load
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package loganintech.regionforcefield;
|
|||||||
|
|
||||||
import loganintech.regionforcefield.command.ForcefieldCommand;
|
import loganintech.regionforcefield.command.ForcefieldCommand;
|
||||||
import loganintech.regionforcefield.forcefield.ForcefieldRenderer;
|
import loganintech.regionforcefield.forcefield.ForcefieldRenderer;
|
||||||
|
import loganintech.regionforcefield.listener.PlayerListener;
|
||||||
import loganintech.regionforcefield.region.RegionPermissionChecker;
|
import loganintech.regionforcefield.region.RegionPermissionChecker;
|
||||||
import loganintech.regionforcefield.task.ForcefieldUpdateTask;
|
import loganintech.regionforcefield.task.ForcefieldUpdateTask;
|
||||||
import org.bukkit.command.PluginCommand;
|
import org.bukkit.command.PluginCommand;
|
||||||
@@ -35,6 +36,9 @@ public final class RegionForcefieldPlugin extends JavaPlugin {
|
|||||||
this.permissionChecker = new RegionPermissionChecker(this);
|
this.permissionChecker = new RegionPermissionChecker(this);
|
||||||
this.forcefieldRenderer = new ForcefieldRenderer(this);
|
this.forcefieldRenderer = new ForcefieldRenderer(this);
|
||||||
|
|
||||||
|
// Register listeners
|
||||||
|
getServer().getPluginManager().registerEvents(new PlayerListener(this), this);
|
||||||
|
|
||||||
// Register commands
|
// Register commands
|
||||||
ForcefieldCommand commandExecutor = new ForcefieldCommand(this);
|
ForcefieldCommand commandExecutor = new ForcefieldCommand(this);
|
||||||
PluginCommand command = getCommand("forcefield");
|
PluginCommand command = getCommand("forcefield");
|
||||||
|
|||||||
@@ -5,15 +5,19 @@ import com.sk89q.worldedit.math.BlockVector3;
|
|||||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||||
|
import loganintech.regionforcefield.RegionForcefieldPlugin;
|
||||||
import org.bukkit.Color;
|
import org.bukkit.Color;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.Particle;
|
import org.bukkit.Particle;
|
||||||
import loganintech.regionforcefield.RegionForcefieldPlugin;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders particle forcefields around protected regions.
|
* Renders particle forcefields around protected regions.
|
||||||
@@ -23,6 +27,8 @@ public class ForcefieldRenderer {
|
|||||||
private final RegionForcefieldPlugin plugin;
|
private final RegionForcefieldPlugin plugin;
|
||||||
private final double particleSpacing;
|
private final double particleSpacing;
|
||||||
private final Particle.DustOptions dustOptions;
|
private final Particle.DustOptions dustOptions;
|
||||||
|
private final PlayerBlockTracker blockTracker;
|
||||||
|
private final BlockData glassBlockData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new forcefield renderer.
|
* Creates a new forcefield renderer.
|
||||||
@@ -32,6 +38,7 @@ public class ForcefieldRenderer {
|
|||||||
public ForcefieldRenderer(@NotNull RegionForcefieldPlugin plugin) {
|
public ForcefieldRenderer(@NotNull RegionForcefieldPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.particleSpacing = plugin.getConfig().getDouble("particle-spacing", 0.5);
|
this.particleSpacing = plugin.getConfig().getDouble("particle-spacing", 0.5);
|
||||||
|
this.blockTracker = new PlayerBlockTracker();
|
||||||
|
|
||||||
// Get color from config or use default (purple)
|
// Get color from config or use default (purple)
|
||||||
int red = plugin.getConfig().getInt("particle-color.red", 147);
|
int red = plugin.getConfig().getInt("particle-color.red", 147);
|
||||||
@@ -40,6 +47,27 @@ public class ForcefieldRenderer {
|
|||||||
float size = (float) plugin.getConfig().getDouble("particle-size", 1.0);
|
float size = (float) plugin.getConfig().getDouble("particle-size", 1.0);
|
||||||
|
|
||||||
this.dustOptions = new Particle.DustOptions(Color.fromRGB(red, green, blue), size);
|
this.dustOptions = new Particle.DustOptions(Color.fromRGB(red, green, blue), size);
|
||||||
|
|
||||||
|
// Get block material from config or use purple stained glass pane
|
||||||
|
String materialName = plugin.getConfig().getString("block-material", "PURPLE_STAINED_GLASS_PANE");
|
||||||
|
Material material;
|
||||||
|
try {
|
||||||
|
material = Material.valueOf(materialName.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
plugin.getLogger().warning("Invalid block material '" + materialName + "', using PURPLE_STAINED_GLASS_PANE");
|
||||||
|
material = Material.PURPLE_STAINED_GLASS_PANE;
|
||||||
|
}
|
||||||
|
this.glassBlockData = material.createBlockData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block tracker for managing fake blocks.
|
||||||
|
*
|
||||||
|
* @return the block tracker
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public PlayerBlockTracker getBlockTracker() {
|
||||||
|
return blockTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,49 +76,59 @@ public class ForcefieldRenderer {
|
|||||||
* @param player the player to show the forcefield to
|
* @param player the player to show the forcefield to
|
||||||
* @param region the region to render
|
* @param region the region to render
|
||||||
* @param world the world the region is in
|
* @param world the world the region is in
|
||||||
|
* @return set of block locations that were rendered
|
||||||
*/
|
*/
|
||||||
public void renderForcefield(@NotNull Player player, @NotNull ProtectedRegion region, @NotNull World world) {
|
@NotNull
|
||||||
|
public Set<Location> renderForcefield(@NotNull Player player, @NotNull ProtectedRegion region, @NotNull World world) {
|
||||||
|
Set<Location> newBlocks = new HashSet<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
plugin.debug("Rendering forcefield for region " + region.getId() + " to player " + player.getName());
|
plugin.debug("Rendering forcefield for region " + region.getId() + " to player " + player.getName());
|
||||||
|
|
||||||
if (region instanceof ProtectedCuboidRegion) {
|
if (region instanceof ProtectedCuboidRegion) {
|
||||||
renderCuboidForcefield(player, (ProtectedCuboidRegion) region, world);
|
renderCuboidForcefield(player, (ProtectedCuboidRegion) region, world, newBlocks);
|
||||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||||
renderPolygonalForcefield(player, (ProtectedPolygonalRegion) region, world);
|
renderPolygonalForcefield(player, (ProtectedPolygonalRegion) region, world, newBlocks);
|
||||||
} else {
|
} else {
|
||||||
// For other region types, fall back to rendering a bounding box
|
// For other region types, fall back to rendering a bounding box
|
||||||
plugin.debug("Using bounding box for region type: " + region.getClass().getSimpleName());
|
plugin.debug("Using bounding box for region type: " + region.getClass().getSimpleName());
|
||||||
renderBoundingBoxForcefield(player, region, world);
|
renderBoundingBoxForcefield(player, region, world, newBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugin.debug("Rendered " + newBlocks.size() + " blocks for region " + region.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
plugin.getLogger().warning("Error rendering forcefield for region " + region.getId() + ": " + e.getMessage());
|
plugin.getLogger().warning("Error rendering forcefield for region " + region.getId() + ": " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return newBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a forcefield for a cuboid region.
|
* Renders a forcefield for a cuboid region.
|
||||||
*/
|
*/
|
||||||
private void renderCuboidForcefield(@NotNull Player player, @NotNull ProtectedCuboidRegion region, @NotNull World world) {
|
private void renderCuboidForcefield(@NotNull Player player, @NotNull ProtectedCuboidRegion region,
|
||||||
|
@NotNull World world, @NotNull Set<Location> blocks) {
|
||||||
BlockVector3 min = region.getMinimumPoint();
|
BlockVector3 min = region.getMinimumPoint();
|
||||||
BlockVector3 max = region.getMaximumPoint();
|
BlockVector3 max = region.getMaximumPoint();
|
||||||
|
|
||||||
// Render vertical edges
|
// Render vertical edges
|
||||||
renderVerticalEdges(player, world, min, max);
|
renderVerticalEdges(player, world, min, max, blocks);
|
||||||
|
|
||||||
// Render horizontal edges at top and bottom
|
// Render horizontal edges at top and bottom
|
||||||
renderHorizontalEdges(player, world, min, max);
|
renderHorizontalEdges(player, world, min, max, blocks);
|
||||||
|
|
||||||
// Optionally render faces (walls)
|
// Optionally render faces (walls)
|
||||||
if (plugin.getConfig().getBoolean("render-walls", true)) {
|
if (plugin.getConfig().getBoolean("render-walls", true)) {
|
||||||
renderWalls(player, world, min, max);
|
renderWalls(player, world, min, max, blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a forcefield for a polygonal region.
|
* Renders a forcefield for a polygonal region.
|
||||||
*/
|
*/
|
||||||
private void renderPolygonalForcefield(@NotNull Player player, @NotNull ProtectedPolygonalRegion region, @NotNull World world) {
|
private void renderPolygonalForcefield(@NotNull Player player, @NotNull ProtectedPolygonalRegion region,
|
||||||
|
@NotNull World world, @NotNull Set<Location> blocks) {
|
||||||
List<BlockVector2> points = region.getPoints();
|
List<BlockVector2> points = region.getPoints();
|
||||||
int minY = region.getMinimumPoint().y();
|
int minY = region.getMinimumPoint().y();
|
||||||
int maxY = region.getMaximumPoint().y();
|
int maxY = region.getMaximumPoint().y();
|
||||||
@@ -100,64 +138,71 @@ public class ForcefieldRenderer {
|
|||||||
BlockVector2 point1 = points.get(i);
|
BlockVector2 point1 = points.get(i);
|
||||||
BlockVector2 point2 = points.get((i + 1) % points.size());
|
BlockVector2 point2 = points.get((i + 1) % points.size());
|
||||||
|
|
||||||
renderVerticalWall(player, world, point1.x(), point1.z(), point2.x(), point2.z(), minY, maxY);
|
renderVerticalWall(player, world, point1.x(), point1.z(), point2.x(), point2.z(), minY, maxY, blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a bounding box forcefield for unsupported region types.
|
* Renders a bounding box forcefield for unsupported region types.
|
||||||
*/
|
*/
|
||||||
private void renderBoundingBoxForcefield(@NotNull Player player, @NotNull ProtectedRegion region, @NotNull World world) {
|
private void renderBoundingBoxForcefield(@NotNull Player player, @NotNull ProtectedRegion region,
|
||||||
|
@NotNull World world, @NotNull Set<Location> blocks) {
|
||||||
BlockVector3 min = region.getMinimumPoint();
|
BlockVector3 min = region.getMinimumPoint();
|
||||||
BlockVector3 max = region.getMaximumPoint();
|
BlockVector3 max = region.getMaximumPoint();
|
||||||
|
|
||||||
renderVerticalEdges(player, world, min, max);
|
renderVerticalEdges(player, world, min, max, blocks);
|
||||||
renderHorizontalEdges(player, world, min, max);
|
renderHorizontalEdges(player, world, min, max, blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the vertical edges of a cuboid.
|
* Renders the vertical edges of a cuboid.
|
||||||
*/
|
*/
|
||||||
private void renderVerticalEdges(@NotNull Player player, @NotNull World world, @NotNull BlockVector3 min, @NotNull BlockVector3 max) {
|
private void renderVerticalEdges(@NotNull Player player, @NotNull World world,
|
||||||
|
@NotNull BlockVector3 min, @NotNull BlockVector3 max,
|
||||||
|
@NotNull Set<Location> blocks) {
|
||||||
// Four vertical edges
|
// Four vertical edges
|
||||||
renderLine(player, world, min.x(), min.y(), min.z(), min.x(), max.y(), min.z());
|
renderLine(player, world, min.x(), min.y(), min.z(), min.x(), max.y(), min.z(), blocks);
|
||||||
renderLine(player, world, max.x(), min.y(), min.z(), max.x(), max.y(), min.z());
|
renderLine(player, world, max.x(), min.y(), min.z(), max.x(), max.y(), min.z(), blocks);
|
||||||
renderLine(player, world, min.x(), min.y(), max.z(), min.x(), max.y(), max.z());
|
renderLine(player, world, min.x(), min.y(), max.z(), min.x(), max.y(), max.z(), blocks);
|
||||||
renderLine(player, world, max.x(), min.y(), max.z(), max.x(), max.y(), max.z());
|
renderLine(player, world, max.x(), min.y(), max.z(), max.x(), max.y(), max.z(), blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the horizontal edges of a cuboid.
|
* Renders the horizontal edges of a cuboid.
|
||||||
*/
|
*/
|
||||||
private void renderHorizontalEdges(@NotNull Player player, @NotNull World world, @NotNull BlockVector3 min, @NotNull BlockVector3 max) {
|
private void renderHorizontalEdges(@NotNull Player player, @NotNull World world,
|
||||||
|
@NotNull BlockVector3 min, @NotNull BlockVector3 max,
|
||||||
|
@NotNull Set<Location> blocks) {
|
||||||
// Bottom edges
|
// Bottom edges
|
||||||
renderLine(player, world, min.x(), min.y(), min.z(), max.x(), min.y(), min.z());
|
renderLine(player, world, min.x(), min.y(), min.z(), max.x(), min.y(), min.z(), blocks);
|
||||||
renderLine(player, world, min.x(), min.y(), max.z(), max.x(), min.y(), max.z());
|
renderLine(player, world, min.x(), min.y(), max.z(), max.x(), min.y(), max.z(), blocks);
|
||||||
renderLine(player, world, min.x(), min.y(), min.z(), min.x(), min.y(), max.z());
|
renderLine(player, world, min.x(), min.y(), min.z(), min.x(), min.y(), max.z(), blocks);
|
||||||
renderLine(player, world, max.x(), min.y(), min.z(), max.x(), min.y(), max.z());
|
renderLine(player, world, max.x(), min.y(), min.z(), max.x(), min.y(), max.z(), blocks);
|
||||||
|
|
||||||
// Top edges
|
// Top edges
|
||||||
renderLine(player, world, min.x(), max.y(), min.z(), max.x(), max.y(), min.z());
|
renderLine(player, world, min.x(), max.y(), min.z(), max.x(), max.y(), min.z(), blocks);
|
||||||
renderLine(player, world, min.x(), max.y(), max.z(), max.x(), max.y(), max.z());
|
renderLine(player, world, min.x(), max.y(), max.z(), max.x(), max.y(), max.z(), blocks);
|
||||||
renderLine(player, world, min.x(), max.y(), min.z(), min.x(), max.y(), max.z());
|
renderLine(player, world, min.x(), max.y(), min.z(), min.x(), max.y(), max.z(), blocks);
|
||||||
renderLine(player, world, max.x(), max.y(), min.z(), max.x(), max.y(), max.z());
|
renderLine(player, world, max.x(), max.y(), min.z(), max.x(), max.y(), max.z(), blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the walls (faces) of a cuboid.
|
* Renders the walls (faces) of a cuboid.
|
||||||
*/
|
*/
|
||||||
private void renderWalls(@NotNull Player player, @NotNull World world, @NotNull BlockVector3 min, @NotNull BlockVector3 max) {
|
private void renderWalls(@NotNull Player player, @NotNull World world,
|
||||||
|
@NotNull BlockVector3 min, @NotNull BlockVector3 max,
|
||||||
|
@NotNull Set<Location> blocks) {
|
||||||
// North wall (min Z)
|
// North wall (min Z)
|
||||||
renderVerticalWall(player, world, min.x(), min.z(), max.x(), min.z(), min.y(), max.y());
|
renderVerticalWall(player, world, min.x(), min.z(), max.x(), min.z(), min.y(), max.y(), blocks);
|
||||||
|
|
||||||
// South wall (max Z)
|
// South wall (max Z)
|
||||||
renderVerticalWall(player, world, min.x(), max.z(), max.x(), max.z(), min.y(), max.y());
|
renderVerticalWall(player, world, min.x(), max.z(), max.x(), max.z(), min.y(), max.y(), blocks);
|
||||||
|
|
||||||
// West wall (min X)
|
// West wall (min X)
|
||||||
renderVerticalWall(player, world, min.x(), min.z(), min.x(), max.z(), min.y(), max.y());
|
renderVerticalWall(player, world, min.x(), min.z(), min.x(), max.z(), min.y(), max.y(), blocks);
|
||||||
|
|
||||||
// East wall (max X)
|
// East wall (max X)
|
||||||
renderVerticalWall(player, world, max.x(), min.z(), max.x(), max.z(), min.y(), max.y());
|
renderVerticalWall(player, world, max.x(), min.z(), max.x(), max.z(), min.y(), max.y(), blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,10 +210,11 @@ public class ForcefieldRenderer {
|
|||||||
*/
|
*/
|
||||||
private void renderVerticalWall(@NotNull Player player, @NotNull World world,
|
private void renderVerticalWall(@NotNull Player player, @NotNull World world,
|
||||||
double x1, double z1, double x2, double z2,
|
double x1, double z1, double x2, double z2,
|
||||||
double minY, double maxY) {
|
double minY, double maxY, @NotNull Set<Location> blocks) {
|
||||||
double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(z2 - z1, 2));
|
double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(z2 - z1, 2));
|
||||||
int horizontalSteps = (int) Math.ceil(distance / particleSpacing);
|
int horizontalSteps = (int) Math.ceil(distance / particleSpacing);
|
||||||
int verticalSteps = (int) Math.ceil((maxY - minY) / particleSpacing);
|
int verticalSteps = (int) Math.ceil((maxY - minY) / particleSpacing);
|
||||||
|
double blockSpacing = plugin.getConfig().getDouble("block-spacing", 1.0);
|
||||||
|
|
||||||
for (int i = 0; i <= horizontalSteps; i++) {
|
for (int i = 0; i <= horizontalSteps; i++) {
|
||||||
double t = horizontalSteps > 0 ? (double) i / horizontalSteps : 0;
|
double t = horizontalSteps > 0 ? (double) i / horizontalSteps : 0;
|
||||||
@@ -178,6 +224,12 @@ public class ForcefieldRenderer {
|
|||||||
for (int j = 0; j <= verticalSteps; j++) {
|
for (int j = 0; j <= verticalSteps; j++) {
|
||||||
double y = minY + (maxY - minY) * ((double) j / verticalSteps);
|
double y = minY + (maxY - minY) * ((double) j / verticalSteps);
|
||||||
spawnParticle(player, world, x, y, z);
|
spawnParticle(player, world, x, y, z);
|
||||||
|
|
||||||
|
// Place blocks at intervals
|
||||||
|
if (i % ((int) Math.max(1, blockSpacing / particleSpacing)) == 0 &&
|
||||||
|
j % ((int) Math.max(1, blockSpacing / particleSpacing)) == 0) {
|
||||||
|
placeBlock(player, world, x, y, z, blocks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +239,8 @@ public class ForcefieldRenderer {
|
|||||||
*/
|
*/
|
||||||
private void renderLine(@NotNull Player player, @NotNull World world,
|
private void renderLine(@NotNull Player player, @NotNull World world,
|
||||||
double x1, double y1, double z1,
|
double x1, double y1, double z1,
|
||||||
double x2, double y2, double z2) {
|
double x2, double y2, double z2,
|
||||||
|
@NotNull Set<Location> blocks) {
|
||||||
double distance = Math.sqrt(
|
double distance = Math.sqrt(
|
||||||
Math.pow(x2 - x1, 2) +
|
Math.pow(x2 - x1, 2) +
|
||||||
Math.pow(y2 - y1, 2) +
|
Math.pow(y2 - y1, 2) +
|
||||||
@@ -195,6 +248,7 @@ public class ForcefieldRenderer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
int steps = (int) Math.ceil(distance / particleSpacing);
|
int steps = (int) Math.ceil(distance / particleSpacing);
|
||||||
|
double blockSpacing = plugin.getConfig().getDouble("block-spacing", 1.0);
|
||||||
|
|
||||||
for (int i = 0; i <= steps; i++) {
|
for (int i = 0; i <= steps; i++) {
|
||||||
double t = steps > 0 ? (double) i / steps : 0;
|
double t = steps > 0 ? (double) i / steps : 0;
|
||||||
@@ -203,6 +257,11 @@ public class ForcefieldRenderer {
|
|||||||
double z = z1 + (z2 - z1) * t;
|
double z = z1 + (z2 - z1) * t;
|
||||||
|
|
||||||
spawnParticle(player, world, x, y, z);
|
spawnParticle(player, world, x, y, z);
|
||||||
|
|
||||||
|
// Place blocks at intervals
|
||||||
|
if (i % ((int) Math.max(1, blockSpacing / particleSpacing)) == 0) {
|
||||||
|
placeBlock(player, world, x, y, z, blocks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,4 +272,63 @@ public class ForcefieldRenderer {
|
|||||||
Location location = new Location(world, x, y, z);
|
Location location = new Location(world, x, y, z);
|
||||||
player.spawnParticle(Particle.DUST, location, 1, 0, 0, 0, 0, dustOptions);
|
player.spawnParticle(Particle.DUST, location, 1, 0, 0, 0, 0, dustOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places a fake block at the specified location if it's air.
|
||||||
|
*
|
||||||
|
* @param player the player to send the block to
|
||||||
|
* @param world the world
|
||||||
|
* @param x the x coordinate
|
||||||
|
* @param y the y coordinate
|
||||||
|
* @param z the z coordinate
|
||||||
|
* @param blocks the set to add this block location to
|
||||||
|
*/
|
||||||
|
private void placeBlock(@NotNull Player player, @NotNull World world,
|
||||||
|
double x, double y, double z, @NotNull Set<Location> blocks) {
|
||||||
|
if (!plugin.getConfig().getBoolean("render-blocks", true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Location location = new Location(world, Math.floor(x), Math.floor(y), Math.floor(z));
|
||||||
|
|
||||||
|
// Only place blocks where there's currently air
|
||||||
|
if (location.getBlock().getType() == Material.AIR) {
|
||||||
|
player.sendBlockChange(location, glassBlockData);
|
||||||
|
blocks.add(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all fake blocks for a player by restoring the real blocks.
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
*/
|
||||||
|
public void clearBlocks(@NotNull Player player) {
|
||||||
|
Set<Location> blocks = blockTracker.getBlocks(player);
|
||||||
|
for (Location location : blocks) {
|
||||||
|
// Send the real block data back to the player
|
||||||
|
player.sendBlockChange(location, location.getBlock().getBlockData());
|
||||||
|
}
|
||||||
|
blockTracker.clearPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates blocks for a player based on new blocks that should be visible.
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
* @param newBlocks the new set of blocks to show
|
||||||
|
*/
|
||||||
|
public void updateBlocks(@NotNull Player player, @NotNull Set<Location> newBlocks) {
|
||||||
|
Set<Location> oldBlocks = blockTracker.getBlocks(player);
|
||||||
|
|
||||||
|
// Remove blocks that are no longer needed
|
||||||
|
for (Location location : oldBlocks) {
|
||||||
|
if (!newBlocks.contains(location)) {
|
||||||
|
player.sendBlockChange(location, location.getBlock().getBlockData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the tracker
|
||||||
|
blockTracker.setBlocks(player, newBlocks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,21 +83,32 @@ public class RegionPermissionChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a player can enter a specific region.
|
* Checks if a player can actually enter a region.
|
||||||
|
* Takes into account entry deny flag, bypass permissions, and member/owner status.
|
||||||
*
|
*
|
||||||
* @param player the player to check
|
* @param player the player to check
|
||||||
* @param region the region to check
|
* @param region the region to check
|
||||||
* @return true if the player can enter, false otherwise
|
* @return true if the player CAN enter (no forcefield), false if blocked (show forcefield)
|
||||||
*/
|
*/
|
||||||
private boolean canEnterRegion(@NotNull LocalPlayer player, @NotNull ProtectedRegion region) {
|
private boolean canEnterRegion(@NotNull LocalPlayer player, @NotNull ProtectedRegion region) {
|
||||||
// Check the ENTRY flag
|
// Check the ENTRY flag
|
||||||
// If ENTRY is set to DENY, the player cannot enter unless they have bypass permissions
|
|
||||||
if (region.getFlag(Flags.ENTRY) == com.sk89q.worldguard.protection.flags.StateFlag.State.DENY) {
|
if (region.getFlag(Flags.ENTRY) == com.sk89q.worldguard.protection.flags.StateFlag.State.DENY) {
|
||||||
|
// Check if player has bypass permission (includes ops)
|
||||||
|
if (player.hasPermission("worldguard.region.bypass." + region.getId()) ||
|
||||||
|
player.hasPermission("worldguard.region.bypass.*")) {
|
||||||
|
return true; // Can enter (has bypass), no forcefield
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the player is a member or owner of the region
|
// Check if the player is a member or owner of the region
|
||||||
return region.isMember(player) || region.isOwner(player);
|
if (region.isMember(player) || region.isOwner(player)) {
|
||||||
|
return true; // Can enter (is member/owner), no forcefield
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player cannot enter, show forcefield
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If ENTRY is not explicitly denied, the player can enter
|
// If ENTRY is not explicitly denied, player can enter
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import loganintech.regionforcefield.RegionForcefieldPlugin;
|
|||||||
import loganintech.regionforcefield.forcefield.ForcefieldRenderer;
|
import loganintech.regionforcefield.forcefield.ForcefieldRenderer;
|
||||||
import loganintech.regionforcefield.region.RegionPermissionChecker;
|
import loganintech.regionforcefield.region.RegionPermissionChecker;
|
||||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||||
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,24 +43,30 @@ public class ForcefieldUpdateTask extends BukkitRunnable {
|
|||||||
try {
|
try {
|
||||||
// Iterate through all online players
|
// Iterate through all online players
|
||||||
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||||
// Get all regions the player cannot enter in their current world
|
// Get all regions the player should see forcefields for
|
||||||
Set<ProtectedRegion> blockedRegions = permissionChecker.getBlockedRegions(player, player.getWorld());
|
Set<ProtectedRegion> blockedRegions = permissionChecker.getBlockedRegions(player, player.getWorld());
|
||||||
|
|
||||||
if (!blockedRegions.isEmpty()) {
|
if (!blockedRegions.isEmpty()) {
|
||||||
plugin.debug("Processing " + blockedRegions.size() + " blocked regions for " + player.getName());
|
plugin.debug("Processing " + blockedRegions.size() + " blocked regions for " + player.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render forcefields for nearby blocked regions
|
// Collect all blocks that should be rendered for this player
|
||||||
|
Set<Location> allBlocks = new HashSet<>();
|
||||||
int rendered = 0;
|
int rendered = 0;
|
||||||
|
|
||||||
for (ProtectedRegion region : blockedRegions) {
|
for (ProtectedRegion region : blockedRegions) {
|
||||||
if (isRegionNearPlayer(player, region)) {
|
if (isRegionNearPlayer(player, region)) {
|
||||||
forcefieldRenderer.renderForcefield(player, region, player.getWorld());
|
Set<Location> regionBlocks = forcefieldRenderer.renderForcefield(player, region, player.getWorld());
|
||||||
|
allBlocks.addAll(regionBlocks);
|
||||||
rendered++;
|
rendered++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the player's blocks (remove old ones, keep new ones)
|
||||||
|
forcefieldRenderer.updateBlocks(player, allBlocks);
|
||||||
|
|
||||||
if (rendered > 0) {
|
if (rendered > 0) {
|
||||||
plugin.debug("Rendered " + rendered + " forcefields for " + player.getName());
|
plugin.debug("Rendered " + rendered + " forcefields (" + allBlocks.size() + " blocks) for " + player.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -26,3 +26,17 @@ particle-color:
|
|||||||
|
|
||||||
# Particle size (recommended range: 0.5 to 2.0)
|
# Particle size (recommended range: 0.5 to 2.0)
|
||||||
particle-size: 1.0
|
particle-size: 1.0
|
||||||
|
|
||||||
|
# Block rendering settings
|
||||||
|
# Whether to render actual blocks (glass panes) in addition to particles
|
||||||
|
render-blocks: true
|
||||||
|
|
||||||
|
# Distance between blocks (in blocks)
|
||||||
|
# Higher values = fewer blocks = better performance
|
||||||
|
# Must be >= particle-spacing
|
||||||
|
block-spacing: 1.0
|
||||||
|
|
||||||
|
# Block material to use for forcefields
|
||||||
|
# Examples: PURPLE_STAINED_GLASS_PANE, BARRIER, GLASS, LIGHT_BLUE_STAINED_GLASS
|
||||||
|
# See https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html for all options
|
||||||
|
block-material: PURPLE_STAINED_GLASS_PANE
|
||||||
|
|||||||
Reference in New Issue
Block a user