Adds multiple home support.
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
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 java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class HomeNumberAllocator {
|
||||
private HomeNumberAllocator() {}
|
||||
|
||||
public static int nextHomeNumber(List<Plot> plots) {
|
||||
Set<Integer> allocatedNumbers = new HashSet<>();
|
||||
for (Plot plot : plots) {
|
||||
if (plot instanceof PlayerHome home) {
|
||||
allocatedNumbers.add(home.getNumber());
|
||||
}
|
||||
}
|
||||
|
||||
int nextHomeNumber = 1;
|
||||
while (allocatedNumbers.contains(nextHomeNumber)) {
|
||||
nextHomeNumber++;
|
||||
}
|
||||
return nextHomeNumber;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
public final class HomePermissionOverride {
|
||||
private final String _permission;
|
||||
private final int _priority;
|
||||
private final int _plotMultiplier;
|
||||
private final int _maxPlots;
|
||||
|
||||
private HomePermissionOverride(
|
||||
String permission, int priority, int plotMultiplier, int maxPlots) {
|
||||
_permission = permission;
|
||||
_priority = priority;
|
||||
_plotMultiplier = plotMultiplier;
|
||||
_maxPlots = maxPlots;
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return _permission;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return _priority;
|
||||
}
|
||||
|
||||
public int getPlotMultiplier() {
|
||||
return _plotMultiplier;
|
||||
}
|
||||
|
||||
public int getMaxPlots() {
|
||||
return _maxPlots;
|
||||
}
|
||||
|
||||
public static List<HomePermissionOverride> load(
|
||||
ConfigurationSection section, Consumer<String> warningLogger) {
|
||||
if (section == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<HomePermissionOverride> overrides = new ArrayList<>();
|
||||
collect(section, "", overrides, warningLogger);
|
||||
return List.copyOf(overrides);
|
||||
}
|
||||
|
||||
private static void collect(
|
||||
ConfigurationSection section,
|
||||
String permissionPrefix,
|
||||
List<HomePermissionOverride> overrides,
|
||||
Consumer<String> warningLogger) {
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
String permission =
|
||||
permissionPrefix.isEmpty() ? entry.getKey() : permissionPrefix + "." + entry.getKey();
|
||||
Map<?, ?> values = getValues(entry.getValue());
|
||||
if (values != null && !hasRuleValues(values)) {
|
||||
collect((ConfigurationSection) entry.getValue(), permission, overrides, warningLogger);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (permission.isBlank()
|
||||
|| values == null
|
||||
|| !(values.get("priority") instanceof Number priority)
|
||||
|| !(values.get("plotMultiplier") instanceof Number plotMultiplier)
|
||||
|| !(values.get("maxPlots") instanceof Number maxPlots)
|
||||
|| priority.intValue() != priority.doubleValue()
|
||||
|| plotMultiplier.intValue() != plotMultiplier.doubleValue()
|
||||
|| maxPlots.intValue() != maxPlots.doubleValue()
|
||||
|| plotMultiplier.intValue() < 1
|
||||
|| maxPlots.intValue() < 0) {
|
||||
warningLogger.accept("Ignoring invalid permissionOverrides entry '" + permission + "'.");
|
||||
continue;
|
||||
}
|
||||
|
||||
overrides.add(
|
||||
new HomePermissionOverride(
|
||||
permission, priority.intValue(), plotMultiplier.intValue(), maxPlots.intValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasRuleValues(Map<?, ?> values) {
|
||||
return values.containsKey("priority")
|
||||
|| values.containsKey("plotMultiplier")
|
||||
|| values.containsKey("maxPlots");
|
||||
}
|
||||
|
||||
public static HomePermissionOverride findMatch(
|
||||
List<HomePermissionOverride> overrides, Predicate<String> hasPermission) {
|
||||
HomePermissionOverride match = null;
|
||||
for (HomePermissionOverride override : overrides) {
|
||||
if (hasPermission.test(override.getPermission())
|
||||
&& (match == null || override.getPriority() >= match.getPriority())) {
|
||||
match = override;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
public static int resolveMaxPlots(
|
||||
UserOverride userOverride,
|
||||
List<HomePermissionOverride> overrides,
|
||||
Predicate<String> hasPermission,
|
||||
int fallback) {
|
||||
if (userOverride != null) {
|
||||
return userOverride.getMaxPlots();
|
||||
}
|
||||
HomePermissionOverride match = findMatch(overrides, hasPermission);
|
||||
return match == null ? fallback : match.getMaxPlots();
|
||||
}
|
||||
|
||||
public static int resolvePlotMultiplier(
|
||||
UserOverride userOverride,
|
||||
List<HomePermissionOverride> overrides,
|
||||
Predicate<String> hasPermission,
|
||||
int fallback) {
|
||||
if (userOverride != null) {
|
||||
return userOverride.getPlotMultiplier();
|
||||
}
|
||||
HomePermissionOverride match = findMatch(overrides, hasPermission);
|
||||
return match == null ? fallback : match.getPlotMultiplier();
|
||||
}
|
||||
|
||||
private static Map<?, ?> getValues(Object value) {
|
||||
if (value instanceof ConfigurationSection section) {
|
||||
return section.getValues(false);
|
||||
}
|
||||
if (value instanceof Map<?, ?> map) {
|
||||
return map;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,7 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
private List<Plot> _occupiedPlots;
|
||||
private HashMap<UUID, List<Plot>> _ownedPlots;
|
||||
private HashMap<UUID, UserOverride> _userOverrides;
|
||||
private List<HomePermissionOverride> _permissionOverrides;
|
||||
private HashMap<UUID, Integer> _currentHomes;
|
||||
|
||||
private PlayerJoinListener _playerJoinListener = null;
|
||||
@@ -196,10 +197,14 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
wallMaterial = safeGetMaterialFromConfig(config, "wall.material");
|
||||
wallHeight = safeGetIntFromConfig(config, "wall.height");
|
||||
worldName = safeGetStringFromConfig(config, "worldname");
|
||||
_maxPlots = safeGetIntFromConfig(config, "plot.multiplier");
|
||||
_plotMultiplier = safeGetIntFromConfig(config, "plot.maxPerPlayer");
|
||||
_plotMultiplier = safeGetIntFromConfig(config, "plot.multiplier");
|
||||
_maxPlots = safeGetIntFromConfig(config, "plot.maxPerPlayer");
|
||||
|
||||
buildUserOverrides();
|
||||
_permissionOverrides =
|
||||
HomePermissionOverride.load(
|
||||
config.getConfigurationSection("permissionOverrides"),
|
||||
warning -> getLogger().warning(warning));
|
||||
|
||||
saveConfig();
|
||||
if (DEBUG) {
|
||||
@@ -494,9 +499,6 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
ProtectedCuboidRegion cuboidRegion = (ProtectedCuboidRegion) region;
|
||||
if (cuboidRegion.getId().startsWith("h_")) {
|
||||
PlayerHome home = PlayerHome.get(region);
|
||||
if (!_currentHomes.containsKey(home.getPlayerId())) {
|
||||
_currentHomes.put(home.getPlayerId(), home.getNumber());
|
||||
}
|
||||
_occupiedPlots.add(home);
|
||||
addOwnedPlot(home.getPlayerId(), home);
|
||||
} else if (cuboidRegion.getId().startsWith("r_")) {
|
||||
@@ -505,7 +507,17 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
for (UUID playerId : _ownedPlots.keySet()) {
|
||||
if (!_currentHomes.containsKey(playerId)) {
|
||||
PlayerHome firstHome = getFirstOwnedHome(playerId);
|
||||
if (firstHome != null) {
|
||||
_currentHomes.put(playerId, firstHome.getNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size = calculateCitySize();
|
||||
saveCurrentHomes();
|
||||
}
|
||||
|
||||
private void addOwnedPlot(UUID playerId, Plot plot) {
|
||||
@@ -527,20 +539,27 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
public PlayerHome getPlayerHome(Player player) {
|
||||
PlayerHome home = null;
|
||||
|
||||
int homeNumber = _currentHomes.getOrDefault(player.getUniqueId(), 1);
|
||||
UUID playerId = player.getUniqueId();
|
||||
int homeNumber = _currentHomes.getOrDefault(playerId, 1);
|
||||
String regionName = String.format("h_%d_%s", homeNumber, player.getUniqueId());
|
||||
ProtectedRegion homeRegion = regionManager.getRegion(regionName);
|
||||
|
||||
if (homeRegion == null) {
|
||||
PlayerHome existingHome = getOwnedHome(player.getUniqueId(), homeNumber);
|
||||
PlayerHome existingHome = getOwnedHome(playerId, homeNumber);
|
||||
if (existingHome != null) {
|
||||
existingHome.setPlayerName(player.getName());
|
||||
return existingHome;
|
||||
}
|
||||
existingHome = getFirstOwnedHome(playerId);
|
||||
if (existingHome != null) {
|
||||
existingHome.setPlayerName(player.getName());
|
||||
setHome(playerId, existingHome.getNumber());
|
||||
return existingHome;
|
||||
}
|
||||
if (DEBUG) {
|
||||
getLogger().info(String.format("Creating home for player %s", player.getName()));
|
||||
}
|
||||
home = generateHome(player);
|
||||
home = generateHome(player, 1);
|
||||
} else {
|
||||
home = new PlayerHome(homeRegion);
|
||||
home.setPlayerName(player.getName());
|
||||
@@ -891,7 +910,7 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
|
||||
BlockVector3 bv =
|
||||
BlockVector3.at(
|
||||
(col + plotMultiplier) * gridSizeX * plotMultiplier - 1,
|
||||
(col + plotMultiplier) * gridSizeX - 1,
|
||||
(level + 1 /*plotMultiplier*/) * gridSizeY - 1,
|
||||
(row + plotMultiplier) * gridSizeZ - 1);
|
||||
getLogger().info(String.format("getGridMax (%d, %d, %d)", bv.x(), bv.y(), bv.z()));
|
||||
@@ -924,10 +943,14 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
}
|
||||
|
||||
public PlayerHome generateHome(OfflinePlayer player) {
|
||||
int homeNumber = _currentHomes.getOrDefault(player.getUniqueId(), 1);
|
||||
return generateHome(player, homeNumber);
|
||||
}
|
||||
|
||||
public PlayerHome generateHome(OfflinePlayer player, int homeNumber) {
|
||||
UUID playerId = player.getUniqueId();
|
||||
String playerName = player.getName() == null ? playerId.toString() : player.getName();
|
||||
int homeNumber = _currentHomes.getOrDefault(playerId, 1);
|
||||
int multiplier = getPlotMultiplier(playerId);
|
||||
int multiplier = getPlotMultiplier(player);
|
||||
|
||||
if (DEBUG) {
|
||||
getLogger().info(String.format("Generating home for %s", playerName));
|
||||
@@ -993,7 +1016,7 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
homeNumber,
|
||||
newHomeRegion.getMinimumPoint(),
|
||||
newHomeRegion.getMaximumPoint());
|
||||
_currentHomes.putIfAbsent(playerId, homeNumber);
|
||||
_currentHomes.put(playerId, homeNumber);
|
||||
saveCurrentHomes();
|
||||
|
||||
createRoads(homeCuboid);
|
||||
@@ -1130,10 +1153,28 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxPlots(Player player) {
|
||||
return HomePermissionOverride.resolveMaxPlots(
|
||||
_userOverrides.get(player.getUniqueId()),
|
||||
_permissionOverrides,
|
||||
player::hasPermission,
|
||||
_maxPlots);
|
||||
}
|
||||
|
||||
public void assignPlot(OfflinePlayer player) {
|
||||
generateHome(player);
|
||||
}
|
||||
|
||||
public PlayerHome acquireHome(OfflinePlayer player) {
|
||||
List<Plot> ownedPlots = _ownedPlots.getOrDefault(player.getUniqueId(), List.of());
|
||||
int nextHomeNumber = HomeNumberAllocator.nextHomeNumber(ownedPlots);
|
||||
PlayerHome home = generateHome(player, nextHomeNumber);
|
||||
if (home != null) {
|
||||
setHome(player.getUniqueId(), home.getNumber());
|
||||
}
|
||||
return home;
|
||||
}
|
||||
|
||||
private int getPlotMultiplier(UUID playerId) {
|
||||
if (_userOverrides.containsKey(playerId)) {
|
||||
return _userOverrides.get(playerId).getPlotMultiplier();
|
||||
@@ -1142,6 +1183,21 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private int getPlotMultiplier(OfflinePlayer player) {
|
||||
if (player instanceof Player onlinePlayer) {
|
||||
return HomePermissionOverride.resolvePlotMultiplier(
|
||||
_userOverrides.get(player.getUniqueId()),
|
||||
_permissionOverrides,
|
||||
onlinePlayer::hasPermission,
|
||||
_plotMultiplier);
|
||||
}
|
||||
return HomePermissionOverride.resolvePlotMultiplier(
|
||||
_userOverrides.get(player.getUniqueId()),
|
||||
_permissionOverrides,
|
||||
ignored -> false,
|
||||
_plotMultiplier);
|
||||
}
|
||||
|
||||
public Plot getPlot(String string) {
|
||||
/**
|
||||
* string is the name of the region to get a plot for
|
||||
@@ -1229,6 +1285,18 @@ public class MetropolisPlugin extends JavaPlugin {
|
||||
return null;
|
||||
}
|
||||
|
||||
private PlayerHome getFirstOwnedHome(UUID playerId) {
|
||||
List<Plot> plots = _ownedPlots.get(playerId);
|
||||
if (plots == null) {
|
||||
return null;
|
||||
}
|
||||
return plots.stream()
|
||||
.filter(PlayerHome.class::isInstance)
|
||||
.map(PlayerHome.class::cast)
|
||||
.min(java.util.Comparator.comparingInt(PlayerHome::getNumber))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private void saveCurrentHomes() {
|
||||
File homesFile = new File(getDataFolder(), "currentHomes.yml");
|
||||
try {
|
||||
|
||||
@@ -40,12 +40,11 @@ public class MetropolisHomeAcquire implements CommandExecutor {
|
||||
|
||||
Player player = (Player) sender;
|
||||
|
||||
if (_plugin.getNumPlots(player.getUniqueId()) >= _plugin.getMaxPlots(player.getUniqueId())) {
|
||||
if (_plugin.getNumPlots(player.getUniqueId()) >= _plugin.getMaxPlots(player)) {
|
||||
sender.sendMessage("You cannot have any more plots");
|
||||
return false;
|
||||
}
|
||||
|
||||
_plugin.assignPlot(player);
|
||||
return true;
|
||||
return _plugin.acquireHome(player) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
Goal: Have 1 plot size for mods and 1 plot size for normals.
|
||||
Implementation:
|
||||
1. Set the plotSize in the config to the larger plot size there are other possible sizes but powers of 2 work really well so lets give mods a 64x64 plot. Roads are recomended but will eat into the plot on all sides so the roadsize can be ignored for this example.
|
||||
2. Start the server and run /metropolis-home-generate <username> for each mod.
|
||||
3. If you have not completed a full ring then run /metropolis-home-generate <test n> where n starts at 1 and increases until you have filled a full ring. This will reserve those plots for later or anyone who happens to have that username, but that can be dealth with later.
|
||||
4. Stop the server and change the plotSize to a smaller number like 32.
|
||||
5. Restart the server and let your users log in.
|
||||
|
||||
Notes:
|
||||
All users will be assigned a small plot when they first connect. When you make them a mod just delete their current region and change the name of one of the reserved home regions to h_<their username>.
|
||||
/region remove h_jonnynewadmin
|
||||
/region select test_1
|
||||
/region remove test_1
|
||||
/region define h_johnnynewadmin
|
||||
JohnnyNewAdmin's old home will become available for resettlement. If you want to copy johnny's old blocks into his new home region I'd suggest WorldEdit.
|
||||
This process including optionally copying the blocks will be an upcoming feature. Probably in 0.5.x and maybe in 0.6.
|
||||
|
||||
If you later run out of larger plots you can just set the size back to the larger number and let more generate. Moving and evictions will be coming soon and make this easier.
|
||||
You can manually just create a region for a user before they login named like h_<username> if you want a specific user to have a really big space as well.
|
||||
Reference in New Issue
Block a user