PlotSquared/Core/src/main/java/com/plotsquared/core/PlotSquared.java

1627 lines
66 KiB
Java
Raw Normal View History

/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
2020-04-15 21:26:54 +02:00
package com.plotsquared.core;
2020-04-30 12:53:07 +02:00
import com.plotsquared.core.configuration.ConfigurationSection;
2020-04-16 06:14:33 +02:00
import com.plotsquared.core.configuration.ConfigurationUtil;
2020-04-30 12:53:07 +02:00
import com.plotsquared.core.configuration.MemorySection;
2020-04-16 06:14:33 +02:00
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.Storage;
2020-07-17 17:24:45 +02:00
import com.plotsquared.core.configuration.caption.CaptionMap;
2020-07-24 17:19:25 +02:00
import com.plotsquared.core.configuration.caption.DummyCaptionMap;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.configuration.caption.load.CaptionLoader;
import com.plotsquared.core.configuration.caption.load.DefaultCaptionProvider;
import com.plotsquared.core.configuration.file.YamlConfiguration;
import com.plotsquared.core.configuration.serialization.ConfigurationSerialization;
2020-04-15 21:26:54 +02:00
import com.plotsquared.core.database.DBFunc;
import com.plotsquared.core.database.Database;
import com.plotsquared.core.database.MySQL;
import com.plotsquared.core.database.SQLManager;
import com.plotsquared.core.database.SQLite;
import com.plotsquared.core.generator.GeneratorWrapper;
import com.plotsquared.core.generator.HybridPlotWorld;
import com.plotsquared.core.generator.HybridUtils;
import com.plotsquared.core.generator.IndependentPlotGenerator;
2020-07-11 17:19:19 +02:00
import com.plotsquared.core.inject.factory.HybridPlotWorldFactory;
import com.plotsquared.core.listener.PlotListener;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.player.PlayerMetaDataKeys;
2020-04-15 21:26:54 +02:00
import com.plotsquared.core.plot.BlockBucket;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.PlotAreaTerrainType;
2020-04-15 21:26:54 +02:00
import com.plotsquared.core.plot.PlotAreaType;
import com.plotsquared.core.plot.PlotCluster;
import com.plotsquared.core.plot.PlotId;
import com.plotsquared.core.plot.PlotManager;
import com.plotsquared.core.plot.expiration.ExpireManager;
import com.plotsquared.core.plot.expiration.ExpiryTask;
2020-07-14 13:14:02 +02:00
import com.plotsquared.core.plot.flag.GlobalFlagContainer;
2020-04-15 21:26:54 +02:00
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.plot.world.SinglePlotArea;
import com.plotsquared.core.plot.world.SinglePlotAreaManager;
import com.plotsquared.core.util.EventDispatcher;
import com.plotsquared.core.util.FileUtils;
import com.plotsquared.core.util.LegacyConverter;
import com.plotsquared.core.util.MathMan;
2020-04-15 21:26:54 +02:00
import com.plotsquared.core.util.ReflectionUtils;
import com.plotsquared.core.util.task.TaskManager;
2020-05-18 00:22:34 +02:00
import com.plotsquared.core.uuid.UUIDPipeline;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.util.eventbus.EventHandler;
import com.sk89q.worldedit.util.eventbus.Subscribe;
2021-08-18 11:58:18 +02:00
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
2018-08-10 17:01:10 +02:00
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.sql.SQLException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
2020-05-18 00:22:34 +02:00
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
2015-07-03 11:30:26 +02:00
/**
2016-03-23 02:41:37 +01:00
* An implementation of the core, with a static getter for easy access.
2015-07-03 11:30:26 +02:00
*/
2020-07-08 15:09:25 +02:00
@SuppressWarnings({"WeakerAccess"})
2020-04-30 12:53:07 +02:00
public class PlotSquared {
private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + PlotSquared.class.getSimpleName());
private static @MonotonicNonNull PlotSquared instance;
2019-02-04 14:59:11 +01:00
// Implementation
private final PlotPlatform<?> platform;
2019-02-04 14:59:11 +01:00
// Current thread
private final Thread thread;
2020-05-18 00:22:34 +02:00
// UUID pipelines
2020-07-17 17:24:45 +02:00
private final UUIDPipeline impromptuUUIDPipeline =
new UUIDPipeline(Executors.newCachedThreadPool());
2020-07-17 17:24:45 +02:00
private final UUIDPipeline backgroundUUIDPipeline =
new UUIDPipeline(Executors.newSingleThreadExecutor());
// Localization
private final Map<String, CaptionMap> captionMaps = new HashMap<>();
public HashMap<String, HashMap<PlotId, Plot>> plots_tmp;
2021-08-18 11:58:18 +02:00
private CaptionLoader captionLoader;
2019-02-04 14:59:11 +01:00
// WorldEdit instance
2020-07-17 17:24:45 +02:00
private WorldEdit worldedit;
private File configFile;
private File worldsFile;
private YamlConfiguration worldConfiguration;
2019-02-04 14:59:11 +01:00
// Temporary hold the plots/clusters before the worlds load
2020-07-10 19:25:05 +02:00
private HashMap<String, Set<PlotCluster>> clustersTmp;
private YamlConfiguration config;
2019-02-04 14:59:11 +01:00
// Platform / Version / Update URL
private PlotVersion version;
// Files and configuration
2020-07-17 17:24:45 +02:00
private File jarFile = null; // This file
2019-02-04 14:59:11 +01:00
private File storageFile;
2020-07-17 17:24:45 +02:00
private EventDispatcher eventDispatcher;
private PlotListener plotListener;
private boolean weInitialised;
2019-02-04 14:59:11 +01:00
/**
* Initialize PlotSquared with the desired Implementation class.
*
* @param iPlotMain Implementation of {@link PlotPlatform} used
2019-02-04 14:59:11 +01:00
* @param platform The platform being used
*/
public PlotSquared(
final @NonNull PlotPlatform<?> iPlotMain,
final @NonNull String platform
) {
2019-02-04 14:59:11 +01:00
if (instance != null) {
throw new IllegalStateException("Cannot re-initialize the PlotSquared singleton");
}
2019-02-04 14:59:11 +01:00
instance = this;
2019-02-04 14:59:11 +01:00
this.thread = Thread.currentThread();
this.platform = iPlotMain;
2019-02-04 14:59:11 +01:00
Settings.PLATFORM = platform;
// Initialize the class
PlayerMetaDataKeys.load();
2020-07-22 21:06:23 +02:00
2019-02-04 14:59:11 +01:00
//
// Register configuration serializable classes
//
ConfigurationSerialization.registerClass(BlockBucket.class, "BlockBucket");
// load configs before reading from settings
if (!setupConfigs()) {
return;
}
this.captionLoader = CaptionLoader.of(
Locale.ENGLISH,
CaptionLoader.patternExtractor(Pattern.compile("messages_(.*)\\.json")),
DefaultCaptionProvider.forClassLoaderFormatString(
this.getClass().getClassLoader(),
"lang/messages_%s.json" // the path in our jar file
2021-06-08 15:35:29 +02:00
),
TranslatableCaption.DEFAULT_NAMESPACE
);
// Load caption map
try {
this.loadCaptionMap();
} catch (final Exception e) {
LOGGER.error("Failed to load caption map", e);
LOGGER.error("Shutting down server to prevent further issues");
this.platform.shutdownServer();
throw new RuntimeException("Abort loading PlotSquared");
}
2020-07-14 13:14:02 +02:00
// Setup the global flag container
GlobalFlagContainer.setup();
2015-09-13 06:04:31 +02:00
try {
new ReflectionUtils(this.platform.serverNativePackage());
2019-02-04 14:59:11 +01:00
try {
URL logurl = PlotSquared.class.getProtectionDomain().getCodeSource().getLocation();
2019-02-04 14:59:11 +01:00
this.jarFile = new File(
new URL(logurl.toURI().toString().split("\\!")[0].replaceAll("jar:file", "file"))
.toURI().getPath());
2019-02-04 14:59:11 +01:00
} catch (MalformedURLException | URISyntaxException | SecurityException e) {
e.printStackTrace();
this.jarFile = new File(this.platform.getDirectory().getParentFile(), "PlotSquared.jar");
2019-02-04 14:59:11 +01:00
if (!this.jarFile.exists()) {
this.jarFile = new File(
this.platform.getDirectory().getParentFile(),
"PlotSquared-" + platform + ".jar"
);
2019-02-04 14:59:11 +01:00
}
}
2020-07-10 22:12:37 +02:00
this.worldedit = WorldEdit.getInstance();
WorldEdit.getInstance().getEventBus().register(new WEPlatformReadyListener());
Pull/2693 (#2694) * Commit WIP flag work. * More ported flag types, and additions to the flag API. * Make PlotFlag more generic to allow generic flag creation * Pull Captions methods into a Caption interface. * Port MusicFlag * Port flight flag * Port UntrustedVisitFlag * Port DenyExitFlag * Remove paper suggestion * Make ListFlag lists immutable * Update Flag containers. Add javadocs. Add missing methods. * Port description flag * Port greeting and farewell flags * Port weather flag * Move getExample implementation to BooleanFlag * Port reserved flags * Port all boolean flags. * Remove unused flag types * Invert liquid-flow flag * Find the real (legacy) flag name * Change NOITFY -> NOTIFY in Captions * Make IntegerFlag extendable * Port integer flags * Update Flag command to current API state * Begin remaking flag command * Create DoubleFlag + extract common parsing stuff * Supply arguments in flag parse exceptions * Implement missing flag subcommands * Update Flag command to current API state * Implement PriceFlag * Port deny-teleport * Port gamemode flags * Port BreakFlag * Port PlaceFlag * Port UseFlag * Remove old unused flag constants * Port blocked-cmds flag * Fix entity util * Port TimeFlag * Use CaptionUtility for formatting * Port keep flag * Fix imports * Reformat code * Remove unused classes * Fix MainUtil.java * Remove FlagCmd * Add flag info header and footer * Comment out flag related stuff in SchematicHandler * Remove FlagManager * Finalize Plot.java * Finalize PlotArea.java * Finalize PlotListener * Fix API issues * Fix a bunch of compile errors * Fix `/plot flag remove` * Fix initialization of GlobalFlagContainer * Apply API changes to events * Update SQLManager to new API * Invert default value for DenyExitFlag * Replace flag.getValue().toString() with flag.toString() * Make FlagContainer instance in Plot final * Fix various command issues * Clean up PlotSettings * Don't show internal flags in flag list * Fix `/plot flag add` * Remove the info inventory as it's 100% broken * Add plot info entries and fix up the default format * Fix default flag state in Captions * 781c200 part 2 * Fix odd grammar in captions * Fix odd grammar in captions v2 * Add create table statements for plot_flags * Remove old flag references in SQLManager * Use the new plot_flags table * Add tab completion to `/plot flag` * Improve parse error handling * Make flag permission check recognize parse exceptions * Initial progress towards flag conversion * Fix minor issues * Don't validate flags during flag conversion * Allow unrecognized flags to be parsed * Filter out internal flags from command sugguestions * Use the wrong caption when there's no plot description set * Limit command suggestions for boolean flags * Make blocktypelistflags accept blockcategories * Require categories to be prefixed with '#' and fix some minor display issues * Fix plot database conversion * Update PlotFlagEvent.java Updated return description * Fix command annotation wrapping * Add FLAG_UPDATE event for FlagContainer listeners * Make getParentContainer nullable * Document castUnsafe in FlagContainer * Document FlagContainer constructors * Add missing documentation to FlagContainer * Document FlagParseException * Fix wording in FlagContainer javadoc * Document InternalFlag * Document PlotFlag * Minor changes * Remove revisit comments Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> Co-authored-by: NotMyFault <mc.cache@web.de> Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com>
2020-02-24 18:42:02 +01:00
// Create Event utility class
2020-07-10 19:25:05 +02:00
this.eventDispatcher = new EventDispatcher(this.worldedit);
// Create plot listener
this.plotListener = new PlotListener(this.eventDispatcher);
2019-02-04 14:59:11 +01:00
// Copy files
copyFile("town.template", Settings.Paths.TEMPLATES);
copyFile("bridge.template", Settings.Paths.TEMPLATES);
2020-07-29 14:53:09 +02:00
copyFile("skyblock.template", Settings.Paths.TEMPLATES);
2019-02-04 14:59:11 +01:00
showDebug();
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* Gets an instance of PlotSquared.
*
* @return instance of PlotSquared
*/
public static @NonNull PlotSquared get() {
return PlotSquared.instance;
}
/**
* Get the platform specific implementation of PlotSquared
*
* @return Platform implementation
*/
public static @NonNull PlotPlatform<?> platform() {
if (instance != null && instance.platform != null) {
return instance.platform;
}
throw new IllegalStateException("Plot platform implementation is missing");
}
public void loadCaptionMap() throws Exception {
this.platform.copyCaptionMaps();
2020-08-05 12:48:10 +02:00
// Setup localization
CaptionMap captionMap;
if (Settings.Enabled_Components.PER_USER_LOCALE) {
captionMap = this.captionLoader.loadAll(this.platform.getDirectory().toPath().resolve("lang"));
2020-08-05 12:48:10 +02:00
} else {
String fileName = "messages_" + Settings.Enabled_Components.DEFAULT_LOCALE + ".json";
captionMap = this.captionLoader.loadOrCreateSingle(this.platform
.getDirectory()
.toPath()
.resolve("lang")
.resolve(fileName));
2020-08-05 12:48:10 +02:00
}
this.captionMaps.put(TranslatableCaption.DEFAULT_NAMESPACE, captionMap);
LOGGER.info(
"Loaded caption map for namespace 'plotsquared': {}",
this.captionMaps.get(TranslatableCaption.DEFAULT_NAMESPACE).getClass().getCanonicalName()
);
2020-08-05 12:48:10 +02:00
}
2019-02-04 14:59:11 +01:00
/**
2020-07-14 18:49:40 +02:00
* Get the platform specific {@link PlotAreaManager} instance
2019-02-04 14:59:11 +01:00
*
2020-07-14 18:49:40 +02:00
* @return Plot area manager
2019-02-04 14:59:11 +01:00
*/
public @NonNull PlotAreaManager getPlotAreaManager() {
return this.platform.plotAreaManager();
}
2020-07-14 18:49:40 +02:00
public void startExpiryTasks() {
2019-02-04 14:59:11 +01:00
if (Settings.Enabled_Components.PLOT_EXPIRY) {
ExpireManager expireManager = PlotSquared.platform().expireManager();
expireManager.runAutomatedTask();
2019-02-04 14:59:11 +01:00
for (Settings.Auto_Clear settings : Settings.AUTO_CLEAR.getInstances()) {
2020-07-14 18:49:40 +02:00
ExpiryTask task = new ExpiryTask(settings, this.getPlotAreaManager());
expireManager.addTask(task);
2019-02-04 14:59:11 +01:00
}
}
}
public boolean isMainThread(final @NonNull Thread thread) {
2019-02-04 14:59:11 +01:00
return this.thread == thread;
}
/**
2020-04-11 03:23:48 +02:00
* Check if `version` is &gt;= `version2`.
2019-02-04 14:59:11 +01:00
*
* @param version First version
* @param version2 Second version
* @return {@code true} if `version` is &gt;= `version2`
2019-02-04 14:59:11 +01:00
*/
public boolean checkVersion(
final int[] version,
final int... version2
) {
2019-02-04 14:59:11 +01:00
return version[0] > version2[0] || version[0] == version2[0] && version[1] > version2[1]
|| version[0] == version2[0] && version[1] == version2[1] && version[2] >= version2[2];
2019-02-04 14:59:11 +01:00
}
/**
* Gets the current PlotSquared version.
2019-02-04 14:59:11 +01:00
*
* @return current version in config or null
*/
public @NonNull PlotVersion getVersion() {
2019-02-04 14:59:11 +01:00
return this.version;
}
/**
* Gets the server platform this plugin is running on this is running on.
*
2019-02-04 14:59:11 +01:00
* <p>This will be either <b>Bukkit</b> or <b>Sponge</b></p>
*
* @return the server implementation
*/
public @NonNull String getPlatform() {
2019-02-04 14:59:11 +01:00
return Settings.PLATFORM;
}
/**
* Add a global reference to a plot world.
* <p>
* You can remove the reference by calling {@link #removePlotArea(PlotArea)}
* </p>
2019-02-04 14:59:11 +01:00
*
2021-08-24 15:34:21 +02:00
* @param plotArea the {@link PlotArea} to add.
2019-02-04 14:59:11 +01:00
*/
2022-01-20 21:01:38 +01:00
@SuppressWarnings("unchecked")
public void addPlotArea(final @NonNull PlotArea plotArea) {
2019-02-04 14:59:11 +01:00
HashMap<PlotId, Plot> plots;
if (plots_tmp == null || (plots = plots_tmp.remove(plotArea.toString())) == null) {
if (plotArea.getType() == PlotAreaType.PARTIAL) {
plots = this.plots_tmp != null ? this.plots_tmp.get(plotArea.getWorldName()) : null;
2019-02-04 14:59:11 +01:00
if (plots != null) {
Iterator<Entry<PlotId, Plot>> iterator = plots.entrySet().iterator();
while (iterator.hasNext()) {
Entry<PlotId, Plot> next = iterator.next();
PlotId id = next.getKey();
if (plotArea.contains(id)) {
next.getValue().setArea(plotArea);
iterator.remove();
}
}
}
}
} else {
for (Plot entry : plots.values()) {
entry.setArea(plotArea);
}
}
Set<PlotCluster> clusters;
2020-07-10 19:25:05 +02:00
if (clustersTmp == null || (clusters = clustersTmp.remove(plotArea.toString())) == null) {
if (plotArea.getType() == PlotAreaType.PARTIAL) {
2020-07-10 19:25:05 +02:00
clusters = this.clustersTmp != null ?
this.clustersTmp.get(plotArea.getWorldName()) :
null;
2019-02-04 14:59:11 +01:00
if (clusters != null) {
Iterator<PlotCluster> iterator = clusters.iterator();
while (iterator.hasNext()) {
PlotCluster next = iterator.next();
if (next.intersects(plotArea.getMin(), plotArea.getMax())) {
next.setArea(plotArea);
iterator.remove();
}
}
}
}
} else {
for (PlotCluster cluster : clusters) {
cluster.setArea(plotArea);
}
}
2020-07-14 18:49:40 +02:00
getPlotAreaManager().addPlotArea(plotArea);
2019-02-04 14:59:11 +01:00
plotArea.setupBorder();
if (!Settings.Enabled_Components.PERSISTENT_ROAD_REGEN) {
return;
}
File file = new File(
this.platform.getDirectory() + File.separator + "persistent_regen_data_" + plotArea.getId()
+ "_" + plotArea.getWorldName());
if (!file.exists()) {
return;
}
2021-01-08 13:59:33 +01:00
TaskManager.runTaskAsync(() -> {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
List<Object> list = (List<Object>) ois.readObject();
ArrayList<int[]> regionInts = (ArrayList<int[]>) list.get(0);
ArrayList<int[]> chunkInts = (ArrayList<int[]>) list.get(1);
HashSet<BlockVector2> regions = new HashSet<>();
Set<BlockVector2> chunks = new HashSet<>();
regionInts.forEach(l -> regions.add(BlockVector2.at(l[0], l[1])));
chunkInts.forEach(l -> chunks.add(BlockVector2.at(l[0], l[1])));
int height = (int) list.get(2);
LOGGER.info(
"Incomplete road regeneration found. Restarting in world {} with height {}",
plotArea.getWorldName(),
height
);
LOGGER.info("- Regions: {}", regions.size());
LOGGER.info("- Chunks: {}", chunks.size());
HybridUtils.UPDATE = true;
PlotSquared.platform().hybridUtils().scheduleRoadUpdate(plotArea, regions, height, chunks);
} catch (IOException | ClassNotFoundException e) {
LOGGER.error("Error restarting road regeneration", e);
} finally {
if (!file.delete()) {
LOGGER.error("Error deleting persistent_regen_data_{}. Please delete this file manually", plotArea.getId());
}
}
});
}
2019-02-04 14:59:11 +01:00
/**
* Remove a plot world reference.
*
2021-08-24 15:34:21 +02:00
* @param area the {@link PlotArea} to remove
2019-02-04 14:59:11 +01:00
*/
public void removePlotArea(final @NonNull PlotArea area) {
2020-07-14 18:49:40 +02:00
getPlotAreaManager().removePlotArea(area);
2019-02-04 14:59:11 +01:00
setPlotsTmp(area);
}
public void removePlotAreas(final @NonNull String world) {
for (final PlotArea area : this.getPlotAreaManager().getPlotAreasSet(world)) {
if (area.getWorldName().equals(world)) {
2019-02-04 14:59:11 +01:00
removePlotArea(area);
}
}
}
private void setPlotsTmp(final @NonNull PlotArea area) {
2019-02-04 14:59:11 +01:00
if (this.plots_tmp == null) {
this.plots_tmp = new HashMap<>();
}
HashMap<PlotId, Plot> map =
this.plots_tmp.computeIfAbsent(area.toString(), k -> new HashMap<>());
2019-02-04 14:59:11 +01:00
for (Plot plot : area.getPlots()) {
map.put(plot.getId(), plot);
}
2020-07-10 19:25:05 +02:00
if (this.clustersTmp == null) {
this.clustersTmp = new HashMap<>();
2019-02-04 14:59:11 +01:00
}
2020-07-10 19:25:05 +02:00
this.clustersTmp.put(area.toString(), area.getClusters());
}
public Set<PlotCluster> getClusters(final @NonNull String world) {
final Set<PlotCluster> set = new HashSet<>();
for (final PlotArea area : this.getPlotAreaManager().getPlotAreasSet(world)) {
2019-02-04 14:59:11 +01:00
set.addAll(area.getClusters());
}
2019-02-04 14:59:11 +01:00
return Collections.unmodifiableSet(set);
}
public List<Plot> sortPlotsByTemp(Collection<Plot> plots) {
int max = 0;
int overflowCount = 0;
for (Plot plot : plots) {
if (plot.temp > 0) {
if (plot.temp > max) {
max = plot.temp;
}
} else {
overflowCount++;
}
}
2019-02-04 14:59:11 +01:00
Plot[] array = new Plot[max + 1];
List<Plot> overflow = new ArrayList<>(overflowCount);
for (Plot plot : plots) {
if (plot.temp <= 0) {
overflow.add(plot);
} else {
array[plot.temp] = plot;
}
}
ArrayList<Plot> result = new ArrayList<>(plots.size());
for (Plot plot : array) {
if (plot != null) {
result.add(plot);
}
}
overflow.sort(Comparator.comparingInt(Plot::hashCode));
2019-02-04 14:59:11 +01:00
result.addAll(overflow);
return result;
}
/**
* Sort plots by hashcode.
*
* @param plots the collection of plots to sort
2019-02-04 16:18:50 +01:00
* @return the sorted collection
2019-02-04 14:59:11 +01:00
*/
private ArrayList<Plot> sortPlotsByHash(Collection<Plot> plots) {
2019-02-04 14:59:11 +01:00
int hardmax = 256000;
int max = 0;
int overflowSize = 0;
for (Plot plot : plots) {
int hash = MathMan.getPositiveId(plot.hashCode());
if (hash > max) {
if (hash >= hardmax) {
overflowSize++;
} else {
max = hash;
}
}
}
hardmax = Math.min(hardmax, max);
Plot[] cache = new Plot[hardmax + 1];
List<Plot> overflow = new ArrayList<>(overflowSize);
ArrayList<Plot> extra = new ArrayList<>();
for (Plot plot : plots) {
int hash = MathMan.getPositiveId(plot.hashCode());
if (hash < hardmax) {
if (hash >= 0) {
cache[hash] = plot;
} else {
extra.add(plot);
}
2020-07-18 11:05:16 +02:00
} else if (Math.abs(plot.getId().getX()) > 15446 || Math.abs(plot.getId().getY()) > 15446) {
2019-02-04 14:59:11 +01:00
extra.add(plot);
} else {
overflow.add(plot);
}
}
Plot[] overflowArray = overflow.toArray(new Plot[0]);
2019-02-04 14:59:11 +01:00
sortPlotsByHash(overflowArray);
ArrayList<Plot> result = new ArrayList<>(cache.length + overflowArray.length);
for (Plot plot : cache) {
if (plot != null) {
result.add(plot);
}
}
Collections.addAll(result, overflowArray);
result.addAll(extra);
2019-02-04 14:59:11 +01:00
return result;
}
2019-02-04 16:18:50 +01:00
/**
* Unchecked, use {@link #sortPlots(Collection, SortType, PlotArea)} instead which will in turn call this.
*
* @param input an array of plots to sort
*/
2022-01-20 21:01:38 +01:00
@SuppressWarnings("unchecked")
2021-08-18 11:58:18 +02:00
private void sortPlotsByHash(final @NonNull Plot @NonNull [] input) {
2019-02-04 14:59:11 +01:00
List<Plot>[] bucket = new ArrayList[32];
Arrays.fill(bucket, new ArrayList<>());
2019-02-04 14:59:11 +01:00
boolean maxLength = false;
int placement = 1;
while (!maxLength) {
maxLength = true;
for (Plot plot : input) {
int tmp = MathMan.getPositiveId(plot.hashCode()) / placement;
bucket[tmp & 31].add(plot);
2019-02-04 14:59:11 +01:00
if (maxLength && tmp > 0) {
maxLength = false;
}
}
int a = 0;
for (int i = 0; i < 32; i++) {
for (Plot plot : bucket[i]) {
input[a++] = plot;
2019-02-04 14:59:11 +01:00
}
bucket[i].clear();
2019-02-04 14:59:11 +01:00
}
placement *= 32;
}
}
private @NonNull List<Plot> sortPlotsByTimestamp(final @NonNull Collection<Plot> plots) {
2019-02-04 14:59:11 +01:00
int hardMax = 256000;
int max = 0;
int overflowSize = 0;
for (final Plot plot : plots) {
2019-02-04 14:59:11 +01:00
int hash = MathMan.getPositiveId(plot.hashCode());
if (hash > max) {
if (hash >= hardMax) {
overflowSize++;
} else {
max = hash;
}
}
}
2019-02-04 14:59:11 +01:00
hardMax = Math.min(hardMax, max);
Plot[] cache = new Plot[hardMax + 1];
List<Plot> overflow = new ArrayList<>(overflowSize);
ArrayList<Plot> extra = new ArrayList<>();
for (Plot plot : plots) {
int hash = MathMan.getPositiveId(plot.hashCode());
if (hash < hardMax) {
if (hash >= 0) {
cache[hash] = plot;
} else {
extra.add(plot);
}
2020-07-18 11:05:16 +02:00
} else if (Math.abs(plot.getId().getX()) > 15446 || Math.abs(plot.getId().getY()) > 15446) {
2019-02-04 14:59:11 +01:00
extra.add(plot);
} else {
overflow.add(plot);
}
}
Plot[] overflowArray = overflow.toArray(new Plot[0]);
2019-02-04 14:59:11 +01:00
sortPlotsByHash(overflowArray);
ArrayList<Plot> result = new ArrayList<>(cache.length + overflowArray.length);
for (Plot plot : cache) {
if (plot != null) {
result.add(plot);
}
}
Collections.addAll(result, overflowArray);
result.addAll(extra);
2019-02-04 14:59:11 +01:00
return result;
}
2019-02-04 14:59:11 +01:00
/**
* Sort plots by creation timestamp.
2019-02-04 16:18:50 +01:00
*
* @param input Plots to sort
* @return Sorted list
2019-02-04 14:59:11 +01:00
*/
private @NonNull List<Plot> sortPlotsByModified(final @NonNull Collection<Plot> input) {
2019-02-04 14:59:11 +01:00
List<Plot> list;
if (input instanceof List) {
list = (List<Plot>) input;
2015-09-13 06:04:31 +02:00
} else {
2019-02-04 14:59:11 +01:00
list = new ArrayList<>(input);
}
ExpireManager expireManager = PlotSquared.platform().expireManager();
list.sort(Comparator.comparingLong(a -> expireManager.getTimestamp(a.getOwnerAbs())));
2019-02-04 14:59:11 +01:00
return list;
}
/**
2019-02-04 16:18:50 +01:00
* Sort a collection of plots by world (with a priority world), then
* by hashcode.
2019-02-04 14:59:11 +01:00
*
* @param plots the plots to sort
* @param type The sorting method to use for each world (timestamp, or hash)
2019-02-04 16:18:50 +01:00
* @param priorityArea Use null, "world", or "gibberish" if you
* want default world order
2019-02-04 14:59:11 +01:00
* @return ArrayList of plot
*/
public @NonNull List<Plot> sortPlots(
final @NonNull Collection<Plot> plots,
final @NonNull SortType type,
final @Nullable PlotArea priorityArea
) {
2019-02-04 14:59:11 +01:00
// group by world
// sort each
HashMap<PlotArea, Collection<Plot>> map = new HashMap<>();
2020-07-14 18:49:40 +02:00
int totalSize = Arrays.stream(this.getPlotAreaManager().getAllPlotAreas()).mapToInt(PlotArea::getPlotCount).sum();
2019-02-04 14:59:11 +01:00
if (plots.size() == totalSize) {
2020-07-14 18:49:40 +02:00
for (PlotArea area : getPlotAreaManager().getAllPlotAreas()) {
2019-02-04 14:59:11 +01:00
map.put(area, area.getPlots());
}
} else {
2020-07-14 18:49:40 +02:00
for (PlotArea area : getPlotAreaManager().getAllPlotAreas()) {
map.put(area, new ArrayList<>(0));
2019-02-04 14:59:11 +01:00
}
Collection<Plot> lastList = null;
PlotArea lastWorld = null;
for (Plot plot : plots) {
if (lastWorld == plot.getArea()) {
lastList.add(plot);
} else {
lastWorld = plot.getArea();
lastList = map.get(lastWorld);
lastList.add(plot);
}
}
}
2020-07-14 18:49:40 +02:00
List<PlotArea> areas = Arrays.asList(getPlotAreaManager().getAllPlotAreas());
areas.sort((a, b) -> {
if (priorityArea != null) {
if (a.equals(priorityArea)) {
return -1;
} else if (b.equals(priorityArea)) {
return 1;
2019-02-04 14:59:11 +01:00
}
}
return a.hashCode() - b.hashCode();
2019-02-04 14:59:11 +01:00
});
ArrayList<Plot> toReturn = new ArrayList<>(plots.size());
for (PlotArea area : areas) {
switch (type) {
2022-01-20 21:01:38 +01:00
case CREATION_DATE -> toReturn.addAll(sortPlotsByTemp(map.get(area)));
case CREATION_DATE_TIMESTAMP -> toReturn.addAll(sortPlotsByTimestamp(map.get(area)));
case DISTANCE_FROM_ORIGIN -> toReturn.addAll(sortPlotsByHash(map.get(area)));
case LAST_MODIFIED -> toReturn.addAll(sortPlotsByModified(map.get(area)));
default -> {
}
}
}
2019-02-04 14:59:11 +01:00
return toReturn;
}
public void setPlots(final @NonNull Map<String, HashMap<PlotId, Plot>> plots) {
2019-02-04 16:18:50 +01:00
if (this.plots_tmp == null) {
this.plots_tmp = new HashMap<>();
}
for (final Entry<String, HashMap<PlotId, Plot>> entry : plots.entrySet()) {
final String world = entry.getKey();
final PlotArea plotArea = this.getPlotAreaManager().getPlotArea(world, null);
if (plotArea == null) {
Map<PlotId, Plot> map = this.plots_tmp.computeIfAbsent(world, k -> new HashMap<>());
2019-02-04 16:18:50 +01:00
map.putAll(entry.getValue());
} else {
for (Plot plot : entry.getValue().values()) {
plot.setArea(plotArea);
plotArea.addPlot(plot);
2019-02-04 16:18:50 +01:00
}
}
}
}
2019-02-04 14:59:11 +01:00
/**
* Unregisters a plot from local memory without calling the database.
2019-02-04 14:59:11 +01:00
*
* @param plot the plot to remove
* @param callEvent If to call an event about the plot being removed
* @return {@code true} if plot existed | {@code false} if it didn't
2019-02-04 14:59:11 +01:00
*/
public boolean removePlot(
final @NonNull Plot plot,
final boolean callEvent
) {
2019-02-04 14:59:11 +01:00
if (plot == null) {
return false;
}
2019-02-04 14:59:11 +01:00
if (callEvent) {
eventDispatcher.callDelete(plot);
}
2019-02-04 14:59:11 +01:00
if (plot.getArea().removePlot(plot.getId())) {
PlotId last = (PlotId) plot.getArea().getMeta("lastPlot");
2020-07-18 11:05:16 +02:00
int last_max = Math.max(Math.abs(last.getX()), Math.abs(last.getY()));
int this_max = Math.max(Math.abs(plot.getId().getX()), Math.abs(plot.getId().getY()));
2019-02-04 14:59:11 +01:00
if (this_max < last_max) {
plot.getArea().setMeta("lastPlot", plot.getId());
}
if (callEvent) {
eventDispatcher.callPostDelete(plot);
}
2019-02-04 14:59:11 +01:00
return true;
}
return false;
}
/**
* This method is called by the PlotGenerator class normally.
* <ul>
* <li>Initializes the PlotArea and PlotManager classes
* <li>Registers the PlotArea and PlotManager classes
* <li>Loads (and/or generates) the PlotArea configuration
* <li>Sets up the world border if configured
* </ul>
*
* <p>If loading an augmented plot world:
* <ul>
* <li>Creates the AugmentedPopulator classes
* <li>Injects the AugmentedPopulator classes if required
* </ul>
*
* @param world the world to load
* @param baseGenerator The generator for that world, or null
*/
public void loadWorld(
final @NonNull String world,
final @Nullable GeneratorWrapper<?> baseGenerator
) {
2019-02-04 14:59:11 +01:00
if (world.equals("CheckingPlotSquaredGenerator")) {
return;
}
if (!this.getPlotAreaManager().addWorld(world)) {
return;
}
2019-02-04 14:59:11 +01:00
Set<String> worlds;
2020-07-10 19:25:05 +02:00
if (this.worldConfiguration.contains("worlds")) {
worlds = this.worldConfiguration.getConfigurationSection("worlds").getKeys(false);
2019-02-04 14:59:11 +01:00
} else {
worlds = new HashSet<>();
}
String path = "worlds." + world;
2020-07-10 19:25:05 +02:00
ConfigurationSection worldSection = this.worldConfiguration.getConfigurationSection(path);
PlotAreaType type;
2019-02-04 14:59:11 +01:00
if (worldSection != null) {
type = ConfigurationUtil.getType(worldSection);
2019-02-04 14:59:11 +01:00
} else {
type = PlotAreaType.NORMAL;
2019-02-04 14:59:11 +01:00
}
if (type == PlotAreaType.NORMAL) {
2020-07-14 18:49:40 +02:00
if (getPlotAreaManager().getPlotAreas(world, null).length != 0) {
2019-02-04 14:59:11 +01:00
return;
}
IndependentPlotGenerator plotGenerator;
if (baseGenerator != null && baseGenerator.isFull()) {
plotGenerator = baseGenerator.getPlotGenerator();
} else if (worldSection != null) {
String secondaryGeneratorName = worldSection.getString("generator.plugin");
GeneratorWrapper<?> secondaryGenerator =
this.platform.getGenerator(world, secondaryGeneratorName);
2019-02-04 14:59:11 +01:00
if (secondaryGenerator != null && secondaryGenerator.isFull()) {
plotGenerator = secondaryGenerator.getPlotGenerator();
} else {
String primaryGeneratorName = worldSection.getString("generator.init");
GeneratorWrapper<?> primaryGenerator =
this.platform.getGenerator(world, primaryGeneratorName);
2019-02-04 14:59:11 +01:00
if (primaryGenerator != null && primaryGenerator.isFull()) {
plotGenerator = primaryGenerator.getPlotGenerator();
} else {
return;
}
}
} else {
return;
}
// Conventional plot generator
PlotArea plotArea = plotGenerator.getNewPlotArea(world, null, null, null);
PlotManager plotManager = plotArea.getPlotManager();
LOGGER.info("Detected world load for '{}'", world);
LOGGER.info("- generator: {}>{}", baseGenerator, plotGenerator);
LOGGER.info("- plot world: {}", plotArea.getClass().getCanonicalName());
LOGGER.info("- plot area manager: {}", plotManager.getClass().getCanonicalName());
if (!this.worldConfiguration.contains(path)) {
this.worldConfiguration.createSection(path);
worldSection = this.worldConfiguration.getConfigurationSection(path);
2019-02-04 14:59:11 +01:00
}
plotArea.saveConfiguration(worldSection);
plotArea.loadDefaultConfiguration(worldSection);
2015-09-13 06:04:31 +02:00
try {
2020-07-10 19:25:05 +02:00
this.worldConfiguration.save(this.worldsFile);
} catch (IOException e) {
2019-02-04 14:59:11 +01:00
e.printStackTrace();
2015-02-19 09:51:10 +01:00
}
2019-02-04 14:59:11 +01:00
// Now add it
addPlotArea(plotArea);
plotGenerator.initialize(plotArea);
} else {
if (!worlds.contains(world)) {
return;
}
ConfigurationSection areasSection = worldSection.getConfigurationSection("areas");
if (areasSection == null) {
2020-07-14 18:49:40 +02:00
if (getPlotAreaManager().getPlotAreas(world, null).length != 0) {
2019-02-04 14:59:11 +01:00
return;
}
LOGGER.info("Detected world load for '{}'", world);
String gen_string = worldSection.getString("generator.plugin", platform.pluginName());
if (type == PlotAreaType.PARTIAL) {
2019-02-13 18:13:57 +01:00
Set<PlotCluster> clusters =
this.clustersTmp != null ? this.clustersTmp.get(world) : new HashSet<>();
2019-02-04 14:59:11 +01:00
if (clusters == null) {
throw new IllegalArgumentException("No cluster exists for world: " + world);
}
ArrayDeque<PlotArea> toLoad = new ArrayDeque<>();
for (PlotCluster cluster : clusters) {
PlotId pos1 = cluster.getP1(); // Cluster pos1
PlotId pos2 = cluster.getP2(); // Cluster pos2
String name = cluster.getName(); // Cluster name
String fullId = name + "-" + pos1 + "-" + pos2;
worldSection.createSection("areas." + fullId);
DBFunc.replaceWorld(world, world + ";" + name, pos1, pos2); // NPE
LOGGER.info("- {}-{}-{}", name, pos1, pos2);
GeneratorWrapper<?> areaGen = this.platform.getGenerator(world, gen_string);
2019-02-04 14:59:11 +01:00
if (areaGen == null) {
throw new IllegalArgumentException("Invalid Generator: " + gen_string);
}
PlotArea pa =
areaGen.getPlotGenerator().getNewPlotArea(world, name, pos1, pos2);
2019-02-04 14:59:11 +01:00
pa.saveConfiguration(worldSection);
pa.loadDefaultConfiguration(worldSection);
try {
2020-07-10 19:25:05 +02:00
this.worldConfiguration.save(this.worldsFile);
2019-02-04 14:59:11 +01:00
} catch (IOException e) {
e.printStackTrace();
}
LOGGER.info("| generator: {}>{}", baseGenerator, areaGen);
LOGGER.info("| plot world: {}", pa.getClass().getCanonicalName());
LOGGER.info("| manager: {}", pa.getPlotManager().getClass().getCanonicalName());
LOGGER.info("Note: Area created for cluster '{}' (invalid or old configuration?)", name);
2019-02-04 14:59:11 +01:00
areaGen.getPlotGenerator().initialize(pa);
areaGen.augment(pa);
toLoad.add(pa);
}
for (PlotArea area : toLoad) {
addPlotArea(area);
}
return;
}
GeneratorWrapper<?> areaGen = this.platform.getGenerator(world, gen_string);
2019-02-04 14:59:11 +01:00
if (areaGen == null) {
throw new IllegalArgumentException("Invalid Generator: " + gen_string);
}
PlotArea pa = areaGen.getPlotGenerator().getNewPlotArea(world, null, null, null);
LOGGER.info("- generator: {}>{}", baseGenerator, areaGen);
LOGGER.info("- plot world: {}", pa.getClass().getCanonicalName());
LOGGER.info("- plot area manager: {}", pa.getPlotManager().getClass().getCanonicalName());
if (!this.worldConfiguration.contains(path)) {
this.worldConfiguration.createSection(path);
worldSection = this.worldConfiguration.getConfigurationSection(path);
}
2019-02-04 14:59:11 +01:00
pa.saveConfiguration(worldSection);
pa.loadDefaultConfiguration(worldSection);
try {
2020-07-10 19:25:05 +02:00
this.worldConfiguration.save(this.worldsFile);
2019-02-04 14:59:11 +01:00
} catch (IOException e) {
e.printStackTrace();
}
areaGen.getPlotGenerator().initialize(pa);
areaGen.augment(pa);
addPlotArea(pa);
return;
}
if (type == PlotAreaType.AUGMENTED) {
2019-02-04 14:59:11 +01:00
throw new IllegalArgumentException(
"Invalid type for multi-area world. Expected `PARTIAL`, got `"
+ PlotAreaType.AUGMENTED + "`");
2019-02-04 14:59:11 +01:00
}
for (String areaId : areasSection.getKeys(false)) {
LOGGER.info("- {}", areaId);
2019-02-04 14:59:11 +01:00
String[] split = areaId.split("(?<=[^;-])-");
if (split.length != 3) {
throw new IllegalArgumentException("Invalid Area identifier: " + areaId
+ ". Expected form `<name>-<pos1>-<pos2>`");
2019-02-04 14:59:11 +01:00
}
String name = split[0];
PlotId pos1 = PlotId.fromString(split[1]);
PlotId pos2 = PlotId.fromString(split[2]);
if (name.isEmpty()) {
2019-02-04 14:59:11 +01:00
throw new IllegalArgumentException("Invalid Area identifier: " + areaId
+ ". Expected form `<name>-<x1;z1>-<x2;z2>`");
2019-02-04 14:59:11 +01:00
}
final PlotArea existing = this.getPlotAreaManager().getPlotArea(world, name);
if (existing != null && name.equals(existing.getId())) {
2019-02-04 14:59:11 +01:00
continue;
}
ConfigurationSection section = areasSection.getConfigurationSection(areaId);
YamlConfiguration clone = new YamlConfiguration();
for (String key : section.getKeys(true)) {
if (section.get(key) instanceof MemorySection) {
continue;
}
if (!clone.contains(key)) {
clone.set(key, section.get(key));
}
}
for (String key : worldSection.getKeys(true)) {
if (worldSection.get(key) instanceof MemorySection) {
continue;
}
if (!key.startsWith("areas") && !clone.contains(key)) {
clone.set(key, worldSection.get(key));
}
}
String gen_string = clone.getString("generator.plugin", platform.pluginName());
GeneratorWrapper<?> areaGen = this.platform.getGenerator(world, gen_string);
2019-02-04 14:59:11 +01:00
if (areaGen == null) {
throw new IllegalArgumentException("Invalid Generator: " + gen_string);
}
PlotArea pa = areaGen.getPlotGenerator().getNewPlotArea(world, name, pos1, pos2);
pa.saveConfiguration(clone);
// netSections is the combination of
for (String key : clone.getKeys(true)) {
if (clone.get(key) instanceof MemorySection) {
continue;
}
if (!worldSection.contains(key)) {
worldSection.set(key, clone.get(key));
} else {
Object value = worldSection.get(key);
if (!Objects.equals(value, clone.get(key))) {
section.set(key, clone.get(key));
}
}
}
pa.loadDefaultConfiguration(clone);
try {
2020-07-10 19:25:05 +02:00
this.worldConfiguration.save(this.worldsFile);
2019-02-04 14:59:11 +01:00
} catch (IOException e) {
e.printStackTrace();
}
LOGGER.info("Detected area load for '{}'", world);
LOGGER.info("| generator: {}>{}", baseGenerator, areaGen);
LOGGER.info("| plot world: {}", pa);
LOGGER.info("| manager: {}", pa.getPlotManager());
2019-02-04 14:59:11 +01:00
areaGen.getPlotGenerator().initialize(pa);
areaGen.augment(pa);
addPlotArea(pa);
}
}
}
2019-02-04 14:59:11 +01:00
/**
* Setup the configuration for a plot world based on world arguments.
* <p>
2019-02-04 14:59:11 +01:00
*
* <i>e.g. /mv create &lt;world&gt; normal -g PlotSquared:&lt;args&gt;</i>
*
* @param world The name of the world
* @param args The arguments
* @param generator the plot generator
* @return boolean | if valid arguments were provided
*/
public boolean setupPlotWorld(
final @NonNull String world,
final @Nullable String args,
final @NonNull IndependentPlotGenerator generator
) {
2019-02-04 14:59:11 +01:00
if (args != null && !args.isEmpty()) {
// save configuration
final List<String> validArguments = Arrays
Implement extended world heights from Y-64 to Y319 #3473 (#3473) * Begin to implement extended world heights: - Implemented in Bukkit module (and where required in Core module) * Implement extended world heights into core module * Add min gen height to setup, * Default gen/build heights based on minecraft version * Few fixes * Fix up queues * Address comments * Make road schematic stuff slightly more efficient by sharing queues * Minor fixes, don't overlay error many times for the same y * Fix incorrect schematic paste height, undo changes to HybridUtils * Overhall regenallroads method to make it work, make sure BukkitChunkCoordinator can/will finish * Process chunks in order when regenerating all roads * Address comments * Address comments * Ground level//bedrock is at min gen height - Add comment on == rather than <= being used - It's because it's only checking for the bedrock layer being broken if that's disabled * Fix offset for min build height in SchematicHandler * Better javadoc Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> * Address inclusivity issues for max world height * Javadocs/comments/deprecation * Use world min/max heights if present in QueueCoordinator * Address some deprecations for regions and biome setting * Add a count for chunks we're currently trying to load to not skip chunks at the end of a queue's edit * Use minGenHeight + 1 rather than build height in AugmentedUtils * Create utility method for layer index in GenChunk * Correct height in HybridUtils, also use minGenHeight + 1 * Don't magically split to 128 height in regeneration * Add utility methods for world height in QueueCoordinator * Clean up ClassicPlotManager road creation/removal * Start generation at min gen height if bedrock is disabled * min gen height is set in PlotArea * Add note on schem y normalisation * Improve plot getVolume method readability * Don't overly extend height when regenerating road region * y index utility method in ChunknQueueCoordinator * Layer index utility method in LocalChunk * Use version min/max heights if world not present in QueueCoordinator * Fix min -> max * Don't allow players to modify outside build height when using plot set / schematics. - Also fixes schematic height issues * Remove debug * Address comments * Switch loadingChunks to AtomicInteger to be safe (in case of multi-threaded) * Fix "security" issue that was already present * Ensure sign isn't physicsed Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com>
2022-03-05 19:03:39 +01:00
.asList("s=", "size=", "g=", "gap=", "h=", "height=", "minh=", "minheight=", "maxh=", "maxheight=",
"f=", "floor=", "m=", "main=", "w=", "wall=", "b=", "border="
);
2019-02-04 14:59:11 +01:00
// Calculate the number of expected arguments
int expected = (int) validArguments.stream()
.filter(validArgument -> args.toLowerCase(Locale.ENGLISH).contains(validArgument))
.count();
2019-11-10 14:08:34 +01:00
String[] split = args.toLowerCase(Locale.ENGLISH).split(",(?![^\\(\\[]*[\\]\\)])");
2019-02-04 14:59:11 +01:00
if (split.length > expected) {
// This means we have multi-block block buckets
String[] combinedArgs = new String[expected];
int index = 0;
StringBuilder argBuilder = new StringBuilder();
outer:
for (final String string : split) {
for (final String validArgument : validArguments) {
if (string.contains(validArgument)) {
if (!argBuilder.toString().isEmpty()) {
combinedArgs[index++] = argBuilder.toString();
argBuilder = new StringBuilder();
}
argBuilder.append(string);
continue outer;
}
}
if (argBuilder.toString().charAt(argBuilder.length() - 1) != '=') {
argBuilder.append(",");
}
argBuilder.append(string);
}
2019-02-04 14:59:11 +01:00
if (!argBuilder.toString().isEmpty()) {
combinedArgs[index] = argBuilder.toString();
}
2019-02-04 14:59:11 +01:00
split = combinedArgs;
}
final HybridPlotWorldFactory hybridPlotWorldFactory = this.platform
.injector()
.getInstance(HybridPlotWorldFactory.class);
2020-07-11 17:19:19 +02:00
final HybridPlotWorld plotWorld = hybridPlotWorldFactory.create(world, null, generator, null, null);
2019-02-04 14:59:11 +01:00
for (String element : split) {
String[] pair = element.split("=");
if (pair.length != 2) {
LOGGER.error("No value provided for '{}'", element);
2019-02-04 14:59:11 +01:00
return false;
}
String key = pair[0].toLowerCase();
String value = pair[1];
try {
2019-05-17 22:21:03 +02:00
String base = "worlds." + world + ".";
2019-02-04 14:59:11 +01:00
switch (key) {
case "s", "size" -> this.worldConfiguration.set(
base + "plot.size",
ConfigurationUtil.INTEGER.parseString(value).shortValue()
);
case "g", "gap" -> this.worldConfiguration.set(
base + "road.width",
ConfigurationUtil.INTEGER.parseString(value).shortValue()
);
case "h", "height" -> {
this.worldConfiguration.set(
base + "road.height",
ConfigurationUtil.INTEGER.parseString(value).shortValue()
);
this.worldConfiguration.set(
base + "plot.height",
ConfigurationUtil.INTEGER.parseString(value).shortValue()
);
this.worldConfiguration.set(
base + "wall.height",
ConfigurationUtil.INTEGER.parseString(value).shortValue()
);
}
Implement extended world heights from Y-64 to Y319 #3473 (#3473) * Begin to implement extended world heights: - Implemented in Bukkit module (and where required in Core module) * Implement extended world heights into core module * Add min gen height to setup, * Default gen/build heights based on minecraft version * Few fixes * Fix up queues * Address comments * Make road schematic stuff slightly more efficient by sharing queues * Minor fixes, don't overlay error many times for the same y * Fix incorrect schematic paste height, undo changes to HybridUtils * Overhall regenallroads method to make it work, make sure BukkitChunkCoordinator can/will finish * Process chunks in order when regenerating all roads * Address comments * Address comments * Ground level//bedrock is at min gen height - Add comment on == rather than <= being used - It's because it's only checking for the bedrock layer being broken if that's disabled * Fix offset for min build height in SchematicHandler * Better javadoc Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> * Address inclusivity issues for max world height * Javadocs/comments/deprecation * Use world min/max heights if present in QueueCoordinator * Address some deprecations for regions and biome setting * Add a count for chunks we're currently trying to load to not skip chunks at the end of a queue's edit * Use minGenHeight + 1 rather than build height in AugmentedUtils * Create utility method for layer index in GenChunk * Correct height in HybridUtils, also use minGenHeight + 1 * Don't magically split to 128 height in regeneration * Add utility methods for world height in QueueCoordinator * Clean up ClassicPlotManager road creation/removal * Start generation at min gen height if bedrock is disabled * min gen height is set in PlotArea * Add note on schem y normalisation * Improve plot getVolume method readability * Don't overly extend height when regenerating road region * y index utility method in ChunknQueueCoordinator * Layer index utility method in LocalChunk * Use version min/max heights if world not present in QueueCoordinator * Fix min -> max * Don't allow players to modify outside build height when using plot set / schematics. - Also fixes schematic height issues * Remove debug * Address comments * Switch loadingChunks to AtomicInteger to be safe (in case of multi-threaded) * Fix "security" issue that was already present * Ensure sign isn't physicsed Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com>
2022-03-05 19:03:39 +01:00
case "minh", "minheight" -> this.worldConfiguration.set(
base + "world.min_gen_height",
ConfigurationUtil.INTEGER.parseString(value).shortValue()
);
case "maxh", "maxheight" -> this.worldConfiguration.set(
base + "world.max_gen_height",
ConfigurationUtil.INTEGER.parseString(value).shortValue()
);
case "f", "floor" -> this.worldConfiguration.set(
base + "plot.floor",
ConfigurationUtil.BLOCK_BUCKET.parseString(value).toString()
);
case "m", "main" -> this.worldConfiguration.set(
base + "plot.filling",
ConfigurationUtil.BLOCK_BUCKET.parseString(value).toString()
);
case "w", "wall" -> this.worldConfiguration.set(
base + "wall.filling",
ConfigurationUtil.BLOCK_BUCKET.parseString(value).toString()
);
case "b", "border" -> this.worldConfiguration.set(
base + "wall.block",
ConfigurationUtil.BLOCK_BUCKET.parseString(value).toString()
);
default -> {
LOGGER.error("Key not found: {}", element);
2019-02-04 14:59:11 +01:00
return false;
}
2019-02-04 14:59:11 +01:00
}
} catch (Exception e) {
LOGGER.error("Invalid value '{}' for arg '{}'", value, element);
2019-02-04 14:59:11 +01:00
e.printStackTrace();
return false;
}
}
try {
ConfigurationSection section =
this.worldConfiguration.getConfigurationSection("worlds." + world);
2020-07-11 17:19:19 +02:00
plotWorld.saveConfiguration(section);
plotWorld.loadDefaultConfiguration(section);
2020-07-10 19:25:05 +02:00
this.worldConfiguration.save(this.worldsFile);
2019-02-04 14:59:11 +01:00
} catch (IOException e) {
e.printStackTrace();
}
}
2019-02-04 14:59:11 +01:00
return true;
}
2019-02-04 14:59:11 +01:00
/**
* Copies a file from inside the jar to a location
2019-02-04 14:59:11 +01:00
*
2020-04-30 12:53:07 +02:00
* @param file Name of the file inside PlotSquared.jar
2019-02-04 14:59:11 +01:00
* @param folder The output location relative to /plugins/PlotSquared/
*/
public void copyFile(
final @NonNull String file,
final @NonNull String folder
) {
2019-02-04 14:59:11 +01:00
try {
File output = this.platform.getDirectory();
2019-02-04 14:59:11 +01:00
if (!output.exists()) {
output.mkdirs();
}
File newFile = FileUtils.getFile(output, folder + File.separator + file);
2019-02-04 14:59:11 +01:00
if (newFile.exists()) {
return;
}
try (InputStream stream = this.platform.getClass().getResourceAsStream(file)) {
2019-02-04 14:59:11 +01:00
byte[] buffer = new byte[2048];
if (stream == null) {
try (ZipInputStream zis = new ZipInputStream(
new FileInputStream(this.jarFile))) {
2019-02-04 14:59:11 +01:00
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
String name = ze.getName();
if (name.equals(file)) {
new File(newFile.getParent()).mkdirs();
try (FileOutputStream fos = new FileOutputStream(newFile)) {
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
}
ze = null;
} else {
ze = zis.getNextEntry();
}
}
zis.closeEntry();
}
return;
}
newFile.createNewFile();
try (FileOutputStream fos = new FileOutputStream(newFile)) {
2019-02-04 14:59:11 +01:00
int len;
while ((len = stream.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
2017-03-23 01:10:29 +01:00
}
2019-02-04 14:59:11 +01:00
}
} catch (IOException e) {
LOGGER.error("Could not save {}", file);
2019-02-04 14:59:11 +01:00
e.printStackTrace();
}
}
2019-02-04 14:59:11 +01:00
/**
* Safely closes the database connection.
2019-02-04 14:59:11 +01:00
*/
public void disable() {
try {
eventDispatcher.unregisterAll();
checkRoadRegenPersistence();
2019-02-04 14:59:11 +01:00
// Validate that all data in the db is correct
final HashSet<Plot> plots = new HashSet<>();
try {
forEachPlotRaw(plots::add);
2019-02-04 14:59:11 +01:00
} catch (final Exception ignored) {
}
DBFunc.validatePlots(plots);
// Close the connection
DBFunc.close();
} catch (NullPointerException throwable) {
LOGGER.error("Could not close database connection", throwable);
throwable.printStackTrace();
2019-02-04 14:59:11 +01:00
}
}
/**
* Handle road regen persistence
*/
private void checkRoadRegenPersistence() {
if (!HybridUtils.UPDATE || !Settings.Enabled_Components.PERSISTENT_ROAD_REGEN || (
HybridUtils.regions.isEmpty() && HybridUtils.chunks.isEmpty())) {
return;
}
LOGGER.info("Road regeneration incomplete. Saving incomplete regions to disk");
LOGGER.info("- regions: {}", HybridUtils.regions.size());
LOGGER.info("- chunks: {}", HybridUtils.chunks.size());
ArrayList<int[]> regions = new ArrayList<>();
ArrayList<int[]> chunks = new ArrayList<>();
for (BlockVector2 r : HybridUtils.regions) {
regions.add(new int[]{r.getBlockX(), r.getBlockZ()});
}
for (BlockVector2 c : HybridUtils.chunks) {
chunks.add(new int[]{c.getBlockX(), c.getBlockZ()});
}
List<Object> list = new ArrayList<>();
list.add(regions);
list.add(chunks);
list.add(HybridUtils.height);
2020-04-30 12:53:07 +02:00
File file = new File(
this.platform.getDirectory() + File.separator + "persistent_regen_data_" + HybridUtils.area
.getId() + "_" + HybridUtils.area.getWorldName());
if (file.exists() && !file.delete()) {
LOGGER.error("persistent_regene_data file already exists and could not be deleted");
return;
}
try (ObjectOutputStream oos = new ObjectOutputStream(
Files.newOutputStream(file.toPath(), StandardOpenOption.CREATE_NEW))) {
oos.writeObject(list);
} catch (IOException e) {
LOGGER.error("Error creating persistent_region_data file", e);
}
}
2019-02-04 14:59:11 +01:00
/**
* Setup the database connection.
*/
public void setupDatabase() {
try {
if (DBFunc.dbManager != null) {
DBFunc.dbManager.close();
}
Database database;
if (Storage.MySQL.USE) {
database = new MySQL(Storage.MySQL.HOST, Storage.MySQL.PORT, Storage.MySQL.DATABASE,
Storage.MySQL.USER, Storage.MySQL.PASSWORD
);
2019-02-04 14:59:11 +01:00
} else if (Storage.SQLite.USE) {
File file = FileUtils.getFile(platform.getDirectory(), Storage.SQLite.DB + ".db");
2019-02-04 14:59:11 +01:00
database = new SQLite(file);
} else {
LOGGER.error("No storage type is set. Disabling PlotSquared");
this.platform.shutdown(); //shutdown used instead of disable because no database is set
2019-02-04 14:59:11 +01:00
return;
}
DBFunc.dbManager = new SQLManager(
database,
Storage.PREFIX,
this.eventDispatcher,
this.plotListener,
this.worldConfiguration
);
2019-02-04 14:59:11 +01:00
this.plots_tmp = DBFunc.getPlots();
2020-07-14 18:49:40 +02:00
if (getPlotAreaManager() instanceof SinglePlotAreaManager) {
SinglePlotArea area = ((SinglePlotAreaManager) getPlotAreaManager()).getArea();
2019-02-04 14:59:11 +01:00
addPlotArea(area);
2020-07-10 19:25:05 +02:00
ConfigurationSection section = worldConfiguration.getConfigurationSection("worlds.*");
2019-02-04 14:59:11 +01:00
if (section == null) {
2020-07-10 19:25:05 +02:00
section = worldConfiguration.createSection("worlds.*");
2019-02-04 14:59:11 +01:00
}
area.saveConfiguration(section);
area.loadDefaultConfiguration(section);
}
2020-07-10 19:25:05 +02:00
this.clustersTmp = DBFunc.getClusters();
LOGGER.info("Connection to database established. Type: {}", Storage.MySQL.USE ? "MySQL" : "SQLite");
2019-02-04 14:59:11 +01:00
} catch (ClassNotFoundException | SQLException e) {
LOGGER.error(
"Failed to open database connection ({}). Disabling PlotSquared",
Storage.MySQL.USE ? "MySQL" : "SQLite"
);
LOGGER.error("==== Here is an ugly stacktrace, if you are interested in those things ===");
2019-02-04 14:59:11 +01:00
e.printStackTrace();
LOGGER.error("==== End of stacktrace ====");
LOGGER.error(
"Please go to the {} 'storage.yml' and configure the database correctly",
platform.pluginName()
);
this.platform.shutdown(); //shutdown used instead of disable because of database error
}
}
2019-02-04 14:59:11 +01:00
/**
* Setup the default configuration.
*/
public void setupConfig() {
String lastVersionString = this.getConfig().getString("version");
2019-02-04 14:59:11 +01:00
if (lastVersionString != null) {
String[] split = lastVersionString.split("\\.");
int[] lastVersion = new int[]{Integer.parseInt(split[0]), Integer.parseInt(split[1]),
Integer.parseInt(split[2])};
if (checkVersion(new int[]{3, 4, 0}, lastVersion)) {
2019-02-04 14:59:11 +01:00
Settings.convertLegacy(configFile);
if (getConfig().contains("worlds")) {
ConfigurationSection worldSection =
getConfig().getConfigurationSection("worlds");
2020-07-10 19:25:05 +02:00
worldConfiguration.set("worlds", worldSection);
2019-02-04 14:59:11 +01:00
try {
2020-07-10 19:25:05 +02:00
worldConfiguration.save(worldsFile);
2019-02-04 14:59:11 +01:00
} catch (IOException e) {
LOGGER.error("Failed to save worlds.yml", e);
2019-02-04 14:59:11 +01:00
e.printStackTrace();
}
}
Settings.save(configFile);
}
}
Settings.load(configFile);
//Sets the version information for the settings.yml file
try (InputStream stream = getClass().getResourceAsStream("/plugin.properties")) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
String versionString = br.readLine();
String commitString = br.readLine();
String dateString = br.readLine();
this.version = PlotVersion.tryParse(versionString, commitString, dateString);
}
} catch (IOException throwable) {
throwable.printStackTrace();
}
Settings.save(configFile);
config = YamlConfiguration.loadConfiguration(configFile);
}
2019-02-04 14:59:11 +01:00
/**
2019-02-04 16:18:50 +01:00
* Setup all configuration files<br>
* - Config: settings.yml<br>
* - Storage: storage.yml<br>
*
* @return success or not
2019-02-04 14:59:11 +01:00
*/
public boolean setupConfigs() {
File folder = new File(this.platform.getDirectory(), "config");
2019-02-04 14:59:11 +01:00
if (!folder.exists() && !folder.mkdirs()) {
LOGGER.error("Failed to create the {} config folder. Please create it manually", this.platform.getDirectory());
}
2019-02-04 14:59:11 +01:00
try {
this.worldsFile = new File(folder, "worlds.yml");
if (!this.worldsFile.exists() && !this.worldsFile.createNewFile()) {
LOGGER.error("Could not create the worlds file. Please create 'worlds.yml' manually");
2019-02-04 14:59:11 +01:00
}
2020-07-10 19:25:05 +02:00
this.worldConfiguration = YamlConfiguration.loadConfiguration(this.worldsFile);
2019-02-04 14:59:11 +01:00
2020-07-10 19:25:05 +02:00
if (this.worldConfiguration.contains("worlds")) {
if (!this.worldConfiguration.contains("configuration_version") || (
!this.worldConfiguration.getString("configuration_version")
.equalsIgnoreCase(LegacyConverter.CONFIGURATION_VERSION) && !this.worldConfiguration
.getString("configuration_version").equalsIgnoreCase("v5"))) {
2019-02-04 14:59:11 +01:00
// Conversion needed
LOGGER.info("A legacy configuration file was detected. Conversion will be attempted.");
2019-02-04 14:59:11 +01:00
try {
com.google.common.io.Files
.copy(this.worldsFile, new File(folder, "worlds.yml.old"));
LOGGER.info("A copy of worlds.yml has been saved in the file worlds.yml.old");
2019-02-04 14:59:11 +01:00
final ConfigurationSection worlds =
this.worldConfiguration.getConfigurationSection("worlds");
2019-02-04 14:59:11 +01:00
final LegacyConverter converter = new LegacyConverter(worlds);
converter.convert();
2020-07-10 19:25:05 +02:00
this.worldConfiguration.set("worlds", worlds);
Pull/2693 (#2694) * Commit WIP flag work. * More ported flag types, and additions to the flag API. * Make PlotFlag more generic to allow generic flag creation * Pull Captions methods into a Caption interface. * Port MusicFlag * Port flight flag * Port UntrustedVisitFlag * Port DenyExitFlag * Remove paper suggestion * Make ListFlag lists immutable * Update Flag containers. Add javadocs. Add missing methods. * Port description flag * Port greeting and farewell flags * Port weather flag * Move getExample implementation to BooleanFlag * Port reserved flags * Port all boolean flags. * Remove unused flag types * Invert liquid-flow flag * Find the real (legacy) flag name * Change NOITFY -> NOTIFY in Captions * Make IntegerFlag extendable * Port integer flags * Update Flag command to current API state * Begin remaking flag command * Create DoubleFlag + extract common parsing stuff * Supply arguments in flag parse exceptions * Implement missing flag subcommands * Update Flag command to current API state * Implement PriceFlag * Port deny-teleport * Port gamemode flags * Port BreakFlag * Port PlaceFlag * Port UseFlag * Remove old unused flag constants * Port blocked-cmds flag * Fix entity util * Port TimeFlag * Use CaptionUtility for formatting * Port keep flag * Fix imports * Reformat code * Remove unused classes * Fix MainUtil.java * Remove FlagCmd * Add flag info header and footer * Comment out flag related stuff in SchematicHandler * Remove FlagManager * Finalize Plot.java * Finalize PlotArea.java * Finalize PlotListener * Fix API issues * Fix a bunch of compile errors * Fix `/plot flag remove` * Fix initialization of GlobalFlagContainer * Apply API changes to events * Update SQLManager to new API * Invert default value for DenyExitFlag * Replace flag.getValue().toString() with flag.toString() * Make FlagContainer instance in Plot final * Fix various command issues * Clean up PlotSettings * Don't show internal flags in flag list * Fix `/plot flag add` * Remove the info inventory as it's 100% broken * Add plot info entries and fix up the default format * Fix default flag state in Captions * 781c200 part 2 * Fix odd grammar in captions * Fix odd grammar in captions v2 * Add create table statements for plot_flags * Remove old flag references in SQLManager * Use the new plot_flags table * Add tab completion to `/plot flag` * Improve parse error handling * Make flag permission check recognize parse exceptions * Initial progress towards flag conversion * Fix minor issues * Don't validate flags during flag conversion * Allow unrecognized flags to be parsed * Filter out internal flags from command sugguestions * Use the wrong caption when there's no plot description set * Limit command suggestions for boolean flags * Make blocktypelistflags accept blockcategories * Require categories to be prefixed with '#' and fix some minor display issues * Fix plot database conversion * Update PlotFlagEvent.java Updated return description * Fix command annotation wrapping * Add FLAG_UPDATE event for FlagContainer listeners * Make getParentContainer nullable * Document castUnsafe in FlagContainer * Document FlagContainer constructors * Add missing documentation to FlagContainer * Document FlagParseException * Fix wording in FlagContainer javadoc * Document InternalFlag * Document PlotFlag * Minor changes * Remove revisit comments Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> Co-authored-by: NotMyFault <mc.cache@web.de> Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com>
2020-02-24 18:42:02 +01:00
this.setConfigurationVersion(LegacyConverter.CONFIGURATION_VERSION);
LOGGER.info(
"The conversion has finished. PlotSquared will now be disabled and the new configuration file will be used at next startup. Please review the new worlds.yml file. Please note that schematics will not be converted, as we are now using WorldEdit to handle schematics. You need to re-generate the schematics.");
2019-02-04 14:59:11 +01:00
} catch (final Exception e) {
LOGGER.error("Failed to convert the legacy configuration file. See stack trace for information.", e);
2019-02-04 14:59:11 +01:00
}
// Disable plugin
this.platform.shutdown();
2019-02-04 14:59:11 +01:00
return false;
}
} else {
2020-07-10 19:25:05 +02:00
this.worldConfiguration.set("configuration_version", LegacyConverter.CONFIGURATION_VERSION);
2019-02-04 14:59:11 +01:00
}
} catch (IOException ignored) {
LOGGER.error("Failed to save worlds.yml");
}
2019-02-04 14:59:11 +01:00
try {
this.configFile = new File(folder, "settings.yml");
if (!this.configFile.exists() && !this.configFile.createNewFile()) {
LOGGER.error("Could not create the settings file. Please create 'settings.yml' manually");
2019-02-04 14:59:11 +01:00
}
this.config = YamlConfiguration.loadConfiguration(this.configFile);
setupConfig();
} catch (IOException ignored) {
LOGGER.error("Failed to save settings.yml");
2019-02-04 14:59:11 +01:00
}
try {
this.storageFile = new File(folder, "storage.yml");
if (!this.storageFile.exists() && !this.storageFile.createNewFile()) {
LOGGER.error("Could not create the storage settings file. Please create 'storage.yml' manually");
2019-02-04 14:59:11 +01:00
}
2020-07-10 19:25:05 +02:00
YamlConfiguration.loadConfiguration(this.storageFile);
2019-02-04 14:59:11 +01:00
setupStorage();
} catch (IOException ignored) {
LOGGER.error("Failed to save storage.yml");
2019-02-04 14:59:11 +01:00
}
return true;
}
public @NonNull String getConfigurationVersion() {
2020-07-10 19:25:05 +02:00
return this.worldConfiguration.get("configuration_version", LegacyConverter.CONFIGURATION_VERSION)
.toString();
Pull/2693 (#2694) * Commit WIP flag work. * More ported flag types, and additions to the flag API. * Make PlotFlag more generic to allow generic flag creation * Pull Captions methods into a Caption interface. * Port MusicFlag * Port flight flag * Port UntrustedVisitFlag * Port DenyExitFlag * Remove paper suggestion * Make ListFlag lists immutable * Update Flag containers. Add javadocs. Add missing methods. * Port description flag * Port greeting and farewell flags * Port weather flag * Move getExample implementation to BooleanFlag * Port reserved flags * Port all boolean flags. * Remove unused flag types * Invert liquid-flow flag * Find the real (legacy) flag name * Change NOITFY -> NOTIFY in Captions * Make IntegerFlag extendable * Port integer flags * Update Flag command to current API state * Begin remaking flag command * Create DoubleFlag + extract common parsing stuff * Supply arguments in flag parse exceptions * Implement missing flag subcommands * Update Flag command to current API state * Implement PriceFlag * Port deny-teleport * Port gamemode flags * Port BreakFlag * Port PlaceFlag * Port UseFlag * Remove old unused flag constants * Port blocked-cmds flag * Fix entity util * Port TimeFlag * Use CaptionUtility for formatting * Port keep flag * Fix imports * Reformat code * Remove unused classes * Fix MainUtil.java * Remove FlagCmd * Add flag info header and footer * Comment out flag related stuff in SchematicHandler * Remove FlagManager * Finalize Plot.java * Finalize PlotArea.java * Finalize PlotListener * Fix API issues * Fix a bunch of compile errors * Fix `/plot flag remove` * Fix initialization of GlobalFlagContainer * Apply API changes to events * Update SQLManager to new API * Invert default value for DenyExitFlag * Replace flag.getValue().toString() with flag.toString() * Make FlagContainer instance in Plot final * Fix various command issues * Clean up PlotSettings * Don't show internal flags in flag list * Fix `/plot flag add` * Remove the info inventory as it's 100% broken * Add plot info entries and fix up the default format * Fix default flag state in Captions * 781c200 part 2 * Fix odd grammar in captions * Fix odd grammar in captions v2 * Add create table statements for plot_flags * Remove old flag references in SQLManager * Use the new plot_flags table * Add tab completion to `/plot flag` * Improve parse error handling * Make flag permission check recognize parse exceptions * Initial progress towards flag conversion * Fix minor issues * Don't validate flags during flag conversion * Allow unrecognized flags to be parsed * Filter out internal flags from command sugguestions * Use the wrong caption when there's no plot description set * Limit command suggestions for boolean flags * Make blocktypelistflags accept blockcategories * Require categories to be prefixed with '#' and fix some minor display issues * Fix plot database conversion * Update PlotFlagEvent.java Updated return description * Fix command annotation wrapping * Add FLAG_UPDATE event for FlagContainer listeners * Make getParentContainer nullable * Document castUnsafe in FlagContainer * Document FlagContainer constructors * Add missing documentation to FlagContainer * Document FlagParseException * Fix wording in FlagContainer javadoc * Document InternalFlag * Document PlotFlag * Minor changes * Remove revisit comments Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> Co-authored-by: NotMyFault <mc.cache@web.de> Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com>
2020-02-24 18:42:02 +01:00
}
public void setConfigurationVersion(final @NonNull String newVersion) throws IOException {
2020-07-10 19:25:05 +02:00
this.worldConfiguration.set("configuration_version", newVersion);
this.worldConfiguration.save(this.worldsFile);
Pull/2693 (#2694) * Commit WIP flag work. * More ported flag types, and additions to the flag API. * Make PlotFlag more generic to allow generic flag creation * Pull Captions methods into a Caption interface. * Port MusicFlag * Port flight flag * Port UntrustedVisitFlag * Port DenyExitFlag * Remove paper suggestion * Make ListFlag lists immutable * Update Flag containers. Add javadocs. Add missing methods. * Port description flag * Port greeting and farewell flags * Port weather flag * Move getExample implementation to BooleanFlag * Port reserved flags * Port all boolean flags. * Remove unused flag types * Invert liquid-flow flag * Find the real (legacy) flag name * Change NOITFY -> NOTIFY in Captions * Make IntegerFlag extendable * Port integer flags * Update Flag command to current API state * Begin remaking flag command * Create DoubleFlag + extract common parsing stuff * Supply arguments in flag parse exceptions * Implement missing flag subcommands * Update Flag command to current API state * Implement PriceFlag * Port deny-teleport * Port gamemode flags * Port BreakFlag * Port PlaceFlag * Port UseFlag * Remove old unused flag constants * Port blocked-cmds flag * Fix entity util * Port TimeFlag * Use CaptionUtility for formatting * Port keep flag * Fix imports * Reformat code * Remove unused classes * Fix MainUtil.java * Remove FlagCmd * Add flag info header and footer * Comment out flag related stuff in SchematicHandler * Remove FlagManager * Finalize Plot.java * Finalize PlotArea.java * Finalize PlotListener * Fix API issues * Fix a bunch of compile errors * Fix `/plot flag remove` * Fix initialization of GlobalFlagContainer * Apply API changes to events * Update SQLManager to new API * Invert default value for DenyExitFlag * Replace flag.getValue().toString() with flag.toString() * Make FlagContainer instance in Plot final * Fix various command issues * Clean up PlotSettings * Don't show internal flags in flag list * Fix `/plot flag add` * Remove the info inventory as it's 100% broken * Add plot info entries and fix up the default format * Fix default flag state in Captions * 781c200 part 2 * Fix odd grammar in captions * Fix odd grammar in captions v2 * Add create table statements for plot_flags * Remove old flag references in SQLManager * Use the new plot_flags table * Add tab completion to `/plot flag` * Improve parse error handling * Make flag permission check recognize parse exceptions * Initial progress towards flag conversion * Fix minor issues * Don't validate flags during flag conversion * Allow unrecognized flags to be parsed * Filter out internal flags from command sugguestions * Use the wrong caption when there's no plot description set * Limit command suggestions for boolean flags * Make blocktypelistflags accept blockcategories * Require categories to be prefixed with '#' and fix some minor display issues * Fix plot database conversion * Update PlotFlagEvent.java Updated return description * Fix command annotation wrapping * Add FLAG_UPDATE event for FlagContainer listeners * Make getParentContainer nullable * Document castUnsafe in FlagContainer * Document FlagContainer constructors * Add missing documentation to FlagContainer * Document FlagParseException * Fix wording in FlagContainer javadoc * Document InternalFlag * Document PlotFlag * Minor changes * Remove revisit comments Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> Co-authored-by: NotMyFault <mc.cache@web.de> Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com>
2020-02-24 18:42:02 +01:00
}
2019-02-04 14:59:11 +01:00
/**
* Setup the storage file (load + save missing nodes).
*/
private void setupStorage() {
Storage.load(storageFile);
Storage.save(storageFile);
2020-07-10 19:25:05 +02:00
YamlConfiguration.loadConfiguration(storageFile);
2019-02-04 14:59:11 +01:00
}
/**
* Show startup debug information.
*/
private void showDebug() {
if (Settings.DEBUG) {
Map<String, Object> components = Settings.getFields(Settings.Enabled_Components.class);
for (Entry<String, Object> component : components.entrySet()) {
LOGGER.info("Key: {} | Value: {}", component.getKey(), component.getValue());
2019-02-04 14:59:11 +01:00
}
}
}
public void forEachPlotRaw(final @NonNull Consumer<Plot> consumer) {
2020-07-14 18:49:40 +02:00
for (final PlotArea area : this.getPlotAreaManager().getAllPlotAreas()) {
area.getPlots().forEach(consumer);
2019-02-04 14:59:11 +01:00
}
if (this.plots_tmp != null) {
for (final HashMap<PlotId, Plot> entry : this.plots_tmp.values()) {
entry.values().forEach(consumer);
2019-02-04 14:59:11 +01:00
}
}
}
/**
* Check if the chunk uses vanilla/non-PlotSquared generation
*
* @param world World name
* @param chunkCoordinates Chunk coordinates
* @return {@code true} if the chunk uses non-standard generation, {@code false} if not
*/
public boolean isNonStandardGeneration(
final @NonNull String world,
final @NonNull BlockVector2 chunkCoordinates
) {
final Location location = Location.at(world, chunkCoordinates.getBlockX() << 4, 64, chunkCoordinates.getBlockZ() << 4);
2020-07-14 18:49:40 +02:00
final PlotArea area = getPlotAreaManager().getApplicablePlotArea(location);
if (area == null) {
return true;
}
return area.getTerrain() != PlotAreaTerrainType.NONE;
}
public @NonNull YamlConfiguration getConfig() {
return config;
}
public @NonNull UUIDPipeline getImpromptuUUIDPipeline() {
2020-07-17 17:24:45 +02:00
return this.impromptuUUIDPipeline;
}
public @NonNull UUIDPipeline getBackgroundUUIDPipeline() {
2020-07-17 17:24:45 +02:00
return this.backgroundUUIDPipeline;
}
public @NonNull WorldEdit getWorldEdit() {
2020-07-17 17:24:45 +02:00
return this.worldedit;
}
public @NonNull File getConfigFile() {
2020-07-17 17:24:45 +02:00
return this.configFile;
}
public @NonNull File getWorldsFile() {
2020-07-17 17:24:45 +02:00
return this.worldsFile;
}
public @NonNull YamlConfiguration getWorldConfiguration() {
2020-07-17 17:24:45 +02:00
return this.worldConfiguration;
}
2020-07-24 17:19:25 +02:00
/**
* Get the caption map belonging to a namespace. If none exists, a dummy
* caption map will be returned.
* <p>
* You can register a caption map by calling {@link #registerCaptionMap(String, CaptionMap)}
* </p>
2020-07-24 17:19:25 +02:00
*
* @param namespace Namespace
* @return Map instance
*/
public @NonNull CaptionMap getCaptionMap(final @NonNull String namespace) {
return this.captionMaps.computeIfAbsent(
namespace.toLowerCase(Locale.ENGLISH),
missingNamespace -> new DummyCaptionMap()
);
2020-07-24 17:19:25 +02:00
}
/**
2022-01-20 21:01:38 +01:00
* Register a caption map. The namespace needs to be equal to the namespace used for
2021-06-08 15:35:29 +02:00
* the {@link TranslatableCaption}s inside the map.
2020-07-24 17:19:25 +02:00
*
* @param namespace Namespace
2020-07-24 17:19:25 +02:00
* @param captionMap Map instance
*/
public void registerCaptionMap(
final @NonNull String namespace,
final @NonNull CaptionMap captionMap
) {
2020-07-24 17:19:25 +02:00
if (namespace.equalsIgnoreCase(TranslatableCaption.DEFAULT_NAMESPACE)) {
throw new IllegalArgumentException("Cannot replace default caption map");
}
this.captionMaps.put(namespace.toLowerCase(Locale.ENGLISH), captionMap);
2020-07-17 17:24:45 +02:00
}
public @NonNull EventDispatcher getEventDispatcher() {
2020-07-17 17:24:45 +02:00
return this.eventDispatcher;
}
public @NonNull PlotListener getPlotListener() {
2020-07-17 17:24:45 +02:00
return this.plotListener;
}
/**
* Get if the {@link PlatformReadyEvent} has been sent by WorldEdit. There is no way to query this within WorldEdit itself.
*/
public boolean isWeInitialised() {
return weInitialised;
}
2021-01-10 00:01:48 +01:00
/**
* Different ways of sorting {@link Plot plots}
*/
2019-02-04 14:59:11 +01:00
public enum SortType {
2021-01-10 00:01:48 +01:00
/**
* Sort plots by their creation, using their index in the database
*/
CREATION_DATE,
2021-01-10 00:01:48 +01:00
/**
* Sort plots by their creation timestamp
*/
CREATION_DATE_TIMESTAMP,
2021-01-10 00:01:48 +01:00
/**
* Sort plots by when they were last modified
*/
LAST_MODIFIED,
2021-01-10 00:01:48 +01:00
/**
* Sort plots based on their distance from the origin of the world
*/
DISTANCE_FROM_ORIGIN
2019-02-04 14:59:11 +01:00
}
private final class WEPlatformReadyListener {
@SuppressWarnings("unused")
@Subscribe(priority = EventHandler.Priority.VERY_EARLY)
public void onPlatformReady(PlatformReadyEvent event) {
weInitialised = true;
WorldEdit.getInstance().getEventBus().unregister(WEPlatformReadyListener.this);
}
}
2015-02-19 07:08:15 +01:00
}