Skip to content

Commit

Permalink
Add a maybe-working legacy outpost converter.
Browse files Browse the repository at this point in the history
  • Loading branch information
LlmDl committed Nov 4, 2024
1 parent 2957759 commit 69cc052
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ protected MenuBuilder load() {
protected MenuBuilder load() {
return new MenuBuilder("town claim")
.add("", Translatable.of("msg_block_claim"))
.add("outpost", Translatable.of("mayor_help_3"))
.add("outpost [name]", Translatable.of("mayor_help_3"))
.add("[auto]", Translatable.of("mayor_help_5"))
.add("[circle/rect] [radius]", Translatable.of("mayor_help_4"))
.add("[circle/rect] auto", Translatable.of("mayor_help_5"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.palmergames.bukkit.towny.object.jail.Jail;
import com.palmergames.bukkit.towny.tasks.CooldownTimerTask;
import com.palmergames.bukkit.towny.tasks.DeleteFileTask;
import com.palmergames.bukkit.towny.tasks.LegacyOutpostConversionTask;
import com.palmergames.bukkit.towny.utils.MapUtil;
import com.palmergames.bukkit.util.BukkitTools;
import com.palmergames.util.FileMgmt;
Expand Down Expand Up @@ -926,11 +927,14 @@ public boolean loadTown(Town town) {
line = keys.get("outpostspawns");
if (line != null) {
String[] outposts = line.split(";");
int i = 0;
for (String spawn : outposts) {
i++;
tokens = spawn.split(",");
if (tokens.length >= 4)
try {
// TODO: Load legacy Outposts
Position pos = Position.deserialize(tokens);
plugin.getScheduler().runLater(new LegacyOutpostConversionTask(plugin, pos, town), i * 100L);
} catch (IllegalArgumentException e) {
plugin.getLogger().warning("Failed to load an outpost spawn location for town " + town.getName() + ": " + e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.palmergames.bukkit.towny.object.metadata.MetadataLoader;
import com.palmergames.bukkit.towny.object.jail.Jail;
import com.palmergames.bukkit.towny.tasks.CooldownTimerTask;
import com.palmergames.bukkit.towny.tasks.LegacyOutpostConversionTask;
import com.palmergames.bukkit.towny.utils.MapUtil;
import com.palmergames.bukkit.util.BukkitTools;
import com.palmergames.util.FileMgmt;
Expand Down Expand Up @@ -1105,12 +1106,15 @@ private boolean loadTown(ResultSet rs) {
line = rs.getString("outpostSpawns");
if (line != null) {
String[] outposts = line.split(";");
int i = 0;
for (String spawn : outposts) {
i++;
search = (line.contains("#")) ? "#" : ",";
tokens = spawn.split(search);
if (tokens.length >= 4)
try {
// TODO: handle loading of legacy outposts
Position pos = Position.deserialize(tokens);
plugin.getScheduler().runLater(new LegacyOutpostConversionTask(plugin, pos, town), i * 100L);
} catch (IllegalArgumentException e) {
plugin.getLogger().warning("Failed to load an outpost spawn location for town " + town.getName() + ": " + e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.palmergames.bukkit.towny.tasks;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import com.palmergames.bukkit.towny.Towny;
import com.palmergames.bukkit.towny.TownyAPI;
import com.palmergames.bukkit.towny.TownyMessaging;
import com.palmergames.bukkit.towny.exceptions.TownyException;
import com.palmergames.bukkit.towny.object.Outpost;
import com.palmergames.bukkit.towny.object.Position;
import com.palmergames.bukkit.towny.object.Town;
import com.palmergames.bukkit.towny.object.TownBlock;
import com.palmergames.bukkit.towny.object.WorldCoord;
import com.palmergames.bukkit.towny.utils.BorderUtil;
import com.palmergames.bukkit.towny.utils.BorderUtil.FloodfillResult;

public class LegacyOutpostConversionTask extends TownyTimerTask {

final Position pos;
final Town town;

public LegacyOutpostConversionTask(Towny plugin, Position pos, Town town) {
super(plugin);
this.pos = pos;
this.town = town;
}

@Override
public void run() {
if (plugin.isError())
return;

TownBlock townBlock = TownyAPI.getInstance().getTownBlock(pos.asLocation());
if (!town.hasTownBlock(townBlock))
return;

WorldCoord coord = townBlock.getWorldCoord();
FloodfillResult result = null;
try {
result = BorderUtil.getFloodFillableCoordsForOutpostConversion(town, coord);
if (result.type() != BorderUtil.FloodfillResult.Type.SUCCESS)
throw result.feedback() != null ? new TownyException(result.feedback()) : new TownyException();
else if (result.feedback() != null)
TownyMessaging.sendMsg(result.feedback());
} catch (TownyException e) {
TownyMessaging.sendMsg(e.getLocalizedMessage());
}

List<WorldCoord> selection = new ArrayList<>(result.coords());
Outpost outpost = new Outpost(UUID.randomUUID(), townBlock.getName() != "" ? townBlock.getName() : String.valueOf(town.getMaxOutpostSpawn() + 1));
outpost.addTownblock(townBlock);
for (WorldCoord wc : selection) {
TownBlock tb = wc.getTownBlockOrNull();
if (tb != null)
outpost.addTownblock(tb);
}
outpost.save();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,76 @@ public static boolean allowedMove(Block block, Block blockTo, @Nullable Player p
return FloodfillResult.success(valid);
}

@ApiStatus.Internal
public static @NotNull FloodfillResult getFloodFillableCoordsForOutpostConversion(final @NotNull Town town, final @NotNull WorldCoord origin) {
final TownyWorld originWorld = origin.getTownyWorld();
if (originWorld == null)
return FloodfillResult.fail(null);

if (!origin.hasTownBlock())
return FloodfillResult.fail(Translatable.of("msg_err_floodfill_not_in_wild"));

// Filter out any coords not in the same world
final Set<WorldCoord> coords = new HashSet<>(town.getTownBlockMap().keySet());
coords.removeIf(coord -> !originWorld.equals(coord.getTownyWorld()));
if (coords.isEmpty())
return FloodfillResult.fail(null);

int minX = origin.getX();
int maxX = origin.getX();
int minZ = origin.getZ();
int maxZ = origin.getZ();

// Establish a min and max X & Z to avoid possibly looking very far
for (final WorldCoord coord : coords) {
minX = Math.min(minX, coord.getX());
maxX = Math.max(maxX, coord.getX());
minZ = Math.min(minZ, coord.getZ());
maxZ = Math.max(maxZ, coord.getZ());
}

final Set<WorldCoord> valid = new HashSet<>();
final Set<WorldCoord> visited = new HashSet<>();

final Queue<WorldCoord> queue = new LinkedList<>();
queue.offer(origin);
visited.add(origin);

while (!queue.isEmpty()) {
if (valid.size() >= town.getMaxAllowedOutpostLandmass())
return FloodfillResult.success(valid);

final WorldCoord current = queue.poll();

valid.add(current);

for (final int[] direction : DIRECTIONS) {
final int xOffset = direction[0];
final int zOffset = direction[1];

final WorldCoord candidate = current.add(xOffset, zOffset);

if (!coords.contains(candidate) && (candidate.getX() >= maxX || candidate.getX() <= minX || candidate.getZ() >= maxZ || candidate.getZ() <= minZ)) {
return FloodfillResult.oob();
}

final TownBlock townBlock = candidate.getTownBlockOrNull();

// Fail if we're touching another town
if (townBlock == null || townBlock.hasTown() && !town.equals(townBlock.getTownOrNull())) {
continue;
}

if (!visited.contains(candidate) && !coords.contains(candidate)) {
queue.offer(candidate);
visited.add(candidate);
}
}
}

return FloodfillResult.success(valid);
}

public record FloodfillResult(@NotNull Type type, @Nullable Translatable feedback, @NotNull Collection<WorldCoord> coords) {
public enum Type {
SUCCESS,
Expand Down

0 comments on commit 69cc052

Please sign in to comment.