From 00b59ed7c4863ef1e9d66a963972b0af4a20c635 Mon Sep 17 00:00:00 2001 From: Andreas Troelsen Date: Sun, 6 Oct 2024 14:42:02 +0200 Subject: [PATCH] Add per-arena min/max spawnpoint distance settings. Introduces two new settings for controlling how far away from or close to a spawnpoint players must be in order for the spawnpoint to be valid when spawning monsters. This is a pretty big one. In ancient times, MobArena had no limit on how close players had to be to spawnpoints for them to be valid. The result was mobs spawning too far away from the players, frozen in place until their target approached them or until another player attacked them. This was clearly undesirable, so the 15-block max distance was introduced to solve this problem. And it worked, hurray! In the meantime, it also imposed a really cumbersome limitation on all arena designs, since aesthetically (or strategically) placed spawnpoints were seldom sufficient, and server owners have been needing to litter their arena floors with spawnpoints to avoid warnings and to get their expected monster behavior. Modern versions of Minecraft no longer exhibit that "frozen in place" behavior the hardcoded max distance set out to solve, and server owners have been asking for configurability on this front for years. With this commit, that distance is now per-arena configurable. The change has no real impact on the performance of the plugin. It's worth noting that we don't modify the pathfinding attribute `generic.followRange`. We might want to revisit this in a future commit, but since it can _definitely_ affect performance, we should have some actual servers run a test build with it before jumping on that wagon. Having many spawnpoints might still be preferable to some, but it comes with another problem of players standing right on top of a spawnpoint when a monster spawns. To combat this, an additional setting to control the _minimum_ distance that _all_ players must be from the spawnpoint for it to be valid is introduced as well. This means it is possible to have lots of spawnpoints for a less predictable session, but without the risk of being instantly attacked when a new wave spawns. This change has a theoretical performance impact on the plugin, because it's a lot more brute force without the early returns of the old algorithm. However, if the setting is left at 0, the old algorithm is used. The only real downside to these changes is that it's more code and more settings to maintain. It doesn't improve on the clusterfuck that is the arena settings in the config-file - we're just making things worse. I do think it's worth having, though, since the bigger revamps on the drawing board are at best months away, and at worst, they'll never happen. When or if the time comes, it's probably better to rethink certain aspects of the plugin instead of trying to convert everything gracefully. Closes #412 --- changelog.md | 2 + .../com/garbagemule/MobArena/ArenaImpl.java | 14 ++++++ .../com/garbagemule/MobArena/MAUtils.java | 3 +- .../garbagemule/MobArena/framework/Arena.java | 4 ++ .../MobArena/region/ArenaRegion.java | 5 ++- .../garbagemule/MobArena/waves/WaveUtils.java | 44 +++++++++++++++---- src/main/resources/res/settings.yml | 2 + 7 files changed, 64 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 1b1c8e6..be6d1ff 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,8 @@ These changes will (most likely) be included in the next version. ### Added - MobArena now properly supports Vault economy providers registered after MobArena has started. This should make it possible to use custom economy providers that aren't built into Vault, such as those created with Denizen. - New wave rewards section `tiers` allows for _non-stacking_ reward tiers for beating certain waves. This allows for configuring reward sets that get "upgraded" as the waves progress, e.g. by granting a full leather armor set for beating wave 15, but beating wave 20 _replaces_ that leather armor set with an iron armor set. +- New per-arena setting `spawnpoint-max-distance` can be used to tweak how close to any player a given spawnpoint must be to be considered valid when spawning monsters. This should help reduce the amount of spawnpoints required, especially for larger arenas. +- New per-arena setting `spawnpoint-min-distance` can be used to tweak how far away from all players a given spawnpoint must be to be considered valid when spawning monsters. This should help prevent monsters from spawning directly on top of players. ### Changed - Recurrent waves can now be randomized. If two or more recurrent waves clash on wave number, frequency, _and_ priority, MobArena will now randomly pick between them. This should make it easier to create more varied and interesting wave setups without having to resort to only massively randomized default waves. diff --git a/src/main/java/com/garbagemule/MobArena/ArenaImpl.java b/src/main/java/com/garbagemule/MobArena/ArenaImpl.java index 1f4801a..ed8eeb1 100644 --- a/src/main/java/com/garbagemule/MobArena/ArenaImpl.java +++ b/src/main/java/com/garbagemule/MobArena/ArenaImpl.java @@ -125,6 +125,8 @@ public class ArenaImpl implements Arena private MASpawnThread spawnThread; private SheepBouncer sheepBouncer; private Map everyWaveMap, afterWaveMap, waveTiersMap; + private double spawnpointMinDistanceSquared; + private double spawnpointMaxDistanceSquared; // Misc private ArenaListener eventListener; @@ -209,6 +211,8 @@ public class ArenaImpl implements Arena this.everyWaveMap = MAUtils.getArenaRewardMap(plugin, section, name, "every"); this.afterWaveMap = MAUtils.getArenaRewardMap(plugin, section, name, "after"); this.waveTiersMap = MAUtils.getArenaRewardMap(plugin, section, name, "tiers"); + this.spawnpointMinDistanceSquared = Math.pow(settings.getDouble("spawnpoint-min-distance", 0), 2); + this.spawnpointMaxDistanceSquared = Math.pow(settings.getDouble("spawnpoint-max-distance", 15), 2); // Misc this.eventListener = new ArenaListener(this, plugin); @@ -382,6 +386,16 @@ public class ArenaImpl implements Arena return spawnThread; } + @Override + public double getSpawnpointMinDistanceSquared() { + return spawnpointMinDistanceSquared; + } + + @Override + public double getSpawnpointMaxDistanceSquared() { + return spawnpointMaxDistanceSquared; + } + @Override public WaveManager getWaveManager() { return waveManager; diff --git a/src/main/java/com/garbagemule/MobArena/MAUtils.java b/src/main/java/com/garbagemule/MobArena/MAUtils.java index 3a0afc9..2d08b99 100644 --- a/src/main/java/com/garbagemule/MobArena/MAUtils.java +++ b/src/main/java/com/garbagemule/MobArena/MAUtils.java @@ -88,6 +88,7 @@ public class MAUtils /* Iterate through the ArrayList, and update current and result every * time a squared distance smaller than current is found. */ List players = new ArrayList<>(arena.getPlayersInArena()); + double max = arena.getSpawnpointMaxDistanceSquared(); for (Player p : players) { if (!arena.getWorld().equals(p.getWorld())) { plugin.getLogger().info("Player '" + p.getName() + "' is not in the right world. Kicking..."); @@ -97,7 +98,7 @@ public class MAUtils } double dist = distanceSquared(plugin, p, e.getLocation()); - if (dist < current && dist < 256D) { + if (dist < current && dist < max) { current = dist; result = p; } diff --git a/src/main/java/com/garbagemule/MobArena/framework/Arena.java b/src/main/java/com/garbagemule/MobArena/framework/Arena.java index e0059b4..d9f556a 100644 --- a/src/main/java/com/garbagemule/MobArena/framework/Arena.java +++ b/src/main/java/com/garbagemule/MobArena/framework/Arena.java @@ -82,6 +82,10 @@ public interface Arena MASpawnThread getSpawnThread(); + double getSpawnpointMinDistanceSquared(); + + double getSpawnpointMaxDistanceSquared(); + WaveManager getWaveManager(); ArenaListener getEventListener(); diff --git a/src/main/java/com/garbagemule/MobArena/region/ArenaRegion.java b/src/main/java/com/garbagemule/MobArena/region/ArenaRegion.java index 1d6cd3b..d68ee1b 100644 --- a/src/main/java/com/garbagemule/MobArena/region/ArenaRegion.java +++ b/src/main/java/com/garbagemule/MobArena/region/ArenaRegion.java @@ -648,9 +648,12 @@ public class ArenaRegion } // Find all the spawnpoints that cover the location + double min = arena.getSpawnpointMinDistanceSquared(); + double max = arena.getSpawnpointMaxDistanceSquared(); Map map = new HashMap<>(); for (Map.Entry entry : spawnpoints.entrySet()) { - if (p.getLocation().distanceSquared(entry.getValue()) < MobArena.MIN_PLAYER_DISTANCE_SQUARED) { + double dist = p.getLocation().distanceSquared(entry.getValue()); + if (min <= dist && dist <= max) { map.put(entry.getKey(), entry.getValue()); } } diff --git a/src/main/java/com/garbagemule/MobArena/waves/WaveUtils.java b/src/main/java/com/garbagemule/MobArena/waves/WaveUtils.java index 0f13460..53320dc 100644 --- a/src/main/java/com/garbagemule/MobArena/waves/WaveUtils.java +++ b/src/main/java/com/garbagemule/MobArena/waves/WaveUtils.java @@ -27,14 +27,42 @@ public class WaveUtils spawnpoints = arena.getRegion().getSpawnpointList(); } - // Loop through each one and check if any players are in range. - for (Location l : spawnpoints) { - for (Player p : players) { - if (MAUtils.distanceSquared(plugin, p, l) >= MobArena.MIN_PLAYER_DISTANCE_SQUARED) { - continue; + double min = arena.getSpawnpointMinDistanceSquared(); + double max = arena.getSpawnpointMaxDistanceSquared(); + + if (min > 0) { + // If the min distance is greater than 0, we need to check the + // distance of every player, because one player within the max + // range is no longer enough to make a spawnpoint valid. If even + // a single player is too close, the spawnpoint is invalid. + for (Location l : spawnpoints) { + boolean valid = false; + for (Player p : players) { + double dist = MAUtils.distanceSquared(plugin, p, l); + if (dist < min) { + valid = false; + break; + } + if (dist <= max) { + valid = true; + } + } + if (valid) { + result.add(l); + } + } + } else { + // If the min distance is 0, the old "any player within range" + // approach is sufficient, because we can never invalidate a + // spawnpoint for being too close to a player. + for (Location l : spawnpoints) { + for (Player p : players) { + double dist = MAUtils.distanceSquared(plugin, p, l); + if (dist <= max) { + result.add(l); + break; + } } - result.add(l); - break; } } @@ -70,7 +98,7 @@ public class WaveUtils } dist = p.getLocation().distanceSquared(e.getLocation()); - if (dist < current && dist < MobArena.MIN_PLAYER_DISTANCE_SQUARED) + if (dist < current && dist < arena.getSpawnpointMaxDistanceSquared()) { current = dist; result = p; diff --git a/src/main/resources/res/settings.yml b/src/main/resources/res/settings.yml index 862238e..54719e5 100644 --- a/src/main/resources/res/settings.yml +++ b/src/main/resources/res/settings.yml @@ -27,6 +27,8 @@ first-wave-delay: 5 next-wave-delay: 0 wave-interval: 15 final-wave: 0 +spawnpoint-min-distance: 0 +spawnpoint-max-distance: 15 monster-limit: 100 monster-exp: false keep-exp: false