mirror of
https://github.com/loganintech/render-region-forcefield.git
synced 2026-05-30 22:31:15 +00:00
Vibe code a forcefield renderer
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package loganintech.regionforcefield;
|
||||
|
||||
import loganintech.regionforcefield.forcefield.ForcefieldRenderer;
|
||||
import loganintech.regionforcefield.region.RegionPermissionChecker;
|
||||
import loganintech.regionforcefield.task.ForcefieldUpdateTask;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Main plugin class for RegionForcefield.
|
||||
* Displays particle forcefields around WorldGuard regions that players cannot enter.
|
||||
*/
|
||||
public final class RegionForcefieldPlugin extends JavaPlugin {
|
||||
|
||||
private RegionPermissionChecker permissionChecker;
|
||||
private ForcefieldRenderer forcefieldRenderer;
|
||||
private ForcefieldUpdateTask updateTask;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Save default config
|
||||
saveDefaultConfig();
|
||||
|
||||
// Initialize components
|
||||
this.permissionChecker = new RegionPermissionChecker();
|
||||
this.forcefieldRenderer = new ForcefieldRenderer(this);
|
||||
|
||||
// Start the periodic update task
|
||||
this.updateTask = new ForcefieldUpdateTask(this, permissionChecker, forcefieldRenderer);
|
||||
long updateInterval = getConfig().getLong("update-interval-ticks", 20L);
|
||||
updateTask.runTaskTimer(this, 0L, updateInterval);
|
||||
|
||||
getLogger().info("RegionForcefield has been enabled!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// Cancel the update task
|
||||
if (updateTask != null) {
|
||||
updateTask.cancel();
|
||||
}
|
||||
|
||||
getLogger().info("RegionForcefield has been disabled!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the region permission checker.
|
||||
*
|
||||
* @return the permission checker
|
||||
*/
|
||||
@NotNull
|
||||
public RegionPermissionChecker getPermissionChecker() {
|
||||
return permissionChecker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the forcefield renderer.
|
||||
*
|
||||
* @return the forcefield renderer
|
||||
*/
|
||||
@NotNull
|
||||
public ForcefieldRenderer getForcefieldRenderer() {
|
||||
return forcefieldRenderer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package loganintech.regionforcefield.forcefield;
|
||||
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Renders particle forcefields around protected regions.
|
||||
*/
|
||||
public class ForcefieldRenderer {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final double particleSpacing;
|
||||
private final Particle.DustOptions dustOptions;
|
||||
|
||||
/**
|
||||
* Creates a new forcefield renderer.
|
||||
*
|
||||
* @param plugin the plugin instance
|
||||
*/
|
||||
public ForcefieldRenderer(@NotNull Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.particleSpacing = plugin.getConfig().getDouble("particle-spacing", 0.5);
|
||||
|
||||
// Get color from config or use default (cyan)
|
||||
int red = plugin.getConfig().getInt("particle-color.red", 0);
|
||||
int green = plugin.getConfig().getInt("particle-color.green", 255);
|
||||
int blue = plugin.getConfig().getInt("particle-color.blue", 255);
|
||||
float size = (float) plugin.getConfig().getDouble("particle-size", 1.0);
|
||||
|
||||
this.dustOptions = new Particle.DustOptions(Color.fromRGB(red, green, blue), size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a forcefield around a region for a specific player.
|
||||
*
|
||||
* @param player the player to show the forcefield to
|
||||
* @param region the region to render
|
||||
* @param world the world the region is in
|
||||
*/
|
||||
public void renderForcefield(@NotNull Player player, @NotNull ProtectedRegion region, @NotNull World world) {
|
||||
if (region instanceof ProtectedCuboidRegion) {
|
||||
renderCuboidForcefield(player, (ProtectedCuboidRegion) region, world);
|
||||
} else if (region instanceof ProtectedPolygonalRegion) {
|
||||
renderPolygonalForcefield(player, (ProtectedPolygonalRegion) region, world);
|
||||
} else {
|
||||
// For other region types, fall back to rendering a bounding box
|
||||
renderBoundingBoxForcefield(player, region, world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a forcefield for a cuboid region.
|
||||
*/
|
||||
private void renderCuboidForcefield(@NotNull Player player, @NotNull ProtectedCuboidRegion region, @NotNull World world) {
|
||||
BlockVector3 min = region.getMinimumPoint();
|
||||
BlockVector3 max = region.getMaximumPoint();
|
||||
|
||||
// Render vertical edges
|
||||
renderVerticalEdges(player, world, min, max);
|
||||
|
||||
// Render horizontal edges at top and bottom
|
||||
renderHorizontalEdges(player, world, min, max);
|
||||
|
||||
// Optionally render faces (walls)
|
||||
if (plugin.getConfig().getBoolean("render-walls", true)) {
|
||||
renderWalls(player, world, min, max);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a forcefield for a polygonal region.
|
||||
*/
|
||||
private void renderPolygonalForcefield(@NotNull Player player, @NotNull ProtectedPolygonalRegion region, @NotNull World world) {
|
||||
List<BlockVector2> points = region.getPoints();
|
||||
int minY = region.getMinimumPoint().y();
|
||||
int maxY = region.getMaximumPoint().y();
|
||||
|
||||
// Render vertical walls between each pair of points
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
BlockVector2 point1 = points.get(i);
|
||||
BlockVector2 point2 = points.get((i + 1) % points.size());
|
||||
|
||||
renderVerticalWall(player, world, point1.x(), point1.z(), point2.x(), point2.z(), minY, maxY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a bounding box forcefield for unsupported region types.
|
||||
*/
|
||||
private void renderBoundingBoxForcefield(@NotNull Player player, @NotNull ProtectedRegion region, @NotNull World world) {
|
||||
BlockVector3 min = region.getMinimumPoint();
|
||||
BlockVector3 max = region.getMaximumPoint();
|
||||
|
||||
renderVerticalEdges(player, world, min, max);
|
||||
renderHorizontalEdges(player, world, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the vertical edges of a cuboid.
|
||||
*/
|
||||
private void renderVerticalEdges(@NotNull Player player, @NotNull World world, @NotNull BlockVector3 min, @NotNull BlockVector3 max) {
|
||||
// Four vertical edges
|
||||
renderLine(player, world, min.x(), min.y(), min.z(), min.x(), max.y(), min.z());
|
||||
renderLine(player, world, max.x(), min.y(), min.z(), max.x(), max.y(), min.z());
|
||||
renderLine(player, world, min.x(), min.y(), max.z(), min.x(), max.y(), max.z());
|
||||
renderLine(player, world, max.x(), min.y(), max.z(), max.x(), max.y(), max.z());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the horizontal edges of a cuboid.
|
||||
*/
|
||||
private void renderHorizontalEdges(@NotNull Player player, @NotNull World world, @NotNull BlockVector3 min, @NotNull BlockVector3 max) {
|
||||
// Bottom edges
|
||||
renderLine(player, world, min.x(), min.y(), min.z(), max.x(), min.y(), min.z());
|
||||
renderLine(player, world, min.x(), min.y(), max.z(), max.x(), min.y(), max.z());
|
||||
renderLine(player, world, min.x(), min.y(), min.z(), min.x(), min.y(), max.z());
|
||||
renderLine(player, world, max.x(), min.y(), min.z(), max.x(), min.y(), max.z());
|
||||
|
||||
// Top edges
|
||||
renderLine(player, world, min.x(), max.y(), min.z(), max.x(), max.y(), min.z());
|
||||
renderLine(player, world, min.x(), max.y(), max.z(), max.x(), max.y(), max.z());
|
||||
renderLine(player, world, min.x(), max.y(), min.z(), min.x(), max.y(), max.z());
|
||||
renderLine(player, world, max.x(), max.y(), min.z(), max.x(), max.y(), max.z());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the walls (faces) of a cuboid.
|
||||
*/
|
||||
private void renderWalls(@NotNull Player player, @NotNull World world, @NotNull BlockVector3 min, @NotNull BlockVector3 max) {
|
||||
// North wall (min Z)
|
||||
renderVerticalWall(player, world, min.x(), min.z(), max.x(), min.z(), min.y(), max.y());
|
||||
|
||||
// South wall (max Z)
|
||||
renderVerticalWall(player, world, min.x(), max.z(), max.x(), max.z(), min.y(), max.y());
|
||||
|
||||
// West wall (min X)
|
||||
renderVerticalWall(player, world, min.x(), min.z(), min.x(), max.z(), min.y(), max.y());
|
||||
|
||||
// East wall (max X)
|
||||
renderVerticalWall(player, world, max.x(), min.z(), max.x(), max.z(), min.y(), max.y());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a vertical wall between two points.
|
||||
*/
|
||||
private void renderVerticalWall(@NotNull Player player, @NotNull World world,
|
||||
double x1, double z1, double x2, double z2,
|
||||
double minY, double maxY) {
|
||||
double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(z2 - z1, 2));
|
||||
int horizontalSteps = (int) Math.ceil(distance / particleSpacing);
|
||||
int verticalSteps = (int) Math.ceil((maxY - minY) / particleSpacing);
|
||||
|
||||
for (int i = 0; i <= horizontalSteps; i++) {
|
||||
double t = horizontalSteps > 0 ? (double) i / horizontalSteps : 0;
|
||||
double x = x1 + (x2 - x1) * t;
|
||||
double z = z1 + (z2 - z1) * t;
|
||||
|
||||
for (int j = 0; j <= verticalSteps; j++) {
|
||||
double y = minY + (maxY - minY) * ((double) j / verticalSteps);
|
||||
spawnParticle(player, world, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a line of particles between two points.
|
||||
*/
|
||||
private void renderLine(@NotNull Player player, @NotNull World world,
|
||||
double x1, double y1, double z1,
|
||||
double x2, double y2, double z2) {
|
||||
double distance = Math.sqrt(
|
||||
Math.pow(x2 - x1, 2) +
|
||||
Math.pow(y2 - y1, 2) +
|
||||
Math.pow(z2 - z1, 2)
|
||||
);
|
||||
|
||||
int steps = (int) Math.ceil(distance / particleSpacing);
|
||||
|
||||
for (int i = 0; i <= steps; i++) {
|
||||
double t = steps > 0 ? (double) i / steps : 0;
|
||||
double x = x1 + (x2 - x1) * t;
|
||||
double y = y1 + (y2 - y1) * t;
|
||||
double z = z1 + (z2 - z1) * t;
|
||||
|
||||
spawnParticle(player, world, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a single particle at the specified location for a player.
|
||||
*/
|
||||
private void spawnParticle(@NotNull Player player, @NotNull World world, double x, double y, double z) {
|
||||
Location location = new Location(world, x, y, z);
|
||||
player.spawnParticle(Particle.DUST, location, 1, 0, 0, 0, 0, dustOptions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package loganintech.regionforcefield.region;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.protection.flags.Flags;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Checks whether players have permission to enter WorldGuard regions.
|
||||
*/
|
||||
public class RegionPermissionChecker {
|
||||
|
||||
/**
|
||||
* Gets all regions in a world that the specified player cannot enter.
|
||||
*
|
||||
* @param player the player to check
|
||||
* @param world the world to check regions in
|
||||
* @return a set of regions the player cannot enter
|
||||
*/
|
||||
@NotNull
|
||||
public Set<ProtectedRegion> getBlockedRegions(@NotNull Player player, @NotNull World world) {
|
||||
Set<ProtectedRegion> blockedRegions = new HashSet<>();
|
||||
|
||||
// Get the region manager for this world
|
||||
RegionManager regionManager = WorldGuard.getInstance()
|
||||
.getPlatform()
|
||||
.getRegionContainer()
|
||||
.get(BukkitAdapter.adapt(world));
|
||||
|
||||
if (regionManager == null) {
|
||||
return blockedRegions;
|
||||
}
|
||||
|
||||
// Convert Bukkit player to WorldGuard LocalPlayer
|
||||
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
|
||||
|
||||
// Check each region
|
||||
for (ProtectedRegion region : regionManager.getRegions().values()) {
|
||||
if (!canEnterRegion(localPlayer, region)) {
|
||||
blockedRegions.add(region);
|
||||
}
|
||||
}
|
||||
|
||||
return blockedRegions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a player can enter a specific region.
|
||||
*
|
||||
* @param player the player to check
|
||||
* @param region the region to check
|
||||
* @return true if the player can enter, false otherwise
|
||||
*/
|
||||
private boolean canEnterRegion(@NotNull LocalPlayer player, @NotNull ProtectedRegion region) {
|
||||
// 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) {
|
||||
// Check if the player is a member or owner of the region
|
||||
return region.isMember(player) || region.isOwner(player);
|
||||
}
|
||||
|
||||
// If ENTRY is not explicitly denied, the player can enter
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package loganintech.regionforcefield.task;
|
||||
|
||||
import loganintech.regionforcefield.RegionForcefieldPlugin;
|
||||
import loganintech.regionforcefield.forcefield.ForcefieldRenderer;
|
||||
import loganintech.regionforcefield.region.RegionPermissionChecker;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Periodic task that updates and renders forcefields for all online players.
|
||||
*/
|
||||
public class ForcefieldUpdateTask extends BukkitRunnable {
|
||||
|
||||
private final RegionForcefieldPlugin plugin;
|
||||
private final RegionPermissionChecker permissionChecker;
|
||||
private final ForcefieldRenderer forcefieldRenderer;
|
||||
private final int maxRenderDistance;
|
||||
|
||||
/**
|
||||
* Creates a new forcefield update task.
|
||||
*
|
||||
* @param plugin the plugin instance
|
||||
* @param permissionChecker the permission checker
|
||||
* @param forcefieldRenderer the forcefield renderer
|
||||
*/
|
||||
public ForcefieldUpdateTask(@NotNull RegionForcefieldPlugin plugin,
|
||||
@NotNull RegionPermissionChecker permissionChecker,
|
||||
@NotNull ForcefieldRenderer forcefieldRenderer) {
|
||||
this.plugin = plugin;
|
||||
this.permissionChecker = permissionChecker;
|
||||
this.forcefieldRenderer = forcefieldRenderer;
|
||||
this.maxRenderDistance = plugin.getConfig().getInt("max-render-distance", 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Iterate through all online players
|
||||
for (Player player : plugin.getServer().getOnlinePlayers()) {
|
||||
// Get all regions the player cannot enter in their current world
|
||||
Set<ProtectedRegion> blockedRegions = permissionChecker.getBlockedRegions(player, player.getWorld());
|
||||
|
||||
// Render forcefields for nearby blocked regions
|
||||
for (ProtectedRegion region : blockedRegions) {
|
||||
if (isRegionNearPlayer(player, region)) {
|
||||
forcefieldRenderer.renderForcefield(player, region, player.getWorld());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a region is near enough to a player to render.
|
||||
*
|
||||
* @param player the player
|
||||
* @param region the region
|
||||
* @return true if the region should be rendered for this player
|
||||
*/
|
||||
private boolean isRegionNearPlayer(@NotNull Player player, @NotNull ProtectedRegion region) {
|
||||
// Get player's position
|
||||
double playerX = player.getLocation().getX();
|
||||
double playerY = player.getLocation().getY();
|
||||
double playerZ = player.getLocation().getZ();
|
||||
|
||||
// Get region's center (approximate)
|
||||
double regionCenterX = (region.getMinimumPoint().x() + region.getMaximumPoint().x()) / 2.0;
|
||||
double regionCenterY = (region.getMinimumPoint().y() + region.getMaximumPoint().y()) / 2.0;
|
||||
double regionCenterZ = (region.getMinimumPoint().z() + region.getMaximumPoint().z()) / 2.0;
|
||||
|
||||
// Calculate distance
|
||||
double distance = Math.sqrt(
|
||||
Math.pow(playerX - regionCenterX, 2) +
|
||||
Math.pow(playerY - regionCenterY, 2) +
|
||||
Math.pow(playerZ - regionCenterZ, 2)
|
||||
);
|
||||
|
||||
return distance <= maxRenderDistance;
|
||||
}
|
||||
}
|
||||
25
src/main/resources/config.yml
Normal file
25
src/main/resources/config.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
# RegionForcefield Configuration
|
||||
|
||||
# How often to update forcefields (in ticks, 20 ticks = 1 second)
|
||||
update-interval-ticks: 20
|
||||
|
||||
# Maximum distance (in blocks) at which forcefields will be rendered
|
||||
# Reducing this can improve performance on servers with many regions
|
||||
max-render-distance: 100
|
||||
|
||||
# Distance between particles (in blocks)
|
||||
# Smaller values = more particles = more detailed forcefields but more performance intensive
|
||||
particle-spacing: 0.5
|
||||
|
||||
# Whether to render the walls (faces) of regions, or just the edges
|
||||
# Setting to false will only render the outlines/edges
|
||||
render-walls: true
|
||||
|
||||
# Particle color (RGB values from 0-255)
|
||||
particle-color:
|
||||
red: 0
|
||||
green: 255
|
||||
blue: 255
|
||||
|
||||
# Particle size (recommended range: 0.5 to 2.0)
|
||||
particle-size: 1.0
|
||||
8
src/main/resources/plugin.yml
Normal file
8
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
name: RegionForcefield
|
||||
version: ${version}
|
||||
main: loganintech.regionforcefield.RegionForcefieldPlugin
|
||||
api-version: '1.21'
|
||||
depend: [WorldGuard]
|
||||
author: loganintech
|
||||
description: Renders visible forcefields around WorldGuard regions that players cannot enter
|
||||
website: https://github.com/loganintech
|
||||
Reference in New Issue
Block a user