mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-09-07 00:08:20 +00:00
aa3ae63682
* start v6 * Update classes to v6 method signatures * change rootVersion to signify p2v6 compat * Use 16 as toolchain version but target 11 for build output * add minimessage as api * Require v6 and don't attempt to "setup" hook from FAWE * Address comments * *address /all/ comments * FAWE classes should only act as a delegate * Uppercase logger * Settings for v6-hook have moved to P2-v6, remove unneeded if statements * Rename classes to Delegate * add whenDone task to setCuboids * Remove bad spaces * Fix plot swap * Initial work on 1.17 support * Remove data versions from the Bukkit adapters (#1507) * Remove data versions from the Bukkit adapters * Don't allow saving schematics without an adapter in place on Bukkit. * Removed confusing line (cherry picked from commit 2056218b4a8644836b1d127105dfa289e9cdbc1c) * More progress * Fix chunk sending * Repackage from com.boydti to com.fastasyncworldedit.<module> (#1119) * Preliminary work on repackaging * Rename build artifacts matching our pattern * Finish up repackaging * Fix a few field accesses and old imports * Dirty fix for chunks container ChunkSections outside of 0-15 * Correctly read from NibbleArrays for lighting * Fix getSections and BlockMaterial for 1.17 * Fix writing blocks to the world. - The issue isn't the presence of a "-1" chunk, it's the constructor for ChunkSection requiring the layer (0 to 15) rather than the y chord * Fix more field accesses * More work towards 1.17 * Update Upstream a57f66f Fix watchdog, add negative y support. (1782) * Add azalea tree to `/tool tree` * Don't define toolchain twice * Repackage GriefDefender * Relocate under new namespace * Bye bye ecma left overs * Add 1.17 to issue templates and instructions * Move to adventure-nbt (#918) * Initial work for adventure-nbt * Some more FAWE specific stuff * Fix erroneous deprecation check * Workflow change * Continued merging all adventure NBT related changes * Continued merging all adventure NBT related changes * Made a constructor public again This needs to be public for BlockTransformExtent.java * Finished converting all NBT data to adventure. * Make this compile * Fix conflicts Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com> * Update adapters to 1.17 * Change build prefix to 1.17 * Move more nms classes to adapters * Move left over nms classes * Move Spigot 1.17 class * Remove unneeded adapter loader code The loader will find the appropriate class now itself * Update adapters * Update adapters * Lazy fix tests * Update adapters * Update Upstream 43da91a Remove method reflection for getMinHeight in BukkitWorld. (1796) * Relocate adventure-nbt under proper namespace * Add LazyCompoundTag as a non-version-specific class to be used by adapters * Better integration between old NBT and Adventure NBT - begin fixing the issues seen recently * Correctly NBT conversion method * LazyCompoundTags should actually be overriding and correctly returning a CompoundBinaryTag. * Update worldedit-adapters Fixes #1141 * Remove unnecessary massive lag machine * Refactor apply to applyBlock in subclasses * applyBlock should be overriden by all subclasses. Default apply to applyBlock * Closes #1130 Closes #1132 * Squashed commit of the following: commit a9bfa1a07c77083c844a0c3ba62f4bd94bed107c Author: NotMyFault <mc.cache@web.de> Date: Sun Jun 27 21:53:21 2021 +0200 [ci skip] Update gradle wrapper validation commit aa7471f95317d28a16f62e4b200de8d0fea2fa95 Author: Matthew Miller <mnmiller1@me.com> Date: Sat Oct 10 15:49:13 2020 +1000 Add ^x,y,z relative offset support to the offset parser (#1545) * Add ^x,y,z relative offset support to the offset parser * Wrap in a try-catch (cherry picked from commit 28bdf7ff9254bbc85bb4f5f792b303943a3930a8) * Add `fawe.error.schematic.not.found` translation key * Update Upstream 728a152 Skip notify if chunk section doesn't exist (1794) * Fixed #1157 * Add a null check to prevent NPE in nbt code * Update adapters * Update Upstream fbb047a Optimize legacy schematic loading (1808) * Hurr durr I don't want to update Java * Update Upstream 0790e6e Fix CLI Mess (1811) * Fixes #1160 * Expose minimessage transitively thru PlotSquared Touches #32 * [ci skip] Remove unneeded maven repository * Steal tab completion from PlotSquared for P2 related commands * Don't error on startup when building locally Co-Authored-By: goldfishapp <8278196+goldfishapp@users.noreply.github.com> * [ci skip] Update gh actions to Java 16 * Update textures to grab 1.17 jar Co-authored-by: NotMyFault <mc.cache@web.de> Co-authored-by: SirYwell <hannesgreule@outlook.de> Co-authored-by: Matthew Miller <mnmiller1@me.com> Co-authored-by: Matt <4009945+MattBDev@users.noreply.github.com> Co-authored-by: goldfishapp <8278196+goldfishapp@users.noreply.github.com>
396 lines
15 KiB
Java
396 lines
15 KiB
Java
package com.fastasyncworldedit.core;
|
|
|
|
import com.fastasyncworldedit.core.beta.IQueueChunk;
|
|
import com.fastasyncworldedit.core.beta.IQueueExtent;
|
|
import com.fastasyncworldedit.core.beta.implementation.lighting.Relighter;
|
|
import com.fastasyncworldedit.core.beta.implementation.queue.ParallelQueueExtent;
|
|
import com.fastasyncworldedit.core.configuration.Settings;
|
|
import com.fastasyncworldedit.core.object.RegionWrapper;
|
|
import com.fastasyncworldedit.core.object.RelightMode;
|
|
import com.fastasyncworldedit.core.object.changeset.DiskStorageHistory;
|
|
import com.fastasyncworldedit.core.object.changeset.SimpleChangeSetSummary;
|
|
import com.fastasyncworldedit.core.object.exception.FaweException;
|
|
import com.fastasyncworldedit.core.regions.FaweMaskManager;
|
|
import com.fastasyncworldedit.core.util.EditSessionBuilder;
|
|
import com.fastasyncworldedit.core.util.MainUtil;
|
|
import com.fastasyncworldedit.core.util.MemUtil;
|
|
import com.fastasyncworldedit.core.util.TaskManager;
|
|
import com.fastasyncworldedit.core.util.WEManager;
|
|
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
|
|
import com.google.common.collect.Sets;
|
|
import com.sk89q.worldedit.EditSession;
|
|
import com.sk89q.worldedit.WorldEdit;
|
|
import com.sk89q.worldedit.WorldEditException;
|
|
import com.sk89q.worldedit.entity.Player;
|
|
import com.sk89q.worldedit.extension.platform.Capability;
|
|
import com.sk89q.worldedit.extension.platform.Platform;
|
|
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
|
|
import com.sk89q.worldedit.extent.Extent;
|
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
|
import com.sk89q.worldedit.math.BlockVector3;
|
|
import com.sk89q.worldedit.regions.Region;
|
|
import com.sk89q.worldedit.util.Location;
|
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
|
import com.sk89q.worldedit.world.World;
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
import javax.annotation.Nullable;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
|
|
/**
|
|
* The FaweAPI class offers a few useful functions.<br>
|
|
* - This class is not intended to replace the WorldEdit API<br>
|
|
* - With FAWE installed, you can use the EditSession and other WorldEdit classes from an async thread.<br>
|
|
* <br>
|
|
* FaweAPI.[some method]
|
|
*/
|
|
public class FaweAPI {
|
|
|
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
|
|
|
/**
|
|
* Offers a lot of options for building an EditSession.
|
|
*
|
|
* @return A new EditSessionBuilder
|
|
* @see EditSessionBuilder
|
|
*/
|
|
@Deprecated
|
|
public static EditSessionBuilder getEditSessionBuilder(World world) {
|
|
return new EditSessionBuilder(world);
|
|
}
|
|
|
|
/**
|
|
* The TaskManager has some useful methods for doing things asynchronously.
|
|
*
|
|
* @return TaskManager
|
|
*/
|
|
public static TaskManager getTaskManager() {
|
|
return TaskManager.IMP;
|
|
}
|
|
|
|
/**
|
|
* You can either use a {@code IQueueExtent} or an {@code EditSession} to change blocks.
|
|
*
|
|
* <p>
|
|
* The {@link IQueueExtent} skips a bit of overhead, so it is marginally faster. {@link
|
|
* EditSession} can do a lot more. Remember to commit when you are done!
|
|
* </p>
|
|
*
|
|
* @param world The name of the world
|
|
* @param autoQueue If it should start dispatching before you enqueue it.
|
|
* @return the queue extent
|
|
*/
|
|
public static IQueueExtent<IQueueChunk> createQueue(World world, boolean autoQueue) {
|
|
IQueueExtent<IQueueChunk> queue = Fawe.get().getQueueHandler().getQueue(world);
|
|
if (!autoQueue) {
|
|
queue.disableQueue();
|
|
}
|
|
return queue;
|
|
}
|
|
|
|
public static World getWorld(String worldName) {
|
|
Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING);
|
|
List<? extends World> worlds = platform.getWorlds();
|
|
for (World current : worlds) {
|
|
if (current.getName().equals(worldName)) {
|
|
return WorldWrapper.wrap(current);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Upload the clipboard to the configured web interface.
|
|
*
|
|
* @param clipboard The clipboard (may not be null)
|
|
* @param format The format to use (some formats may not be supported)
|
|
* @return The download URL or null
|
|
*/
|
|
public static URL upload(final Clipboard clipboard, final ClipboardFormat format) {
|
|
return format.upload(clipboard);
|
|
}
|
|
|
|
/**
|
|
* Just forwards to ClipboardFormat.SCHEMATIC.load(file).
|
|
*
|
|
* @param file the file to load
|
|
* @return a clipboard containing the schematic
|
|
* @see ClipboardFormat
|
|
*/
|
|
public static Clipboard load(File file) throws IOException {
|
|
return ClipboardFormats.findByFile(file).load(file);
|
|
}
|
|
|
|
/**
|
|
* Get a list of supported protection plugin masks.
|
|
*
|
|
* @return Set of FaweMaskManager
|
|
*/
|
|
public static Set<FaweMaskManager> getMaskManagers() {
|
|
return new HashSet<>(WEManager.IMP.managers);
|
|
}
|
|
|
|
/**
|
|
* Check if the server has more than the configured low memory threshold.
|
|
*
|
|
* @return True if the server has limited memory
|
|
*/
|
|
public static boolean isMemoryLimited() {
|
|
return MemUtil.isMemoryLimited();
|
|
}
|
|
|
|
/**
|
|
* Get a player's allowed WorldEdit region.
|
|
*/
|
|
public static Region[] getRegions(Player player) {
|
|
return WEManager.IMP.getMask(player);
|
|
}
|
|
|
|
/**
|
|
* Cancel the edit with the following extent.
|
|
*
|
|
* <p>
|
|
* The extent must be the one being used by an EditSession, otherwise an error will be thrown.
|
|
* Insert an extent into the EditSession using the EditSessionEvent.
|
|
* </p>
|
|
*
|
|
* @see EditSession#getRegionExtent() How to get the FaweExtent for an EditSession
|
|
*/
|
|
public static void cancelEdit(AbstractDelegateExtent extent, Component reason) {
|
|
try {
|
|
WEManager.IMP.cancelEdit(extent, new FaweException(reason));
|
|
} catch (WorldEditException ignored) {
|
|
}
|
|
}
|
|
|
|
public static void addMaskManager(FaweMaskManager maskMan) {
|
|
WEManager.IMP.managers.add(maskMan);
|
|
}
|
|
|
|
/**
|
|
* Get the DiskStorageHistory object representing a File.
|
|
*/
|
|
public static DiskStorageHistory getChangeSetFromFile(File file) {
|
|
if (!file.exists() || file.isDirectory()) {
|
|
throw new IllegalArgumentException("Not a file!");
|
|
}
|
|
if (Settings.IMP.HISTORY.USE_DISK) {
|
|
throw new IllegalArgumentException("History on disk not enabled!");
|
|
}
|
|
if (!file.getName().toLowerCase(Locale.ROOT).endsWith(".bd")) {
|
|
throw new IllegalArgumentException("Not a BD file!");
|
|
}
|
|
String[] path = file.getPath().split(File.separatorChar=='\\' ? "\\\\" : File.separator);
|
|
if (path.length < 3) {
|
|
throw new IllegalArgumentException("Not in history directory!");
|
|
}
|
|
String worldName = path[path.length - 3];
|
|
String uuidString = path[path.length - 2];
|
|
World world = getWorld(worldName);
|
|
if (world == null) {
|
|
throw new IllegalArgumentException("Corresponding world does not exist: " + worldName);
|
|
}
|
|
UUID uuid;
|
|
try {
|
|
uuid = UUID.fromString(uuidString);
|
|
} catch (IllegalArgumentException e) {
|
|
throw new IllegalArgumentException("Invalid UUID from file path: " + uuidString);
|
|
}
|
|
return new DiskStorageHistory(world, uuid, Integer.parseInt(file.getName().split("\\.")[0]));
|
|
}
|
|
|
|
/**
|
|
* Used in the rollback to generate a list of {@link DiskStorageHistory} objects.
|
|
*
|
|
* @param origin - The origin location
|
|
* @param user - The uuid (may be null)
|
|
* @param radius - The radius from the origin of the edit
|
|
* @param timediff - The max age of the file in milliseconds
|
|
* @param shallow - If shallow is true, FAWE will only read the first {@link
|
|
* Settings.HISTORY#BUFFER_SIZE} bytes to obtain history info
|
|
* @return a list of DiskStorageHistory Objects
|
|
* @apiNote An edit outside the radius may be included if it overlaps with an edit inside
|
|
* that depends on it. Reading only part of the file will result in unreliable bounds info
|
|
* for large edits.
|
|
*/
|
|
public static List<DiskStorageHistory> getBDFiles(Location origin, UUID user, int radius, long timediff, boolean shallow) {
|
|
Extent extent = origin.getExtent();
|
|
if (!(extent instanceof World)) {
|
|
throw new IllegalArgumentException("Origin is not a valid world");
|
|
}
|
|
World world = (World) extent;
|
|
File history = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HISTORY + File.separator + world.getName());
|
|
if (!history.exists()) {
|
|
return new ArrayList<>();
|
|
}
|
|
long now = System.currentTimeMillis();
|
|
ArrayList<File> files = new ArrayList<>();
|
|
for (File userFile : history.listFiles()) {
|
|
if (!userFile.isDirectory()) {
|
|
continue;
|
|
}
|
|
UUID userUUID;
|
|
try {
|
|
userUUID = UUID.fromString(userFile.getName());
|
|
} catch (IllegalArgumentException e) {
|
|
continue;
|
|
}
|
|
if (user != null && !userUUID.equals(user)) {
|
|
continue;
|
|
}
|
|
ArrayList<Integer> ids = new ArrayList<>();
|
|
for (File file : userFile.listFiles()) {
|
|
if (file.getName().endsWith(".bd")) {
|
|
if (timediff >= Integer.MAX_VALUE || now - file.lastModified() <= timediff) {
|
|
files.add(file);
|
|
if (files.size() > 2048) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
files.sort((a, b) -> {
|
|
String aName = a.getName();
|
|
String bName = b.getName();
|
|
int aI = Integer.parseInt(aName.substring(0, aName.length() - 3));
|
|
int bI = Integer.parseInt(bName.substring(0, bName.length() - 3));
|
|
long value = aI - bI;
|
|
return value == 0 ? 0 : value < 0 ? -1 : 1;
|
|
});
|
|
RegionWrapper bounds = new RegionWrapper(origin.getBlockX() - radius, origin.getBlockX() + radius, origin.getBlockZ() - radius, origin.getBlockZ() + radius);
|
|
RegionWrapper boundsPlus = new RegionWrapper(bounds.minX - 64, bounds.maxX + 512, bounds.minZ - 64, bounds.maxZ + 512);
|
|
HashSet<RegionWrapper> regionSet = Sets.<RegionWrapper>newHashSet(bounds);
|
|
ArrayList<DiskStorageHistory> result = new ArrayList<>();
|
|
for (File file : files) {
|
|
UUID uuid = UUID.fromString(file.getParentFile().getName());
|
|
DiskStorageHistory dsh = new DiskStorageHistory(world, uuid, Integer.parseInt(file.getName().split("\\.")[0]));
|
|
SimpleChangeSetSummary summary = dsh.summarize(boundsPlus, shallow);
|
|
RegionWrapper region = new RegionWrapper(summary.minX, summary.maxX, summary.minZ, summary.maxZ);
|
|
boolean encompassed = false;
|
|
boolean isIn = false;
|
|
for (RegionWrapper allowed : regionSet) {
|
|
isIn = isIn || allowed.intersects(region);
|
|
if (encompassed = allowed.isIn(region.minX, region.maxX) && allowed.isIn(region.minZ, region.maxZ)) {
|
|
break;
|
|
}
|
|
}
|
|
if (isIn) {
|
|
result.add(0, dsh);
|
|
if (!encompassed) {
|
|
regionSet.add(region);
|
|
}
|
|
if (shallow && result.size() > 64) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* The DiskStorageHistory class is what FAWE uses to represent the undo on disk.
|
|
*
|
|
*/
|
|
public static DiskStorageHistory getChangeSetFromDisk(World world, UUID uuid, int index) {
|
|
return new DiskStorageHistory(world, uuid, index);
|
|
}
|
|
|
|
/**
|
|
* Fix the lighting in a selection. This is a multi-step process as outlined below.
|
|
*
|
|
* <ol>
|
|
* <li>Removes all lighting, then relights.</li>
|
|
* <li>Relights in parallel (if enabled) for best performance.</li>
|
|
* <li>Resends the chunks to the client.</li>
|
|
* </ol>
|
|
*
|
|
* @param world World to relight in
|
|
* @param selection Region to relight
|
|
* @param queue Queue to relight in/from
|
|
* @param mode The mode to relight with
|
|
* @return Chunks changed
|
|
*/
|
|
public static int fixLighting(World world, Region selection, @Nullable IQueueExtent<IQueueChunk> queue, final RelightMode mode) {
|
|
final BlockVector3 bot = selection.getMinimumPoint();
|
|
final BlockVector3 top = selection.getMaximumPoint();
|
|
|
|
final int minX = bot.getBlockX() >> 4;
|
|
final int minZ = bot.getBlockZ() >> 4;
|
|
|
|
final int maxX = top.getBlockX() >> 4;
|
|
final int maxZ = top.getBlockZ() >> 4;
|
|
|
|
int count = 0;
|
|
|
|
if (queue == null) {
|
|
World unwrapped = WorldWrapper.unwrap(world);
|
|
if (unwrapped instanceof IQueueExtent) {
|
|
queue = (IQueueExtent) unwrapped;
|
|
} else if (Settings.IMP.QUEUE.PARALLEL_THREADS > 1) {
|
|
ParallelQueueExtent parallel =
|
|
new ParallelQueueExtent(Fawe.get().getQueueHandler(), world, true);
|
|
queue = parallel.getExtent();
|
|
} else {
|
|
queue = Fawe.get().getQueueHandler().getQueue(world);
|
|
}
|
|
}
|
|
|
|
try (Relighter relighter = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING)
|
|
.getRelighterFactory()
|
|
.createRelighter(mode, world, queue)) {
|
|
|
|
for (int x = minX; x <= maxX; x++) {
|
|
for (int z = minZ; z <= maxZ; z++) {
|
|
relighter.addChunk(x, z, null, 65535);
|
|
count++;
|
|
}
|
|
}
|
|
if (mode != RelightMode.NONE) {
|
|
if (Settings.IMP.LIGHTING.REMOVE_FIRST) {
|
|
relighter.removeAndRelight(true);
|
|
} else {
|
|
relighter.fixSkyLighting();
|
|
relighter.fixBlockLighting();
|
|
}
|
|
} else {
|
|
relighter.removeLighting();
|
|
}
|
|
} catch (Exception e) {
|
|
LOGGER.error("Error occurred on fix lighting", e);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Runs a task when the server is low on memory.
|
|
*/
|
|
public static void addMemoryLimitedTask(Runnable run) {
|
|
MemUtil.addMemoryLimitedTask(run);
|
|
}
|
|
|
|
/**
|
|
* Runs a task when the server is no longer low on memory.
|
|
*/
|
|
public static void addMemoryPlentifulTask(Runnable run) {
|
|
MemUtil.addMemoryPlentifulTask(run);
|
|
}
|
|
|
|
public static Map<String, String> getTranslations(Locale locale) {
|
|
return WorldEdit.getInstance().getTranslationManager().getTranslationMap(locale);
|
|
}
|
|
|
|
}
|