This commit is contained in:
IronApollo 2020-04-29 14:17:48 -04:00
commit aae760c625
28 changed files with 332 additions and 101 deletions

View File

@ -73,12 +73,12 @@ public class DelegateLock extends ReentrantLockWithGetOwner {
@Override @Override
public synchronized int getHoldCount() { public synchronized int getHoldCount() {
throw new UnsupportedOperationException(); return parent.getHoldCount();
} }
@Override @Override
public synchronized boolean isHeldByCurrentThread() { public synchronized boolean isHeldByCurrentThread() {
throw new UnsupportedOperationException(); return parent.isHeldByCurrentThread();
} }
@Override @Override
@ -96,12 +96,12 @@ public class DelegateLock extends ReentrantLockWithGetOwner {
@Override @Override
public synchronized boolean hasWaiters(Condition condition) { public synchronized boolean hasWaiters(Condition condition) {
throw new UnsupportedOperationException(); return parent.hasWaiters(condition);
} }
@Override @Override
public synchronized int getWaitQueueLength(Condition condition) { public synchronized int getWaitQueueLength(Condition condition) {
throw new UnsupportedOperationException(); return parent.getWaitQueueLength(condition);
} }
@Override @Override

View File

@ -104,7 +104,7 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
} }
protected static DelegateLock applyLock(ChunkSection section) { protected static DelegateLock applyLock(ChunkSection section) {
//todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which aquires the lock in NMS? //todo there has to be a better way to do this. Maybe using a() in DataPaletteBlock which acquires the lock in NMS?
try { try {
synchronized (section) { synchronized (section) {
DataPaletteBlock<IBlockData> blocks = section.getBlocks(); DataPaletteBlock<IBlockData> blocks = section.getBlocks();

View File

@ -221,10 +221,9 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
if (this.sections == null) { if (this.sections == null) {
this.sections = sections.clone(); this.sections = sections.clone();
} }
//TODO: Understand why this causes #329, what the purpose of this is, and what may or may not break after commenting this out. if (this.sections[layer] != section) {
// if (this.sections[layer] != section) { this.sections[layer] = new ChunkSection[]{section}.clone()[0];
// this.sections[layer] = section; }
// }
this.blocks[layer] = arr; this.blocks[layer] = arr;
} }
} }
@ -299,19 +298,17 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
synchronized (this) { synchronized (this) {
synchronized (lock) { synchronized (lock) {
lock.untilFree(); lock.untilFree();
ChunkSection getSection;
if (this.nmsChunk != nmsChunk) { if (this.nmsChunk != nmsChunk) {
this.nmsChunk = nmsChunk; this.nmsChunk = nmsChunk;
this.sections = null; this.sections = null;
this.reset(); this.reset();
} else { } else if (existingSection != getSections()[layer]) {
getSection = this.getSections()[layer]; this.sections[layer] = existingSection;
if (getSection != existingSection) { this.reset();
this.sections[layer] = existingSection; } else if (!Arrays.equals(update(layer, new char[4096]), load(layer))) {
this.reset(); this.reset(layer);
} else if (lock.isModified()) { } else if (lock.isModified()) {
this.reset(layer); this.reset(layer);
}
} }
newSection = BukkitAdapter_1_15_2.newChunkSection(layer, this::load, setArr); newSection = BukkitAdapter_1_15_2.newChunkSection(layer, this::load, setArr);
if (!BukkitAdapter_1_15_2.setSectionAtomic(sections, existingSection, newSection, layer)) { if (!BukkitAdapter_1_15_2.setSectionAtomic(sections, existingSection, newSection, layer)) {

View File

@ -184,7 +184,7 @@ public class BukkitWorld extends AbstractWorld {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
try { try {
if (adapter != null) { if (adapter != null) {
return adapter.regenerate(getWorld(), region, null, null, editSession); return adapter.regenerate(getWorld(), region, editSession);
} else { } else {
throw new UnsupportedOperationException("Missing BukkitImplAdapater for this version."); throw new UnsupportedOperationException("Missing BukkitImplAdapater for this version.");
} }

View File

@ -240,11 +240,7 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
* @return true on success, false on failure * @return true on success, false on failure
*/ */
default boolean regenerate(org.bukkit.World world, Region region, EditSession session) { default boolean regenerate(org.bukkit.World world, Region region, EditSession session) {
return regenerate(world, region, null, null, session); return session.regenerate(region);
}
default boolean regenerate(org.bukkit.World world, Region region, @Nullable Long seed, @Nullable BiomeType biome, EditSession editSession) {
return editSession.regenerate(region);
} }
default IChunkGet get(World world, int chunkX, int chunkZ) { default IChunkGet get(World world, int chunkX, int chunkZ) {

View File

@ -54,6 +54,7 @@ import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.server.v1_14_R1.*; import net.minecraft.server.v1_14_R1.*;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World.Environment;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_14_R1.CraftChunk; import org.bukkit.craftbukkit.v1_14_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; import org.bukkit.craftbukkit.v1_14_R1.CraftWorld;
@ -361,7 +362,7 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I
} }
@Override @Override
public boolean regenerate(org.bukkit.World world, Region region, @Nullable Long seed, @Nullable BiomeType biome, EditSession editSession) { public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) {
WorldServer originalWorld = ((CraftWorld) world).getHandle(); WorldServer originalWorld = ((CraftWorld) world).getHandle();
ChunkProviderServer provider = originalWorld.getChunkProvider(); ChunkProviderServer provider = originalWorld.getChunkProvider();
if (!(provider instanceof ChunkProviderServer)) { if (!(provider instanceof ChunkProviderServer)) {
@ -379,26 +380,16 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I
WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null), WorldData newWorldData = new WorldData(originalWorld.worldData.a((NBTTagCompound) null),
server.dataConverterManager, getDataVersion(), null); server.dataConverterManager, getDataVersion(), null);
ChunkGenerator generator = world.getGenerator(); ChunkGenerator gen = world.getGenerator();
org.bukkit.World.Environment environment = world.getEnvironment(); Environment env = world.getEnvironment();
if (seed != null) {
if (biome == BiomeTypes.NETHER) {
environment = org.bukkit.World.Environment.NETHER;
} else if (biome == BiomeTypes.THE_END) {
environment = org.bukkit.World.Environment.THE_END;
} else {
environment = org.bukkit.World.Environment.NORMAL;
}
generator = null;
}
try (WorldServer freshWorld = new WorldServer(server, try (WorldServer freshWorld = new WorldServer(server,
server.executorService, saveHandler, server.executorService, saveHandler,
newWorldData, newWorldData,
originalWorld.worldProvider.getDimensionManager(), originalWorld.worldProvider.getDimensionManager(),
originalWorld.getMethodProfiler(), originalWorld.getMethodProfiler(),
server.worldLoadListenerFactory.create(11), server.worldLoadListenerFactory.create(11),
environment, env,
generator){ gen){
@Override @Override
public boolean addEntityChunk(net.minecraft.server.v1_14_R1.Entity entity) { public boolean addEntityChunk(net.minecraft.server.v1_14_R1.Entity entity) {
//Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously

View File

@ -56,6 +56,7 @@ import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.server.v1_15_R1.*; import net.minecraft.server.v1_15_R1.*;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World.Environment;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_15_R1.CraftChunk; import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
@ -368,7 +369,7 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I
} }
@Override @Override
public boolean regenerate(org.bukkit.World world, Region region, @Nullable Long seed, @Nullable BiomeType biome, EditSession editSession) { public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) {
WorldServer originalWorld = ((CraftWorld) world).getHandle(); WorldServer originalWorld = ((CraftWorld) world).getHandle();
ChunkProviderServer provider = originalWorld.getChunkProvider(); ChunkProviderServer provider = originalWorld.getChunkProvider();
if (!(provider instanceof ChunkProviderServer)) { if (!(provider instanceof ChunkProviderServer)) {
@ -387,26 +388,16 @@ public final class FAWE_Spigot_v1_15_R1 extends CachedBukkitAdapter implements I
server.dataConverterManager, getDataVersion(), null); server.dataConverterManager, getDataVersion(), null);
newWorldData.setName(UUID.randomUUID().toString()); newWorldData.setName(UUID.randomUUID().toString());
ChunkGenerator generator = world.getGenerator(); ChunkGenerator gen = world.getGenerator();
org.bukkit.World.Environment environment = world.getEnvironment(); Environment env = world.getEnvironment();
if (seed != null) {
if (biome == BiomeTypes.NETHER) {
environment = org.bukkit.World.Environment.NETHER;
} else if (biome == BiomeTypes.THE_END) {
environment = org.bukkit.World.Environment.THE_END;
} else {
environment = org.bukkit.World.Environment.NORMAL;
}
generator = null;
}
try (WorldServer freshWorld = new WorldServer(server, try (WorldServer freshWorld = new WorldServer(server,
server.executorService, saveHandler, server.executorService, saveHandler,
newWorldData, newWorldData,
originalWorld.worldProvider.getDimensionManager(), originalWorld.worldProvider.getDimensionManager(),
originalWorld.getMethodProfiler(), originalWorld.getMethodProfiler(),
server.worldLoadListenerFactory.create(11), server.worldLoadListenerFactory.create(11),
environment, env,
generator){ gen){
@Override @Override
public boolean addEntityChunk(net.minecraft.server.v1_15_R1.Entity entity) { public boolean addEntityChunk(net.minecraft.server.v1_15_R1.Entity entity) {
//Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously

View File

@ -56,6 +56,7 @@ import com.sk89q.worldedit.world.registry.BlockMaterial;
import net.minecraft.server.v1_15_R1.*; import net.minecraft.server.v1_15_R1.*;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World.Environment;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_15_R1.CraftChunk; import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
@ -370,7 +371,7 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I
} }
@Override @Override
public boolean regenerate(org.bukkit.World world, Region region, @Nullable Long seed, @Nullable BiomeType biome, EditSession editSession) { public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) {
WorldServer originalWorld = ((CraftWorld) world).getHandle(); WorldServer originalWorld = ((CraftWorld) world).getHandle();
ChunkProviderServer provider = originalWorld.getChunkProvider(); ChunkProviderServer provider = originalWorld.getChunkProvider();
if (!(provider instanceof ChunkProviderServer)) { if (!(provider instanceof ChunkProviderServer)) {
@ -389,26 +390,16 @@ public final class FAWE_Spigot_v1_15_R2 extends CachedBukkitAdapter implements I
server.dataConverterManager, getDataVersion(), null); server.dataConverterManager, getDataVersion(), null);
newWorldData.setName(UUID.randomUUID().toString()); newWorldData.setName(UUID.randomUUID().toString());
ChunkGenerator generator = world.getGenerator(); ChunkGenerator gen = world.getGenerator();
org.bukkit.World.Environment environment = world.getEnvironment(); Environment env = world.getEnvironment();
if (seed != null) {
if (biome == BiomeTypes.NETHER) {
environment = org.bukkit.World.Environment.NETHER;
} else if (biome == BiomeTypes.THE_END) {
environment = org.bukkit.World.Environment.THE_END;
} else {
environment = org.bukkit.World.Environment.NORMAL;
}
generator = null;
}
try (WorldServer freshWorld = new WorldServer(server, try (WorldServer freshWorld = new WorldServer(server,
server.executorService, saveHandler, server.executorService, saveHandler,
newWorldData, newWorldData,
originalWorld.worldProvider.getDimensionManager(), originalWorld.worldProvider.getDimensionManager(),
originalWorld.getMethodProfiler(), originalWorld.getMethodProfiler(),
server.worldLoadListenerFactory.create(11), server.worldLoadListenerFactory.create(11),
environment, env,
generator){ gen){
@Override @Override
public boolean addEntityChunk(net.minecraft.server.v1_15_R1.Entity entity) { public boolean addEntityChunk(net.minecraft.server.v1_15_R1.Entity entity) {
//Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously //Fixes #320; Prevent adding entities so we aren't attempting to spawn them asynchronously

View File

@ -31,7 +31,7 @@ dependencies {
"compile"(project(":worldedit-libs:core")) "compile"(project(":worldedit-libs:core"))
"compile"("de.schlichtherle:truezip:6.8.3") "compile"("de.schlichtherle:truezip:6.8.3")
"compile"("net.java.truevfs:truevfs-profile-default_2.13:0.12.1") "compile"("net.java.truevfs:truevfs-profile-default_2.13:0.12.1")
"compile"("org.mozilla:rhino:1.7.11") "compile"("org.mozilla:rhino-runtime:1.7.12")
"compile"("org.yaml:snakeyaml:1.23") "compile"("org.yaml:snakeyaml:1.23")
"compile"("com.google.guava:guava:21.0") "compile"("com.google.guava:guava:21.0")
"compile"("com.google.code.findbugs:jsr305:3.0.2") "compile"("com.google.code.findbugs:jsr305:3.0.2")

View File

@ -243,6 +243,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public IChunkGet get(ChunkHolder chunk) { public IChunkGet get(ChunkHolder chunk) {
chunk.getOrCreateGet(); chunk.getOrCreateGet();
chunk.delegate = BOTH; chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
return chunk.chunkExisting; return chunk.chunkExisting;
} }
@ -271,6 +272,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) { public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet(); chunk.getOrCreateGet();
chunk.delegate = GET; chunk.delegate = GET;
chunk.chunkExisting.trim(false);
return chunk.getBiomeType(x, y, z); return chunk.getBiomeType(x, y, z);
} }
@ -278,6 +280,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public BlockState getBlock(ChunkHolder chunk, int x, int y, int z) { public BlockState getBlock(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet(); chunk.getOrCreateGet();
chunk.delegate = GET; chunk.delegate = GET;
chunk.chunkExisting.trim(false);
return chunk.getBlock(x, y, z); return chunk.getBlock(x, y, z);
} }
@ -286,6 +289,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
int z) { int z) {
chunk.getOrCreateGet(); chunk.getOrCreateGet();
chunk.delegate = GET; chunk.delegate = GET;
chunk.chunkExisting.trim(false);
return chunk.getFullBlock(x, y, z); return chunk.getFullBlock(x, y, z);
} }
}; };

View File

@ -167,7 +167,7 @@ public abstract class QueueHandler implements Trimable, Runnable {
return sync(call, syncTasks); return sync(call, syncTasks);
} }
// Lower priorty sync task (runs only when there are no other tasks) // Lower priority sync task (runs only when there are no other tasks)
public <T> Future<T> syncWhenFree(Runnable run, T value) { public <T> Future<T> syncWhenFree(Runnable run, T value) {
return sync(run, value, syncWhenFree); return sync(run, value, syncWhenFree);
} }

View File

@ -61,7 +61,7 @@ public abstract class ReadOnlyClipboard extends SimpleClipboard {
if (current.getWorld().equals(world)) { if (current.getWorld().equals(world)) {
return current; return current;
} }
throw new UnsupportedOperationException("TODO: Cannot lazy copy accross worlds (bug jesse)"); throw new UnsupportedOperationException("TODO: Cannot lazy copy across worlds (bug jesse)");
} }
throw new IllegalStateException("No world"); throw new IllegalStateException("No world");
}; };

View File

@ -100,7 +100,7 @@ public class SparseBitSet implements Cloneable, Serializable
bits (setting, flipping, clearing, etc.) do not attempt to normalize the bits (setting, flipping, clearing, etc.) do not attempt to normalize the
set, in the interests of speed. However, when a set is scanned as the set, in the interests of speed. However, when a set is scanned as the
resultant set of some operation, then, in most cases, the set will be resultant set of some operation, then, in most cases, the set will be
normalized--the exception being level2 areas that are not completly scanned normalized--the exception being level2 areas that are not completely scanned
in a particular pass. in a particular pass.
The sizes of the blocks and areas has been the result of some investigation The sizes of the blocks and areas has been the result of some investigation
@ -338,8 +338,8 @@ public class SparseBitSet implements Cloneable, Serializable
* needed actions to initialise the bit set. * needed actions to initialise the bit set.
* <p> * <p>
* The capacity is taken to be a <i>suggestion</i> for a size of the bit set, * The capacity is taken to be a <i>suggestion</i> for a size of the bit set,
* in bits. An appropiate table size (a power of two) is then determined and * in bits. An appropriate table size (a power of two) is then determined and
* used. The size will be grown as needed to accomodate any bits addressed * used. The size will be grown as needed to accommodate any bits addressed
* during the use of the bit set. * during the use of the bit set.
* *
* @param capacity a size in terms of bits * @param capacity a size in terms of bits
@ -530,7 +530,7 @@ public class SparseBitSet implements Cloneable, Serializable
} }
/** /**
* Creates a bit set from thie first <code>SparseBitSet</code> whose * Creates a bit set from the first <code>SparseBitSet</code> whose
* corresponding bits are cleared by the set bits of the second * corresponding bits are cleared by the set bits of the second
* <code>SparseBitSet</code>. The resulting bit set is created so that a bit * <code>SparseBitSet</code>. The resulting bit set is created so that a bit
* in it has the value <code>true</code> if and only if the corresponding bit * in it has the value <code>true</code> if and only if the corresponding bit
@ -1669,7 +1669,7 @@ public class SparseBitSet implements Cloneable, Serializable
} }
/** /**
* Intializes all the additional objects required for correct operation. * Initializes all the additional objects required for correct operation.
* *
* @since 1.6 * @since 1.6
*/ */

View File

@ -513,11 +513,11 @@ public class MainUtil {
} }
try (FileInputStream fIn = new FileInputStream(sourceFile); FileChannel source = fIn.getChannel(); try (FileInputStream fIn = new FileInputStream(sourceFile); FileChannel source = fIn.getChannel();
FileOutputStream fOut = new FileOutputStream(destFile); FileChannel destination = fOut.getChannel()) { FileOutputStream fOut = new FileOutputStream(destFile); FileChannel destination = fOut.getChannel()) {
long transfered = 0; long transferred = 0;
long bytes = source.size(); long bytes = source.size();
while (transfered < bytes) { while (transferred < bytes) {
transfered += destination.transferFrom(source, 0, source.size()); transferred += destination.transferFrom(source, 0, source.size());
destination.position(transfered); destination.position(transferred);
} }
} }
return destFile; return destFile;

View File

@ -131,13 +131,13 @@ public class ReflectionUtils {
} }
} }
public static <T> T callConstructor(Constructor<T> constructor, Object... paramaters) { public static <T> T callConstructor(Constructor<T> constructor, Object... parameters) {
if (constructor == null) { if (constructor == null) {
throw new RuntimeException("No such constructor"); throw new RuntimeException("No such constructor");
} }
constructor.setAccessible(true); constructor.setAccessible(true);
try { try {
return constructor.newInstance(paramaters); return constructor.newInstance(parameters);
} catch (InvocationTargetException ex) { } catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause()); throw new RuntimeException(ex.getCause());
} catch (Exception ex) { } catch (Exception ex) {

View File

@ -37,7 +37,7 @@ public class ReflectionUtils9 {
// 3. build new enum // 3. build new enum
T newValue = (T) makeEnum(enumType, // The target enum class T newValue = (T) makeEnum(enumType, // The target enum class
enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
values.size()); // can be used to pass values to the enum constuctor values.size()); // can be used to pass values to the enum constructor
// 4. add new value // 4. add new value
values.add(newValue); values.add(newValue);

View File

@ -72,7 +72,7 @@ public final class NBTUtils {
} else if (clazz.equals(LongArrayTag.class)) { } else if (clazz.equals(LongArrayTag.class)) {
return "TAG_Long_Array"; return "TAG_Long_Array";
} else { } else {
throw new IllegalArgumentException("Invalid tag classs (" throw new IllegalArgumentException("Invalid tag class ("
+ clazz.getName() + ")."); + clazz.getName() + ").");
} }
} }
@ -112,7 +112,7 @@ public final class NBTUtils {
} else if (clazz.equals(LongArrayTag.class)) { } else if (clazz.equals(LongArrayTag.class)) {
return NBTConstants.TYPE_LONG_ARRAY; return NBTConstants.TYPE_LONG_ARRAY;
} else { } else {
throw new IllegalArgumentException("Invalid tag classs (" throw new IllegalArgumentException("Invalid tag class ("
+ clazz.getName() + ")."); + clazz.getName() + ").");
} }
} }

View File

@ -36,7 +36,7 @@ public final class StringUtil {
/** /**
* Trim a string if it is longer than a certain length. * Trim a string if it is longer than a certain length.
* *
* @param str the stirng * @param str the string
* @param len the length to trim to * @param len the length to trim to
* @return a new string * @return a new string
*/ */

View File

@ -23,7 +23,7 @@ import com.sk89q.worldedit.event.Event;
/** /**
* Fired when configuration has been loaded and the platform is in the * Fired when configuration has been loaded and the platform is in the
* intialization stage. * initialization stage.
* *
* <p>This event is fired once.</p> * <p>This event is fired once.</p>
*/ */

View File

@ -382,6 +382,14 @@ public class DefaultBlockParser extends InputParser<BaseBlock> {
// Check if the item is allowed // Check if the item is allowed
BlockType blockType = state.getBlockType(); BlockType blockType = state.getBlockType();
if (context.isRestricted()) {
Actor actor = context.requireActor();
if (actor != null && !actor.hasPermission("worldedit.anyblock")
&& worldEdit.getConfiguration().disallowedBlocks.contains(blockType.getId())) {
throw new DisallowedUsageException("You are not allowed to use '" + input + "'");
}
}
if (nbt != null) return validate(context, state.toBaseBlock(nbt)); if (nbt != null) return validate(context, state.toBaseBlock(nbt));
if (blockType == BlockTypes.SIGN || blockType == BlockTypes.WALL_SIGN if (blockType == BlockTypes.SIGN || blockType == BlockTypes.WALL_SIGN

View File

@ -84,7 +84,7 @@ class CompilingVisitor extends ExpressionBaseVisitor<MethodHandle> {
* General idea is that we don't need to pass around variables, they're all in ExecutionData. * General idea is that we don't need to pass around variables, they're all in ExecutionData.
* We do need to pass that around, so most MethodHandles will be of the type * We do need to pass that around, so most MethodHandles will be of the type
* (ExecutionData)Double, with a few as (ExecutionData,Double)Double where it needs an existing * (ExecutionData)Double, with a few as (ExecutionData,Double)Double where it needs an existing
* value passed in. EVERY handle returned from an overriden method must be of the first type. * value passed in. EVERY handle returned from an overridden method must be of the first type.
*/ */
private final Functions functions; private final Functions functions;

View File

@ -93,7 +93,7 @@ public interface RegionSelector {
void explainSecondarySelection(Actor actor, LocalSession session, BlockVector3 position); void explainSecondarySelection(Actor actor, LocalSession session, BlockVector3 position);
/** /**
* The the player information about the region's changes. This may resend * Tell the player information about the region's changes. This may resend
* all the defining region information if needed. * all the defining region information if needed.
* *
* @param actor the actor * @param actor the actor

View File

@ -19,7 +19,10 @@
package com.sk89q.worldedit.scripting; package com.sk89q.worldedit.scripting;
import com.google.common.io.CharStreams;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.scripting.compat.BabelScriptTranspiler;
import com.sk89q.worldedit.scripting.compat.ScriptTranspiler;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.ImporterTopLevel; import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.JavaScriptException; import org.mozilla.javascript.JavaScriptException;
@ -28,11 +31,13 @@ import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.WrappedException; import org.mozilla.javascript.WrappedException;
import javax.script.ScriptException;
import java.io.StringReader;
import java.util.Map; import java.util.Map;
import javax.script.ScriptException;
public class RhinoCraftScriptEngine implements CraftScriptEngine { public class RhinoCraftScriptEngine implements CraftScriptEngine {
private static final ScriptTranspiler TRANSPILER = new BabelScriptTranspiler();
private int timeLimit; private int timeLimit;
@Override @Override
@ -48,6 +53,7 @@ public class RhinoCraftScriptEngine implements CraftScriptEngine {
@Override @Override
public Object evaluate(String script, String filename, Map<String, Object> args) public Object evaluate(String script, String filename, Map<String, Object> args)
throws ScriptException, Throwable { throws ScriptException, Throwable {
String transpiled = CharStreams.toString(TRANSPILER.transpile(new StringReader(script)));
RhinoContextFactory factory = new RhinoContextFactory(timeLimit); RhinoContextFactory factory = new RhinoContextFactory(timeLimit);
Context cx = factory.enterContext(); Context cx = factory.enterContext();
cx.setClassShutter(new MinecraftHidingClassShutter()); cx.setClassShutter(new MinecraftHidingClassShutter());
@ -59,7 +65,7 @@ public class RhinoCraftScriptEngine implements CraftScriptEngine {
Context.javaToJS(entry.getValue(), scope)); Context.javaToJS(entry.getValue(), scope));
} }
try { try {
return cx.evaluateString(scope, script, filename, 1, null); return cx.evaluateString(scope, transpiled, filename, 1, null);
} catch (Error e) { } catch (Error e) {
throw new ScriptException(e.getMessage()); throw new ScriptException(e.getMessage());
} catch (RhinoException e) { } catch (RhinoException e) {

View File

@ -0,0 +1,83 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.scripting.compat;
import com.google.common.io.CharStreams;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.concurrent.TimeUnit;
public class BabelScriptTranspiler implements ScriptTranspiler {
private static final RemoteScript BABEL = new RemoteScript(
"https://unpkg.com/@babel/standalone@7.9/babel.min.js",
"babel.min.js",
new RemoteScript(
"https://unpkg.com/core-js-bundle@3.6.5/index.js",
"core-js-bundle.js"
),
new RemoteScript(
"https://unpkg.com/regenerator-runtime@0.13.5/runtime.js",
"regenerator-runtime.js"
)
);
private final ContextFactory contextFactory = new ContextFactory() {
@Override
protected Context makeContext() {
Context context = super.makeContext();
context.setLanguageVersion(Context.VERSION_ES6);
return context;
}
};
private final Function executeBabel;
public BabelScriptTranspiler() {
Scriptable babel = BABEL.getScope();
executeBabel = contextFactory.call(ctx -> {
ctx.setOptimizationLevel(9);
String execBabelSource = "function(source) {\n" +
"return Babel.transform(source, { presets: ['env'] }).code;\n" +
"}\n";
return ctx.compileFunction(
babel, execBabelSource, "<execute-babel>", 1, null
);
});
}
@Override
public Reader transpile(Reader script) throws IOException {
long startTranspile = System.nanoTime();
Scriptable babel = BABEL.getScope();
String source = CharStreams.toString(script);
String result = (String) contextFactory.call(ctx ->
executeBabel.call(ctx, babel, null, new Object[] { source })
);
System.err.println(result);
System.err.println("Took " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTranspile));
return new StringReader(result);
}
}

View File

@ -0,0 +1,120 @@
package com.sk89q.worldedit.scripting.compat;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.util.net.HttpRequest;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.TopLevel;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static com.google.common.base.Preconditions.checkState;
public class RemoteScript {
private static final int MAX_REDIRECTS = 100;
private final ContextFactory contextFactory = new ContextFactory() {
@Override
protected boolean hasFeature(Context cx, int featureIndex) {
if (featureIndex == Context.FEATURE_OLD_UNDEF_NULL_THIS) {
return true;
}
return super.hasFeature(cx, featureIndex);
}
@Override
protected Context makeContext() {
Context context = super.makeContext();
context.setLanguageVersion(Context.VERSION_ES6);
return context;
}
};
private final Path cacheDir = WorldEdit.getInstance()
.getWorkingDirectoryFile("craftscripts/.cache").toPath();
private final URL source;
private final String cacheFileName;
private final Path cachePath;
private final List<RemoteScript> dependencies;
private volatile Scriptable cachedScope;
public RemoteScript(String source, String cacheFileName, RemoteScript... dependencies) {
this.source = HttpRequest.url(source);
this.cacheFileName = cacheFileName;
this.cachePath = cacheDir.resolve(cacheFileName);
this.dependencies = ImmutableList.copyOf(dependencies);
}
private synchronized void ensureCached() throws IOException {
if (!Files.exists(cacheDir)) {
Files.createDirectories(cacheDir);
}
if (!Files.exists(cachePath)) {
boolean downloadedBabel = false;
int redirects = 0;
URL url = source;
while (redirects < MAX_REDIRECTS && !downloadedBabel) {
try (HttpRequest request = HttpRequest.get(url)) {
request.execute();
request.expectResponseCode(200, 301, 302);
if (request.getResponseCode() > 300) {
redirects++;
url = HttpRequest.url(request.getSingleHeaderValue("Location"));
continue;
}
request.saveContent(cachePath.toFile());
downloadedBabel = true;
}
}
checkState(downloadedBabel, "Too many redirects following: %s", url);
checkState(Files.exists(cachePath), "Failed to actually download %s", cacheFileName);
}
}
protected synchronized void loadIntoScope(Context ctx, Scriptable scope) {
try {
ensureCached();
try (Reader reader = Files.newBufferedReader(cachePath)) {
ctx.evaluateReader(scope, reader, cacheFileName, 1, null);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* Get a scope that the script has been evaluated in.
*
* @return the scope
*/
public synchronized Scriptable getScope() {
if (cachedScope != null) {
return cachedScope;
}
// parse + execute standalone script to load it into the scope
cachedScope = contextFactory.call(ctx -> {
ScriptableObject scriptable = new TopLevel();
Scriptable newScope = ctx.initStandardObjects(scriptable);
ctx.setOptimizationLevel(9);
for (RemoteScript dependency : dependencies) {
dependency.loadIntoScope(ctx, newScope);
}
loadIntoScope(ctx, newScope);
return newScope;
});
return cachedScope;
}
}

View File

@ -0,0 +1,38 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.scripting.compat;
import java.io.IOException;
import java.io.Reader;
/**
* Transpile a script from one (version) of a language to another.
*/
public interface ScriptTranspiler {
/**
* Given input {@code script}, return the transpiled script.
*
* @param script the script to transpile
* @return the new script
*/
Reader transpile(Reader script) throws IOException;
}

View File

@ -43,6 +43,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static com.google.common.base.Preconditions.checkState;
public class HttpRequest implements Closeable { public class HttpRequest implements Closeable {
private static final int CONNECT_TIMEOUT = 1000 * 5; private static final int CONNECT_TIMEOUT = 1000 * 5;
@ -200,6 +202,13 @@ public class HttpRequest implements Closeable {
return conn.getResponseCode(); return conn.getResponseCode();
} }
public String getSingleHeaderValue(String header) {
checkState(conn != null, "No connection has been made");
// maybe we should check for multi-header?
return conn.getHeaderField(header);
}
/** /**
* Get the input stream. * Get the input stream.
* *
@ -214,9 +223,8 @@ public class HttpRequest implements Closeable {
* *
* @return the buffered response * @return the buffered response
* @throws java.io.IOException on I/O error * @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/ */
public BufferedResponse returnContent() throws IOException, InterruptedException { public BufferedResponse returnContent() throws IOException {
if (inputStream == null) { if (inputStream == null) {
throw new IllegalArgumentException("No input stream available"); throw new IllegalArgumentException("No input stream available");
} }
@ -239,9 +247,8 @@ public class HttpRequest implements Closeable {
* @param file the file * @param file the file
* @return this object * @return this object
* @throws java.io.IOException on I/O error * @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/ */
public HttpRequest saveContent(File file) throws IOException, InterruptedException { public HttpRequest saveContent(File file) throws IOException {
Closer closer = Closer.create(); Closer closer = Closer.create();
try { try {
@ -262,9 +269,8 @@ public class HttpRequest implements Closeable {
* @param out the output stream * @param out the output stream
* @return this object * @return this object
* @throws java.io.IOException on I/O error * @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/ */
public HttpRequest saveContent(OutputStream out) throws IOException, InterruptedException { public HttpRequest saveContent(OutputStream out) throws IOException {
BufferedInputStream bis; BufferedInputStream bis;
try { try {

View File

@ -88,10 +88,10 @@ tasks.named<ShadowJar>("shadowJar") {
include(dependency("org.apache.logging.log4j:log4j-slf4j-impl")) include(dependency("org.apache.logging.log4j:log4j-slf4j-impl"))
include(dependency("de.schlichtherle:truezip")) include(dependency("de.schlichtherle:truezip"))
include(dependency("net.java.truevfs:truevfs-profile-default_2.13")) include(dependency("net.java.truevfs:truevfs-profile-default_2.13"))
include(dependency("org.mozilla:rhino")) include(dependency("org.mozilla:rhino-runtime"))
} }
minimize { minimize {
exclude(dependency("org.mozilla:rhino")) exclude(dependency("org.mozilla:rhino-runtime"))
} }
} }