diff --git a/.travis.yml b/.travis.yml
index 7013c738a..f8b8492f7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,6 @@ install: ./gradlew setupCIWorkspace -s
script: ./gradlew build -s
jdk:
- oraclejdk8
- - oraclejdk7
# Caching for Gradle files, prevents hitting Maven too much.
before_cache:
- find $HOME/.gradle/ -name '*.lock' -print -exec rm -f {} \;
diff --git a/build.gradle b/build.gradle
index 05adb6e39..b526a2647 100644
--- a/build.gradle
+++ b/build.gradle
@@ -90,7 +90,7 @@ subprojects {
archives javadocJar
}
- if (!name.equals('worldedit-forge')) {
+ if (!(name.equals('worldedit-forge') || name.equals('worldedit-sponge'))) {
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
diff --git a/config/checkstyle/import-control.xml b/config/checkstyle/import-control.xml
index 3e4233471..d620978fd 100644
--- a/config/checkstyle/import-control.xml
+++ b/config/checkstyle/import-control.xml
@@ -41,12 +41,6 @@
-
-
-
-
-
-
@@ -57,5 +51,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/settings.gradle b/settings.gradle
index 271359482..576283ecc 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,3 @@
rootProject.name = 'worldedit'
-include 'worldedit-core', 'worldedit-bukkit', 'worldedit-forge'
\ No newline at end of file
+include 'worldedit-core', 'worldedit-bukkit', 'worldedit-forge', 'worldedit-sponge'
\ No newline at end of file
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/ChangeSetExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/ChangeSetExtent.java
index 84af07ab3..794183355 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/ChangeSetExtent.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/ChangeSetExtent.java
@@ -68,7 +68,7 @@ public class ChangeSetExtent extends AbstractDelegateExtent {
@Override
public Entity createEntity(Location location, BaseEntity state) {
Entity entity = super.createEntity(location, state);
- if (state != null) {
+ if (entity != null) {
changeSet.add(new EntityCreate(location, state, entity));
}
return entity;
diff --git a/worldedit-sponge/build.gradle b/worldedit-sponge/build.gradle
new file mode 100644
index 000000000..abdfb6455
--- /dev/null
+++ b/worldedit-sponge/build.gradle
@@ -0,0 +1,88 @@
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+
+buildscript {
+ repositories {
+ mavenCentral()
+ maven { url = "http://files.minecraftforge.net/maven" }
+ maven { url = "http://repo.minecrell.net/snapshots" }
+ maven { url = "https://oss.sonatype.org/content/repositories/snapshots/" }
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'net.minecrell:VanillaGradle:2.0.3-SNAPSHOT'
+ classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT'
+ }
+}
+
+apply plugin: 'net.minecrell.vanilla.server.library'
+
+dependencies {
+ compile project(':worldedit-core')
+ compile 'org.spongepowered:spongeapi:4.1.0-SNAPSHOT'
+ compile 'ninja.leaping.configurate:configurate-hocon:3.1'
+ testCompile group: 'org.mockito', name: 'mockito-core', version:'1.9.0-rc1'
+}
+
+repositories {
+ maven {
+ name = 'forge'
+ url = 'http://files.minecraftforge.net/maven'
+ }
+ maven {
+ name = "Sponge"
+ url = "https://repo.spongepowered.org/maven"
+ }
+}
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+minecraft {
+ version = "1.8.9"
+ mappings = "stable_22"
+ runDir = 'run'
+
+ replaceIn "com/sk89q/worldedit/sponge/SpongeWorldEdit.java"
+ replace "%VERSION%", project.version
+}
+
+project.archivesBaseName = "${project.archivesBaseName}-mc${minecraft.version}"
+
+processResources {
+ from (sourceSets.main.resources.srcDirs) {
+ expand 'version': project.version,
+ 'mcVersion': project.minecraft.version,
+ 'internalVersion': project.internalVersion
+ }
+}
+
+jar {
+ manifest {
+ attributes("Class-Path": "truezip.jar WorldEdit/truezip.jar js.jar WorldEdit/js.jar",
+ "WorldEdit-Version": version,
+ "FMLAT": "worldedit_at.cfg")
+ }
+}
+
+shadowJar {
+ dependencies {
+ include(dependency(':worldedit-core'))
+ }
+}
+
+reobf {
+ shadowJar {
+ mappingType = 'SEARGE'
+ }
+}
+
+task deobfJar(type: Jar) {
+ from sourceSets.main.output
+ classifier = 'dev'
+}
+
+artifacts {
+ archives deobfJar
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CUIChannelHandler.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CUIChannelHandler.java
new file mode 100644
index 000000000..1be22b0b0
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CUIChannelHandler.java
@@ -0,0 +1,60 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.sk89q.worldedit.LocalSession;
+import org.spongepowered.api.Platform;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.network.*;
+
+import java.nio.charset.StandardCharsets;
+
+public class CUIChannelHandler implements RawDataListener {
+ public static final String CUI_PLUGIN_CHANNEL = "WECUI";
+
+ private static ChannelBinding.RawDataChannel channel;
+
+ public static void init() {
+ channel = Sponge.getChannelRegistrar().createRawChannel(SpongeWorldEdit.inst(), CUI_PLUGIN_CHANNEL);
+ channel.addListener(Platform.Type.SERVER, new CUIChannelHandler());
+ }
+
+
+ public static ChannelBinding.RawDataChannel getActiveChannel() {
+ return channel;
+ }
+
+ @Override
+ public void handlePayload(ChannelBuf data, RemoteConnection connection, Platform.Type side) {
+ if (connection instanceof PlayerConnection) {
+ Player player = ((PlayerConnection) connection).getPlayer();
+
+ LocalSession session = SpongeWorldEdit.inst().getSession(player);
+
+ if (session.hasCUISupport()) {
+ return;
+ }
+
+ session.handleCUIInitializationMessage(new String(data.readBytes(data.available()), StandardCharsets.UTF_8));
+ session.describeCUI(SpongeWorldEdit.inst().wrapPlayer(player));
+ }
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CommandAdapter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CommandAdapter.java
new file mode 100644
index 000000000..1398ba389
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CommandAdapter.java
@@ -0,0 +1,68 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.sk89q.worldedit.util.command.CommandMapping;
+import org.spongepowered.api.command.CommandCallable;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.text.Text;
+
+import java.util.Optional;
+
+public abstract class CommandAdapter implements CommandCallable {
+ private CommandMapping command;
+
+ protected CommandAdapter(CommandMapping command) {
+ this.command = command;
+ }
+
+ @Override
+ public boolean testPermission(CommandSource source) {
+ for (String perm : command.getDescription().getPermissions()) {
+ if (!source.hasPermission(perm)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Optional extends Text> getShortDescription(CommandSource source) {
+ String description = command.getDescription().getDescription();
+ if (description != null && !description.isEmpty()) {
+ return Optional.of(Text.of(description));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional extends Text> getHelp(CommandSource source) {
+ String help = command.getDescription().getHelp();
+ if (help != null && !help.isEmpty()) {
+ return Optional.of(Text.of(help));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Text getUsage(CommandSource source) {
+ return Text.of(command.getDescription().getUsage());
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java
new file mode 100644
index 000000000..51723964f
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.flowpowered.math.vector.Vector3d;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.sponge.nms.SpongeNMSWorld;
+import com.sk89q.worldedit.util.Location;
+import com.sk89q.worldedit.world.World;
+
+final class SpongeAdapter {
+
+ private SpongeAdapter() {
+ }
+
+ public static World adapt(org.spongepowered.api.world.World world) {
+ return new SpongeNMSWorld(world);
+ }
+
+ public static Location adapt(org.spongepowered.api.world.Location loc, Vector3d rot) {
+ Vector position = new Vector(loc.getX(), loc.getY(), loc.getZ());
+
+ return new Location(SpongeAdapter.adapt(loc.getExtent()), position, (float) rot.getY(), (float) rot.getX());
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBiomeRegistry.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBiomeRegistry.java
new file mode 100644
index 000000000..3e5a0a2f1
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBiomeRegistry.java
@@ -0,0 +1,77 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.sk89q.worldedit.sponge.nms.IDHelper;
+import com.sk89q.worldedit.world.biome.BaseBiome;
+import com.sk89q.worldedit.world.biome.BiomeData;
+import com.sk89q.worldedit.world.registry.BiomeRegistry;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.world.biome.BiomeType;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides access to biome data in Sponge.
+ */
+class SpongeBiomeRegistry implements BiomeRegistry {
+
+ @Nullable
+ @Override
+ public BaseBiome createFromId(int id) {
+ return new BaseBiome(id);
+ }
+
+ @Override
+ public List getBiomes() {
+ List list = new ArrayList();
+ for (BiomeType biome : Sponge.getGame().getRegistry().getAllOf(BiomeType.class)) {
+ list.add(new BaseBiome(IDHelper.resolve(biome)));
+ }
+ return list;
+ }
+
+ @Nullable
+ @Override
+ public BiomeData getData(BaseBiome biome) {
+ return new SpongeBiomeData(IDHelper.resolveBiome(biome.getId()));
+ }
+
+ private static class SpongeBiomeData implements BiomeData {
+ private final BiomeType biome;
+
+ /**
+ * Create a new instance.
+ *
+ * @param biome the base biome
+ */
+ private SpongeBiomeData(BiomeType biome) {
+ this.biome = biome;
+ }
+
+ @Override
+ public String getName() {
+ return biome.getName();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java
new file mode 100644
index 000000000..a2a3fa138
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java
@@ -0,0 +1,160 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+import com.sk89q.worldedit.session.SessionKey;
+import com.sk89q.worldedit.util.auth.AuthorizationException;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.text.Text;
+import org.spongepowered.api.text.format.TextColor;
+import org.spongepowered.api.text.format.TextColors;
+import org.spongepowered.api.text.serializer.TextSerializers;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.util.UUID;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class SpongeCommandSender implements Actor {
+
+ /**
+ * One time generated ID.
+ */
+ private static final UUID DEFAULT_ID = UUID.fromString("a233eb4b-4cab-42cd-9fd9-7e7b9a3f74be");
+
+ private CommandSource sender;
+ private SpongeWorldEdit plugin;
+
+ public SpongeCommandSender(SpongeWorldEdit plugin, CommandSource sender) {
+ checkNotNull(plugin);
+ checkNotNull(sender);
+ checkArgument(!(sender instanceof Player), "Cannot wrap a player");
+
+ this.plugin = plugin;
+ this.sender = sender;
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return DEFAULT_ID;
+ }
+
+ @Override
+ public String getName() {
+ return sender.getName();
+ }
+
+ @Override
+ public void printRaw(String msg) {
+ for (String part : msg.split("\n")) {
+ sender.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(part));
+ }
+ }
+
+ @Override
+ public void print(String msg) {
+ sendColorized(msg, TextColors.LIGHT_PURPLE);
+ }
+
+ @Override
+ public void printDebug(String msg) {
+ sendColorized(msg, TextColors.GRAY);
+ }
+
+ @Override
+ public void printError(String msg) {
+ sendColorized(msg, TextColors.RED);
+ }
+
+ private void sendColorized(String msg, TextColor formatting) {
+ for (String part : msg.split("\n")) {
+ sender.sendMessage(Text.of(formatting, TextSerializers.LEGACY_FORMATTING_CODE.deserialize(part)));
+ }
+ }
+
+ @Override
+ public boolean canDestroyBedrock() {
+ return true;
+ }
+
+ @Override
+ public String[] getGroups() {
+ return new String[0];
+ }
+
+ @Override
+ public boolean hasPermission(String perm) {
+ return true;
+ }
+
+ @Override
+ public void checkPermission(String permission) throws AuthorizationException {
+ }
+
+ @Override
+ public boolean isPlayer() {
+ return false;
+ }
+
+ @Override
+ public File openFileOpenDialog(String[] extensions) {
+ return null;
+ }
+
+ @Override
+ public File openFileSaveDialog(String[] extensions) {
+ return null;
+ }
+
+ @Override
+ public void dispatchCUIEvent(CUIEvent event) {
+ }
+
+ @Override
+ public SessionKey getSessionKey() {
+ return new SessionKey() {
+ @Nullable
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public boolean isActive() {
+ return false;
+ }
+
+ @Override
+ public boolean isPersistent() {
+ return false;
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return DEFAULT_ID;
+ }
+ };
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntity.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntity.java
new file mode 100644
index 000000000..b078f7fa2
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntity.java
@@ -0,0 +1,103 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.flowpowered.math.vector.Vector3d;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.entity.Entity;
+import com.sk89q.worldedit.entity.metadata.EntityType;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.sponge.nms.NMSHelper;
+import com.sk89q.worldedit.util.Location;
+import com.sk89q.worldedit.world.NullWorld;
+import org.spongepowered.api.world.World;
+
+import javax.annotation.Nullable;
+import java.lang.ref.WeakReference;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+class SpongeEntity implements Entity {
+
+ private final WeakReference entityRef;
+
+ SpongeEntity(org.spongepowered.api.entity.Entity entity) {
+ checkNotNull(entity);
+ this.entityRef = new WeakReference<>(entity);
+ }
+
+ @Override
+ public BaseEntity getState() {
+ org.spongepowered.api.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ return NMSHelper.createBaseEntity(entity);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Location getLocation() {
+ org.spongepowered.api.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ org.spongepowered.api.world.Location entityLoc = entity.getLocation();
+ Vector3d entityRot = entity.getRotation();
+
+ return SpongeAdapter.adapt(entityLoc, entityRot);
+ } else {
+ return new Location(NullWorld.getInstance());
+ }
+ }
+
+ @Override
+ public Extent getExtent() {
+ org.spongepowered.api.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ return SpongeAdapter.adapt(entity.getWorld());
+ } else {
+ return NullWorld.getInstance();
+ }
+ }
+
+ @Override
+ public boolean remove() {
+ org.spongepowered.api.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ entity.remove();
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Nullable
+ @Override
+ public T getFacet(Class extends T> cls) {
+ org.spongepowered.api.entity.Entity entity = entityRef.get();
+ if (entity != null) {
+ if (EntityType.class.isAssignableFrom(cls)) {
+ return (T) new SpongeEntityType(entity);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntityType.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntityType.java
new file mode 100644
index 000000000..d3d73cffa
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntityType.java
@@ -0,0 +1,142 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.sk89q.worldedit.entity.metadata.EntityType;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.entity.ExperienceOrb;
+import org.spongepowered.api.entity.FallingBlock;
+import org.spongepowered.api.entity.Item;
+import org.spongepowered.api.entity.explosive.PrimedTNT;
+import org.spongepowered.api.entity.hanging.ItemFrame;
+import org.spongepowered.api.entity.hanging.Painting;
+import org.spongepowered.api.entity.living.*;
+import org.spongepowered.api.entity.living.animal.Animal;
+import org.spongepowered.api.entity.living.golem.Golem;
+import org.spongepowered.api.entity.projectile.Projectile;
+import org.spongepowered.api.entity.vehicle.Boat;
+import org.spongepowered.api.entity.vehicle.minecart.Minecart;
+import org.spongepowered.api.text.Text;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class SpongeEntityType implements EntityType {
+
+ private final Entity entity;
+
+ public SpongeEntityType(Entity entity) {
+ checkNotNull(entity);
+ this.entity = entity;
+ }
+
+ @Override
+ public boolean isPlayerDerived() {
+ return entity instanceof Humanoid;
+ }
+
+ @Override
+ public boolean isProjectile() {
+ return entity instanceof Projectile;
+ }
+
+ @Override
+ public boolean isItem() {
+ return entity instanceof Item;
+ }
+
+ @Override
+ public boolean isFallingBlock() {
+ return entity instanceof FallingBlock;
+ }
+
+ @Override
+ public boolean isPainting() {
+ return entity instanceof Painting;
+ }
+
+ @Override
+ public boolean isItemFrame() {
+ return entity instanceof ItemFrame;
+ }
+
+ @Override
+ public boolean isBoat() {
+ return entity instanceof Boat;
+ }
+
+ @Override
+ public boolean isMinecart() {
+ return entity instanceof Minecart;
+ }
+
+ @Override
+ public boolean isTNT() {
+ return entity instanceof PrimedTNT;
+ }
+
+ @Override
+ public boolean isExperienceOrb() {
+ return entity instanceof ExperienceOrb;
+ }
+
+ @Override
+ public boolean isLiving() {
+ return entity instanceof Living;
+ }
+
+ @Override
+ public boolean isAnimal() {
+ return entity instanceof Animal;
+ }
+
+ @Override
+ public boolean isAmbient() {
+ return entity instanceof Ambient;
+ }
+
+ @Override
+ public boolean isNPC() {
+ return entity instanceof Villager;
+ }
+
+ @Override
+ public boolean isGolem() {
+ return entity instanceof Golem;
+ }
+
+ @Override
+ public boolean isTamed() {
+ return entity.get(Keys.TAMED_OWNER).orElse(Optional.empty()).isPresent();
+ }
+
+ @Override
+ public boolean isTagged() {
+ return !entity.get(Keys.DISPLAY_NAME).orElse(Text.EMPTY).isEmpty();
+ }
+
+ @Override
+ public boolean isArmorStand() {
+ return entity instanceof ArmorStand;
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePermissionsProvider.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePermissionsProvider.java
new file mode 100644
index 000000000..62c75d5f3
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePermissionsProvider.java
@@ -0,0 +1,32 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import org.spongepowered.api.command.CommandCallable;
+import org.spongepowered.api.entity.living.player.Player;
+
+public class SpongePermissionsProvider {
+
+ public boolean hasPermission(Player player, String permission) {
+ return player.hasPermission(permission);
+ }
+
+ public void registerPermission(CommandCallable command, String permission) { }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java
new file mode 100644
index 000000000..93dbc0174
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java
@@ -0,0 +1,190 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.entity.Player;
+import com.sk89q.worldedit.event.platform.CommandEvent;
+import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
+import com.sk89q.worldedit.extension.platform.*;
+import com.sk89q.worldedit.sponge.config.SpongeConfiguration;
+import com.sk89q.worldedit.sponge.nms.IDHelper;
+import com.sk89q.worldedit.sponge.nms.SpongeNMSWorld;
+import com.sk89q.worldedit.util.command.CommandMapping;
+import com.sk89q.worldedit.util.command.Dispatcher;
+import com.sk89q.worldedit.world.World;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.command.CommandResult;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.entity.EntityType;
+import org.spongepowered.api.item.ItemType;
+import org.spongepowered.api.scheduler.Task;
+
+import javax.annotation.Nullable;
+import java.util.*;
+
+class SpongePlatform extends AbstractPlatform implements MultiUserPlatform {
+
+ private final SpongeWorldEdit mod;
+ private boolean hookingEvents = false;
+
+ SpongePlatform(SpongeWorldEdit mod) {
+ this.mod = mod;
+ }
+
+ boolean isHookingEvents() {
+ return hookingEvents;
+ }
+
+ @Override
+ public int resolveItem(String name) {
+ if (name == null) return 0;
+
+ Optional optType = Sponge.getRegistry().getType(ItemType.class, name);
+
+ if (optType.isPresent()) {
+ return IDHelper.resolve(optType.get());
+ }
+
+ return 0;
+ }
+
+ @Override
+ public boolean isValidMobType(String type) {
+ return Sponge.getRegistry().getType(EntityType.class, type).isPresent();
+ }
+
+ @Override
+ public void reload() {
+ getConfiguration().load();
+ }
+
+ @Override
+ public int schedule(long delay, long period, Runnable task) {
+ Task.builder().delayTicks(delay).intervalTicks(period).execute(task).submit(SpongeWorldEdit.inst());
+ return 0; // TODO This isn't right, but we only check for -1 values
+ }
+
+ @Override
+ public List extends com.sk89q.worldedit.world.World> getWorlds() {
+ Collection worlds = Sponge.getServer().getWorlds();
+ List ret = new ArrayList<>(worlds.size());
+ for (org.spongepowered.api.world.World world : worlds) {
+ ret.add(new SpongeNMSWorld(world));
+ }
+ return ret;
+ }
+
+ @Nullable
+ @Override
+ public Player matchPlayer(Player player) {
+ if (player instanceof SpongePlayer) {
+ return player;
+ } else {
+ Optional optPlayer = Sponge.getServer().getPlayer(player.getUniqueId());
+ return optPlayer.isPresent() ? new SpongePlayer(this, optPlayer.get()) : null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public World matchWorld(World world) {
+ if (world instanceof SpongeWorld) {
+ return world;
+ } else {
+ for (org.spongepowered.api.world.World ws : Sponge.getServer().getWorlds()) {
+ if (ws.getName().equals(world.getName())) {
+ return new SpongeNMSWorld(ws);
+ }
+ }
+
+ return null;
+ }
+ }
+
+ @Override
+ public void registerCommands(Dispatcher dispatcher) {
+ for (CommandMapping command : dispatcher.getCommands()) {
+ CommandAdapter adapter = new CommandAdapter(command) {
+ @Override
+ public CommandResult process(CommandSource source, String arguments) throws org.spongepowered.api.command.CommandException {
+ CommandEvent weEvent = new CommandEvent(SpongeWorldEdit.inst().wrapCommandSource(source), command.getPrimaryAlias() + " " + arguments);
+ WorldEdit.getInstance().getEventBus().post(weEvent);
+ return weEvent.isCancelled() ? CommandResult.success() : CommandResult.empty();
+ }
+
+ @Override
+ public List getSuggestions(CommandSource source, String arguments) throws org.spongepowered.api.command.CommandException {
+ CommandSuggestionEvent weEvent = new CommandSuggestionEvent(SpongeWorldEdit.inst().wrapCommandSource(source), command.getPrimaryAlias() + " " + arguments);
+ WorldEdit.getInstance().getEventBus().post(weEvent);
+ return weEvent.getSuggestions();
+ }
+ };
+ Sponge.getCommandManager().register(SpongeWorldEdit.inst(), adapter, command.getAllAliases());
+ }
+ }
+
+ @Override
+ public void registerGameHooks() {
+ // We registered the events already anyway, so we just 'turn them on'
+ hookingEvents = true;
+ }
+
+ @Override
+ public SpongeConfiguration getConfiguration() {
+ return mod.getConfig();
+ }
+
+ @Override
+ public String getVersion() {
+ return mod.getInternalVersion();
+ }
+
+ @Override
+ public String getPlatformName() {
+ return "Sponge-Official";
+ }
+
+ @Override
+ public String getPlatformVersion() {
+ return mod.getInternalVersion();
+ }
+
+ @Override
+ public Map getCapabilities() {
+ Map capabilities = new EnumMap<>(Capability.class);
+ capabilities.put(Capability.CONFIGURATION, Preference.NORMAL);
+ capabilities.put(Capability.WORLDEDIT_CUI, Preference.NORMAL);
+ capabilities.put(Capability.GAME_HOOKS, Preference.NORMAL);
+ capabilities.put(Capability.PERMISSIONS, Preference.NORMAL);
+ capabilities.put(Capability.USER_COMMANDS, Preference.NORMAL);
+ capabilities.put(Capability.WORLD_EDITING, Preference.PREFERRED);
+ return capabilities;
+ }
+
+ @Override
+ public Collection getConnectedUsers() {
+ List users = new ArrayList();
+ for (org.spongepowered.api.entity.living.player.Player player : Sponge.getServer().getOnlinePlayers()) {
+ users.add(new SpongePlayer(this, player));
+ }
+ return users;
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java
new file mode 100644
index 000000000..d88f6ecc2
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java
@@ -0,0 +1,222 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.flowpowered.math.vector.Vector3d;
+import com.sk89q.util.StringUtil;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.WorldVector;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.extension.platform.AbstractPlayerActor;
+import com.sk89q.worldedit.extent.inventory.BlockBag;
+import com.sk89q.worldedit.internal.LocalWorldAdapter;
+import com.sk89q.worldedit.internal.cui.CUIEvent;
+import com.sk89q.worldedit.session.SessionKey;
+import com.sk89q.worldedit.sponge.nms.IDHelper;
+import com.sk89q.worldedit.util.Location;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.item.inventory.ItemStack;
+import org.spongepowered.api.text.Text;
+import org.spongepowered.api.text.format.TextColor;
+import org.spongepowered.api.text.format.TextColors;
+import org.spongepowered.api.text.serializer.TextSerializers;
+import org.spongepowered.api.world.World;
+
+import javax.annotation.Nullable;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+import java.util.UUID;
+
+public class SpongePlayer extends AbstractPlayerActor {
+
+ private final Player player;
+
+ protected SpongePlayer(SpongePlatform platform, Player player) {
+ this.player = player;
+ ThreadSafeCache.getInstance().getOnlineIds().add(getUniqueId());
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return player.getUniqueId();
+ }
+
+ @Override
+ public int getItemInHand() {
+ Optional is = this.player.getItemInHand();
+ return is.isPresent() ? IDHelper.resolve(is.get().getItem()) : 0;
+ }
+
+ @Override
+ public String getName() {
+ return this.player.getName();
+ }
+
+ @Override
+ public BaseEntity getState() {
+ throw new UnsupportedOperationException("Cannot create a state from this object");
+ }
+
+ @Override
+ public Location getLocation() {
+ org.spongepowered.api.world.Location entityLoc = this.player.getLocation();
+ Vector3d entityRot = this.player.getRotation();
+
+ return SpongeAdapter.adapt(entityLoc, entityRot);
+ }
+
+ @Override
+ public WorldVector getPosition() {
+ Vector3d pos = this.player.getLocation().getPosition();
+ return new WorldVector(LocalWorldAdapter.adapt(SpongeAdapter.adapt(this.player.getWorld())), pos.getX(), pos.getY(), pos.getZ());
+ }
+
+ @Override
+ public com.sk89q.worldedit.world.World getWorld() {
+ return SpongeAdapter.adapt(player.getWorld());
+ }
+
+ @Override
+ public double getPitch() {
+ return getLocation().getPitch();
+ }
+
+ @Override
+ public double getYaw() {
+ return getLocation().getYaw();
+ }
+
+ @Override
+ public void giveItem(int type, int amt) {
+ this.player.getInventory().offer(ItemStack.of(IDHelper.resolveItem(type), amt));
+ }
+
+ @Override
+ public void dispatchCUIEvent(CUIEvent event) {
+ String[] params = event.getParameters();
+ String send = event.getTypeId();
+ if (params.length > 0) {
+ send = send + "|" + StringUtil.joinString(params, "|");
+ }
+
+ String finalData = send;
+ CUIChannelHandler.getActiveChannel().sendTo(player, buffer -> buffer.writeBytes(finalData.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ @Override
+ public void printRaw(String msg) {
+ for (String part : msg.split("\n")) {
+ this.player.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(part));
+ }
+ }
+
+ @Override
+ public void printDebug(String msg) {
+ sendColorized(msg, TextColors.GRAY);
+ }
+
+ @Override
+ public void print(String msg) {
+ sendColorized(msg, TextColors.LIGHT_PURPLE);
+ }
+
+ @Override
+ public void printError(String msg) {
+ sendColorized(msg, TextColors.RED);
+ }
+
+ private void sendColorized(String msg, TextColor formatting) {
+ for (String part : msg.split("\n")) {
+ this.player.sendMessage(Text.of(formatting, TextSerializers.LEGACY_FORMATTING_CODE.deserialize(part)));
+ }
+ }
+
+ @Override
+ public void setPosition(Vector pos, float pitch, float yaw) {
+ org.spongepowered.api.world.Location loc = new org.spongepowered.api.world.Location<>(
+ this.player.getWorld(), pos.getX(), pos.getY(), pos.getZ()
+ );
+
+ this.player.setLocationAndRotation(loc, new Vector3d(pitch, yaw, 0));
+ }
+
+ @Override
+ public String[] getGroups() {
+ return new String[]{}; // WorldEditMod.inst.getPermissionsResolver().getGroups(this.player.username);
+ }
+
+ @Override
+ public BlockBag getInventoryBlockBag() {
+ return null;
+ }
+
+ @Override
+ public boolean hasPermission(String perm) {
+ return SpongeWorldEdit.inst().getPermissionsProvider().hasPermission(player, perm);
+ }
+
+ @Nullable
+ @Override
+ public T getFacet(Class extends T> cls) {
+ return null;
+ }
+
+ @Override
+ public SessionKey getSessionKey() {
+ return new SessionKeyImpl(player.getUniqueId(), player.getName());
+ }
+
+ private static class SessionKeyImpl implements SessionKey {
+ // If not static, this will leak a reference
+
+ private final UUID uuid;
+ private final String name;
+
+ private SessionKeyImpl(UUID uuid, String name) {
+ this.uuid = uuid;
+ this.name = name;
+ }
+
+ @Override
+ public UUID getUniqueId() {
+ return uuid;
+ }
+
+ @Nullable
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isActive() {
+ // We can't directly check if the player is online because
+ // the list of players is not thread safe
+ return ThreadSafeCache.getInstance().getOnlineIds().contains(uuid);
+ }
+
+ @Override
+ public boolean isPersistent() {
+ return true;
+ }
+
+ }
+
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java
new file mode 100644
index 000000000..7d97bc7b4
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java
@@ -0,0 +1,311 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.flowpowered.math.vector.Vector3d;
+import com.flowpowered.math.vector.Vector3i;
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.Vector2D;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.entity.Entity;
+import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.sponge.nms.IDHelper;
+import com.sk89q.worldedit.util.Location;
+import com.sk89q.worldedit.world.AbstractWorld;
+import com.sk89q.worldedit.world.biome.BaseBiome;
+import com.sk89q.worldedit.world.registry.WorldData;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.block.BlockState;
+import org.spongepowered.api.block.tileentity.TileEntity;
+import org.spongepowered.api.data.key.Keys;
+import org.spongepowered.api.data.property.block.GroundLuminanceProperty;
+import org.spongepowered.api.data.property.block.SkyLuminanceProperty;
+import org.spongepowered.api.entity.EntityType;
+import org.spongepowered.api.entity.EntityTypes;
+import org.spongepowered.api.event.cause.Cause;
+import org.spongepowered.api.event.cause.NamedCause;
+import org.spongepowered.api.event.cause.entity.spawn.SpawnCause;
+import org.spongepowered.api.event.cause.entity.spawn.SpawnTypes;
+import org.spongepowered.api.world.World;
+
+import javax.annotation.Nullable;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An adapter to Minecraft worlds for WorldEdit.
+ */
+public abstract class SpongeWorld extends AbstractWorld {
+
+ protected static final Random random = new Random();
+
+ private final WeakReference worldRef;
+
+ /**
+ * Construct a new world.
+ *
+ * @param world the world
+ */
+ protected SpongeWorld(World world) {
+ checkNotNull(world);
+ this.worldRef = new WeakReference<>(world);
+ }
+
+ /**
+ * Get the underlying handle to the world.
+ *
+ * @return the world
+ * @throws WorldEditException thrown if a reference to the world was lost (i.e. world was unloaded)
+ */
+ public World getWorldChecked() throws WorldEditException {
+ World world = worldRef.get();
+ if (world != null) {
+ return world;
+ } else {
+ throw new WorldReferenceLostException("The reference to the world was lost (i.e. the world may have been unloaded)");
+ }
+ }
+
+ /**
+ * Get the underlying handle to the world.
+ *
+ * @return the world
+ * @throws RuntimeException thrown if a reference to the world was lost (i.e. world was unloaded)
+ */
+ public World getWorld() {
+ World world = worldRef.get();
+ if (world != null) {
+ return world;
+ } else {
+ throw new RuntimeException("The reference to the world was lost (i.e. the world may have been unloaded)");
+ }
+ }
+
+ @Override
+ public String getName() {
+ return getWorld().getName();
+ }
+
+ protected abstract BlockState getBlockState(BaseBlock block);
+
+ protected abstract void applyTileEntityData(TileEntity entity, BaseBlock block);
+
+ private static final BlockSnapshot.Builder builder = BlockSnapshot.builder();
+
+ @Override
+ public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException {
+ checkNotNull(position);
+ checkNotNull(block);
+
+ World world = getWorldChecked();
+
+ // First set the block
+ Vector3i pos = new Vector3i(position.getX(), position.getY(), position.getZ());
+ BlockState newState = getBlockState(block);
+
+ BlockSnapshot snapshot = builder.reset()
+ .blockState(newState)
+ .position(pos)
+ .world(world.getProperties())
+ .build();
+
+ snapshot.restore(true, notifyAndLight);
+
+ // Create the TileEntity
+ if (block.hasNbtData()) {
+ // Kill the old TileEntity
+ Optional optTile = world.getTileEntity(pos);
+ if (optTile.isPresent()) {
+ applyTileEntityData(optTile.get(), block);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean regenerate(Region region, EditSession editSession) {
+ return false;
+ }
+
+ @Override
+ public int getBlockLightLevel(Vector position) {
+ checkNotNull(position);
+
+ BlockState state = getWorld().getBlock(new Vector3i(position.getX(), position.getY(), position.getZ()));
+
+ Optional groundLuminanceProperty = state.getProperty(GroundLuminanceProperty.class);
+ Optional skyLuminanceProperty = state.getProperty(SkyLuminanceProperty.class);
+
+ if (!groundLuminanceProperty.isPresent() || !skyLuminanceProperty.isPresent()) {
+ return 0;
+ }
+
+ //noinspection ConstantConditions
+ return (int) Math.max(groundLuminanceProperty.get().getValue(), skyLuminanceProperty.get().getValue());
+
+ }
+
+ @Override
+ public BaseBiome getBiome(Vector2D position) {
+ checkNotNull(position);
+ return new BaseBiome(IDHelper.resolve(getWorld().getBiome(position.getBlockX(), position.getBlockZ())));
+ }
+
+ @Override
+ public boolean setBiome(Vector2D position, BaseBiome biome) {
+ checkNotNull(position);
+ checkNotNull(biome);
+
+ getWorld().setBiome(position.getBlockX(), position.getBlockZ(), IDHelper.resolveBiome(biome.getId()));
+ return true;
+ }
+
+ @Override
+ public void dropItem(Vector position, BaseItemStack item) {
+ checkNotNull(position);
+ checkNotNull(item);
+
+ if (item.getType() == 0) {
+ return;
+ }
+
+ Optional optItem = getWorld().createEntity(
+ EntityTypes.ITEM,
+ new Vector3d(position.getX(), position.getY(), position.getZ())
+ );
+
+ if (optItem.isPresent()) {
+ org.spongepowered.api.entity.Entity entity = optItem.get();
+ entity.offer(Keys.REPRESENTED_ITEM, SpongeWorldEdit.toSpongeItemStack(item).createSnapshot());
+ getWorld().spawnEntity(entity, Cause.source(SpongeWorldEdit.container()).build());
+ }
+ }
+
+ @Override
+ public WorldData getWorldData() {
+ return SpongeWorldData.getInstance();
+ }
+
+ @Override
+ public boolean isValidBlockType(int id) {
+ return (id == 0) || (IDHelper.resolveBlock(id) != null);
+ }
+
+ @Override
+ public int hashCode() {
+ return getWorld().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ } else if ((o instanceof SpongeWorld)) {
+ SpongeWorld other = ((SpongeWorld) o);
+ World otherWorld = other.worldRef.get();
+ World thisWorld = worldRef.get();
+ return otherWorld != null && thisWorld != null && otherWorld.equals(thisWorld);
+ } else if (o instanceof com.sk89q.worldedit.world.World) {
+ return ((com.sk89q.worldedit.world.World) o).getName().equals(getName());
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public List extends Entity> getEntities(Region region) {
+ List entities = new ArrayList<>();
+ for (org.spongepowered.api.entity.Entity entity : getWorld().getEntities()) {
+ org.spongepowered.api.world.Location loc = entity.getLocation();
+ if (region.contains(new Vector(loc.getX(), loc.getY(), loc.getZ()))) {
+ entities.add(new SpongeEntity(entity));
+ }
+ }
+ return entities;
+ }
+
+ @Override
+ public List extends Entity> getEntities() {
+ List entities = new ArrayList<>();
+ for (org.spongepowered.api.entity.Entity entity : getWorld().getEntities()) {
+ entities.add(new SpongeEntity(entity));
+ }
+ return entities;
+ }
+
+ protected abstract void applyEntityData(org.spongepowered.api.entity.Entity entity, BaseEntity data);
+
+ private static final Cause ENTITY_SPAWN_CAUSE = Cause
+ .source(SpawnCause.builder().type(SpawnTypes.PLUGIN).build())
+ .suggestNamed(NamedCause.SOURCE, SpongeWorldEdit.inst())
+ .build();
+
+ @Nullable
+ @Override
+ public Entity createEntity(Location location, BaseEntity entity) {
+ World world = getWorld();
+
+ EntityType entityType = Sponge.getRegistry().getType(EntityType.class, entity.getTypeId()).get();
+ Vector3d pos = new Vector3d(location.getX(), location.getY(), location.getZ());
+
+ Optional optNewEnt = world.createEntity(entityType, pos);
+ if (optNewEnt.isPresent()) {
+ org.spongepowered.api.entity.Entity newEnt = optNewEnt.get();
+ if (entity.hasNbtData()) {
+ applyEntityData(newEnt, entity);
+ }
+
+ // Overwrite any data set by the NBT application
+ Vector dir = location.getDirection();
+
+ newEnt.setLocationAndRotation(
+ new org.spongepowered.api.world.Location<>(getWorld(), pos),
+ new Vector3d(dir.getX(), dir.getY(), dir.getZ())
+ );
+
+ if (world.spawnEntity(newEnt, ENTITY_SPAWN_CAUSE)) {
+ return new SpongeEntity(newEnt);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Thrown when the reference to the world is lost.
+ */
+ private static class WorldReferenceLostException extends WorldEditException {
+ private WorldReferenceLostException(String message) {
+ super(message);
+ }
+ }
+
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldData.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldData.java
new file mode 100644
index 000000000..789dfc0c3
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldData.java
@@ -0,0 +1,47 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.sk89q.worldedit.world.registry.BiomeRegistry;
+import com.sk89q.worldedit.world.registry.LegacyWorldData;
+
+/**
+ * World data for the Sponge platform.
+ */
+class SpongeWorldData extends LegacyWorldData {
+
+ private static final SpongeWorldData INSTANCE = new SpongeWorldData();
+ private final BiomeRegistry biomeRegistry = new SpongeBiomeRegistry();
+
+ @Override
+ public BiomeRegistry getBiomeRegistry() {
+ return biomeRegistry;
+ }
+
+ /**
+ * Get a static instance.
+ *
+ * @return an instance
+ */
+ public static SpongeWorldData getInstance() {
+ return INSTANCE;
+ }
+
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldEdit.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldEdit.java
new file mode 100644
index 000000000..4609c53fa
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldEdit.java
@@ -0,0 +1,311 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import com.google.inject.Inject;
+import com.sk89q.worldedit.LocalSession;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.WorldVector;
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
+import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.extension.platform.Platform;
+import com.sk89q.worldedit.internal.LocalWorldAdapter;
+import com.sk89q.worldedit.sponge.config.SpongeConfiguration;
+import com.sk89q.worldedit.sponge.nms.NMSHelper;
+import com.sk89q.worldedit.sponge.nms.SpongeNMSWorld;
+import org.slf4j.Logger;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.block.BlockSnapshot;
+import org.spongepowered.api.block.BlockType;
+import org.spongepowered.api.block.BlockTypes;
+import org.spongepowered.api.command.CommandSource;
+import org.spongepowered.api.config.ConfigManager;
+import org.spongepowered.api.entity.living.player.Player;
+import org.spongepowered.api.event.Listener;
+import org.spongepowered.api.event.block.InteractBlockEvent;
+import org.spongepowered.api.event.game.state.*;
+import org.spongepowered.api.item.inventory.ItemStack;
+import org.spongepowered.api.plugin.Plugin;
+import org.spongepowered.api.plugin.PluginContainer;
+import org.spongepowered.api.scheduler.Task;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * The Sponge implementation of WorldEdit.
+ */
+@Plugin(id = SpongeWorldEdit.MOD_ID, name = "WorldEdit", version = "%VERSION%")
+public class SpongeWorldEdit {
+
+ @Inject
+ private Logger logger;
+
+ public static final String MOD_ID = "worldedit";
+
+ private SpongePermissionsProvider provider;
+
+ @Inject
+ private PluginContainer container;
+
+ private static SpongeWorldEdit inst;
+
+ public static PluginContainer container() {
+ return inst.container;
+ }
+
+ public static SpongeWorldEdit inst() {
+ return inst;
+ }
+
+ private SpongePlatform platform;
+ private SpongeConfiguration config;
+ private File workingDir;
+
+ public SpongeWorldEdit() {
+ inst = this;
+ }
+
+ @Listener
+ public void preInit(GamePreInitializationEvent event) {
+ // Setup working directory
+ ConfigManager service = Sponge.getGame().getConfigManager();
+
+ Path path = service.getPluginConfig(this).getDirectory();
+ workingDir = path.toFile();
+ workingDir.mkdir();
+
+ config = new SpongeConfiguration(service.getPluginConfig(this).getConfig(), logger);
+ config.load();
+
+ Task.builder().interval(30, TimeUnit.SECONDS).execute(ThreadSafeCache.getInstance()).submit(this);
+ }
+
+ @Listener
+ public void init(GameInitializationEvent event) {
+ CUIChannelHandler.init();
+ }
+
+ @Listener
+ public void postInit(GamePostInitializationEvent event) {
+ logger.info("WorldEdit for Sponge (version " + getInternalVersion() + ") is loaded");
+ }
+
+ @Listener
+ public void serverAboutToStart(GameAboutToStartServerEvent event) {
+ if (this.platform != null) {
+ logger.warn("GameAboutToStartServerEvent occurred when GameStoppingServerEvent hasn't");
+ WorldEdit.getInstance().getPlatformManager().unregister(platform);
+ }
+
+ this.platform = new SpongePlatform(this);
+ this.provider = new SpongePermissionsProvider();
+
+ WorldEdit.getInstance().getPlatformManager().register(platform);
+ }
+
+ @Listener
+ public void serverStopping(GameStoppingServerEvent event) {
+ WorldEdit.getInstance().getPlatformManager().unregister(platform);
+ }
+
+ @Listener
+ public void serverStarted(GameStartedServerEvent event) {
+ WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent());
+ }
+
+ private boolean ignoreLeftClickAir = false;
+
+ @Listener
+ public void onPlayerInteract(InteractBlockEvent event) {
+ if (platform == null) {
+ return;
+ }
+
+ if (!platform.isHookingEvents()) return; // We have to be told to catch these events
+
+ WorldEdit we = WorldEdit.getInstance();
+ Object rootObj = event.getCause().root();
+ if (!(rootObj instanceof Player)) {
+ return;
+ }
+
+ SpongePlayer player = wrapPlayer((Player) rootObj);
+ com.sk89q.worldedit.world.World world = player.getWorld();
+
+ BlockSnapshot targetBlock = event.getTargetBlock();
+ Optional> optLoc = targetBlock.getLocation();
+
+ BlockType interactedType = targetBlock.getState().getType();
+ if (event instanceof InteractBlockEvent.Primary) {
+ if (interactedType != BlockTypes.AIR) {
+ if (!optLoc.isPresent()) {
+ return;
+ }
+
+ Location loc = optLoc.get();
+ WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), loc.getX(), loc.getY(), loc.getZ());
+
+ if (we.handleBlockLeftClick(player, pos)) {
+ event.setCancelled(true);
+ }
+
+ if (we.handleArmSwing(player)) {
+ event.setCancelled(true);
+ }
+
+ if (!ignoreLeftClickAir) {
+ Task.builder().delayTicks(2).execute(() -> {
+ ignoreLeftClickAir = false;
+
+ }).submit(this);
+
+ ignoreLeftClickAir = true;
+ }
+ } else {
+ if (ignoreLeftClickAir) {
+ return;
+ }
+
+ if (we.handleArmSwing(player)) {
+ event.setCancelled(true);
+ }
+ }
+ } else if (event instanceof InteractBlockEvent.Secondary) {
+ if (interactedType != BlockTypes.AIR) {
+ if (!optLoc.isPresent()) {
+ return;
+ }
+
+ Location loc = optLoc.get();
+ WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), loc.getX(), loc.getY(), loc.getZ());
+
+ if (we.handleBlockRightClick(player, pos)) {
+ event.setCancelled(true);
+ }
+
+ if (we.handleRightClick(player)) {
+ event.setCancelled(true);
+ }
+ } else {
+ if (we.handleRightClick(player)) {
+ event.setCancelled(true);
+ }
+ }
+ }
+ }
+
+ public static ItemStack toSpongeItemStack(BaseItemStack item) {
+ return NMSHelper.makeSpongeStack(item);
+ }
+
+ /**
+ * Get the configuration.
+ *
+ * @return the Sponge configuration
+ */
+ SpongeConfiguration getConfig() {
+ return this.config;
+ }
+
+ /**
+ * Get the WorldEdit proxy for the given player.
+ *
+ * @param player the player
+ * @return the WorldEdit player
+ */
+ public SpongePlayer wrapPlayer(Player player) {
+ checkNotNull(player);
+ return new SpongePlayer(platform, player);
+ }
+
+ public Actor wrapCommandSource(CommandSource sender) {
+ if (sender instanceof Player) {
+ return wrapPlayer((Player) sender);
+ }
+
+ return new SpongeCommandSender(this, sender);
+ }
+
+ /**
+ * Get the session for a player.
+ *
+ * @param player the player
+ * @return the session
+ */
+ public LocalSession getSession(Player player) {
+ checkNotNull(player);
+ return WorldEdit.getInstance().getSessionManager().get(wrapPlayer(player));
+ }
+
+ /**
+ * Get the WorldEdit proxy for the given world.
+ *
+ * @param world the world
+ * @return the WorldEdit world
+ */
+ public SpongeWorld getWorld(World world) {
+ checkNotNull(world);
+ return new SpongeNMSWorld(world);
+ }
+
+ /**
+ * Get the WorldEdit proxy for the platform.
+ *
+ * @return the WorldEdit platform
+ */
+ public Platform getPlatform() {
+ return this.platform;
+ }
+
+ /**
+ * Get the working directory where WorldEdit's files are stored.
+ *
+ * @return the working directory
+ */
+ public File getWorkingDir() {
+ return this.workingDir;
+ }
+
+ /**
+ * Get the version of the WorldEdit Sponge implementation.
+ *
+ * @return a version string
+ */
+ String getInternalVersion() {
+ return SpongeWorldEdit.class.getAnnotation(Plugin.class).version();
+ }
+
+ public void setPermissionsProvider(SpongePermissionsProvider provider) {
+ this.provider = provider;
+ }
+
+ public SpongePermissionsProvider getPermissionsProvider() {
+ return provider;
+ }
+
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/ThreadSafeCache.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/ThreadSafeCache.java
new file mode 100644
index 000000000..43a7c0541
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/ThreadSafeCache.java
@@ -0,0 +1,63 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge;
+
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.entity.living.player.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * Caches data that cannot be accessed from another thread safely.
+ */
+public class ThreadSafeCache implements Runnable {
+
+ private static final ThreadSafeCache INSTANCE = new ThreadSafeCache();
+ private Set onlineIds = new CopyOnWriteArraySet<>();
+
+ /**
+ * Get an concurrent-safe set of UUIDs of online players.
+ *
+ * @return a set of UUIDs
+ */
+ public Set getOnlineIds() {
+ return onlineIds;
+ }
+
+ @Override
+ public void run() {
+ List onlineIds = new ArrayList<>();
+
+ for (Player player : Sponge.getServer().getOnlinePlayers()) {
+ onlineIds.add(player.getUniqueId());
+ }
+
+ this.onlineIds = new CopyOnWriteArraySet<>(onlineIds);
+ }
+
+ public static ThreadSafeCache getInstance() {
+ return INSTANCE;
+ }
+
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java
new file mode 100644
index 000000000..7db17801b
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java
@@ -0,0 +1,122 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge.config;
+
+import com.google.common.reflect.TypeToken;
+import com.sk89q.worldedit.LocalConfiguration;
+import com.sk89q.worldedit.LocalSession;
+import com.sk89q.worldedit.session.SessionManager;
+import com.sk89q.worldedit.world.snapshot.SnapshotRepository;
+import ninja.leaping.configurate.ConfigurationOptions;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.loader.ConfigurationLoader;
+import ninja.leaping.configurate.objectmapping.ObjectMappingException;
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.util.HashSet;
+
+public class ConfigurateConfiguration extends LocalConfiguration {
+
+ protected final ConfigurationLoader config;
+ protected final Logger logger;
+
+ protected CommentedConfigurationNode node;
+
+ public ConfigurateConfiguration(ConfigurationLoader config, Logger logger) {
+ this.config = config;
+ this.logger = logger;
+ }
+
+ @Override
+ public void load() {
+ try {
+ ConfigurationOptions options = ConfigurationOptions.defaults();
+ options = options.setShouldCopyDefaults(true);
+
+ node = config.load(options);
+ } catch (IOException e) {
+ logger.warn("Error loading WorldEdit configuration", e);
+ }
+
+ profile = node.getNode("debug").getBoolean(profile);
+ wandItem = node.getNode("wand-item").getInt(wandItem);
+
+ defaultChangeLimit = Math.max(-1, node.getNode("limits", "max-blocks-changed", "default").getInt(defaultChangeLimit));
+ maxChangeLimit = Math.max(-1, node.getNode("limits", "max-blocks-changed", "maximum").getInt(maxChangeLimit));
+
+ defaultMaxPolygonalPoints = Math.max(-1, node.getNode("limits", "max-polygonal-points", "default").getInt(defaultMaxPolygonalPoints));
+ maxPolygonalPoints = Math.max(-1, node.getNode("limits", "max-polygonal-points", "maximum").getInt(maxPolygonalPoints));
+
+ maxRadius = Math.max(-1, node.getNode("limits", "max-radius").getInt(maxRadius));
+ maxBrushRadius = node.getNode("limits", "max-brush-radius").getInt(maxBrushRadius);
+ maxSuperPickaxeSize = Math.max(1, node.getNode("limits", "max-super-pickaxe-size").getInt(maxSuperPickaxeSize));
+
+ butcherDefaultRadius = Math.max(-1, node.getNode("limits", "butcher-radius", "default").getInt(butcherDefaultRadius));
+ butcherMaxRadius = Math.max(-1, node.getNode("limits", "butcher-radius", "maximum").getInt(butcherMaxRadius));
+
+ try {
+ disallowedBlocks = new HashSet<>(node.getNode("limits", "disallowed-blocks").getList(TypeToken.of(Integer.class)));
+ } catch (ObjectMappingException e) {
+ logger.warn("Error loading WorldEdit configuration", e);
+ }
+ try {
+ allowedDataCycleBlocks = new HashSet<>(node.getNode("limits", "allowed-data-cycle-blocks").getList(TypeToken.of(Integer.class)));
+ } catch (ObjectMappingException e) {
+ logger.warn("Error loading WorldEdit configuration", e);
+ }
+
+ registerHelp = node.getNode("register-help").getBoolean(true);
+ logCommands = node.getNode("logging", "log-commands").getBoolean(logCommands);
+ logFile = node.getNode("logging", "file").getString(logFile);
+
+ superPickaxeDrop = node.getNode("super-pickaxe", "drop-items").getBoolean(superPickaxeDrop);
+ superPickaxeManyDrop = node.getNode("super-pickaxe", "many-drop-items").getBoolean(superPickaxeManyDrop);
+
+ noDoubleSlash = node.getNode("no-double-slash").getBoolean(noDoubleSlash);
+
+ useInventory = node.getNode("use-inventory", "enable").getBoolean(useInventory);
+ useInventoryOverride = node.getNode("use-inventory", "allow-override").getBoolean(useInventoryOverride);
+ useInventoryCreativeOverride = node.getNode("use-inventory", "creative-mode-overrides").getBoolean(useInventoryCreativeOverride);
+
+ navigationWand = node.getNode("navigation-wand", "item").getInt(navigationWand);
+ navigationWandMaxDistance = node.getNode("navigation-wand", "max-distance").getInt(navigationWandMaxDistance);
+ navigationUseGlass = node.getNode("navigation", "use-glass").getBoolean(navigationUseGlass);
+
+ scriptTimeout = node.getNode("scripting", "timeout").getInt(scriptTimeout);
+ scriptsDir = node.getNode("scripting", "dir").getString(scriptsDir);
+
+ saveDir = node.getNode("saving", "dir").getString(saveDir);
+
+ allowSymlinks = node.getNode("files", "allow-symbolic-links").getBoolean(false);
+ LocalSession.MAX_HISTORY_SIZE = Math.max(0, node.getNode("history", "size").getInt(15));
+ SessionManager.EXPIRATION_GRACE = node.getNode("history", "expiration").getInt(10) * 60 * 1000;
+
+ showHelpInfo = node.getNode("show-help-on-first-use").getBoolean(true);
+
+ String snapshotsDir = node.getNode("snapshots", "directory").getString("");
+ if (!snapshotsDir.isEmpty()) {
+ snapshotRepo = new SnapshotRepository(snapshotsDir);
+ }
+
+ String type = node.getNode("shell-save-type").getString("").trim();
+ shellSaveType = type.equals("") ? null : type;
+ }
+}
\ No newline at end of file
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/SpongeConfiguration.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/SpongeConfiguration.java
new file mode 100644
index 000000000..7fbe941ad
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/SpongeConfiguration.java
@@ -0,0 +1,57 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge.config;
+
+import com.sk89q.worldedit.sponge.SpongeWorldEdit;
+import ninja.leaping.configurate.commented.CommentedConfigurationNode;
+import ninja.leaping.configurate.loader.ConfigurationLoader;
+import org.slf4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+
+public class SpongeConfiguration extends ConfigurateConfiguration {
+
+ public boolean creativeEnable = false;
+ public boolean cheatMode = false;
+
+ public SpongeConfiguration(ConfigurationLoader config, Logger logger) {
+ super(config, logger);
+ }
+
+ @Override
+ public void load() {
+ super.load();
+
+ creativeEnable = node.getNode("use-in-creative").getBoolean(false);
+ cheatMode = node.getNode("cheat-mode").getBoolean(false);
+
+ try {
+ config.save(node);
+ } catch (IOException e) {
+ logger.warn("Error loading WorldEdit configuration", e);
+ }
+ }
+
+ @Override
+ public File getWorkingDirectory() {
+ return SpongeWorldEdit.inst().getWorkingDir();
+ }
+}
\ No newline at end of file
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/IDHelper.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/IDHelper.java
new file mode 100644
index 000000000..52aa06c0c
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/IDHelper.java
@@ -0,0 +1,57 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge.nms;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import net.minecraft.world.biome.BiomeGenBase;
+import org.spongepowered.api.block.BlockType;
+import org.spongepowered.api.item.ItemType;
+import org.spongepowered.api.world.biome.BiomeType;
+
+@Deprecated
+public final class IDHelper {
+
+ private IDHelper() { }
+
+ public static int resolve(ItemType type) {
+ return Item.getIdFromItem((Item) type);
+ }
+
+ public static int resolve(BlockType type) {
+ return Block.getIdFromBlock((Block) type);
+ }
+
+ public static int resolve(BiomeType type) {
+ return ((BiomeGenBase) type).biomeID;
+ }
+
+ public static ItemType resolveItem(int intID) {
+ return (ItemType) Item.getItemById(intID);
+ }
+
+ public static BlockType resolveBlock(int intID) {
+ return (BlockType) Block.getBlockById(intID);
+ }
+
+ public static BiomeType resolveBiome(int intID) {
+ return (BiomeType) BiomeGenBase.getBiome(intID);
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/NBTConverter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/NBTConverter.java
new file mode 100644
index 000000000..48e8b5da0
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/NBTConverter.java
@@ -0,0 +1,237 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge.nms;
+
+import com.sk89q.jnbt.*;
+import net.minecraft.nbt.*;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+/**
+ * Converts between JNBT and Minecraft NBT classes.
+ */
+@Deprecated
+final class NBTConverter {
+
+ private NBTConverter() {
+ }
+
+ public static NBTBase toNative(Tag tag) {
+ if (tag instanceof IntArrayTag) {
+ return toNative((IntArrayTag) tag);
+
+ } else if (tag instanceof ListTag) {
+ return toNative((ListTag) tag);
+
+ } else if (tag instanceof LongTag) {
+ return toNative((LongTag) tag);
+
+ } else if (tag instanceof StringTag) {
+ return toNative((StringTag) tag);
+
+ } else if (tag instanceof IntTag) {
+ return toNative((IntTag) tag);
+
+ } else if (tag instanceof ByteTag) {
+ return toNative((ByteTag) tag);
+
+ } else if (tag instanceof ByteArrayTag) {
+ return toNative((ByteArrayTag) tag);
+
+ } else if (tag instanceof CompoundTag) {
+ return toNative((CompoundTag) tag);
+
+ } else if (tag instanceof FloatTag) {
+ return toNative((FloatTag) tag);
+
+ } else if (tag instanceof ShortTag) {
+ return toNative((ShortTag) tag);
+
+ } else if (tag instanceof DoubleTag) {
+ return toNative((DoubleTag) tag);
+ } else {
+ throw new IllegalArgumentException("Can't convert tag of type " + tag.getClass().getCanonicalName());
+ }
+ }
+
+ public static NBTTagIntArray toNative(IntArrayTag tag) {
+ int[] value = tag.getValue();
+ return new NBTTagIntArray(Arrays.copyOf(value, value.length));
+ }
+
+ public static NBTTagList toNative(ListTag tag) {
+ NBTTagList list = new NBTTagList();
+ for (Tag child : tag.getValue()) {
+ if (child instanceof EndTag) {
+ continue;
+ }
+ list.appendTag(toNative(child));
+ }
+ return list;
+ }
+
+ public static NBTTagLong toNative(LongTag tag) {
+ return new NBTTagLong(tag.getValue());
+ }
+
+ public static NBTTagString toNative(StringTag tag) {
+ return new NBTTagString(tag.getValue());
+ }
+
+ public static NBTTagInt toNative(IntTag tag) {
+ return new NBTTagInt(tag.getValue());
+ }
+
+ public static NBTTagByte toNative(ByteTag tag) {
+ return new NBTTagByte(tag.getValue());
+ }
+
+ public static NBTTagByteArray toNative(ByteArrayTag tag) {
+ byte[] value = tag.getValue();
+ return new NBTTagByteArray(Arrays.copyOf(value, value.length));
+ }
+
+ public static NBTTagCompound toNative(CompoundTag tag) {
+ NBTTagCompound compound = new NBTTagCompound();
+ for (Entry child : tag.getValue().entrySet()) {
+ compound.setTag(child.getKey(), toNative(child.getValue()));
+ }
+ return compound;
+ }
+
+ public static NBTTagFloat toNative(FloatTag tag) {
+ return new NBTTagFloat(tag.getValue());
+ }
+
+ public static NBTTagShort toNative(ShortTag tag) {
+ return new NBTTagShort(tag.getValue());
+ }
+
+ public static NBTTagDouble toNative(DoubleTag tag) {
+ return new NBTTagDouble(tag.getValue());
+ }
+
+ public static Tag fromNative(NBTBase other) {
+ if (other instanceof NBTTagIntArray) {
+ return fromNative((NBTTagIntArray) other);
+
+ } else if (other instanceof NBTTagList) {
+ return fromNative((NBTTagList) other);
+
+ } else if (other instanceof NBTTagEnd) {
+ return fromNative((NBTTagEnd) other);
+
+ } else if (other instanceof NBTTagLong) {
+ return fromNative((NBTTagLong) other);
+
+ } else if (other instanceof NBTTagString) {
+ return fromNative((NBTTagString) other);
+
+ } else if (other instanceof NBTTagInt) {
+ return fromNative((NBTTagInt) other);
+
+ } else if (other instanceof NBTTagByte) {
+ return fromNative((NBTTagByte) other);
+
+ } else if (other instanceof NBTTagByteArray) {
+ return fromNative((NBTTagByteArray) other);
+
+ } else if (other instanceof NBTTagCompound) {
+ return fromNative((NBTTagCompound) other);
+
+ } else if (other instanceof NBTTagFloat) {
+ return fromNative((NBTTagFloat) other);
+
+ } else if (other instanceof NBTTagShort) {
+ return fromNative((NBTTagShort) other);
+
+ } else if (other instanceof NBTTagDouble) {
+ return fromNative((NBTTagDouble) other);
+ } else {
+ throw new IllegalArgumentException("Can't convert other of type " + other.getClass().getCanonicalName());
+ }
+ }
+
+ public static IntArrayTag fromNative(NBTTagIntArray other) {
+ int[] value = other.getIntArray();
+ return new IntArrayTag(Arrays.copyOf(value, value.length));
+ }
+
+ public static ListTag fromNative(NBTTagList other) {
+ other = (NBTTagList) other.copy();
+ List list = new ArrayList();
+ Class extends Tag> listClass = StringTag.class;
+ int tags = other.tagCount();
+ for (int i = 0; i < tags; i++) {
+ Tag child = fromNative(other.removeTag(0));
+ list.add(child);
+ listClass = child.getClass();
+ }
+ return new ListTag(listClass, list);
+ }
+
+ public static EndTag fromNative(NBTTagEnd other) {
+ return new EndTag();
+ }
+
+ public static LongTag fromNative(NBTTagLong other) {
+ return new LongTag(other.getLong());
+ }
+
+ public static StringTag fromNative(NBTTagString other) {
+ return new StringTag(other.getString());
+ }
+
+ public static IntTag fromNative(NBTTagInt other) {
+ return new IntTag(other.getInt());
+ }
+
+ public static ByteTag fromNative(NBTTagByte other) {
+ return new ByteTag(other.getByte());
+ }
+
+ public static ByteArrayTag fromNative(NBTTagByteArray other) {
+ byte[] value = other.getByteArray();
+ return new ByteArrayTag(Arrays.copyOf(value, value.length));
+ }
+
+ public static CompoundTag fromNative(NBTTagCompound other) {
+ @SuppressWarnings("unchecked") Set tags = other.getKeySet();
+ Map map = new HashMap();
+ for (String tagName : tags) {
+ map.put(tagName, fromNative(other.getTag(tagName)));
+ }
+ return new CompoundTag(map);
+ }
+
+ public static FloatTag fromNative(NBTTagFloat other) {
+ return new FloatTag(other.getFloat());
+ }
+
+ public static ShortTag fromNative(NBTTagShort other) {
+ return new ShortTag(other.getShort());
+ }
+
+ public static DoubleTag fromNative(NBTTagDouble other) {
+ return new DoubleTag(other.getDouble());
+ }
+
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/NMSHelper.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/NMSHelper.java
new file mode 100644
index 000000000..4917c1f68
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/NMSHelper.java
@@ -0,0 +1,50 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge.nms;
+
+import com.sk89q.worldedit.blocks.BaseItemStack;
+import com.sk89q.worldedit.entity.BaseEntity;
+import net.minecraft.item.Item;
+import net.minecraft.nbt.NBTTagCompound;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.item.inventory.ItemStack;
+
+import java.util.Map;
+
+@Deprecated
+public final class NMSHelper {
+
+ private NMSHelper() { }
+
+ public static ItemStack makeSpongeStack(BaseItemStack itemStack) {
+ net.minecraft.item.ItemStack newStack = new net.minecraft.item.ItemStack(Item.getItemById(itemStack.getType()), itemStack.getAmount(), itemStack.getData());
+ for (Map.Entry entry : itemStack.getEnchantments().entrySet()) {
+ newStack.addEnchantment(net.minecraft.enchantment.Enchantment.getEnchantmentById(entry.getKey()), entry.getValue());
+ }
+ return (ItemStack) (Object) newStack;
+ }
+
+ public static BaseEntity createBaseEntity(Entity entity) {
+ String id = entity.getType().getId();
+ NBTTagCompound tag = new NBTTagCompound();
+ ((net.minecraft.entity.Entity) entity).writeToNBT(tag);
+ return new BaseEntity(id, NBTConverter.fromNative(tag));
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/SpongeNMSWorld.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/SpongeNMSWorld.java
new file mode 100644
index 000000000..4e1c38d23
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/SpongeNMSWorld.java
@@ -0,0 +1,169 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge.nms;
+
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.MaxChangedBlocksException;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.blocks.LazyBlock;
+import com.sk89q.worldedit.entity.BaseEntity;
+import com.sk89q.worldedit.internal.Constants;
+import com.sk89q.worldedit.sponge.SpongeWorld;
+import com.sk89q.worldedit.util.TreeGenerator;
+import net.minecraft.block.*;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.init.Blocks;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagInt;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.BlockPos;
+import net.minecraft.world.gen.feature.*;
+import org.spongepowered.api.block.BlockState;
+import org.spongepowered.api.entity.Entity;
+import org.spongepowered.api.world.Location;
+import org.spongepowered.api.world.World;
+
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+@Deprecated
+public class SpongeNMSWorld extends SpongeWorld {
+
+ private static final IBlockState JUNGLE_LOG = Blocks.log.getDefaultState().withProperty(BlockOldLog.VARIANT, BlockPlanks.EnumType.JUNGLE);
+ private static final IBlockState JUNGLE_LEAF = Blocks.leaves.getDefaultState().withProperty(BlockOldLeaf.VARIANT, BlockPlanks.EnumType.JUNGLE).withProperty(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false));
+ private static final IBlockState JUNGLE_SHRUB = Blocks.leaves.getDefaultState().withProperty(BlockOldLeaf.VARIANT, BlockPlanks.EnumType.OAK).withProperty(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false));
+
+ /**
+ * Construct a new world.
+ *
+ * @param world the world
+ */
+ public SpongeNMSWorld(World world) {
+ super(world);
+ }
+
+ @Override
+ protected BlockState getBlockState(BaseBlock block) {
+ return (BlockState) Block.getBlockById(block.getId()).getStateFromMeta(block.getData());
+ }
+
+ private NBTTagCompound updateForSet(NBTTagCompound tag, Vector position) {
+ checkNotNull(tag);
+ checkNotNull(position);
+
+ tag.setTag("x", new NBTTagInt(position.getBlockX()));
+ tag.setTag("y", new NBTTagInt(position.getBlockY()));
+ tag.setTag("z", new NBTTagInt(position.getBlockZ()));
+
+ return tag;
+ }
+
+ @Override
+ protected void applyTileEntityData(org.spongepowered.api.block.tileentity.TileEntity entity, BaseBlock block) {
+ NBTTagCompound tag = NBTConverter.toNative(block.getNbtData());
+
+ Location loc = entity.getLocation();
+
+ updateForSet(tag, new Vector(loc.getX(), loc.getY(), loc.getZ()));
+ ((TileEntity) entity).readFromNBT(tag);
+ }
+
+ @Override
+ protected void applyEntityData(Entity entity, BaseEntity data) {
+ NBTTagCompound tag = NBTConverter.toNative(data.getNbtData());
+ for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
+ tag.removeTag(name);
+ }
+ ((net.minecraft.entity.Entity) entity).readFromNBT(tag);
+ }
+
+ @Override
+ public boolean clearContainerBlockContents(Vector position) {
+ BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
+ TileEntity tile =((net.minecraft.world.World) getWorld()).getTileEntity(pos);
+ if (tile instanceof IInventory) {
+ IInventory inv = (IInventory) tile;
+ int size = inv.getSizeInventory();
+ for (int i = 0; i < size; i++) {
+ inv.setInventorySlotContents(i, null);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Nullable
+ private static WorldGenerator createWorldGenerator(TreeGenerator.TreeType type) {
+ switch (type) {
+ case TREE: return new WorldGenTrees(true);
+ case BIG_TREE: return new WorldGenBigTree(true);
+ case REDWOOD: return new WorldGenTaiga2(true);
+ case TALL_REDWOOD: return new WorldGenTaiga1();
+ case BIRCH: return new WorldGenForest(true, false);
+ case JUNGLE: return new WorldGenMegaJungle(true, 10, 20, JUNGLE_LOG, JUNGLE_LEAF);
+ case SMALL_JUNGLE: return new WorldGenTrees(true, 4 + random.nextInt(7), JUNGLE_LOG, JUNGLE_LEAF, false);
+ case SHORT_JUNGLE: return new WorldGenTrees(true, 4 + random.nextInt(7), JUNGLE_LOG, JUNGLE_LEAF, true);
+ case JUNGLE_BUSH: return new WorldGenShrub(JUNGLE_LOG, JUNGLE_SHRUB);
+ case RED_MUSHROOM: return new WorldGenBigMushroom(Blocks.brown_mushroom_block);
+ case BROWN_MUSHROOM: return new WorldGenBigMushroom(Blocks.red_mushroom_block);
+ case SWAMP: return new WorldGenSwamp();
+ case ACACIA: return new WorldGenSavannaTree(true);
+ case DARK_OAK: return new WorldGenCanopyTree(true);
+ case MEGA_REDWOOD: return new WorldGenMegaPineTree(false, random.nextBoolean());
+ case TALL_BIRCH: return new WorldGenForest(true, true);
+ case RANDOM:
+ case PINE:
+ case RANDOM_REDWOOD:
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector pos) throws MaxChangedBlocksException {
+ WorldGenerator generator = createWorldGenerator(type);
+ return generator != null && generator.generate((net.minecraft.world.World) getWorld(), random, new BlockPos(pos.getX(), pos.getY(), pos.getZ()));
+ }
+
+ @Override
+ public BaseBlock getBlock(Vector position) {
+ World world = getWorld();
+ BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
+ IBlockState state = ((net.minecraft.world.World) world).getBlockState(pos);
+ TileEntity tile = ((net.minecraft.world.World) world).getTileEntity(pos);
+
+ if (tile != null) {
+ return new TileEntityBaseBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state), tile);
+ } else {
+ return new BaseBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state));
+ }
+ }
+
+ @Override
+ public BaseBlock getLazyBlock(Vector position) {
+ World world = getWorld();
+ BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
+ IBlockState state = ((net.minecraft.world.World) world).getBlockState(pos);
+ return new LazyBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state), this, position);
+ }
+}
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/TileEntityBaseBlock.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/TileEntityBaseBlock.java
new file mode 100644
index 000000000..524d680de
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/TileEntityBaseBlock.java
@@ -0,0 +1,41 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge.nms;
+
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.blocks.TileEntityBlock;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+
+@Deprecated
+public class TileEntityBaseBlock extends BaseBlock implements TileEntityBlock {
+
+ public TileEntityBaseBlock(int type, int data, TileEntity tile) {
+ super(type, data);
+ setNbtData(NBTConverter.fromNative(copyNbtData(tile)));
+ }
+
+ private static NBTTagCompound copyNbtData(TileEntity tile) {
+ NBTTagCompound tag = new NBTTagCompound();
+ tile.writeToNBT(tag);
+ return tag;
+ }
+
+}
\ No newline at end of file
diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/TileEntityUtils.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/TileEntityUtils.java
new file mode 100644
index 000000000..357cf53ad
--- /dev/null
+++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/nms/TileEntityUtils.java
@@ -0,0 +1,145 @@
+/*
+ * WorldEdit, a Minecraft world manipulation toolkit
+ * Copyright (C) sk89q
+ * 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 .
+ */
+
+package com.sk89q.worldedit.sponge.nms;
+
+import com.sk89q.worldedit.Vector;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagInt;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.BlockPos;
+import net.minecraft.world.World;
+
+import javax.annotation.Nullable;
+import java.lang.reflect.Constructor;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Utility methods for setting tile entities in the world.
+ */
+@Deprecated
+final class TileEntityUtils {
+
+ private TileEntityUtils() {
+ }
+
+ /**
+ * Update the given tag compound with position information.
+ *
+ * @param tag the tag
+ * @param position the position
+ * @return a tag compound
+ */
+ private static NBTTagCompound updateForSet(NBTTagCompound tag, Vector position) {
+ checkNotNull(tag);
+ checkNotNull(position);
+
+ tag.setTag("x", new NBTTagInt(position.getBlockX()));
+ tag.setTag("y", new NBTTagInt(position.getBlockY()));
+ tag.setTag("z", new NBTTagInt(position.getBlockZ()));
+
+ return tag;
+ }
+
+ /**
+ * Set a tile entity at the given location.
+ *
+ * @param world the world
+ * @param position the position
+ * @param clazz the tile entity class
+ * @param tag the tag for the tile entity (may be null to not set NBT data)
+ */
+ static void setTileEntity(World world, Vector position, Class extends TileEntity> clazz, @Nullable NBTTagCompound tag) {
+ checkNotNull(world);
+ checkNotNull(position);
+ checkNotNull(clazz);
+
+ TileEntity tileEntity = constructTileEntity(world, position, clazz);
+
+ if (tileEntity == null) {
+ return;
+ }
+
+ if (tag != null) {
+ // Set X, Y, Z
+ updateForSet(tag, position);
+ tileEntity.readFromNBT(tag);
+ }
+
+ world.setTileEntity(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()), tileEntity);
+ }
+
+ /**
+ * Set a tile entity at the given location using the tile entity ID from
+ * the tag.
+ *
+ * @param world the world
+ * @param position the position
+ * @param tag the tag for the tile entity (may be null to do nothing)
+ */
+ static void setTileEntity(World world, Vector position, @Nullable NBTTagCompound tag) {
+ if (tag != null) {
+ updateForSet(tag, position);
+ TileEntity tileEntity = TileEntity.createAndLoadEntity(tag);
+ if (tileEntity != null) {
+ world.setTileEntity(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()), tileEntity);
+ }
+ }
+ }
+
+ /**
+ * Construct a tile entity from the given class.
+ *
+ * @param world the world
+ * @param position the position
+ * @param clazz the class
+ * @return a tile entity (may be null if it failed)
+ */
+ @Nullable
+ static TileEntity constructTileEntity(World world, Vector position, Class extends TileEntity> clazz) {
+ Constructor extends TileEntity> baseConstructor;
+ try {
+ baseConstructor = clazz.getConstructor(); // creates "blank" TE
+ } catch (Throwable e) {
+ return null; // every TE *should* have this constructor, so this isn't necessary
+ }
+
+ TileEntity genericTE;
+ try {
+ // Downcast here for return while retaining the type
+ genericTE = (TileEntity) baseConstructor.newInstance();
+ } catch (Throwable e) {
+ return null;
+ }
+
+ /*
+ genericTE.blockType = Block.blocksList[block.getId()];
+ genericTE.blockMetadata = block.getData();
+ genericTE.xCoord = pt.getBlockX();
+ genericTE.yCoord = pt.getBlockY();
+ genericTE.zCoord = pt.getBlockZ();
+ genericTE.worldObj = world;
+ */ // handled by internal code
+
+ return genericTE;
+ }
+
+
+}