From e68530e8f80faa727fce1ea79f954a5db63a1241 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Mon, 5 Sep 2016 01:34:38 +1000 Subject: [PATCH] FAWE for MCPE (WIP) --- build.gradle | 1 + forge194/build.gradle | 1 - nukkit/build.gradle | 31 ++ .../boydti/fawe/nukkit/core/CommandInfo.java | 41 +++ .../nukkit/core/DynamicPluginCommand.java | 54 ++++ .../boydti/fawe/nukkit/core/NBTConverter.java | 244 ++++++++++++++ .../nukkit/core/NukkitCommandManager.java | 27 ++ .../fawe/nukkit/core/NukkitConfiguration.java | 48 +++ .../boydti/fawe/nukkit/core/NukkitEntity.java | 109 +++++++ .../fawe/nukkit/core/NukkitEntityType.java | 124 ++++++++ .../fawe/nukkit/core/NukkitPlatform.java | 174 ++++++++++ .../boydti/fawe/nukkit/core/NukkitPlayer.java | 245 ++++++++++++++ .../nukkit/core/NukkitPlayerBlockBag.java | 206 ++++++++++++ .../fawe/nukkit/core/NukkitTaskManager.java | 67 ++++ .../boydti/fawe/nukkit/core/NukkitUtil.java | 139 ++++++++ .../boydti/fawe/nukkit/core/NukkitWorld.java | 299 ++++++++++++++++++ .../fawe/nukkit/core/NukkitWorldEdit.java | 130 ++++++++ .../fawe/nukkit/core/WorldEditListener.java | 145 +++++++++ settings.gradle | 2 +- 19 files changed, 2085 insertions(+), 2 deletions(-) create mode 100644 nukkit/build.gradle create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/CommandInfo.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/DynamicPluginCommand.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NBTConverter.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitCommandManager.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitConfiguration.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitEntity.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitEntityType.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlatform.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlayer.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlayerBlockBag.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitTaskManager.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitUtil.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitWorld.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitWorldEdit.java create mode 100644 nukkit/src/main/java/com/boydti/fawe/nukkit/core/WorldEditListener.java diff --git a/build.gradle b/build.gradle index e6b810a0..83a6bba2 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,7 @@ subprojects { repositories { mavenCentral() + maven {url "http://ci.regularbox.com/plugin/repository/everything/"} maven {url "http://empcraft.com/maven2"} maven {url "http://repo.mcstats.org/content/repositories/public"} maven {url "https://hub.spigotmc.org/nexus/content/groups/public/"} diff --git a/forge194/build.gradle b/forge194/build.gradle index d5b75327..6e784a01 100644 --- a/forge194/build.gradle +++ b/forge194/build.gradle @@ -17,7 +17,6 @@ apply plugin: 'com.github.johnrengelman.shadow' dependencies { compile project(':core') - compile 'org.spongepowered:spongeapi:4.+' compile 'com.sk89q.worldedit:worldedit-forge-mc1.8.9:6.1.1' } diff --git a/nukkit/build.gradle b/nukkit/build.gradle new file mode 100644 index 00000000..767af849 --- /dev/null +++ b/nukkit/build.gradle @@ -0,0 +1,31 @@ +dependencies { + compile project(':core') + compile 'cn.nukkit:nukkit:1.0-SNAPSHOT' +} + +processResources { + from('src/main/resources') { + include 'plugin.yml' + expand( + name: project.parent.name, + version: project.parent.version + ) + } +} + +apply plugin: 'com.github.johnrengelman.shadow' +// We only want the shadow jar produced +jar.enabled = false +shadowJar { + dependencies { + include(dependency(':core')) + } + archiveName = "${parent.name}-${project.name}-${parent.version}.jar" + destinationDir = file '../target' +} +shadowJar.doLast { + task -> + ant.checksum file: task.archivePath +} + +build.dependsOn(shadowJar); \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/CommandInfo.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/CommandInfo.java new file mode 100644 index 00000000..197300b6 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/CommandInfo.java @@ -0,0 +1,41 @@ +package com.boydti.fawe.nukkit.core; + + +public class CommandInfo { + + private final String[] aliases; + private final String usage, desc; + private final String[] permissions; + + public CommandInfo(String usage, String desc, String[] aliases) { + this(usage, desc, aliases, null); + } + + public CommandInfo(String usage, String desc, String[] aliases, String[] permissions) { + this.usage = usage; + this.desc = desc; + this.aliases = aliases; + this.permissions = permissions; + } + + public String[] getAliases() { + return aliases; + } + + public String getName() { + return aliases[0]; + } + + public String getUsage() { + return usage; + } + + public String getDesc() { + return desc; + } + + public String[] getPermissions() { + return permissions; + } + +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/DynamicPluginCommand.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/DynamicPluginCommand.java new file mode 100644 index 00000000..366f1b10 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/DynamicPluginCommand.java @@ -0,0 +1,54 @@ +package com.boydti.fawe.nukkit.core; + +import cn.nukkit.command.Command; +import cn.nukkit.command.CommandExecutor; +import cn.nukkit.command.CommandSender; +import cn.nukkit.command.PluginIdentifiableCommand; +import cn.nukkit.plugin.Plugin; +import com.sk89q.util.StringUtil; + +/** + * An implementation of a dynamically registered {@link org.bukkit.command.Command} attached to a plugin + */ +public class DynamicPluginCommand extends Command implements PluginIdentifiableCommand { + + protected final CommandExecutor owner; + private final Plugin plugin; + protected String[] permissions = new String[0]; + + public DynamicPluginCommand(String[] aliases, String desc, String usage, CommandExecutor owner, Plugin plugin) { + super(aliases[0], desc, usage, aliases); + this.owner = owner; + this.plugin = plugin; + } + + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + return owner.onCommand(sender, this, label, args); + } + + public Object getOwner() { + return owner; + } + + public void setPermissions(String[] permissions) { + this.permissions = permissions; + if (permissions != null) { + super.setPermission(StringUtil.joinString(permissions, ";")); + } + } + + public String[] getPermissions() { + return permissions; + } + + @Override + public boolean testPermissionSilent(CommandSender sender) { + return super.testPermissionSilent(sender); + } + + @Override + public Plugin getPlugin() { + return plugin; + } +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NBTConverter.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NBTConverter.java new file mode 100644 index 00000000..8d8ea45d --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NBTConverter.java @@ -0,0 +1,244 @@ +package com.boydti.fawe.nukkit.core; + + +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntArrayTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Converts between Jcn.nukkit.nbt.tag. and Minecraft cn.nukkit.nbt.tag. classes. + */ +final class NBTConverter { + + private NBTConverter() { + } + +// private static Tag fromNativeSlow(cn.nukkit.nbt.tag.CompoundTag other) { +// try { +// byte[] bytes = NBTIO.write(other); +// FastByteArrayInputStream in = new FastByteArrayInputStream(bytes); +// NBTInputStream nin = new NBTInputStream(in); +// return nin.readNamedTag(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + + public static CompoundTag fromNative(cn.nukkit.nbt.tag.CompoundTag other) { + Map tags = other.getTags(); + Map map = new HashMap(); + for (Entry entry : tags.entrySet()) { + map.put(entry.getKey(), fromNative(entry.getValue())); + } + return new CompoundTag(map); + } + + public static cn.nukkit.nbt.tag.Tag 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()); + } + } + + private static cn.nukkit.nbt.tag.IntArrayTag toNative(IntArrayTag tag) { + int[] value = tag.getValue(); + return new cn.nukkit.nbt.tag.IntArrayTag("", Arrays.copyOf(value, value.length)); + } + + private static cn.nukkit.nbt.tag.ListTag toNative(ListTag tag) { + cn.nukkit.nbt.tag.ListTag list = new cn.nukkit.nbt.tag.ListTag(); + for (Tag child : tag.getValue()) { + if (child instanceof EndTag) { + continue; + } + list.add(toNative(child)); + } + return list; + } + + private static cn.nukkit.nbt.tag.LongTag toNative(LongTag tag) { + return new cn.nukkit.nbt.tag.LongTag("", tag.getValue()); + } + + private static cn.nukkit.nbt.tag.StringTag toNative(StringTag tag) { + return new cn.nukkit.nbt.tag.StringTag("", tag.getValue()); + } + + private static cn.nukkit.nbt.tag.IntTag toNative(IntTag tag) { + return new cn.nukkit.nbt.tag.IntTag("", tag.getValue()); + } + + private static cn.nukkit.nbt.tag.ByteTag toNative(ByteTag tag) { + return new cn.nukkit.nbt.tag.ByteTag("", tag.getValue()); + } + + private static cn.nukkit.nbt.tag.ByteArrayTag toNative(ByteArrayTag tag) { + byte[] value = tag.getValue(); + return new cn.nukkit.nbt.tag.ByteArrayTag("", Arrays.copyOf(value, value.length)); + } + + private static cn.nukkit.nbt.tag.CompoundTag toNative(CompoundTag tag) { + cn.nukkit.nbt.tag.CompoundTag compound = new cn.nukkit.nbt.tag.CompoundTag(); + for (Entry child : tag.getValue().entrySet()) { + compound.put(child.getKey(), toNative(child.getValue())); + } + return compound; + } + + private static cn.nukkit.nbt.tag.FloatTag toNative(FloatTag tag) { + return new cn.nukkit.nbt.tag.FloatTag("", tag.getValue()); + } + + private static cn.nukkit.nbt.tag.ShortTag toNative(ShortTag tag) { + return new cn.nukkit.nbt.tag.ShortTag("", tag.getValue()); + } + + private static cn.nukkit.nbt.tag.DoubleTag toNative(DoubleTag tag) { + return new cn.nukkit.nbt.tag.DoubleTag("", tag.getValue()); + } + + private static Tag fromNative(cn.nukkit.nbt.tag.Tag other) { + if (other instanceof cn.nukkit.nbt.tag.IntArrayTag) { + return fromNative((cn.nukkit.nbt.tag.IntArrayTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.ListTag) { + return fromNative((cn.nukkit.nbt.tag.ListTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.EndTag) { + return fromNative((cn.nukkit.nbt.tag.EndTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.LongTag) { + return fromNative((cn.nukkit.nbt.tag.LongTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.StringTag) { + return fromNative((cn.nukkit.nbt.tag.StringTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.IntTag) { + return fromNative((cn.nukkit.nbt.tag.IntTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.ByteTag) { + return fromNative((cn.nukkit.nbt.tag.ByteTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.ByteArrayTag) { + return fromNative((cn.nukkit.nbt.tag.ByteArrayTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.CompoundTag) { + return fromNative((cn.nukkit.nbt.tag.CompoundTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.FloatTag) { + return fromNative((cn.nukkit.nbt.tag.FloatTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.ShortTag) { + return fromNative((cn.nukkit.nbt.tag.ShortTag) other); + + } else if (other instanceof cn.nukkit.nbt.tag.DoubleTag) { + return fromNative((cn.nukkit.nbt.tag.DoubleTag) other); + } else { + throw new IllegalArgumentException("Can't convert other of type " + other.getClass().getCanonicalName()); + } + } + + private static IntArrayTag fromNative(cn.nukkit.nbt.tag.IntArrayTag other) { + int[] value = other.data; + return new IntArrayTag(Arrays.copyOf(value, value.length)); + } + + private static ListTag fromNative(cn.nukkit.nbt.tag.ListTag other) { + other = (cn.nukkit.nbt.tag.ListTag) other.copy(); + List list = new ArrayList(); + Class listClass = StringTag.class; + int tags = other.size(); + for (int i = 0; i < tags; i++) { + Tag child = fromNative(other.get(0)); + other.remove(0); + list.add(child); + listClass = child.getClass(); + } + return new ListTag(listClass, list); + } + + private static EndTag fromNative(cn.nukkit.nbt.tag.EndTag other) { + return new EndTag(); + } + + private static LongTag fromNative(cn.nukkit.nbt.tag.LongTag other) { + return new LongTag(other.data); + } + + private static StringTag fromNative(cn.nukkit.nbt.tag.StringTag other) { + return new StringTag(other.data); + } + + private static IntTag fromNative(cn.nukkit.nbt.tag.IntTag other) { + return new IntTag(other.data); + } + + private static ByteTag fromNative(cn.nukkit.nbt.tag.ByteTag other) { + return new ByteTag((byte) other.data); + } + + private static ByteArrayTag fromNative(cn.nukkit.nbt.tag.ByteArrayTag other) { + byte[] value = other.data; + return new ByteArrayTag(Arrays.copyOf(value, value.length)); + } + + private static FloatTag fromNative(cn.nukkit.nbt.tag.FloatTag other) { + return new FloatTag(other.data); + } + + private static ShortTag fromNative(cn.nukkit.nbt.tag.ShortTag other) { + return new ShortTag((short) other.data); + } + + private static DoubleTag fromNative(cn.nukkit.nbt.tag.DoubleTag other) { + return new DoubleTag(other.data); + } + +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitCommandManager.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitCommandManager.java new file mode 100644 index 00000000..4ff6299c --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitCommandManager.java @@ -0,0 +1,27 @@ +package com.boydti.fawe.nukkit.core; + +import cn.nukkit.command.CommandExecutor; +import cn.nukkit.command.SimpleCommandMap; +import cn.nukkit.plugin.Plugin; + +public class NukkitCommandManager { + private final SimpleCommandMap commandMap; + + public NukkitCommandManager(SimpleCommandMap map) { + this.commandMap = map; + } + + public boolean register(CommandInfo command, Plugin plugin, CommandExecutor executor) { + if (command == null || commandMap == null) { + return false; + } + DynamicPluginCommand cmd = new DynamicPluginCommand( + command.getAliases(), + command.getDesc(), "/" + command.getAliases()[0] + " " + command.getUsage(), + executor, + plugin); + cmd.setPermissions(command.getPermissions()); + commandMap.register(plugin.getDescription().getName(), cmd); + return true; + } +} diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitConfiguration.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitConfiguration.java new file mode 100644 index 00000000..b4c1b041 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitConfiguration.java @@ -0,0 +1,48 @@ +package com.boydti.fawe.nukkit.core; + +import com.sk89q.util.yaml.YAMLProcessor; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.util.YAMLConfiguration; +import java.io.File; + +public class NukkitConfiguration extends YAMLConfiguration { + + public boolean noOpPermissions = false; + private final WorldEditPlugin plugin; + + public NukkitConfiguration(YAMLProcessor config, WorldEditPlugin plugin) { + super(config, plugin.getLogger()); + this.plugin = plugin; + } + + @Override + public void load() { + super.load(); + noOpPermissions = config.getBoolean("no-op-permissions", false); + migrateLegacyFolders(); + } + + private void migrateLegacyFolders() { + migrate(scriptsDir, "craftscripts"); + migrate(saveDir, "schematics"); + migrate("drawings", "draw.js images"); + } + + private void migrate(String file, String name) { + File fromDir = new File(".", file); + File toDir = new File(getWorkingDirectory(), file); + if (fromDir.exists() & !toDir.exists()) { + if (fromDir.renameTo(toDir)) { + plugin.getLogger().info("Migrated " + name + " folder '" + file + + "' from server root to plugin data folder."); + } else { + plugin.getLogger().warning("Error while migrating " + name + " folder!"); + } + } + } + + @Override + public File getWorkingDirectory() { + return plugin.getDataFolder(); + } +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitEntity.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitEntity.java new file mode 100644 index 00000000..70815044 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitEntity.java @@ -0,0 +1,109 @@ +/* + * 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.boydti.fawe.nukkit.core; + +import cn.nukkit.entity.Entity; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.entity.metadata.EntityType; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.NullWorld; +import java.lang.ref.WeakReference; +import javax.annotation.Nullable; + + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An adapter to adapt a Bukkit entity into a WorldEdit one. + */ +class NukkitEntity implements com.sk89q.worldedit.entity.Entity { + + private final WeakReference entityRef; + + /** + * Create a new instance. + * + * @param entity the entity + */ + NukkitEntity(cn.nukkit.entity.Entity entity) { + checkNotNull(entity); + this.entityRef = new WeakReference(entity); + } + + @Override + public Extent getExtent() { + Entity entity = entityRef.get(); + if (entity != null) { + return new NukkitWorld(entity.getLevel()); + } else { + return NullWorld.getInstance(); + } + } + + @Override + public Location getLocation() { + Entity entity = entityRef.get(); + if (entity != null) { + return NukkitUtil.toLocation(entity.getLocation()); + } else { + return new Location(NullWorld.getInstance()); + } + } + + @Override + public BaseEntity getState() { + Entity entity = entityRef.get(); + if (entity != null) { + if (entity instanceof Player) { + return null; + } + CompoundTag tag = NBTConverter.fromNative(entity.namedTag); + return new BaseEntity(entity.getSaveId(), tag); + } else { + return null; + } + } + + @Override + public boolean remove() { + Entity entity = entityRef.get(); + if (entity != null) { + entity.kill(); + return !entity.isAlive(); + } else { + return true; + } + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + public T getFacet(Class cls) { + Entity entity = entityRef.get(); + if (entity != null && EntityType.class.isAssignableFrom(cls)) { + return (T) new NukkitEntityType(entity); + } else { + return null; + } + } +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitEntityType.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitEntityType.java new file mode 100644 index 00000000..c09a7ec1 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitEntityType.java @@ -0,0 +1,124 @@ +package com.boydti.fawe.nukkit.core; + +import cn.nukkit.Player; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.EntityLiving; +import cn.nukkit.entity.item.EntityBoat; +import cn.nukkit.entity.item.EntityFallingBlock; +import cn.nukkit.entity.item.EntityItem; +import cn.nukkit.entity.item.EntityMinecartEmpty; +import cn.nukkit.entity.item.EntityPainting; +import cn.nukkit.entity.item.EntityPrimedTNT; +import cn.nukkit.entity.item.EntityXPOrb; +import cn.nukkit.entity.passive.EntityAnimal; +import cn.nukkit.entity.passive.EntityNPC; +import cn.nukkit.entity.passive.EntityTameable; +import cn.nukkit.entity.projectile.EntityProjectile; +import com.sk89q.worldedit.entity.metadata.EntityType; + + +import static com.google.common.base.Preconditions.checkNotNull; + +public class NukkitEntityType implements EntityType { + + private final Entity entity; + + public NukkitEntityType(Entity entity) { + checkNotNull(entity); + this.entity = entity; + } + + @Override + public boolean isPlayerDerived() { + return entity instanceof Player; + } + + @Override + public boolean isProjectile() { + return entity instanceof EntityProjectile; + } + + @Override + public boolean isItem() { + return entity instanceof EntityItem; + } + + @Override + public boolean isFallingBlock() { + return entity instanceof EntityFallingBlock; + } + + @Override + public boolean isPainting() { + return entity instanceof EntityPainting; + } + + @Override + public boolean isItemFrame() { + // No item frames on MCPE + return false; + } + + @Override + public boolean isBoat() { + return entity instanceof EntityBoat; + } + + @Override + public boolean isMinecart() { + return entity instanceof EntityMinecartEmpty; + } + + @Override + public boolean isTNT() { + return entity instanceof EntityPrimedTNT; + } + + @Override + public boolean isExperienceOrb() { + return entity instanceof EntityXPOrb; + } + + @Override + public boolean isLiving() { + return entity instanceof EntityLiving; + } + + @Override + public boolean isAnimal() { + return entity instanceof EntityAnimal; + } + + @Override + public boolean isAmbient() { + // No bats implemented on MCPE + return false; + } + + @Override + public boolean isNPC() { + return entity instanceof EntityNPC; + } + + @Override + public boolean isGolem() { + // No golem on MCPE + return false; + } + + @Override + public boolean isTamed() { + return entity instanceof EntityTameable && ((EntityTameable) entity).isTamed(); + } + + @Override + public boolean isTagged() { + return entity.hasCustomName(); + } + + @Override + public boolean isArmorStand() { + // No armor stand + return false; + } +} diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlatform.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlatform.java new file mode 100644 index 00000000..0f111df4 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlatform.java @@ -0,0 +1,174 @@ +/* + * 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.boydti.fawe.nukkit.core; + +import cn.nukkit.item.Item; +import cn.nukkit.level.Level; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.AbstractPlatform; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.MultiUserPlatform; +import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.util.command.CommandMapping; +import com.sk89q.worldedit.util.command.Description; +import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.world.World; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nullable; + +class NukkitPlatform extends AbstractPlatform implements MultiUserPlatform { + + private final NukkitWorldEdit mod; + private boolean hookingEvents = false; + private NukkitCommandManager commandManager; + + NukkitPlatform(NukkitWorldEdit mod) { + this.mod = mod; + this.commandManager = new NukkitCommandManager(mod.getServer().getCommandMap()); + } + + boolean isHookingEvents() { + return hookingEvents; + } + + @Override + public int resolveItem(String name) { + Item item = Item.fromString(name); + return item == null ? 0 : item.getId(); + } + + public NukkitWorldEdit getMod() { + return mod; + } + + @Override + public boolean isValidMobType(String type) { + System.out.print("Not implemented: isValidMobType"); + return true; + } + + @Override + public void reload() { + getConfiguration().load(); + } + + @Override + public int schedule(long delay, long period, Runnable task) { + TaskManager.IMP.repeat(task, (int) period); + return 0; // TODO This isn't right, but we only check for -1 values + } + + @Override + public List getWorlds() { + Collection levels = mod.getServer().getLevels().values(); + List ret = new ArrayList<>(levels.size()); + for (Level level : levels) { + ret.add(new NukkitWorld(level)); + } + return ret; + } + + @Nullable + @Override + public Player matchPlayer(Player player) { + if (player instanceof NukkitPlayer) { + return player; + } else { + cn.nukkit.Player currentPlayer = mod.getServer().getPlayer(player.getName()); + return currentPlayer != null ? new NukkitPlayer(this, currentPlayer) : null; + } + } + + @Nullable + @Override + public World matchWorld(World world) { + if (world instanceof NukkitWorld) { + return world; + } else { + Level level = NukkitWorldEdit.inst().getServer().getLevelByName(world.getName()); + return level != null ? new NukkitWorld(level) : null; + } + } + + @Override + public void registerCommands(Dispatcher dispatcher) { + for (CommandMapping command : dispatcher.getCommands()) { + Description description = command.getDescription(); + List permissions = description.getPermissions(); + String[] permissionsArray = new String[permissions.size()]; + permissions.toArray(permissionsArray); + commandManager.register(new CommandInfo(description.getUsage(), description.getDescription(), command.getAllAliases(), permissionsArray), mod, mod); + } + } + + @Override + public void registerGameHooks() { + // We registered the events already anyway, so we just 'turn them on' + hookingEvents = true; + } + + @Override + public NukkitConfiguration getConfiguration() { + return mod.getWEConfig(); + } + + @Override + public String getVersion() { + return mod.getInternalVersion(); + } + + @Override + public String getPlatformName() { + return "Nukkit-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 (Map.Entry entry : mod.getServer().getOnlinePlayers().entrySet()) { + users.add(new NukkitPlayer(this, entry.getValue())); + } + return users; + } +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlayer.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlayer.java new file mode 100644 index 00000000..6a1a12fb --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlayer.java @@ -0,0 +1,245 @@ +/* + * 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.boydti.fawe.nukkit.core; + +import cn.nukkit.Player; +import cn.nukkit.inventory.PlayerInventory; +import cn.nukkit.item.Item; +import cn.nukkit.level.Location; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.internal.cui.CUIEvent; +import com.sk89q.worldedit.session.SessionKey; +import java.util.UUID; +import javax.annotation.Nullable; + +public class NukkitPlayer extends LocalPlayer { + + private final NukkitPlatform platform; + private Player player; + + public NukkitPlayer(NukkitPlatform platform, Player player) { + this.platform = platform; + this.player = player; + } + + @Override + public UUID getUniqueId() { + return player.getUniqueId(); + } + + @Override + public int getItemInHand() { + PlayerInventory inv = player.getInventory(); + Item itemStack = inv.getItemInHand(); + return itemStack != null ? itemStack.getId() : 0; + } + + @Override + public BaseBlock getBlockInHand() throws WorldEditException { + PlayerInventory inv = player.getInventory(); + Item itemStack = inv.getItemInHand(); + if (itemStack == null) { + return new BaseBlock(0, 0); + } + return new BaseBlock(itemStack.getId(), itemStack.getDamage()); + } + + @Override + public String getName() { + return player.getName(); + } + + @Override + public WorldVector getPosition() { + Location loc = player.getLocation(); + return new WorldVector(NukkitUtil.getLocalWorld(loc.getLevel()), + loc.getX(), loc.getY(), loc.getZ()); + } + + @Override + public double getPitch() { + return player.getLocation().getPitch(); + } + + @Override + public double getYaw() { + return player.getLocation().getYaw(); + } + + @Override + public void giveItem(int type, int amt) { + player.getInventory().addItem(new Item(type, 0, amt)); + } + + @Override + public void printRaw(String msg) { + for (String part : msg.split("\n")) { + player.sendMessage(part); + } + } + + @Override + public void print(String msg) { + for (String part : msg.split("\n")) { + player.sendMessage("\u00A7d" + part); + } + } + + @Override + public void printDebug(String msg) { + for (String part : msg.split("\n")) { + player.sendMessage("\u00A77" + part); + } + } + + @Override + public void printError(String msg) { + for (String part : msg.split("\n")) { + player.sendMessage("\u00A7c" + part); + } + } + + @Override + public void setPosition(Vector pos, float pitch, float yaw) { + player.teleport(new Location(pos.getX(), pos.getY(), pos.getZ(), yaw, pitch, player.getLevel())); + } + + @Override + public String[] getGroups() { + // Is this ever used? + return new String[0]; + } + + @Override + public BlockBag getInventoryBlockBag() { + return new NukkitPlayerBlockBag(player); + } + + @Override + public boolean hasPermission(String perm) { + NukkitConfiguration config = platform.getMod().getWEConfig(); + return (!config.noOpPermissions && player.isOp()) || player.hasPermission(perm); + } + + @Override + public LocalWorld getWorld() { + return NukkitUtil.getLocalWorld(player.getLevel()); + } + + @Override + public void dispatchCUIEvent(CUIEvent event) { + // No WE-CUI on MCPE + return; + } + + public Player getPlayer() { + return player; + } + + @Override + public boolean hasCreativeMode() { + return player.getGamemode() == 1; + } + + @Override + public void floatAt(int x, int y, int z, boolean alwaysGlass) { + if (alwaysGlass || !player.getAllowFlight()) { + super.floatAt(x, y, z, alwaysGlass); + return; + } + + setPosition(new Vector(x + 0.5, y, z + 0.5)); + player.getAdventureSettings().setCanFly(true); + player.getAdventureSettings().update(); + } + + @Override + public BaseEntity getState() { + throw new UnsupportedOperationException("Cannot create a state from this object"); + } + + @Override + public com.sk89q.worldedit.util.Location getLocation() { + Location nativeLocation = player.getLocation(); + Vector position = NukkitUtil.toVector(nativeLocation); + return new com.sk89q.worldedit.util.Location( + getWorld(), + position, + (float) nativeLocation.getYaw(), + (float) nativeLocation.getPitch()); + } + + @Nullable + @Override + public T getFacet(Class cls) { + return null; + } + + @Override + public SessionKey getSessionKey() { + return new SessionKeyImpl(this.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() { + // This is a thread safe call on CraftNukkit because it uses a + // CopyOnWrite list for the list of players, but the Nukkit + // specification doesn't require thread safety (though the + // spec is extremely incomplete) + return NukkitWorldEdit.inst().getServer().getPlayer(name) != null; + } + + @Override + public boolean isPersistent() { + return true; + } + + } + +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlayerBlockBag.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlayerBlockBag.java new file mode 100644 index 00000000..2166ceab --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitPlayerBlockBag.java @@ -0,0 +1,206 @@ +/* + * 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.boydti.fawe.nukkit.core; + +import cn.nukkit.Player; +import cn.nukkit.inventory.PlayerInventory; +import cn.nukkit.item.Item; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.blocks.ItemType; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.extent.inventory.BlockBagException; +import com.sk89q.worldedit.extent.inventory.OutOfBlocksException; +import com.sk89q.worldedit.extent.inventory.OutOfSpaceException; +import java.util.Map; + +public class NukkitPlayerBlockBag extends BlockBag { + + private Player player; + private Map items; + private int size; + + /** + * Construct the object. + * + * @param player the player + */ + public NukkitPlayerBlockBag(Player player) { + this.player = player; + } + + /** + * Loads inventory on first use. + */ + private void loadInventory() { + if (items == null) { + PlayerInventory inv = player.getInventory(); + items = inv.getContents(); + this.size = inv.getSize(); + } + } + + /** + * Get the player. + * + * @return the player + */ + public Player getPlayer() { + return player; + } + + @Override + public void fetchItem(BaseItem item) throws BlockBagException { + final int id = item.getType(); + final int damage = item.getData(); + int amount = (item instanceof BaseItemStack) ? ((BaseItemStack) item).getAmount() : 1; + assert(amount == 1); + boolean usesDamageValue = ItemType.usesDamageValue(id); + + if (id == BlockID.AIR) { + throw new IllegalArgumentException("Can't fetch air block"); + } + + loadInventory(); + + boolean found = false; + + for (Map.Entry entry : items.entrySet()) { + int slot = entry.getKey(); + Item nukkitItem = entry.getValue(); + if (nukkitItem == null) { + continue; + } + if (nukkitItem.getId() != id) { + // Type id doesn't fit + continue; + } + if (usesDamageValue && nukkitItem.getDamage() != damage) { + // Damage value doesn't fit. + continue; + } + + int currentCount = nukkitItem.getCount(); + if (currentCount < 0) { + // Unlimited + return; + } + + if (currentCount > 1) { + nukkitItem.setCount(currentCount - 1); + found = true; + } else { + items.remove(slot); + found = true; + } + + break; + } + + if (!found) { + throw new OutOfBlocksException(); + } + } + + @Override + public void storeItem(BaseItem item) throws BlockBagException { + final int id = item.getType(); + final int damage = item.getData(); + int amount = (item instanceof BaseItemStack) ? ((BaseItemStack) item).getAmount() : 1; + assert(amount <= 64); + boolean usesDamageValue = ItemType.usesDamageValue(id); + + if (id == BlockID.AIR) { + throw new IllegalArgumentException("Can't store air block"); + } + + loadInventory(); + + int freeSlot = -1; + for (int slot = 0; slot < size; ++slot) { + Item nukkitItem = items.get(slot); + + if (nukkitItem == null) { + // Delay using up a free slot until we know there are no stacks + // of this item to merge into + + if (freeSlot == -1) { + freeSlot = slot; + } + continue; + } + + if (nukkitItem.getId() != id) { + // Type id doesn't fit + continue; + } + + if (usesDamageValue && nukkitItem.getDamage() != damage) { + // Damage value doesn't fit. + continue; + } + + int currentCount = nukkitItem.getCount(); + if (currentCount < 0) { + // Unlimited + return; + } + if (currentCount >= 64) { + // Full stack + continue; + } + + int spaceLeft = 64 - currentCount; + if (spaceLeft >= amount) { + nukkitItem.setCount(currentCount + amount); + return; + } + + nukkitItem.setCount(64); + amount -= spaceLeft; + } + + if (freeSlot > -1) { + items.put(freeSlot, new Item(id, 0, amount)); + return; + } + + throw new OutOfSpaceException(id); + } + + @Override + public void flushChanges() { + if (items != null) { + player.getInventory().setContents(items); + items = null; + } + } + + @Override + public void addSourcePosition(WorldVector pos) { + } + + @Override + public void addSingleSourcePosition(WorldVector pos) { + } + +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitTaskManager.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitTaskManager.java new file mode 100644 index 00000000..6741feed --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitTaskManager.java @@ -0,0 +1,67 @@ +package com.boydti.fawe.nukkit.core; + +import cn.nukkit.plugin.Plugin; +import cn.nukkit.scheduler.TaskHandler; +import com.boydti.fawe.util.TaskManager; +import java.util.HashMap; +import org.apache.commons.lang.mutable.MutableInt; + +public class NukkitTaskManager extends TaskManager { + + private final Plugin plugin; + + public NukkitTaskManager(final Plugin plugin) { + this.plugin = plugin; + } + + @Override + public int repeat(final Runnable r, final int interval) { + TaskHandler task = this.plugin.getServer().getScheduler().scheduleRepeatingTask(r, interval, false); + return task.getTaskId(); + } + + @Override + public int repeatAsync(final Runnable r, final int interval) { + TaskHandler task = this.plugin.getServer().getScheduler().scheduleRepeatingTask(r, interval, true); + return task.getTaskId(); + } + + public MutableInt index = new MutableInt(0); + public HashMap tasks = new HashMap<>(); + + @Override + public void async(final Runnable r) { + if (r == null) { + return; + } + this.plugin.getServer().getScheduler().scheduleTask(r, true); + } + + @Override + public void task(final Runnable r) { + if (r == null) { + return; + } + this.plugin.getServer().getScheduler().scheduleTask(r, false); + } + + @Override + public void later(final Runnable r, final int delay) { + if (r == null) { + return; + } + this.plugin.getServer().getScheduler().scheduleDelayedTask(r, delay); + } + + @Override + public void laterAsync(final Runnable r, final int delay) { + this.plugin.getServer().getScheduler().scheduleDelayedTask(r, delay, true); + } + + @Override + public void cancel(final int task) { + if (task != -1) { + this.plugin.getServer().getScheduler().cancelTask(task); + } + } +} diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitUtil.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitUtil.java new file mode 100644 index 00000000..8ffa0094 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitUtil.java @@ -0,0 +1,139 @@ +/* + * LevelEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) LevelEdit 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.boydti.fawe.nukkit.core; + +import cn.nukkit.Player; +import cn.nukkit.Server; +import cn.nukkit.block.Block; +import cn.nukkit.entity.Entity; +import cn.nukkit.level.Level; +import cn.nukkit.math.Vector3; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockWorldVector; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldVector; + +public final class NukkitUtil { + + private NukkitUtil() { + } + + public static LocalWorld getLocalWorld(Level w) { + return new NukkitWorld(w); + } + + public static BlockVector toVector(Block block) { + return new BlockVector(block.getX(), block.getY(), block.getZ()); + } + + public static BlockWorldVector toWorldVector(Block block) { + return new BlockWorldVector(getLocalWorld(block.getLevel()), block.getX(), block.getY(), block.getZ()); + } + + public static Vector toVector(cn.nukkit.level.Location loc) { + return new Vector(loc.getX(), loc.getY(), loc.getZ()); + } + + public static Location toLocation(cn.nukkit.level.Location loc) { + return new Location( + getLocalWorld(loc.getLevel()), + new Vector(loc.getX(), loc.getY(), loc.getZ()), + (float) loc.getYaw(), (float) loc.getPitch() + ); + } + + public static Vector toVector(Vector3 vector) { + return new Vector(vector.getX(), vector.getY(), vector.getZ()); + } + + public static cn.nukkit.level.Location toLocation(WorldVector pt) { + return new cn.nukkit.level.Location(pt.getX(), pt.getY(), pt.getZ(), 0, 0, toLevel(pt)); + } + + public static cn.nukkit.level.Location toLocation(Level world, Vector pt) { + return new cn.nukkit.level.Location(pt.getX(), pt.getY(), pt.getZ(), 0, 0, world); + } + + public static cn.nukkit.level.Location center(cn.nukkit.level.Location loc) { + return new cn.nukkit.level.Location( + loc.getFloorX() + 0.5, + loc.getFloorY() + 0.5, + loc.getFloorZ() + 0.5, + loc.getPitch(), + loc.getYaw(), + loc.getLevel() + ); + } + + public static Player matchSinglePlayer(Server server, String name) { + Player[] players = server.matchPlayer(name); + return players.length > 0 ? players[0] : null; + } + + public static Block toBlock(BlockWorldVector pt) { + return toLevel(pt).getBlock(toLocation(pt)); + } + + public static Level toLevel(WorldVector pt) { + return ((NukkitWorld) pt.getWorld()).getLevel(); + } + + /** + * Nukkit's Location class has serious problems with floating point + * precision. + */ + @SuppressWarnings("RedundantIfStatement") + public static boolean equals(cn.nukkit.level.Location a, cn.nukkit.level.Location b) { + if (Math.abs(a.getX() - b.getX()) > EQUALS_PRECISION) return false; + if (Math.abs(a.getY() - b.getY()) > EQUALS_PRECISION) return false; + if (Math.abs(a.getZ() - b.getZ()) > EQUALS_PRECISION) return false; + return true; + } + + public static final double EQUALS_PRECISION = 0.0001; + + public static cn.nukkit.level.Location toLocation(Location location) { + return new cn.nukkit.level.Location(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch(), toLevel((LocalWorld) location.getExtent())); + } + + public static Level toLevel(final LocalWorld world) { + return ((NukkitWorld) world).getLevel(); + } + + public static NukkitEntity toLocalEntity(Entity e) { + return new NukkitEntity(e); + } + + public static com.sk89q.worldedit.entity.Entity createEntity(Location location, BaseEntity entity) { + return null; + } + + public static BaseBlock getBlock(Level level, Vector position) { + return null; + } + + public static boolean setBlock(Level level, Vector position, BaseBlock block) { + return false; + } +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitWorld.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitWorld.java new file mode 100644 index 00000000..5946ccbd --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitWorld.java @@ -0,0 +1,299 @@ +package com.boydti.fawe.nukkit.core; + +import cn.nukkit.block.Block; +import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.blockentity.BlockEntityChest; +import cn.nukkit.entity.Entity; +import cn.nukkit.inventory.InventoryHolder; +import cn.nukkit.item.Item; +import cn.nukkit.level.Level; +import cn.nukkit.math.Vector3; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.registry.WorldData; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import org.bukkit.Material; + + +import static com.google.common.base.Preconditions.checkNotNull; + +public class NukkitWorld extends LocalWorld { + + private static final Logger logger = WorldEdit.logger; + + private final WeakReference worldRef; + + /** + * Construct the object. + * + * @param world the world + */ + @SuppressWarnings("unchecked") + public NukkitWorld(Level world) { + this.worldRef = new WeakReference(world); + } + + private Vector3 mutable = new Vector3(0, 0, 0); + private Vector3 setMutable(Vector pt) { + mutable.x = pt.getX(); + mutable.y = pt.getY(); + mutable.z = pt.getZ(); + return mutable; + } + + @Override + public List getEntities(Region region) { + Level world = getLevel(); + cn.nukkit.entity.Entity[] ents = world.getEntities(); + List entities = new ArrayList(); + for (cn.nukkit.entity.Entity ent : ents) { + if (region.contains(NukkitUtil.toVector(ent.getLocation()))) { + entities.add(new NukkitEntity(ent)); + } + } + return entities; + } + + @Override + public List getEntities() { + List list = new ArrayList(); + for (Entity entity : getLevel().getEntities()) { + list.add(new NukkitEntity(entity)); + } + return list; + } + + @Nullable + @Override + public com.sk89q.worldedit.entity.Entity createEntity(com.sk89q.worldedit.util.Location location, BaseEntity entity) { + return NukkitUtil.createEntity(location, entity); + } + + /** + * Get the world handle. + * + * @return the world + */ + public Level getLevel() { + return checkNotNull(worldRef.get(), "The world was unloaded and the reference is unavailable"); + } + + @Override + public String getName() { + return getLevel().getName(); + } + + @Override + public int getBlockLightLevel(Vector pt) { + return getLevel().getBlockLightAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); + } + + @Override + public boolean regenerate(Region region, EditSession editSession) { + BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)]; + + for (Vector2D chunk : region.getChunks()) { + Vector min = new Vector(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16); + + // First save all the blocks inside + for (int x = 0; x < 16; ++x) { + for (int y = 0; y < (getMaxY() + 1); ++y) { + for (int z = 0; z < 16; ++z) { + Vector pt = min.add(x, y, z); + int index = y * 16 * 16 + z * 16 + x; + history[index] = editSession.getBlock(pt); + } + } + } + + try { + getLevel().regenerateChunk(chunk.getBlockX(), chunk.getBlockZ()); + } catch (Throwable t) { + logger.log(java.util.logging.Level.WARNING, "Chunk generation via Nukkit raised an error", t); + } + + // Then restore + for (int x = 0; x < 16; ++x) { + for (int y = 0; y < (getMaxY() + 1); ++y) { + for (int z = 0; z < 16; ++z) { + Vector pt = min.add(x, y, z); + int index = y * 16 * 16 + z * 16 + x; + + // We have to restore the block if it was outside + if (!region.contains(pt)) { + editSession.smartSetBlock(pt, history[index]); + } else { // Otherwise fool with history + editSession.rememberChange(pt, history[index], + editSession.rawGetBlock(pt)); + } + } + } + } + } + + return true; + } + + @Override + public boolean clearContainerBlockContents(Vector pt) { + BlockEntity block = getLevel().getBlockEntity(setMutable(pt)); + if (block == null) { + return false; + } + if (block instanceof InventoryHolder) { + if (block instanceof BlockEntityChest) { + ((BlockEntityChest) block).getRealInventory().clearAll(); + } else { + ((InventoryHolder) block).getInventory().clearAll(); + } + return true; + } + return false; + } + + @Override + @Deprecated + public boolean generateTree(EditSession editSession, Vector pt) { + return generateTree(TreeGenerator.TreeType.TREE, editSession, pt); + } + + @Override + @Deprecated + public boolean generateBigTree(EditSession editSession, Vector pt) { + return generateTree(TreeGenerator.TreeType.BIG_TREE, editSession, pt); + } + + @Override + @Deprecated + public boolean generateBirchTree(EditSession editSession, Vector pt) { + return generateTree(TreeGenerator.TreeType.BIRCH, editSession, pt); + } + + @Override + @Deprecated + public boolean generateRedwoodTree(EditSession editSession, Vector pt) { + return generateTree(TreeGenerator.TreeType.REDWOOD, editSession, pt); + } + + @Override + @Deprecated + public boolean generateTallRedwoodTree(EditSession editSession, Vector pt) { + return generateTree(TreeGenerator.TreeType.TALL_REDWOOD, editSession, pt); + } + + + @Override + public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector pt) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public void dropItem(Vector pt, BaseItemStack item) { + Level world = getLevel(); + Item nukkitItem = new Item(item.getType(), item.getAmount(), + item.getData()); + world.dropItem(NukkitUtil.toLocation(world, pt), nukkitItem); + } + + @SuppressWarnings("deprecation") + @Override + public boolean isValidBlockType(int type) { + Item item = Item.get(type); + return item != null && item.getId() < 256 && item.canBePlaced(); + } + + @Override + public void checkLoadedChunk(Vector pt) { + Level world = getLevel(); + + if (!world.isChunkLoaded(pt.getBlockX() >> 4, pt.getBlockZ() >> 4)) { + world.loadChunk(pt.getBlockX() >> 4, pt.getBlockZ() >> 4); + } + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } else if ((other instanceof NukkitWorld)) { + return ((NukkitWorld) other).getLevel().equals(getLevel()); + } else if (other instanceof com.sk89q.worldedit.world.World) { + return ((com.sk89q.worldedit.world.World) other).getName().equals(getName()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return getLevel().hashCode(); + } + + @Override + public int getMaxY() { + return 255; + } + + @Override + public void fixAfterFastMode(Iterable chunks) { + + } + + @Override + public boolean playEffect(Vector position, int type, int data) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public WorldData getWorldData() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public void simulateBlockMine(Vector pt) { + getLevel().getBlock(setMutable(pt)).onBreak(null); + } + + @Override + public BaseBlock getBlock(Vector position) { + return NukkitUtil.getBlock(getLevel(), position); + } + + @Override + public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) { + return NukkitUtil.setBlock(getLevel(), position, block); + } + + @SuppressWarnings("deprecation") + @Override + public BaseBlock getLazyBlock(Vector position) { + return getBlock(position); + } + + @Override + public BaseBiome getBiome(Vector2D position) { + int id = getLevel().getBiomeId(position.getBlockX(), position.getBlockZ()); + return new BaseBiome(id); + } + + @Override + public boolean setBiome(Vector2D position, BaseBiome biome) { + getLevel().setBiomeId(position.getBlockX(), position.getBlockZ(), biome.getId()); + return true; + } + +} diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitWorldEdit.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitWorldEdit.java new file mode 100644 index 00000000..77692767 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/NukkitWorldEdit.java @@ -0,0 +1,130 @@ +/* + * 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.boydti.fawe.nukkit.core; + +import cn.nukkit.Nukkit; +import cn.nukkit.Player; +import cn.nukkit.plugin.PluginBase; +import cn.nukkit.utils.Logger; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.event.platform.PlatformReadyEvent; +import com.sk89q.worldedit.extension.platform.Platform; +import java.io.File; + + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * The Nukkit implementation of WorldEdit. + */ +public class NukkitWorldEdit extends PluginBase { + + private Logger logger; + + private static NukkitWorldEdit inst; + + public static NukkitWorldEdit inst() { + return inst; + } + + private NukkitPlatform platform; + + private NukkitConfiguration config; + + private File workingDir; + + public NukkitWorldEdit() { + inst = this; + } + + @Override + public void onEnable() { + // TODO load FAWE + config.load(); + this.platform = new NukkitPlatform(this); + WorldEdit.getInstance().getPlatformManager().register(platform); + logger.info("WorldEdit for Nukkit (version " + getInternalVersion() + ") is loaded"); + WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); + } + + @Override + public void onDisable() { + WorldEdit.getInstance().getPlatformManager().unregister(platform); + } + + /** + * Get the configuration. + * + * @return the Nukkit configuration + */ + public NukkitConfiguration getWEConfig() { + return this.config; + } + + /** + * Get the WorldEdit proxy for the given player. + * + * @param player the player + * @return the WorldEdit player + */ + public NukkitPlayer wrapPlayer(Player player) { + checkNotNull(player); + return new NukkitPlayer(platform, player); + } + + /** + * 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 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 Nukkit implementation. + * + * @return a version string + */ + public String getInternalVersion() { + return Nukkit.API_VERSION; + } +} \ No newline at end of file diff --git a/nukkit/src/main/java/com/boydti/fawe/nukkit/core/WorldEditListener.java b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/WorldEditListener.java new file mode 100644 index 00000000..e066d7e9 --- /dev/null +++ b/nukkit/src/main/java/com/boydti/fawe/nukkit/core/WorldEditListener.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 . + */ + +// $Id$ + +package com.boydti.fawe.nukkit.core; + + +import cn.nukkit.block.Block; +import cn.nukkit.event.EventHandler; +import cn.nukkit.event.EventPriority; +import cn.nukkit.event.Listener; +import cn.nukkit.event.player.PlayerCommandPreprocessEvent; +import cn.nukkit.event.player.PlayerGameModeChangeEvent; +import cn.nukkit.event.player.PlayerInteractEvent; +import com.sk89q.util.StringUtil; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.internal.LocalWorldAdapter; +import com.sk89q.worldedit.world.World; + +/** + * Handles all events thrown in relation to a Player + */ +public class WorldEditListener implements Listener { + + private NukkitWorldEdit plugin; + + /** + * Called when a player plays an animation, such as an arm swing + * + * @param event Relevant event details + */ + + /** + * Construct the object; + * + * @param plugin the plugin + */ + public WorldEditListener(NukkitWorldEdit plugin) { + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onGamemode(PlayerGameModeChangeEvent event) { + // this will automatically refresh their session, we don't have to do anything + WorldEdit.getInstance().getSession(plugin.wrapPlayer(event.getPlayer())); + } + + /** + * Called when a player attempts to use a command + * + * @param event Relevant event details + */ + @EventHandler(ignoreCancelled = true,priority = EventPriority.MONITOR) + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { + String[] split = event.getMessage().split(" "); + + if (split.length > 0) { + split[0] = split[0].substring(1); + split = WorldEdit.getInstance().getPlatformManager().getCommandManager().commandDetection(split); + } + final String newMessage = "/" + StringUtil.joinString(split, " "); + + if (!newMessage.equals(event.getMessage())) { + event.setMessage(newMessage); + plugin.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + if (!event.getMessage().isEmpty()) { + plugin.getServer().dispatchCommand(event.getPlayer(), event.getMessage().substring(1)); + } + + event.setCancelled(true); + } + } + } + + /** + * Called when a player interacts + * + * @param event Relevant event details + */ + @EventHandler(ignoreCancelled = true,priority = EventPriority.MONITOR) + public void onPlayerInteract(PlayerInteractEvent event) { + final LocalPlayer player = plugin.wrapPlayer(event.getPlayer()); + final World world = player.getWorld(); + final WorldEdit we = WorldEdit.getInstance(); + + int action = event.getAction(); + if (action == PlayerInteractEvent.LEFT_CLICK_BLOCK) { + final Block clickedBlock = event.getBlock(); + final WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); + + if (we.handleBlockLeftClick(player, pos)) { + event.setCancelled(true); + } + + if (we.handleArmSwing(player)) { + event.setCancelled(true); + } + + } else if (action == PlayerInteractEvent.LEFT_CLICK_AIR) { + + if (we.handleArmSwing(player)) { + event.setCancelled(true); + } + + + } else if (action == PlayerInteractEvent.RIGHT_CLICK_BLOCK) { + final Block clickedBlock = event.getBlock(); + final WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), clickedBlock.getX(), + clickedBlock.getY(), clickedBlock.getZ()); + + if (we.handleBlockRightClick(player, pos)) { + event.setCancelled(true); + } + + if (we.handleRightClick(player)) { + event.setCancelled(true); + } + } else if (action == PlayerInteractEvent.RIGHT_CLICK_AIR) { + if (we.handleRightClick(player)) { + event.setCancelled(true); + } + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index a55a221d..a7b2461a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ rootProject.name = 'FastAsyncWorldEdit' -include 'core', 'bukkit0', 'bukkit1710', 'bukkit18', 'bukkit19', 'bukkit110', 'forge1710', 'forge189', 'forge194', 'forge110', 'favs' +include 'core', 'bukkit0', 'bukkit1710', 'bukkit18', 'bukkit19', 'bukkit110', 'forge1710', 'forge189', 'forge194', 'forge110', 'favs', 'nukkit'