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 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 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 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 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 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 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 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 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 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 clazz) { + Constructor 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; + } + + +}