From 484e657d2dacaa690c861166c20b29a5d78de521 Mon Sep 17 00:00:00 2001 From: headhunter45 Date: Sun, 19 Feb 2012 17:15:17 -0800 Subject: [PATCH] Added a Cuboid constructor that takes a worldedit selection Added a debug mode that enables extra logging. MetropolisPlugin.DEBUG should be true for snapshots and false for release builds. Added an occupied Plots list to MetropolisPlugin to keep track of both player homes and reserved plots. Added the Plot class as a parent of PlayerHome and moved relevant code to it. Added a command to reserve plots that aren't tied to a player. This can be used to setup a larger protected area around spawn --- Metropolis/README.txt | 5 ++ .../bukkitplugins/metropolis/Cuboid.java | 36 +++++++- .../metropolis/MetropolisPlugin.java | 86 +++++++++++++----- .../bukkitplugins/metropolis/PlayerHome.java | 68 +++----------- .../bukkitplugins/metropolis/Plot.java | 89 +++++++++++++++++++ .../MetropolisPlotReserveCommand.java | 53 +++++++++++ Metropolis/src/main/resources/plugin.yml | 4 + 7 files changed, 261 insertions(+), 80 deletions(-) create mode 100644 Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/Plot.java create mode 100644 Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/commands/MetropolisPlotReserveCommand.java diff --git a/Metropolis/README.txt b/Metropolis/README.txt index b866027..c66b2e9 100644 --- a/Metropolis/README.txt +++ b/Metropolis/README.txt @@ -17,6 +17,11 @@ Edit the config.yml file. You can set the height to clear above roads, the road Changelog: v0.4.6 Added optional generation of a sign identifying owner in plots on creation + Added a Cuboid constructor that takes a worldedit selection + Added a debug mode that enables extra logging. MetropolisPlugin.DEBUG should be true for snapshots and false for release builds. + Added an occupied Plots list to MetropolisPlugin to keep track of both player homes and reserved plots. + Added the Plot class as a parent of PlayerHome and moved relevant code to it. + Added a command to reserve plots that aren't tied to a player. This can be used to setup a larger protected area around spawn v0.4.5 v0.4.4 Added a welcome message telling players where their home is. diff --git a/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/Cuboid.java b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/Cuboid.java index df302e1..3671dfb 100644 --- a/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/Cuboid.java +++ b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/Cuboid.java @@ -4,6 +4,7 @@ import java.util.logging.Logger; import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.bukkit.selections.Selection; public class Cuboid implements Comparable { private int id; @@ -51,6 +52,15 @@ public class Cuboid implements Comparable { this.maxZ = 0; } + public Cuboid(Selection selection) { + this.minX = selection.getMinimumPoint().getBlockX(); + this.minY = selection.getMinimumPoint().getBlockY(); + this.minZ = selection.getMinimumPoint().getBlockZ(); + this.maxX = selection.getMaximumPoint().getBlockX(); + this.maxY = selection.getMaximumPoint().getBlockY(); + this.maxZ = selection.getMaximumPoint().getBlockZ(); + } + public BlockVector getMin(){ return new BlockVector(minX, minY, minZ); } @@ -169,5 +179,29 @@ public class Cuboid implements Comparable { public int getCenterZ(){ return (this.minZ + this.maxZ)/2; } - + + public boolean intersects(Cuboid other) { + if (this.maxX >= other.minX && this.minX <= other.maxX) + { + if (this.maxZ >= other.minZ && this.minZ <= other.maxZ) + { + if (this.maxY >= other.minY && this.minY <= other.maxY) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } + else + { + return false; + } + } } diff --git a/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/MetropolisPlugin.java b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/MetropolisPlugin.java index e164317..ce03064 100644 --- a/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/MetropolisPlugin.java +++ b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/MetropolisPlugin.java @@ -19,8 +19,10 @@ import org.bukkit.plugin.java.JavaPlugin; import com.majinnaibu.bukkitplugins.metropolis.commands.MetropolisFlagResetCommand; import com.majinnaibu.bukkitplugins.metropolis.commands.MetropolisHomeGenerateCommand; import com.majinnaibu.bukkitplugins.metropolis.commands.MetropolisHomeListCommand; +import com.majinnaibu.bukkitplugins.metropolis.commands.MetropolisPlotReserveCommand; import com.majinnaibu.bukkitplugins.metropolis.eventlisteners.PlayerJoinListener; import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.domains.DefaultDomain; import com.sk89q.worldguard.protection.flags.DefaultFlag; @@ -30,14 +32,17 @@ import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; public class MetropolisPlugin extends JavaPlugin { + public static final boolean DEBUG = true; public static final Logger log=Logger.getLogger("Minecraft"); public PluginDescriptionFile pdf = null; public WorldGuardPlugin worldGuard = null; + public WorldEditPlugin worldEdit = null; public World world = null; public RegionManager regionManager = null; private List _occupiedHomes; + private List _occupiedPlots; private PlayerJoinListener _playerJoinListener = null; @@ -87,6 +92,12 @@ public class MetropolisPlugin extends JavaPlugin { worldGuard = (WorldGuardPlugin) plugin; + plugin = getServer().getPluginManager().getPlugin("WorldEdit"); + if(plugin == null || !(plugin instanceof WorldEditPlugin)){ + throw new RuntimeException("WorldEdit must be loaded first"); + } + worldEdit = (WorldEditPlugin) plugin; + world = getServer().getWorld(worldName); regionManager = worldGuard.getRegionManager(world); @@ -108,7 +119,7 @@ public class MetropolisPlugin extends JavaPlugin { } _occupiedHomes = new ArrayList(); - fillOccupiedHomes(); + fillOccupiedPlots(); resizeCityRegion(); if(_playerJoinListener == null){ @@ -120,21 +131,27 @@ public class MetropolisPlugin extends JavaPlugin { getCommand("metropolis-home-generate").setExecutor(new MetropolisHomeGenerateCommand(this)); getCommand("metropolis-home-list").setExecutor(new MetropolisHomeListCommand(this)); getCommand("metropolis-flag-reset").setExecutor(new MetropolisFlagResetCommand(this)); + getCommand("metropolis-plot-reserve").setExecutor(new MetropolisPlotReserveCommand(this)); } - private void fillOccupiedHomes() { + private void fillOccupiedPlots(){ + _occupiedPlots = new ArrayList(); _occupiedHomes = new ArrayList(); - for(ProtectedRegion region : regionManager.getRegions().values()){ - if(region instanceof ProtectedCuboidRegion && region.getId().startsWith("h_")){ + for(ProtectedRegion region: regionManager.getRegions().values()){ + if(region instanceof ProtectedCuboidRegion){ ProtectedCuboidRegion cuboidRegion = (ProtectedCuboidRegion) region; - PlayerHome home = new PlayerHome(cuboidRegion); - - _occupiedHomes.add(home); + if(cuboidRegion.getId().startsWith("h_")){ + PlayerHome home = PlayerHome.get(cuboidRegion); + _occupiedPlots.add(home); + _occupiedHomes.add(home); + }else if(cuboidRegion.getId().startsWith("r_")){ + _occupiedPlots.add(Plot.get(cuboidRegion)); + } } } - size = calculateCitySize(); + size=calculateCitySize(); } public PlayerHome getPlayerHome(Player player) { @@ -144,7 +161,9 @@ public class MetropolisPlugin extends JavaPlugin { ProtectedRegion homeRegion = regionManager.getRegion(regionName); if(homeRegion == null){ - log.info(String.format("Creating home for player %s", player.getName())); + if(DEBUG){ + log.info(String.format("Creating home for player %s", player.getName())); + } home = generateHome(player.getName()); }else{ home = new PlayerHome(homeRegion); @@ -178,7 +197,9 @@ public class MetropolisPlugin extends JavaPlugin { int z=0; if(plotCuboid == null){ - log.warning("plotCuboid is null"); + if(DEBUG){ + log.warning("plotCuboid is null"); + } return; } @@ -229,8 +250,9 @@ public class MetropolisPlugin extends JavaPlugin { } public boolean isBlockOccupied(int row, int col){ - for(PlayerHome home : _occupiedHomes){ - if(home.getRow(roadWidth, plotSizeZ) == row && home.getCol(roadWidth, plotSizeX) == col){ + Cuboid cuboid = new Cuboid(getPlotMin(row, col), getPlotMax(row, col)); + for(Plot plot: _occupiedPlots){ + if(plot.getCuboid().intersects(cuboid)){ return true; } } @@ -251,7 +273,7 @@ public class MetropolisPlugin extends JavaPlugin { for(col = -ring; col <= ring; col++){ if(!isBlockOccupied(row, col)){ if(row != 0 || col != 0){ - log.info(String.format("row: %d, col: %d", row, col)); + if(DEBUG){log.info(String.format("row: %d, col: %d", row, col));} return new Cuboid(getPlotMin(row, col), getPlotMax(row, col)); } } @@ -261,7 +283,7 @@ public class MetropolisPlugin extends JavaPlugin { for(row=-ring + 1; row < ring; row++){ if(!isBlockOccupied(row, col)){ if(row != 0 || col != 0){ - log.info(String.format("row: %d, col: %d", row, col)); + if(DEBUG){log.info(String.format("row: %d, col: %d", row, col));} return new Cuboid(getPlotMin(row, col), getPlotMax(row, col)); } } @@ -271,7 +293,7 @@ public class MetropolisPlugin extends JavaPlugin { for(col = ring; col >= -ring; col--){ if(!isBlockOccupied(row, col)){ if(row != 0 || col != 0){ - log.info(String.format("row: %d, col: %d", row, col)); + if(DEBUG){log.info(String.format("row: %d, col: %d", row, col));} return new Cuboid(getPlotMin(row, col), getPlotMax(row, col)); } } @@ -281,7 +303,7 @@ public class MetropolisPlugin extends JavaPlugin { for(row = ring; row > -ring; row--){ if(!isBlockOccupied(row, col)){ if(row != 0 || col != 0){ - log.info(String.format("row: %d, col: %d", row, col)); + if(DEBUG){log.info(String.format("row: %d, col: %d", row, col));} return new Cuboid(getPlotMin(row, col), getPlotMax(row, col)); } } @@ -290,7 +312,7 @@ public class MetropolisPlugin extends JavaPlugin { ring++; } - log.info(String.format("row: %d, col: %d", row, col)); + if(DEBUG){log.info(String.format("row: %d, col: %d", row, col));} return new Cuboid(getPlotMin(row, col), getPlotMax(row, col)); } @@ -317,11 +339,11 @@ public class MetropolisPlugin extends JavaPlugin { for(PlayerHome home: _occupiedHomes){ int plotCol=Math.abs(getPlotXFromMin(home.getCuboid())); int plotRow=Math.abs(getPlotZFromMin(home.getCuboid())); - log.info(String.format("col: %d, row: %d, iSize: %d", plotCol, plotRow, iSize)); + if(DEBUG){log.info(String.format("col: %d, row: %d, iSize: %d", plotCol, plotRow, iSize));} iSize = Math.max(Math.max(plotRow*2+1, plotCol*2+1), iSize); } - log.info(String.format("City size is %d", iSize)); + if(DEBUG){log.info(String.format("City size is %d", iSize));} return iSize; } @@ -356,7 +378,7 @@ public class MetropolisPlugin extends JavaPlugin { public PlayerHome generateHome(String playerName) { - log.info(String.format("Generating home for %s", playerName)); + if(DEBUG){log.info(String.format("Generating home for %s", playerName));} Cuboid plotCuboid = null; Cuboid homeCuboid = null; ProtectedRegion homeRegion = null; @@ -391,12 +413,12 @@ public class MetropolisPlugin extends JavaPlugin { generateFloor(plotCuboid); } - log.info(String.format("generateSign: %s", String.valueOf(generateSign))); + if(DEBUG){log.info(String.format("generateSign: %s", String.valueOf(generateSign)));} if(generateSign){ generateSign(plotCuboid, playerName); } - log.info(String.format("Done generating home for %s", playerName)); + if(DEBUG){log.info(String.format("Done generating home for %s", playerName));} return new PlayerHome(homeRegion); } @@ -419,4 +441,24 @@ public class MetropolisPlugin extends JavaPlugin { public List getCityBlocks() { return Collections.unmodifiableList(_occupiedHomes); } + + public World getWorld(){ + return world; + } + + public void reserveCuboid(String regionName, Cuboid cuboid) { + ProtectedCuboidRegion reservedRegion = new ProtectedCuboidRegion(regionName, cuboid.getMin(), cuboid.getMax()); + reservedRegion.setFlag(DefaultFlag.PVP, StateFlag.State.DENY); + reservedRegion.setFlag(DefaultFlag.MOB_DAMAGE, StateFlag.State.DENY); + reservedRegion.setFlag(DefaultFlag.MOB_SPAWNING, StateFlag.State.DENY); + reservedRegion.setFlag(DefaultFlag.CREEPER_EXPLOSION, StateFlag.State.DENY); + reservedRegion.setFlag(DefaultFlag.ENDER_BUILD, StateFlag.State.DENY); + reservedRegion.setFlag(DefaultFlag.GHAST_FIREBALL, StateFlag.State.DENY); + reservedRegion.setFlag(DefaultFlag.TNT, StateFlag.State.DENY); + reservedRegion.setFlag(DefaultFlag.LAVA_FLOW, StateFlag.State.DENY); + reservedRegion.setFlag(DefaultFlag.SNOW_FALL, StateFlag.State.DENY); + regionManager.addRegion(reservedRegion); + + _occupiedPlots.add(Plot.get(reservedRegion)); + } } diff --git a/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/PlayerHome.java b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/PlayerHome.java index f6f31b5..b824962 100644 --- a/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/PlayerHome.java +++ b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/PlayerHome.java @@ -1,7 +1,6 @@ package com.majinnaibu.bukkitplugins.metropolis; import javax.persistence.Entity; -import javax.persistence.Id; import javax.persistence.Table; import com.avaje.ebean.validation.NotNull; @@ -11,30 +10,20 @@ import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @Entity() -@Table(name="met_home") -public class PlayerHome implements Comparable{ - @Id - private int id; - +@Table(name="Metropolis_PlayerHome") +public class PlayerHome extends Plot{ @NotNull private String playerName; - - @NotNull - private Cuboid cuboid; - - @NotNull - private String regionName; - + public String getPlayerName(){return this.playerName;} + public void setPlayerName(String playerName){this.playerName = playerName;} + public PlayerHome(String owner, BlockVector min, BlockVector max) { - this.cuboid = new Cuboid(min, max); + super("h_" + owner, min, max); this.playerName = owner; - this.regionName = "h_" + owner; } public PlayerHome() { - this.cuboid = new Cuboid(); this.playerName = ""; - this.regionName = ""; } public PlayerHome(ProtectedRegion homeRegion){ @@ -46,7 +35,7 @@ public class PlayerHome implements Comparable{ this.playerName = cuboidRegion.getId(); } - this.cuboid = new Cuboid(cuboidRegion.getMinimumPoint(), cuboidRegion.getMaximumPoint()); + setCuboid(new Cuboid(cuboidRegion.getMinimumPoint(), cuboidRegion.getMaximumPoint())); }else if(homeRegion instanceof ProtectedPolygonalRegion){ ProtectedPolygonalRegion polygonalRegion = (ProtectedPolygonalRegion)homeRegion; if(polygonalRegion.getId().startsWith("h_") && polygonalRegion.getId().length() > 2){ @@ -55,22 +44,10 @@ public class PlayerHome implements Comparable{ this.playerName = polygonalRegion.getId(); } - this.cuboid = new Cuboid(polygonalRegion.getMinimumPoint(), polygonalRegion.getMaximumPoint()); + setCuboid(new Cuboid(polygonalRegion.getMinimumPoint(), polygonalRegion.getMaximumPoint())); } } - public int getId(){return this.id;} - public void setId(int id){this.id = id;} - - public String getPlayerName(){return this.playerName;} - public void setPlayerName(String playerName){this.playerName = playerName;} - - public Cuboid getCuboid(){return this.cuboid;} - public void setCuboid(Cuboid cuboid){this.cuboid = cuboid;} - - public String getRegionName(){return this.regionName;} - public void setRegionName(String regionName){this.regionName = regionName;} - @Override public boolean equals(Object other) { if(!(other instanceof PlayerHome)){ @@ -83,31 +60,13 @@ public class PlayerHome implements Comparable{ return false; } - if(!this.cuboid.equals(otherPlayerHome.cuboid)){ + if(!getCuboid().equals(otherPlayerHome.getCuboid())){ return false; } return true; } - @Override - public int compareTo(PlayerHome another) { - return cuboid.compareTo(another.cuboid); - } - - public BlockVector getPlotMin(int roadWidth) { - return new BlockVector(this.cuboid.minX - roadWidth/2, this.cuboid.minY, this.cuboid.minZ - roadWidth/2); - } - - @Override - public String toString(){ - StringBuilder sb = new StringBuilder(); - - sb.append(String.format("{PlayerHome }")); - - return sb.toString(); - } - public String toFriendlyString() { StringBuilder sb = new StringBuilder(); @@ -116,12 +75,7 @@ public class PlayerHome implements Comparable{ return sb.toString(); } - public int getRow(int roadWidth, int plotSizeZ){ - BlockVector min = getPlotMin(roadWidth); - return min.getBlockZ() / plotSizeZ; - } - - public int getCol(int roadWidth, int plotSizeX){ - return getPlotMin(roadWidth).getBlockX() / plotSizeX; + public static PlayerHome get(ProtectedCuboidRegion cuboidRegion){ + return new PlayerHome(cuboidRegion); } } diff --git a/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/Plot.java b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/Plot.java new file mode 100644 index 0000000..c627107 --- /dev/null +++ b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/Plot.java @@ -0,0 +1,89 @@ +package com.majinnaibu.bukkitplugins.metropolis; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.avaje.ebean.validation.NotNull; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; + +@Entity() +@Table(name="Metropolis_Plot") +public class Plot implements Comparable{ + @Id + private int _id; + public int getId(){return _id;} + public void setId(int id){_id = id;} + + @NotNull + private Cuboid _cuboid; + public Cuboid getCuboid(){return _cuboid;} + public void setCuboid(Cuboid cuboid){_cuboid = cuboid;} + + @NotNull + private String _regionName; + public String getRegionName(){return _regionName;} + public void setRegionName(String regionName){_regionName = regionName;} + + public Plot(String regionName, BlockVector min, BlockVector max){ + _cuboid = new Cuboid(min, max); + _regionName = regionName; + } + + public Plot(ProtectedCuboidRegion cuboid){ + _cuboid = new Cuboid(cuboid.getMinimumPoint(), cuboid.getMaximumPoint()); + _regionName = cuboid.getId(); + } + + public Plot(){ + _cuboid = new Cuboid(); + _regionName = ""; + } + + public BlockVector getPlotMin(int roadWidth) { + return new BlockVector(_cuboid.minX - roadWidth/2, _cuboid.minY, _cuboid.minZ - roadWidth/2); + } + + public int getRow(int roadWidth, int plotSizeZ){ + BlockVector min = getPlotMin(roadWidth); + return min.getBlockZ() / plotSizeZ; + } + + public int getCol(int roadWidth, int plotSizeX){ + return getPlotMin(roadWidth).getBlockX() / plotSizeX; + } + + @Override + public boolean equals(Object other) { + if(!(other instanceof Plot)){ + return super.equals(other); + } + + Plot otherPlayerHome = (Plot)other; + + if(!getCuboid().equals(otherPlayerHome.getCuboid())){ + return false; + } + + return true; + } + + @Override + public int compareTo(PlayerHome another) { + return getCuboid().compareTo(another.getCuboid()); + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + + sb.append(String.format("{PlayerHome }")); + + return sb.toString(); + } + + public static Plot get(ProtectedCuboidRegion cuboidRegion){ + return new Plot(cuboidRegion); + } +} diff --git a/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/commands/MetropolisPlotReserveCommand.java b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/commands/MetropolisPlotReserveCommand.java new file mode 100644 index 0000000..32bc139 --- /dev/null +++ b/Metropolis/src/main/java/com/majinnaibu/bukkitplugins/metropolis/commands/MetropolisPlotReserveCommand.java @@ -0,0 +1,53 @@ +package com.majinnaibu.bukkitplugins.metropolis.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.majinnaibu.bukkitplugins.metropolis.Cuboid; +import com.majinnaibu.bukkitplugins.metropolis.MetropolisPlugin; +import com.sk89q.worldedit.bukkit.selections.Selection; + +public class MetropolisPlotReserveCommand implements CommandExecutor { + MetropolisPlugin _plugin; + + public MetropolisPlotReserveCommand(MetropolisPlugin metropolisPlugin) { + _plugin = metropolisPlugin; + + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + Cuboid cuboid = new Cuboid(); + if(sender instanceof Player && args.length == 1){ + Selection selection = _plugin.worldEdit.getSelection((Player) sender); + cuboid = new Cuboid(selection); + }else if(args.length == 6){ + int minX = Integer.parseInt(args[1]); + int minY = Integer.parseInt(args[2]); + int minZ = Integer.parseInt(args[3]); + int maxX = Integer.parseInt(args[4]); + int maxY = Integer.parseInt(args[5]); + int maxZ = Integer.parseInt(args[6]); + cuboid = new Cuboid(minX, minY, minZ, maxX, maxY, maxZ); + }else if(args.length == 4){ + int minX = Integer.parseInt(args[1]); + int minY = 0; + int minZ = Integer.parseInt(args[2]); + int maxX = Integer.parseInt(args[3]); + int maxY = _plugin.getWorld().getMaxHeight(); + int maxZ = Integer.parseInt(args[4]); + cuboid = new Cuboid(minX, minY, minZ, maxX, maxY, maxZ); + }else{ + return false; + } + + String regionName = args[0]; + + _plugin.reserveCuboid(regionName, cuboid); + + return true; + } + +} diff --git a/Metropolis/src/main/resources/plugin.yml b/Metropolis/src/main/resources/plugin.yml index 3d28410..d4b03e1 100644 --- a/Metropolis/src/main/resources/plugin.yml +++ b/Metropolis/src/main/resources/plugin.yml @@ -17,3 +17,7 @@ commands: description: This command resets the WorldGuard flags for all managed regions (city and h_*). permission: metropolis.flag.reset usage: /metropolis-flag-reset + metropolis-plot-reserve: + description: This command reserves a plot so it won't be assigned as a home. + permission: metropolis.plot.reserve + usage: /metropolis-plot-reserve \ No newline at end of file