Expand City and bound plot search
This commit is contained in:
@@ -24,11 +24,13 @@ Use this checklist for behavior that depends on a real Paper world, player inter
|
|||||||
- Set `plot.initial: 0` on a disposable server, restart, and join as a fresh player. Confirm no home is created until the player runs `/metropolis-home-acquire`; then verify the command creates the initial home and makes it active.
|
- Set `plot.initial: 0` on a disposable server, restart, and join as a fresh player. Confirm no home is created until the player runs `/metropolis-home-acquire`; then verify the command creates the initial home and makes it active.
|
||||||
- Join with a second new player. Confirm the new home is distinct, does not overlap the first home or spawn, and the City region grows to include it.
|
- Join with a second new player. Confirm the new home is distinct, does not overlap the first home or spawn, and the City region grows to include it.
|
||||||
- Start from a disposable world configured with `plot.sizeY` and `plot.maxLevels` from the multi-level example. Confirm the level count is honored, the city does not exceed the world's build height, and upper plots are only allocated above footprints fully supported at every lower level. With a 1×1 spawn and a 2×2 home size, confirm a new home is allocated at base level rather than above the partially supported spawn.
|
- Start from a disposable world configured with `plot.sizeY` and `plot.maxLevels` from the multi-level example. Confirm the level count is honored, the city does not exceed the world's build height, and upper plots are only allocated above footprints fully supported at every lower level. With a 1×1 spawn and a 2×2 home size, confirm a new home is allocated at base level rather than above the partially supported spawn.
|
||||||
|
- Generate homes at base and elevated levels, then verify each full home cuboid is inside the WorldGuard `City` region. Add a home/reservation extending the city bounds and confirm City expands without shrinking its existing bounds or flags.
|
||||||
- Set positive and negative `plot.offsetX`, `plot.offsetY`, and `plot.offsetZ` values on a disposable world. Verify plot regions, road bands, stair flights, and Spawn share the shifted grid origin; allocate multiple plots across negative coordinates and confirm logical-cell spacing stays consistent. Confirm changing offsets does not move existing saved regions; Metropolis-managed homes and reservations should retain their bounds and remain recognized. Treat automatic conversion of other saved regions as unverified.
|
- Set positive and negative `plot.offsetX`, `plot.offsetY`, and `plot.offsetZ` values on a disposable world. Verify plot regions, road bands, stair flights, and Spawn share the shifted grid origin; allocate multiple plots across negative coordinates and confirm logical-cell spacing stays consistent. Confirm changing offsets does not move existing saved regions; Metropolis-managed homes and reservations should retain their bounds and remain recognized. Treat automatic conversion of other saved regions as unverified.
|
||||||
- Set `spawn.sizeX`, `spawn.sizeY`, and `spawn.sizeZ` to distinct positive values on a fresh disposable world. Confirm the Spawn region spans the corresponding plot-plus-road logical cells horizontally and the configured plot-height multiple vertically. Check blocks outside those bounds remain unchanged; configure a height exceeding the world's build limit and confirm startup rejects it clearly.
|
- Set `spawn.sizeX`, `spawn.sizeY`, and `spawn.sizeZ` to distinct positive values on a fresh disposable world. Confirm the Spawn region spans the corresponding plot-plus-road logical cells horizontally and the configured plot-height multiple vertically. Check blocks outside those bounds remain unchanged; configure a height exceeding the world's build limit and confirm startup rejects it clearly.
|
||||||
- Join again as an existing player. Confirm the same home is selected and no duplicate region or plot is created.
|
- Join again as an existing player. Confirm the same home is selected and no duplicate region or plot is created.
|
||||||
- Reserve an area before allocating another home. Confirm future allocations skip it. Repeat with reservations near the city edge and near existing roads to look for overlaps or gaps.
|
- Reserve an area before allocating another home. Confirm future allocations skip it. Repeat with reservations near the city edge and near existing roads to look for overlaps or gaps.
|
||||||
- Create an unrelated saved WorldGuard region in the next candidate area, then allocate a home and confirm it is skipped. Repeat with a non-cuboid region and verify the full bounding cuboid is avoided; confirm `City` itself does not prevent allocation.
|
- Create an unrelated saved WorldGuard region in the next candidate area, then allocate a home and confirm it is skipped. Repeat with a non-cuboid region and verify the full bounding cuboid is avoided; confirm `City` itself does not prevent allocation.
|
||||||
|
- Set a small WorldBorder in a disposable world, occupy or reserve the remaining valid grid cells, and request another home. Confirm search grows beyond 256 rings but never places a plot/road footprint outside the border; when no supported cell remains, check the error log and confirm no region is created.
|
||||||
- Test a user override that grants a different plot multiplier or home limit. Confirm the generated bounds and allowed number of homes match the override, and that the configured global defaults still apply to other players.
|
- Test a user override that grants a different plot multiplier or home limit. Confirm the generated bounds and allowed number of homes match the override, and that the configured global defaults still apply to other players.
|
||||||
|
|
||||||
### Generated blocks and geometry
|
### Generated blocks and geometry
|
||||||
@@ -78,12 +80,14 @@ Use this checklist for behavior that depends on a real Paper world, player inter
|
|||||||
|
|
||||||
## Existing Automated Coverage
|
## Existing Automated Coverage
|
||||||
|
|
||||||
Run `./gradlew test` for the server-free JUnit suite. It has 58 tests covering:
|
Run `./gradlew test` for the server-free JUnit suite. It has 62 tests covering:
|
||||||
|
|
||||||
- `AvenueStairwayLayoutTest`: centered per-segment street and avenue flights, both border directions, independent cadence, top-tread alignment, inverted backing plan, exact-width openings, and invalid-fit rejection.
|
- `AvenueStairwayLayoutTest`: centered per-segment street and avenue flights, both border directions, independent cadence, top-tread alignment, inverted backing plan, exact-width openings, and invalid-fit rejection.
|
||||||
- `PlotLevelSupportTest`: rejection of upper plots with missing logical support cells and complete coverage requirements across all lower levels.
|
- `PlotLevelSupportTest`: rejection of upper plots with missing logical support cells and complete coverage requirements across all lower levels.
|
||||||
|
- `CityRegionBoundsTest`: City bounds expand to include elevated plots and Spawn without shrinking existing protection bounds.
|
||||||
- `PlotGridLayoutTest`: zero-offset compatibility, offsets on X/Y/Z, axis-specific spacing, and negative logical-index recovery.
|
- `PlotGridLayoutTest`: zero-offset compatibility, offsets on X/Y/Z, axis-specific spacing, and negative logical-index recovery.
|
||||||
- `WorldGuardRegionOccupancyTest`: arbitrary saved-region bounds are excluded while City/global regions are treated as non-occupying envelopes.
|
- `WorldGuardRegionOccupancyTest`: arbitrary saved-region bounds are excluded while City/global regions are treated as non-occupying envelopes.
|
||||||
|
- `PlotWorldBorderBoundsTest`: search bounds follow WorldBorder size/center and require the full candidate footprint inside it.
|
||||||
- `SpawnLayoutTest`: default one-cell bounds, independent multipliers, and build-height validation.
|
- `SpawnLayoutTest`: default one-cell bounds, independent multipliers, and build-height validation.
|
||||||
- `AvenueStairwayBuilderTest`: configured stair material and facing/half data for avenue and street directions, upside-down backing stairs, exact upper-road AIR placements, and no block writes at lower-road Y.
|
- `AvenueStairwayBuilderTest`: configured stair material and facing/half data for avenue and street directions, upside-down backing stairs, exact upper-road AIR placements, and no block writes at lower-road Y.
|
||||||
- `RoadLayoutTest`: independently sized street/avenue bands, correct road-type tagging, non-overlapping corners, and disabled roads at zero width.
|
- `RoadLayoutTest`: independently sized street/avenue bands, correct road-type tagging, non-overlapping corners, and disabled roads at zero width.
|
||||||
|
|||||||
@@ -64,12 +64,16 @@ Copy `.env.example` to `.env` and configure the Paper version/build and server p
|
|||||||
|
|
||||||
Before allocating, Metropolis treats every saved WorldGuard region except the enclosing `City` and special `__global__` region as occupied. It uses each region's bounding cuboid; for non-cuboid regions this can conservatively leave extra gaps to ensure new plot regions do not overlap them.
|
Before allocating, Metropolis treats every saved WorldGuard region except the enclosing `City` and special `__global__` region as occupied. It uses each region's bounding cuboid; for non-cuboid regions this can conservatively leave extra gaps to ensure new plot regions do not overlap them.
|
||||||
|
|
||||||
|
Plot allocation continues outward up to the configured WorldBorder. The complete logical plot-and-road footprint must fit inside the border; if no supported, unoccupied location remains, Metropolis logs an error and creates no region.
|
||||||
|
|
||||||
Set `spawn.sizeX`, `spawn.sizeY`, and `spawn.sizeZ` to positive logical-grid multipliers. X and Z include the matching plot dimension plus road spacing; Y sets the spawn volume's height upward from `road.level` in multiples of `plot.sizeY`. All default to `1`. A newly created Spawn region must fit within the world's build height, and generation clears only within that region's configured vertical bounds. Existing saved Spawn regions retain their saved bounds.
|
Set `spawn.sizeX`, `spawn.sizeY`, and `spawn.sizeZ` to positive logical-grid multipliers. X and Z include the matching plot dimension plus road spacing; Y sets the spawn volume's height upward from `road.level` in multiples of `plot.sizeY`. All default to `1`. A newly created Spawn region must fit within the world's build height, and generation clears only within that region's configured vertical bounds. Existing saved Spawn regions retain their saved bounds.
|
||||||
|
|
||||||
The `road.streets.*` and `road.avenues.*` sections independently configure width, level, material, clearance, supports, and stairs. Streets run along X; avenues run along Z. Each section's stair settings (`generate`, `material`, `width`, and `everyNBlocks`) use that section's road width and cadence. Street stair runs ascend along positive X at the north/south borders; avenue runs ascend along positive Z at the east/west borders. Runs are centered within each eligible logical plot-sized segment. The lower road remains the normal-material landing; stairs begin one block above and forward from it. Connected following treads use upside-down stairs facing back down the run as backing, and the final tread reaches the upper road Y. The three blocks before each top tread remain open only across the stair width.
|
The `road.streets.*` and `road.avenues.*` sections independently configure width, level, material, clearance, supports, and stairs. Streets run along X; avenues run along Z. Each section's stair settings (`generate`, `material`, `width`, and `everyNBlocks`) use that section's road width and cadence. Street stair runs ascend along positive X at the north/south borders; avenue runs ascend along positive Z at the east/west borders. Runs are centered within each eligible logical plot-sized segment. The lower road remains the normal-material landing; stairs begin one block above and forward from it. Connected following treads use upside-down stairs facing back down the run as backing, and the final tread reaches the upper road Y. The three blocks before each top tread remain open only across the stair width.
|
||||||
|
|
||||||
Upper-level plots require complete generated support below: every logical plot cell in their footprint must be occupied by a generated home or spawn at every lower level. If a footprint has gaps below, allocation skips that upper candidate and keeps searching for a supported lower-level location.
|
Upper-level plots require complete generated support below: every logical plot cell in their footprint must be occupied by a generated home or spawn at every lower level. If a footprint has gaps below, allocation skips that upper candidate and keeps searching for a supported lower-level location.
|
||||||
|
|
||||||
|
The WorldGuard `City` region expands in all three dimensions to contain its previous bounds, the requested city footprint, Spawn, and generated homes/reservations. New upper-level plots therefore remain inside City protection; adding a home or reservation also triggers a resize.
|
||||||
|
|
||||||
The breaking multi-level schema is documented in the example config at `src/main/resources/examples/multi-level.yml`. It uses split road sections (`road.streets.*` and `road.avenues.*`), a level-aware `plot.maxLevels` field, and the valid stair material `COBBLESTONE_STAIRS`.
|
The breaking multi-level schema is documented in the example config at `src/main/resources/examples/multi-level.yml`. It uses split road sections (`road.streets.*` and `road.avenues.*`), a level-aware `plot.maxLevels` field, and the valid stair material `COBBLESTONE_STAIRS`.
|
||||||
|
|
||||||
Roads are generated at their full configured width when a plot is created. Generating an adjacent plot does not rewrite road blocks that are already correct; it only fills missing blocks, including gaps left by older half-width generation. Existing stair treads and upper-road openings are preserved.
|
Roads are generated at their full configured width when a plot is created. Generating an adjacent plot does not rewrite road blocks that are already correct; it only fills missing blocks, including gaps left by older half-width generation. Existing stair treads and upper-road openings are preserved.
|
||||||
|
|||||||
4
TODO.md
4
TODO.md
@@ -31,7 +31,7 @@
|
|||||||
| GIT-009 | Planning | i have three reserve plots and one just got overwritten when a new player joined. |
|
| GIT-009 | Planning | i have three reserve plots and one just got overwritten when a new player joined. |
|
||||||
| GIT-008 | Ready | Implement multiple plot sizes by granting multiple plots around each other.
|
| GIT-008 | Ready | Implement multiple plot sizes by granting multiple plots around each other.
|
||||||
| GIT-007 | Planning | Allow buying and selling of homes via an ecnoomy plugin. |
|
| GIT-007 | Planning | Allow buying and selling of homes via an ecnoomy plugin. |
|
||||||
| GIT-006 | Ready | Add levels support. |
|
| GIT-006 | Done | Add levels support. |
|
||||||
| GIT-005 | Planning | Add the possibility to create multiple plot homes in shapes. |
|
| GIT-005 | Planning | Add the possibility to create multiple plot homes in shapes. |
|
||||||
| GIT-004 | Done | Add a command to request home allocation. |
|
| GIT-004 | Done | Add a command to request home allocation. |
|
||||||
| GIT-003 | Ready | Add support for multiple homes. |
|
| GIT-003 | Ready | Add support for multiple homes. |
|
||||||
@@ -86,7 +86,7 @@ Add the possibility to create multiple plot homes in contiguous shapes.
|
|||||||
* Sphere closest to a point.
|
* Sphere closest to a point.
|
||||||
|
|
||||||
# GIT-006 - Add levels support.
|
# GIT-006 - Add levels support.
|
||||||
**Status:** Ready
|
**Status:** Done
|
||||||
**Description:**
|
**Description:**
|
||||||
|
|
||||||
Add multiple vertical levels to the city. Use the existing `road.level` as the base Y: the bottom of a ground-level plot is at that Y, and `plot.sizeY` defines the full plot height. For example, a five-block-high plot includes its floor at road level, three usable blocks, and a roof block; all five blocks belong to the plot region. Use the same plot-dimension-plus-road-spacing grid pitch vertically as horizontally; when road width is zero, adjacent plot volumes may touch. Do not allocate a plot if its full extent would exceed the world's buildable area. If the search cannot find any valid plot, log an allocation error and do not create the plot or region. The current config example is not intended for a multilevel world; request an updated `config.yml` before implementation. Existing `plot.offsetX`, `plot.offsetY`, and `plot.offsetZ` nudge the grid; `plot.multiplier`, `userOverrides[username].plotMultiplier`, and future `permissionOverrides[permission_name].plotMultiplier` affect plot size.
|
Add multiple vertical levels to the city. Use the existing `road.level` as the base Y: the bottom of a ground-level plot is at that Y, and `plot.sizeY` defines the full plot height. For example, a five-block-high plot includes its floor at road level, three usable blocks, and a roof block; all five blocks belong to the plot region. Use the same plot-dimension-plus-road-spacing grid pitch vertically as horizontally; when road width is zero, adjacent plot volumes may touch. Do not allocate a plot if its full extent would exceed the world's buildable area. If the search cannot find any valid plot, log an allocation error and do not create the plot or region. The current config example is not intended for a multilevel world; request an updated `config.yml` before implementation. Existing `plot.offsetX`, `plot.offsetY`, and `plot.offsetZ` nudge the grid; `plot.multiplier`, `userOverrides[username].plotMultiplier`, and future `permissionOverrides[permission_name].plotMultiplier` affect plot size.
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Metropolis.
|
||||||
|
|
||||||
|
Metropolis 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.
|
||||||
|
|
||||||
|
Metropolis 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 Metropolis. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.majinnaibu.bukkitplugins.metropolis;
|
||||||
|
|
||||||
|
public final class CityRegionBounds {
|
||||||
|
private CityRegionBounds() {}
|
||||||
|
|
||||||
|
public static Cuboid expandToContain(
|
||||||
|
Cuboid existing, Cuboid requested, Iterable<Cuboid> occupiedRegions) {
|
||||||
|
int minX = Math.min(existing.minX, requested.minX);
|
||||||
|
int minY = Math.min(existing.minY, requested.minY);
|
||||||
|
int minZ = Math.min(existing.minZ, requested.minZ);
|
||||||
|
int maxX = Math.max(existing.maxX, requested.maxX);
|
||||||
|
int maxY = Math.max(existing.maxY, requested.maxY);
|
||||||
|
int maxZ = Math.max(existing.maxZ, requested.maxZ);
|
||||||
|
|
||||||
|
for (Cuboid occupied : occupiedRegions) {
|
||||||
|
minX = Math.min(minX, occupied.minX);
|
||||||
|
minY = Math.min(minY, occupied.minY);
|
||||||
|
minZ = Math.min(minZ, occupied.minZ);
|
||||||
|
maxX = Math.max(maxX, occupied.maxX);
|
||||||
|
maxY = Math.max(maxY, occupied.maxY);
|
||||||
|
maxZ = Math.max(maxZ, occupied.maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Cuboid(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -209,7 +209,7 @@ public class MetropolisPlugin extends JavaPlugin {
|
|||||||
plotOffsetX = safeGetIntFromConfig(config, "plot.offsetX");
|
plotOffsetX = safeGetIntFromConfig(config, "plot.offsetX");
|
||||||
plotOffsetY = safeGetIntFromConfig(config, "plot.offsetY");
|
plotOffsetY = safeGetIntFromConfig(config, "plot.offsetY");
|
||||||
plotOffsetZ = safeGetIntFromConfig(config, "plot.offsetZ");
|
plotOffsetZ = safeGetIntFromConfig(config, "plot.offsetZ");
|
||||||
maxLevels = optionalIntFromConfig(config, "plot.maxLevels", "maxLevels", 1);
|
maxLevels = optionalIntFromConfig(config, "plot.maxLevels", 1);
|
||||||
generateFloor = safeGetBooleanFromConfig(config, "plot.floor.generate");
|
generateFloor = safeGetBooleanFromConfig(config, "plot.floor.generate");
|
||||||
floorMaterial = safeGetMaterialFromConfig(config, "plot.floor.material");
|
floorMaterial = safeGetMaterialFromConfig(config, "plot.floor.material");
|
||||||
spaceAboveFloor = safeGetIntFromConfig(config, "plot.floor.clearSpaceAbove");
|
spaceAboveFloor = safeGetIntFromConfig(config, "plot.floor.clearSpaceAbove");
|
||||||
@@ -919,14 +919,26 @@ public class MetropolisPlugin extends JavaPlugin {
|
|||||||
int ring = 0;
|
int ring = 0;
|
||||||
int min = -ring;
|
int min = -ring;
|
||||||
int max = ring - (plotMultiplier - 1);
|
int max = ring - (plotMultiplier - 1);
|
||||||
boolean done = false;
|
|
||||||
int levelCount = getEffectiveMaxLevels();
|
int levelCount = getEffectiveMaxLevels();
|
||||||
if (levelCount == 0) {
|
if (levelCount == 0) {
|
||||||
getLogger().severe("No configured plot level fits within the world's build height.");
|
getLogger().severe("No configured plot level fits within the world's build height.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!done) {
|
var worldBorder = world.getWorldBorder();
|
||||||
|
Location borderCenter = worldBorder.getCenter();
|
||||||
|
int maxSearchRing =
|
||||||
|
PlotWorldBorderBounds.maximumSearchRing(
|
||||||
|
worldBorder.getSize(),
|
||||||
|
borderCenter.getX(),
|
||||||
|
borderCenter.getZ(),
|
||||||
|
plotOffsetX,
|
||||||
|
plotOffsetZ,
|
||||||
|
gridSizeX,
|
||||||
|
gridSizeZ,
|
||||||
|
plotMultiplier);
|
||||||
|
|
||||||
|
while (ring <= maxSearchRing) {
|
||||||
row = min;
|
row = min;
|
||||||
col = min;
|
col = min;
|
||||||
|
|
||||||
@@ -988,13 +1000,9 @@ public class MetropolisPlugin extends JavaPlugin {
|
|||||||
ring++;
|
ring++;
|
||||||
min = -ring;
|
min = -ring;
|
||||||
max = ring - (plotMultiplier - 1);
|
max = ring - (plotMultiplier - 1);
|
||||||
if (ring > 256) {
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getLogger()
|
getLogger().severe("Unable to find an available supported plot before the world border.");
|
||||||
.severe("Unable to find an available supported plot within the allocation search area.");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1004,7 +1012,17 @@ public class MetropolisPlugin extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAvailableSupportedPlot(int row, int col, int plotMultiplier, int level) {
|
private boolean isAvailableSupportedPlot(int row, int col, int plotMultiplier, int level) {
|
||||||
return !areBlocksOccupied(row, col, plotMultiplier, level)
|
Location borderCenter = world.getWorldBorder().getCenter();
|
||||||
|
Cuboid candidateGrid =
|
||||||
|
new Cuboid(
|
||||||
|
getGridMin(row, col, plotMultiplier, level),
|
||||||
|
getGridMax(row, col, plotMultiplier, level));
|
||||||
|
return PlotWorldBorderBounds.contains(
|
||||||
|
candidateGrid,
|
||||||
|
world.getWorldBorder().getSize(),
|
||||||
|
borderCenter.getX(),
|
||||||
|
borderCenter.getZ())
|
||||||
|
&& !areBlocksOccupied(row, col, plotMultiplier, level)
|
||||||
&& PlotLevelSupport.hasCompleteSupport(
|
&& PlotLevelSupport.hasCompleteSupport(
|
||||||
row, col, plotMultiplier, level, this::isGeneratedLogicalBlock);
|
row, col, plotMultiplier, level, this::isGeneratedLogicalBlock);
|
||||||
}
|
}
|
||||||
@@ -1027,11 +1045,19 @@ public class MetropolisPlugin extends JavaPlugin {
|
|||||||
if (cityRegion instanceof ProtectedCuboidRegion) {
|
if (cityRegion instanceof ProtectedCuboidRegion) {
|
||||||
ProtectedCuboidRegion region = (ProtectedCuboidRegion) cityRegion;
|
ProtectedCuboidRegion region = (ProtectedCuboidRegion) cityRegion;
|
||||||
|
|
||||||
BlockVector3 min;
|
Cuboid requestedCityBounds =
|
||||||
BlockVector3 max;
|
new Cuboid(getPlotMin(-size / 2, -size / 2, 1), getPlotMax(size / 2, size / 2, 1));
|
||||||
|
List<Cuboid> occupiedBounds = new ArrayList<>();
|
||||||
min = getPlotMin(-size / 2, -size / 2, 1);
|
for (Plot plot : _occupiedPlots) {
|
||||||
max = getPlotMax(size / 2, size / 2, 1);
|
occupiedBounds.add(plot.getCuboid());
|
||||||
|
}
|
||||||
|
if (_spawnCuboid != null) {
|
||||||
|
occupiedBounds.add(_spawnCuboid);
|
||||||
|
}
|
||||||
|
Cuboid cityBounds =
|
||||||
|
CityRegionBounds.expandToContain(_cityCuboid, requestedCityBounds, occupiedBounds);
|
||||||
|
BlockVector3 min = cityBounds.getMin();
|
||||||
|
BlockVector3 max = cityBounds.getMax();
|
||||||
|
|
||||||
ProtectedCuboidRegion resizedRegion = new ProtectedCuboidRegion(region.getId(), min, max);
|
ProtectedCuboidRegion resizedRegion = new ProtectedCuboidRegion(region.getId(), min, max);
|
||||||
resizedRegion.copyFrom(region);
|
resizedRegion.copyFrom(region);
|
||||||
@@ -1233,6 +1259,7 @@ public class MetropolisPlugin extends JavaPlugin {
|
|||||||
homeNumber,
|
homeNumber,
|
||||||
newHomeRegion.getMinimumPoint(),
|
newHomeRegion.getMinimumPoint(),
|
||||||
newHomeRegion.getMaximumPoint());
|
newHomeRegion.getMaximumPoint());
|
||||||
|
resizeCityRegion();
|
||||||
_currentHomes.put(playerId, homeNumber);
|
_currentHomes.put(playerId, homeNumber);
|
||||||
saveCurrentHomes();
|
saveCurrentHomes();
|
||||||
|
|
||||||
@@ -1383,6 +1410,7 @@ public class MetropolisPlugin extends JavaPlugin {
|
|||||||
regionManager.addRegion(reservedRegion);
|
regionManager.addRegion(reservedRegion);
|
||||||
|
|
||||||
_occupiedPlots.add(Plot.get(reservedRegion));
|
_occupiedPlots.add(Plot.get(reservedRegion));
|
||||||
|
resizeCityRegion();
|
||||||
saveRegions();
|
saveRegions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Metropolis.
|
||||||
|
|
||||||
|
Metropolis 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.
|
||||||
|
|
||||||
|
Metropolis 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 Metropolis. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.majinnaibu.bukkitplugins.metropolis;
|
||||||
|
|
||||||
|
public final class PlotWorldBorderBounds {
|
||||||
|
private PlotWorldBorderBounds() {}
|
||||||
|
|
||||||
|
public static int maximumSearchRing(
|
||||||
|
double borderSize,
|
||||||
|
double borderCenterX,
|
||||||
|
double borderCenterZ,
|
||||||
|
int offsetX,
|
||||||
|
int offsetZ,
|
||||||
|
int gridSizeX,
|
||||||
|
int gridSizeZ,
|
||||||
|
int plotMultiplier) {
|
||||||
|
if (borderSize <= 0 || gridSizeX <= 0 || gridSizeZ <= 0 || plotMultiplier < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double halfBorder = borderSize / 2;
|
||||||
|
double farthestX = halfBorder + Math.abs(borderCenterX - offsetX);
|
||||||
|
double farthestZ = halfBorder + Math.abs(borderCenterZ - offsetZ);
|
||||||
|
double cellRadius = Math.max(farthestX / gridSizeX, farthestZ / gridSizeZ);
|
||||||
|
long bound = (long) Math.ceil(cellRadius) + plotMultiplier + 1L;
|
||||||
|
return (int) Math.min(bound, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean contains(Cuboid bounds, double borderSize, double centerX, double centerZ) {
|
||||||
|
if (borderSize <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double halfBorder = borderSize / 2;
|
||||||
|
double borderMinX = centerX - halfBorder;
|
||||||
|
double borderMaxX = centerX + halfBorder;
|
||||||
|
double borderMinZ = centerZ - halfBorder;
|
||||||
|
double borderMaxZ = centerZ + halfBorder;
|
||||||
|
return bounds.minX + 0.5 >= borderMinX
|
||||||
|
&& bounds.maxX + 0.5 <= borderMaxX
|
||||||
|
&& bounds.minZ + 0.5 >= borderMinZ
|
||||||
|
&& bounds.maxZ + 0.5 <= borderMaxZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Metropolis.
|
||||||
|
|
||||||
|
Metropolis 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.
|
||||||
|
|
||||||
|
Metropolis 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 Metropolis. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.majinnaibu.bukkitplugins.metropolis;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class CityRegionBoundsTest {
|
||||||
|
@Test
|
||||||
|
void expandsExistingCityToIncludeElevatedHomesAndSpawnWithoutShrinking() {
|
||||||
|
Cuboid existingCity = new Cuboid(-20, 62, -20, 20, 71, 20);
|
||||||
|
Cuboid requestedCity = new Cuboid(-40, 62, -40, 40, 71, 40);
|
||||||
|
Cuboid elevatedHome = new Cuboid(10, 104, 10, 41, 113, 41);
|
||||||
|
Cuboid spawn = new Cuboid(2, 62, 2, 69, 81, 69);
|
||||||
|
|
||||||
|
Cuboid expanded =
|
||||||
|
CityRegionBounds.expandToContain(existingCity, requestedCity, List.of(elevatedHome, spawn));
|
||||||
|
|
||||||
|
assertEquals(-40, expanded.minX);
|
||||||
|
assertEquals(62, expanded.minY);
|
||||||
|
assertEquals(-40, expanded.minZ);
|
||||||
|
assertEquals(69, expanded.maxX);
|
||||||
|
assertEquals(113, expanded.maxY);
|
||||||
|
assertEquals(69, expanded.maxZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Metropolis.
|
||||||
|
|
||||||
|
Metropolis 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.
|
||||||
|
|
||||||
|
Metropolis 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 Metropolis. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.majinnaibu.bukkitplugins.metropolis;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class PlotWorldBorderBoundsTest {
|
||||||
|
@Test
|
||||||
|
void searchBoundFollowsTheWorldBorderInsteadOfAnArbitraryRingCount() {
|
||||||
|
int maximumRing = PlotWorldBorderBounds.maximumSearchRing(60_000_000, 0, 0, 0, 0, 36, 36, 1);
|
||||||
|
|
||||||
|
assertTrue(maximumRing > 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void accountsForAnOffsetBorderCenterAndMultiplier() {
|
||||||
|
int maximumRing = PlotWorldBorderBounds.maximumSearchRing(512, 100, -100, 64, -32, 36, 34, 2);
|
||||||
|
|
||||||
|
assertTrue(maximumRing > 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void requiresTheCompleteGridFootprintInsideTheBorder() {
|
||||||
|
assertTrue(PlotWorldBorderBounds.contains(new Cuboid(-9, 0, -9, 9, 10, 9), 20, 0, 0));
|
||||||
|
assertFalse(PlotWorldBorderBounds.contains(new Cuboid(-11, 0, -9, 9, 10, 9), 20, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user