From c52c1bee27053cf5088f433be54ab85fad2207cd Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Fri, 15 Jul 2016 09:53:30 +1000 Subject: [PATCH] Relight improvements + ports 6 lighting modes (0-5), see config forge194 forge110 bukkit1710 (untested) --- build.gradle | 1 - .../boydti/fawe/bukkit/v0/BukkitQueue_0.java | 35 +- .../fawe/bukkit/v0/BukkitQueue_All.java | 5 + .../fawe/bukkit/v1_10/BukkitQueue_1_10.java | 239 ++---- bukkit1710/build.gradle | 32 + .../fawe/bukkit/v1_7/BukkitChunk_1_7.java | 169 +++++ .../fawe/bukkit/v1_7/BukkitMain_17.java | 12 + .../fawe/bukkit/v1_7/BukkitQueue17.java | 676 +++++++++++++++++ bukkit1710/src/main/resources/plugin.yml | 122 +++ .../fawe/bukkit/v1_8/BukkitQueue18R3.java | 307 ++++---- .../fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java | 308 ++++---- .../main/java/com/boydti/fawe/FaweAPI.java | 84 +++ .../main/java/com/boydti/fawe/FaweCache.java | 132 ++++ .../com/boydti/fawe/command/FixLighting.java | 22 +- .../java/com/boydti/fawe/config/Settings.java | 17 +- .../boydti/fawe/example/CharFaweChunk.java | 9 + .../boydti/fawe/example/MappedFaweQueue.java | 4 +- .../fawe/example/NMSMappedFaweQueue.java | 195 ++++- .../com/boydti/fawe/object/FaweChunk.java | 2 +- .../com/boydti/fawe/object/FaweCommand.java | 9 +- .../com/boydti/fawe/object/FawePlayer.java | 9 +- .../com/boydti/fawe/object/FaweQueue.java | 22 + .../clipboard/DiskOptimizedClipboard.java | 18 +- .../java/com/boydti/fawe/util/FaweTimer.java | 7 +- .../java/com/boydti/fawe/util/MathMan.java | 4 + .../java/com/boydti/fawe/util/SetQueue.java | 2 +- .../com/boydti/fawe/util/TaskManager.java | 4 + forge110/build.gradle | 88 +++ .../java/com/boydti/fawe/forge/FaweForge.java | 161 ++++ .../com/boydti/fawe/forge/ForgeCommand.java | 42 ++ .../java/com/boydti/fawe/forge/ForgeMain.java | 75 ++ .../com/boydti/fawe/forge/ForgeMetrics.java | 481 ++++++++++++ .../com/boydti/fawe/forge/ForgePlayer.java | 78 ++ .../com/boydti/fawe/forge/ForgeTaskMan.java | 168 +++++ .../boydti/fawe/forge/v0/ForgeChunk_All.java | 114 +++ .../boydti/fawe/forge/v0/ForgeQueue_All.java | 702 ++++++++++++++++++ forge110/src/main/resources/config.yml | 0 .../boydti/fawe/forge/v0/ForgeQueue_All.java | 234 +++--- .../boydti/fawe/forge/v0/ForgeQueue_All.java | 255 +++---- forge194/build.gradle | 86 +++ .../java/com/boydti/fawe/forge/FaweForge.java | 161 ++++ .../com/boydti/fawe/forge/ForgeCommand.java | 42 ++ .../java/com/boydti/fawe/forge/ForgeMain.java | 75 ++ .../com/boydti/fawe/forge/ForgeMetrics.java | 481 ++++++++++++ .../com/boydti/fawe/forge/ForgePlayer.java | 78 ++ .../com/boydti/fawe/forge/ForgeTaskMan.java | 168 +++++ .../boydti/fawe/forge/v0/ForgeChunk_All.java | 114 +++ .../boydti/fawe/forge/v0/ForgeQueue_All.java | 702 ++++++++++++++++++ forge194/src/main/resources/config.yml | 0 settings.gradle | 2 +- 50 files changed, 5957 insertions(+), 796 deletions(-) create mode 100644 bukkit1710/build.gradle create mode 100644 bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitChunk_1_7.java create mode 100644 bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitMain_17.java create mode 100644 bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java create mode 100644 bukkit1710/src/main/resources/plugin.yml create mode 100644 forge110/build.gradle create mode 100644 forge110/src/main/java/com/boydti/fawe/forge/FaweForge.java create mode 100644 forge110/src/main/java/com/boydti/fawe/forge/ForgeCommand.java create mode 100644 forge110/src/main/java/com/boydti/fawe/forge/ForgeMain.java create mode 100644 forge110/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java create mode 100644 forge110/src/main/java/com/boydti/fawe/forge/ForgePlayer.java create mode 100644 forge110/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java create mode 100644 forge110/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java create mode 100644 forge110/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java create mode 100644 forge110/src/main/resources/config.yml create mode 100644 forge194/build.gradle create mode 100644 forge194/src/main/java/com/boydti/fawe/forge/FaweForge.java create mode 100644 forge194/src/main/java/com/boydti/fawe/forge/ForgeCommand.java create mode 100644 forge194/src/main/java/com/boydti/fawe/forge/ForgeMain.java create mode 100644 forge194/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java create mode 100644 forge194/src/main/java/com/boydti/fawe/forge/ForgePlayer.java create mode 100644 forge194/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java create mode 100644 forge194/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java create mode 100644 forge194/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java create mode 100644 forge194/src/main/resources/config.yml diff --git a/build.gradle b/build.gradle index f696f9af..bc28eaaa 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,6 @@ subprojects { maven {url "http://maven.sk89q.com/repo/"} maven {url "http://nexus.theyeticave.net/content/repositories/pub_releases"} maven {url "http://repo.maven.apache.org/maven2"} - maven {url "http://hub.spigotmc.org/nexus/content/groups/public/"} maven {url "http://ci.frostcast.net/plugin/repository/everything"} maven {url "http://maven.sk89q.com/artifactory/repo/"} maven {url "http://nexus.theyeticave.net/content/repositories/pub_releases"} diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java index bf97e307..54845d17 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java @@ -43,6 +43,32 @@ public abstract class BukkitQueue_0 extends NMSMa } } + @Override + public void setFullbright(CHUNKSECTIONS sections) {} + + @Override + public boolean initLighting(CHUNK chunk, CHUNKSECTIONS sections, RelightMode mode) { + return false; + } + + @Override + public int getEmmittedLight(CHUNKSECTIONS sections, int x, int y, int z) { + return 0; + } + + @Override + public int getSkyLight(CHUNKSECTIONS sections, int x, int y, int z) { + return 15; + } + + @Override + public void relight(int x, int y, int z) {} + + @Override + public boolean removeLighting(CHUNKSECTIONS sections, RelightMode mode, boolean hasSky) { + return false; + } + public void checkVersion(String supported) { String version = Bukkit.getServer().getClass().getPackage().getName(); if (!version.contains(supported)) { @@ -127,8 +153,8 @@ public abstract class BukkitQueue_0 extends NMSMa } @Override - public World getWorld(String world) { - return Bukkit.getWorld(world); + public World getImpWorld() { + return Bukkit.getWorld(getWorldName()); } @Override @@ -153,9 +179,8 @@ public abstract class BukkitQueue_0 extends NMSMa } @Override - public boolean fixLighting(FaweChunk fc, RelightMode mode) { - // Not implemented - return true; + public boolean hasSky() { + return getWorld().getEnvironment() == World.Environment.NORMAL; } @Override diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java index af38c44b..5c64e8c2 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_All.java @@ -32,6 +32,11 @@ public class BukkitQueue_All extends BukkitQueue_0 { return combined; } + @Override + public boolean fixLighting(FaweChunk fc, RelightMode mode) { + return false; + } + @Override public Chunk getCachedSections(World impWorld, int cx, int cz) { return impWorld.getChunkAt(cx, cz); diff --git a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java index c7d244ce..13ba4c87 100644 --- a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java +++ b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java @@ -6,7 +6,6 @@ import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.object.BytePair; import com.boydti.fawe.object.FaweChunk; -import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; @@ -22,6 +21,7 @@ import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -65,13 +65,13 @@ import net.minecraft.server.v1_10_R1.WorldSettings; import net.minecraft.server.v1_10_R1.WorldType; import org.bukkit.Bukkit; import org.bukkit.Chunk; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.WorldCreator; import org.bukkit.block.Biome; import org.bukkit.craftbukkit.v1_10_R1.CraftChunk; import org.bukkit.craftbukkit.v1_10_R1.CraftServer; +import org.bukkit.craftbukkit.v1_10_R1.CraftWorld; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.world.WorldInitEvent; import org.bukkit.event.world.WorldLoadEvent; @@ -269,176 +269,98 @@ public class BukkitQueue_1_10 extends BukkitQueue_0 0) { - ChunkSection section = sections[y >> 4]; - if (section != null) { - section.a(x, y & 15, z, l); - } - } - --y; - } while (y > 0 && l > 0); - } - } - } - } - if (((bc.getTotalRelight() == 0) && mode == RelightMode.MINIMAL)) { - return true; - } - if (mode == RelightMode.ALL) { - bc = getPrevious(bc, c.getSections(), null, null, null, true); - } - int total = bc.getTotalCount(); - net.minecraft.server.v1_10_R1.World w = c.world; - final int X = chunk.getX() << 4; - final int Z = chunk.getZ() << 4; - for (int j = sections.length - 1; j >= 0; j--) { - final Object section = sections[j]; - if (section == null) { - continue; - } - if (((bc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0)) || bc.getAir(j) == 4096) { - continue; - } - final char[] array = bc.getIdArray(j); - if (array == null) { - continue; - } - if (mode == RelightMode.ALL) { - for (int k = array.length - 1; k >= 0; k--) { - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { - continue; - } - pos.c(X + x, y, Z + z); - w.w(pos); - } - continue; - } - for (int k = array.length - 1; k >= 0; k--) { - final int i = array[k]; - final short id = (short) (i >> 4); - switch (id) { // Lighting - case 0: - continue; - default: - if (mode == RelightMode.MINIMAL) { - continue; - } - if (PseudoRandom.random.random(3) != 0) { - continue; - } - case 10: - case 11: - case 39: - case 40: - case 50: - case 51: - case 62: - case 74: - case 76: - case 89: - case 122: - case 124: - case 130: - case 138: - case 169: - case 213: - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { - continue; - } - pos.c(X + x, y, Z + z); - w.w(pos); - } - } - } - return true; - } catch (Throwable e) { - if (Thread.currentThread() == Fawe.get().getMainThread()) { - MainUtil.handleError(e); - } } - return false; + return true; } - public boolean isSurrounded(final char[][] sections, final int x, final int y, final int z) { - return this.isSolid(this.getId(sections, x, y + 1, z)) - && this.isSolid(this.getId(sections, x + 1, y - 1, z)) - && this.isSolid(this.getId(sections, x - 1, y, z)) - && this.isSolid(this.getId(sections, x, y, z + 1)) - && this.isSolid(this.getId(sections, x, y, z - 1)); + @Override + public boolean initLighting(Chunk chunk, ChunkSection[] sections, RelightMode mode) { + net.minecraft.server.v1_10_R1.Chunk c = ((CraftChunk) chunk).getHandle(); + if (mode == RelightMode.ALL) { + c.initLighting(); + } else { + final int i = c.g(); + final int i2 = i + 15; + int l; + int opacity; + int y; + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + y = i2; + l = 15; + do { + opacity = c.a(x, y, z).c(); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ChunkSection section = sections[y >> 4]; + if (section != null) { + section.a(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + return true; } - public boolean isSolid(final int i) { - if (i != 0) { - final Material material = Material.getMaterial(i); - return (material != null) && Material.getMaterial(i).isOccluding(); + @Override + public void setFullbright(ChunkSection[] sections) { + for (int i = 0; i < sections.length; i++) { + ChunkSection section = sections[i]; + if (section != null) { + byte[] bytes = section.getSkyLightArray().asBytes(); + Arrays.fill(bytes, (byte) 255); + } } - return false; } - public int getId(final char[][] sections, final int x, final int y, final int z) { - if ((x < 0) || (x > 15) || (z < 0) || (z > 15)) { - return 1; + @Override + public int getSkyLight(ChunkSection[] sections, int x, int y, int z) { + ChunkSection section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 15; } - if ((y < 0) || (y > 255)) { - return 1; - } - final int i = FaweCache.CACHE_I[y][x][z]; - final char[] section = sections[i]; + return section.b(x, y & 15, z); + } + + @Override + public int getEmmittedLight(ChunkSection[] sections, int x, int y, int z) { + ChunkSection section = sections[FaweCache.CACHE_I[y][x][z]]; if (section == null) { return 0; } - final int j = FaweCache.CACHE_J[y][x][z]; - return section[j] >> 4; + return section.c(x, y & 15, z); + } + + @Override + public void relight(int x, int y, int z) { + pos.c(x, y, z); + nmsWorld.w(pos); + } + + private WorldServer nmsWorld; + + @Override + public World getImpWorld() { + World world = super.getImpWorld(); + this.nmsWorld = ((CraftWorld) world).getHandle(); + return super.getImpWorld(); } public void setCount(int tickingBlockCount, int nonEmptyBlockCount, ChunkSection section) throws NoSuchFieldException, IllegalAccessException { @@ -594,6 +516,9 @@ public class BukkitQueue_1_10 extends BukkitQueue_0 clazzChunk = nmsChunk.getClass(); diff --git a/bukkit1710/build.gradle b/bukkit1710/build.gradle new file mode 100644 index 00000000..69efbae7 --- /dev/null +++ b/bukkit1710/build.gradle @@ -0,0 +1,32 @@ +dependencies { + compile project(':bukkit0') + compile 'org.bukkit.craftbukkit:Craftbukkit:1.7.10' +} + +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(':bukkit0')) + 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/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitChunk_1_7.java b/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitChunk_1_7.java new file mode 100644 index 00000000..66cc6e56 --- /dev/null +++ b/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitChunk_1_7.java @@ -0,0 +1,169 @@ +package com.boydti.fawe.bukkit.v1_7; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.util.MainUtil; +import net.minecraft.server.v1_7_R4.NibbleArray; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; + +public class BukkitChunk_1_7 extends CharFaweChunk { + + @Override + public Chunk getNewChunk() { + return Bukkit.getWorld(getParent().getWorldName()).getChunkAt(getX(), getZ()); + } + + public byte[][] byteIds; + public NibbleArray[] datas; + + public BukkitChunk_1_7(FaweQueue parent, int x, int z) { + super(parent, x, z); + this.byteIds = new byte[16][]; + this.datas = new NibbleArray[16]; + } + + public byte[] getByteIdArray(int i) { + return this.byteIds[i]; + } + + public NibbleArray getDataArray(int i) { + return datas[i]; + } + + @Override + public void setBlock(int x, int y, int z, int id, int data) { + int i = FaweCache.CACHE_I[y][x][z]; + int j = FaweCache.CACHE_J[y][x][z]; + byte[] vs = this.byteIds[i]; + char[] vs2 = this.ids[i]; + if (vs2 == null) { + vs2 = this.ids[i] = new char[4096]; + } + if (vs == null) { + vs = this.byteIds[i] = new byte[4096]; + } + this.count[i]++; + switch (id) { + case 0: + this.air[i]++; + vs[j] = -1; + vs2[j] = (char) 1; + return; + case 11: + case 39: + case 40: + case 51: + case 74: + case 89: + case 122: + case 124: + case 138: + case 169: + case 213: + this.relight[i]++; + case 2: + case 4: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 30: + case 32: + case 37: + case 41: + case 42: + case 45: + case 46: + case 47: + case 48: + case 49: + case 55: + case 56: + case 57: + case 58: + case 60: + case 7: + case 73: + case 79: + case 80: + case 81: + case 82: + case 83: + case 85: + case 87: + case 88: + case 101: + case 102: + case 103: + case 110: + case 112: + case 113: + case 121: + case 129: + case 133: + case 165: + case 166: + case 170: + case 172: + case 173: + case 174: + case 188: + case 189: + case 190: + case 191: + case 192: + vs[j] = (byte) (id); + vs2[j] = (char) (id << 4); + return; + case 130: + case 76: + case 62: + case 50: + case 10: + this.relight[i]++; + case 54: + case 146: + case 61: + case 65: + case 68: // removed + default: + vs2[j] = (char) ((id << 4) + data); + vs[j] = (byte) id; + if (data != 0) { + NibbleArray dataArray = datas[i]; + if (dataArray == null) { + datas[i] = dataArray = new NibbleArray(4096, 4); + } + dataArray.a(x, y & 15, z, data); + } + return; + } + } + + @Override + public CharFaweChunk copy(boolean shallow) { + BukkitChunk_1_7 copy = new BukkitChunk_1_7(getParent(), getX(), getZ()); + if (shallow) { + copy.byteIds = byteIds; + copy.datas = datas; + copy.air = air; + copy.biomes = biomes; + copy.chunk = chunk; + copy.count = count; + copy.relight = relight; + } else { + copy.byteIds = (byte[][]) MainUtil.copyNd(byteIds); + copy.datas = datas.clone(); + copy.air = air.clone(); + copy.biomes = biomes.clone(); + copy.chunk = chunk; + copy.count = count.clone(); + copy.relight = relight.clone(); + } + return copy; + } +} diff --git a/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitMain_17.java b/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitMain_17.java new file mode 100644 index 00000000..feb4f94a --- /dev/null +++ b/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitMain_17.java @@ -0,0 +1,12 @@ +package com.boydti.fawe.bukkit.v1_7; + +import com.boydti.fawe.bukkit.ABukkitMain; +import com.boydti.fawe.bukkit.v0.BukkitQueue_0; + +public class BukkitMain_17 extends ABukkitMain { + + @Override + public BukkitQueue_0 getQueue(String world) { + return new BukkitQueue17(world); + } +} diff --git a/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java b/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java new file mode 100644 index 00000000..071c7256 --- /dev/null +++ b/bukkit1710/src/main/java/com/boydti/fawe/bukkit/v1_7/BukkitQueue17.java @@ -0,0 +1,676 @@ +package com.boydti.fawe.bukkit.v1_7; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.v0.BukkitQueue_0; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.object.BytePair; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.internal.Constants; +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import net.minecraft.server.v1_7_R4.ChunkCoordIntPair; +import net.minecraft.server.v1_7_R4.ChunkPosition; +import net.minecraft.server.v1_7_R4.ChunkSection; +import net.minecraft.server.v1_7_R4.Entity; +import net.minecraft.server.v1_7_R4.EntityPlayer; +import net.minecraft.server.v1_7_R4.EntityTracker; +import net.minecraft.server.v1_7_R4.EntityTrackerEntry; +import net.minecraft.server.v1_7_R4.EntityTypes; +import net.minecraft.server.v1_7_R4.EnumDifficulty; +import net.minecraft.server.v1_7_R4.EnumGamemode; +import net.minecraft.server.v1_7_R4.LongHashMap; +import net.minecraft.server.v1_7_R4.MinecraftServer; +import net.minecraft.server.v1_7_R4.NBTTagCompound; +import net.minecraft.server.v1_7_R4.NibbleArray; +import net.minecraft.server.v1_7_R4.PacketPlayOutEntityDestroy; +import net.minecraft.server.v1_7_R4.PacketPlayOutMapChunk; +import net.minecraft.server.v1_7_R4.PlayerChunkMap; +import net.minecraft.server.v1_7_R4.ServerNBTManager; +import net.minecraft.server.v1_7_R4.TileEntity; +import net.minecraft.server.v1_7_R4.WorldManager; +import net.minecraft.server.v1_7_R4.WorldServer; +import net.minecraft.server.v1_7_R4.WorldSettings; +import net.minecraft.server.v1_7_R4.WorldType; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.craftbukkit.v1_7_R4.CraftChunk; +import org.bukkit.craftbukkit.v1_7_R4.CraftServer; +import org.bukkit.craftbukkit.v1_7_R4.CraftWorld; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.generator.ChunkGenerator; + +public class BukkitQueue17 extends BukkitQueue_0 { + + public BukkitQueue17(final String world) { + super(world); + checkVersion("v1_7_R4"); + } + + @Override + public boolean isChunkLoaded(int x, int z) { + return getWorld().isChunkLoaded(x, z); + } + + public World getWorld(String world) { + return Bukkit.getWorld(world); + } + + @Override + public boolean regenerateChunk(World world, int x, int z) { + return world.regenerateChunk(x, z); + } + + @Override + public boolean loadChunk(World world, int x, int z, boolean generate) { + return getCachedSections(world, x, z) != null; + } + + @Override + public void setFullbright(ChunkSection[] sections) { + for (int i = 0; i < sections.length; i++) { + ChunkSection section = sections[i]; + if (section != null) { + byte[] bytes = section.getSkyLightArray().a; + Arrays.fill(bytes, Byte.MAX_VALUE); + } + } + } + + @Override + public ChunkSection[] getCachedSections(World world, int x, int z) { + Chunk chunk = world.getChunkAt(x, z); + if (chunk == null) { + return null; + } + if (!chunk.isLoaded()) { + chunk.load(true); + } + return ((CraftChunk) chunk).getHandle().getSections(); + } + + @Override + public int getCombinedId4Data(ChunkSection ls, int x, int y, int z) { + byte[] ids = ls.getIdArray(); + NibbleArray datasNibble = ls.getDataArray(); + int i = FaweCache.CACHE_J[y & 15][x & 15][z & 15]; + int combined = (ids[i] << 4) + (datasNibble == null ? 0 : datasNibble.a(x & 15, y & 15, z & 15)); + return combined; + } + + @Override + public boolean isChunkLoaded(World world, int x, int z) { + return world.isChunkLoaded(x, z); + } + + @Override + public ChunkSection getCachedSection(ChunkSection[] chunkSections, int cy) { + return chunkSections[cy]; + } + + @Override + public CharFaweChunk getPrevious(CharFaweChunk fs, ChunkSection[] sections, Map tilesGeneric, Collection[] entitiesGeneric, Set createdEntities, boolean all) throws Exception { + Map tiles = (Map) tilesGeneric; + Collection[] entities = (Collection[]) entitiesGeneric; + CharFaweChunk previous = (CharFaweChunk) getFaweChunk(fs.getX(), fs.getZ()); + char[][] idPrevious = new char[16][]; + for (int layer = 0; layer < sections.length; layer++) { + if (fs.getCount(layer) != 0 || all) { + ChunkSection section = sections[layer]; + if (section != null) { + byte[] currentIdArray = section.getIdArray(); + NibbleArray currentDataArray = section.getDataArray(); + char[] array = new char[4096]; + for (int j = 0; j < currentIdArray.length; j++) { + int x = FaweCache.CACHE_X[layer][j]; + int y = FaweCache.CACHE_Y[layer][j]; + int z = FaweCache.CACHE_Z[layer][j]; + int id = currentIdArray[j] & 0xFF; + byte data = (byte) currentDataArray.a(x, y & 15, z); + previous.setBlock(x, y, z, id, data); + } + } + } + } + previous.ids = idPrevious; + if (tiles != null) { + for (Map.Entry entry : tiles.entrySet()) { + TileEntity tile = entry.getValue(); + NBTTagCompound tag = new NBTTagCompound(); + ChunkPosition pos = entry.getKey(); + CompoundTag nativeTag = getTag(tile); + previous.setTile(pos.x & 15, pos.y, pos.z & 15, nativeTag); + } + } + if (entities != null) { + for (Collection entityList : entities) { + for (Entity ent : entityList) { + if (ent instanceof EntityPlayer || (!createdEntities.isEmpty() && createdEntities.contains(ent.getUniqueID()))) { + continue; + } + int x = ((int) Math.round(ent.locX) & 15); + int z = ((int) Math.round(ent.locZ) & 15); + int y = (int) Math.round(ent.locY); + int i = FaweCache.CACHE_I[y][x][z]; + char[] array = fs.getIdArray(i); + if (array == null) { + continue; + } + int j = FaweCache.CACHE_J[y][x][z]; + if (array[j] != 0) { + String id = EntityTypes.b(ent); + if (id != null) { + NBTTagCompound tag = new NBTTagCompound(); + ent.e(tag); // readEntityIntoTag + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag); + Map map = ReflectionUtils.getMap(nativeTag.getValue()); + map.put("Id", new StringTag(id)); + previous.setEntity(nativeTag); + } + } + } + } + } + return previous; + } + + public CompoundTag getTag(TileEntity tile) { + try { + NBTTagCompound tag = new NBTTagCompound(); + tile.b(tag); // readTagIntoEntity + return (CompoundTag) methodToNative.invoke(adapter, tag); + } catch (Exception e) { + MainUtil.handleError(e); + return null; + } + } + + @Override + public CompoundTag getTileEntity(Chunk chunk, int x, int y, int z) { + Map tiles = ((CraftChunk) chunk).getHandle().tileEntities; + ChunkPosition pos = new ChunkPosition(x, y, z); + TileEntity tile = tiles.get(pos); + return tile != null ? getTag(tile) : null; + } + + + @Override + public Chunk getChunk(World world, int x, int z) { + return world.getChunkAt(x, z); + } + + @Override + public boolean setComponents(FaweChunk fc, RunnableVal changeTask) { + BukkitChunk_1_7 fs = (BukkitChunk_1_7) fc; + CraftChunk chunk = (CraftChunk) fs.getChunk(); + net.minecraft.server.v1_7_R4.Chunk nmsChunk = chunk.getHandle(); + nmsChunk.e(); // Modified + nmsChunk.mustSave = true; + net.minecraft.server.v1_7_R4.World nmsWorld = nmsChunk.world; + try { + final boolean flag = getWorld().getEnvironment() == World.Environment.NORMAL; + // Sections + ChunkSection[] sections = nmsChunk.getSections(); + Map tiles = nmsChunk.tileEntities; + Collection[] entities = nmsChunk.entitySlices; + + // Remove entities + for (int i = 0; i < 16; i++) { + int count = fs.getCount(i); + if (count == 0) { + continue; + } else if (count >= 4096) { + entities[i].clear(); + } else { + char[] array = fs.getIdArray(i); + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entity instanceof EntityPlayer) { + continue; + } + int x = ((int) Math.round(entity.locX) & 15); + int z = ((int) Math.round(entity.locZ) & 15); + int y = (int) Math.round(entity.locY); + if (array == null) { + continue; + } + if (y < 0 || y > 255 || array[FaweCache.CACHE_J[y][x][z]] != 0) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Set entities + Set createdEntities = new HashSet<>(); + Set entitiesToSpawn = fs.getEntities(); + for (CompoundTag nativeTag : entitiesToSpawn) { + Map entityTagMap = nativeTag.getValue(); + StringTag idTag = (StringTag) entityTagMap.get("Id"); + ListTag posTag = (ListTag) entityTagMap.get("Pos"); + ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + if (idTag == null || posTag == null || rotTag == null) { + Fawe.debug("Unknown entity tag: " + nativeTag); + continue; + } + double x = posTag.getDouble(0); + double y = posTag.getDouble(1); + double z = posTag.getDouble(2); + float yaw = rotTag.getFloat(0); + float pitch = rotTag.getFloat(1); + String id = idTag.getValue(); + Entity entity = EntityTypes.createEntityByName(id, nmsWorld); + if (entity != null) { + if (nativeTag != null) { + NBTTagCompound tag = (NBTTagCompound)methodFromNative.invoke(adapter, nativeTag); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + entity.f(tag); + } + entity.setLocation(x, y, z, yaw, pitch); + nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + createdEntities.add(entity.getUniqueID()); + } + } + // Run change task if applicable + if (changeTask != null) { + CharFaweChunk previous = getPrevious(fs, sections, tiles, entities, createdEntities, false); + changeTask.run(previous); + } + // Trim tiles + Iterator> iterator = tiles.entrySet().iterator(); + HashMap toRemove = null; + while (iterator.hasNext()) { + Map.Entry tile = iterator.next(); + ChunkPosition pos = tile.getKey(); + int lx = pos.x & 15; + int ly = pos.y; + int lz = pos.z & 15; + int j = FaweCache.CACHE_I[ly][lx][lz]; + char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + int k = FaweCache.CACHE_J[ly][lx][lz]; + if (array[k] != 0) { + if (toRemove == null) { + toRemove = new HashMap<>(); + } + toRemove.put(tile.getKey(), tile.getValue()); + } + } + if (toRemove != null) { + for (Map.Entry entry : toRemove.entrySet()) { + ChunkPosition bp = entry.getKey(); + TileEntity tile = entry.getValue(); + tiles.remove(bp); + tile.s(); + nmsWorld.p(bp.x, bp.y, bp.z); + tile.u(); + } + + } + HashSet entsToRemove = fs.getEntityRemoves(); + if (entsToRemove.size() > 0) { + for (int i = 0; i < entities.length; i++) { + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entsToRemove.contains(entity.getUniqueID())) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Set blocks + for (int j = 0; j < sections.length; j++) { + if (fs.getCount(j) == 0) { + continue; + } + byte[] newIdArray = fs.getByteIdArray(j); + if (newIdArray == null) { + continue; + } + NibbleArray newDataArray = fs.getDataArray(j); + ChunkSection section = sections[j]; + if ((section == null) || (fs.getCount(j) >= 4096)) { + sections[j] = section = new ChunkSection(j << 4, flag); + section.setIdArray(newIdArray); + section.setDataArray(newDataArray); + continue; + } + byte[] currentIdArray = section.getIdArray(); + NibbleArray currentDataArray = section.getDataArray(); + boolean data = currentDataArray != null; + if (!data) { + section.setDataArray(newDataArray); + } + boolean fill = true; + int solid = 0; + for (int k = 0; k < newIdArray.length; k++) { + byte n = newIdArray[k]; + switch (n) { + case 0: + fill = false; + continue; + case -1: + fill = false; + if (currentIdArray[k] != 0) { + solid++; + } + currentIdArray[k] = 0; + continue; + default: + solid++; + currentIdArray[k] = n; + if (data) { + int x = FaweCache.CACHE_X[0][k]; + int y = FaweCache.CACHE_Y[0][k]; + int z = FaweCache.CACHE_Z[0][k]; + int newData = newDataArray == null ? 0 : newDataArray.a(x, y, z); + int currentData = currentDataArray == null ? 0 : currentDataArray.a(x, y, z); + if (newData != currentData) { + currentDataArray.a(x, y, z, newData); + } + } + continue; + } + } + setCount(0, solid, section); + if (fill) { + fs.setCount(j, Short.MAX_VALUE); + } + } + + // Set biomes + int[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + int biome = array[z]; + if (biome == 0) { + continue; + } + nmsChunk.m()[((z & 0xF) << 4 | x & 0xF)] = (byte) biome; // Biome array + } + } + } + // Set tiles + Map tilesToSpawn = fs.getTiles(); + int bx = fs.getX() << 4; + int bz = fs.getZ() << 4; + + for (Map.Entry entry : tilesToSpawn.entrySet()) { + CompoundTag nativeTag = entry.getValue(); + BytePair pair = entry.getKey(); + TileEntity tileEntity = nmsWorld.getTileEntity(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); + if (tileEntity != null) { + NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag); + tileEntity.a(tag); // ReadTagIntoTile + } + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + sendChunk(fc, null); + return true; + } + + public void setCount(int tickingBlockCount, int nonEmptyBlockCount, ChunkSection section) throws NoSuchFieldException, IllegalAccessException { + Class clazz = section.getClass(); + Field fieldTickingBlockCount = clazz.getDeclaredField("tickingBlockCount"); + Field fieldNonEmptyBlockCount = clazz.getDeclaredField("nonEmptyBlockCount"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + fieldTickingBlockCount.set(section, tickingBlockCount); + fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount); + } + + @Override + public void refreshChunk(World world, Chunk chunk) { + if (!chunk.isLoaded()) { + return; + } + try { + net.minecraft.server.v1_7_R4.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); + ChunkCoordIntPair pos = nmsChunk.l(); // getPosition() + WorldServer w = (WorldServer) nmsChunk.world; + PlayerChunkMap chunkMap = w.getPlayerChunkMap(); + int x = pos.x; + int z = pos.z; + if (!chunkMap.isChunkInUse(x, z)) { + return; + } + HashSet set = new HashSet(); + EntityTracker tracker = w.getTracker(); + // Get players + + Field fieldChunkMap = chunkMap.getClass().getDeclaredField("d"); + fieldChunkMap.setAccessible(true); + LongHashMap map = (LongHashMap) fieldChunkMap.get(chunkMap); + long pair = (long) x + 2147483647L | (long) z + 2147483647L << 32; + Object playerChunk = map.getEntry(pair); + Field fieldPlayers = playerChunk.getClass().getDeclaredField("b"); + fieldPlayers.setAccessible(true); + final HashSet players = new HashSet<>((Collection)fieldPlayers.get(playerChunk)); + if (players.size() == 0) { + return; + } + HashSet entities = new HashSet<>(); + List[] entitieSlices = nmsChunk.entitySlices; + for (List slice : entitieSlices) { + if (slice == null) { + continue; + } + for (Entity ent : slice) { + EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(ent.getId()); + if (entry == null) { + continue; + } + entities.add(entry); + PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(ent.getId()); + for (EntityPlayer player : players) { + player.playerConnection.sendPacket(packet); + } + } + } + // Send chunks + PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, false, 65535, 25); + for (EntityPlayer player : players) { + player.playerConnection.sendPacket(packet); + } + // send ents + for (final EntityTrackerEntry entry : entities) { + try { + TaskManager.IMP.later(new Runnable() { + @Override + public void run() { + for (EntityPlayer player : players) { + boolean result = entry.trackedPlayers.remove(player); + if (result && entry.tracker != player) { + entry.updatePlayer(player); + } + } + } + }, 2); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + + @Override + public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) { + if (mode == RelightMode.ALL) { + for (int i = 0; i < sections.length; i++) { + ChunkSection section = sections[i]; + if (section != null) { + section.setEmittedLightArray(null); + if (sky) { + section.setSkyLightArray(null); + } + } + } + } + return true; + } + + @Override + public World createWorld(final WorldCreator creator) { + final String name = creator.name(); + ChunkGenerator generator = creator.generator(); + final CraftServer server = (CraftServer) Bukkit.getServer(); + final MinecraftServer console = server.getServer(); + final File folder = new File(server.getWorldContainer(), name); + final World world = server.getWorld(name); + final WorldType type = WorldType.getType(creator.type().getName()); + final boolean generateStructures = creator.generateStructures(); + if (world != null) { + return world; + } + if (folder.exists() && !folder.isDirectory()) { + throw new IllegalArgumentException("File exists with the name '" + name + "' and isn't a folder"); + } + if (generator == null) { + generator = server.getGenerator(name); + } + int dimension = 10 + console.worlds.size(); + boolean used = false; + do { + for (final WorldServer ws : console.worlds) { + used = (ws.dimension == dimension); + if (used) { + ++dimension; + break; + } + } + } while (used); + final boolean hardcore = false; + ServerNBTManager sdm = new ServerNBTManager(server.getWorldContainer(), name, true); + final WorldSettings worldSettings = new WorldSettings(creator.seed(), EnumGamemode.getById(server.getDefaultGameMode().getValue()), generateStructures, hardcore, type); + startSet(true); + final WorldServer internal = new WorldServer(console, sdm, name, dimension, worldSettings, console.methodProfiler, creator.environment(), generator); + endSet(true); + internal.scoreboard = server.getScoreboardManager().getMainScoreboard().getHandle(); + internal.tracker = new EntityTracker(internal); + internal.addIWorldAccess(new WorldManager(console, internal)); + internal.difficulty = EnumDifficulty.EASY; + internal.setSpawnFlags(true, true); + if (generator != null) { + internal.getWorld().getPopulators().addAll(generator.getDefaultPopulators(internal.getWorld())); + } + // Add the world + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(World value) { + console.worlds.add(internal); + server.getPluginManager().callEvent(new WorldInitEvent(internal.getWorld())); + server.getPluginManager().callEvent(new WorldLoadEvent(internal.getWorld())); + this.value = internal.getWorld(); + } + }); + } + + @Override + public boolean initLighting(Chunk chunk, ChunkSection[] sections, RelightMode mode) { + net.minecraft.server.v1_7_R4.Chunk c = ((CraftChunk) chunk).getHandle(); + if (mode == RelightMode.ALL) { + c.initLighting(); + } else { + int i = c.h(); + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + int l = 15; + int y = i + 16 - 1; + do { + int opacity = c.b(x, y, z); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ChunkSection section = sections[y >> 4]; + if (section != null) { + section.setSkyLight(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + return true; + } + + + @Override + public int getSkyLight(ChunkSection[] sections, int x, int y, int z) { + ChunkSection section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 15; + } + return section.getSkyLight(x, y & 15, z); + } + + @Override + public int getEmmittedLight(ChunkSection[] sections, int x, int y, int z) { + ChunkSection section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 0; + } + return section.getEmittedLight(x, y & 15, z); + } + + @Override + public void relight(int x, int y, int z) { + nmsWorld.t(x, y, z); + } + + private WorldServer nmsWorld; + + @Override + public World getImpWorld() { + World world = super.getImpWorld(); + this.nmsWorld = ((CraftWorld) world).getHandle(); + return super.getImpWorld(); + } + + @Override + public FaweChunk getFaweChunk(int x, int z) { + return new CharFaweChunk(this, x, z) { + @Override + public Chunk getNewChunk() { + return BukkitQueue17.this.getWorld().getChunkAt(getX(), getZ()); + } + }; + } +} diff --git a/bukkit1710/src/main/resources/plugin.yml b/bukkit1710/src/main/resources/plugin.yml new file mode 100644 index 00000000..7e93dff0 --- /dev/null +++ b/bukkit1710/src/main/resources/plugin.yml @@ -0,0 +1,122 @@ +name: FastAsyncWorldEdit +main: com.boydti.fawe.bukkit.v1_7.BukkitMain_17 +version: ${version} +description: Fast Async WorldEdit plugin +authors: [Empire92] +loadbefore: [WorldEdit] +load: STARTUP +database: false +#softdepend: [WorldGuard, PlotSquared, MCore, Factions, GriefPrevention, Residence, Towny, PlotMe, PreciousStones] +commands: + wea: + description: (FAWE) Bypass WorldEdit processing and area restrictions + aliases: [weanywhere,worldeditanywhere,/wea,/weanywhere,/worldeditanywhere] + usage: "Vault is required for the toggle. Optionally, you can set the permission fawe.bypass" + fixlighting: + description: (FAWE) Fix the lighting in your current chunk + aliases: [/fixlighting] + stream: + description: (FAWE) Stream a schematic into the world + aliases: [/stream] + fawe: + description: (FAWE) Reload the plugin + aliases: [/fawe,/fawereload] + select: + description: (FAWE) Select your current WorldEdit Region. + aliases: [/select,wer,/wer,worldeditregion,/worldeditregion,/region] + frb: + description: (FAWE) Rollback an edit + aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb] + fcancel: + description: (FAWE) Cancel your edit + aliases: [fawecancel,/fcancel,/cancel,/fawecancel] + '/p': + description: VoxelSniper perform command + aliases: [perform,/perform] + '/d': + description: VoxelSniper default command + aliases: [default,/default] +permissions: + fawe.bypass: + default: false + fawe.admin: + default: false + fawe.stream: + default: false + fawe.fixlighting: + default: false + fawe.reload: + default: false + fawe.voxelbrush: + default: op + children: + voxelsniper.brush.ball: true + voxelsniper.brush.biome: true + voxelsniper.brush.blendball: true + voxelsniper.brush.blenddisc: true + voxelsniper.brush.blendvoxel: true + voxelsniper.brush.blendvoxeldisc: true + voxelsniper.brush.blob: true + voxelsniper.brush.blockreset: true + voxelsniper.brush.blockresetsurface: true + voxelsniper.brush.canyon: true + voxelsniper.brush.canyonselection: true + voxelsniper.brush.checkervoxeldisc: true + voxelsniper.brush.cleansnow: true + voxelsniper.brush.clonestamp: true + voxelsniper.brush.copypasta: true + voxelsniper.brush.cylinder: true + voxelsniper.brush.disc: true + voxelsniper.brush.discface: true + voxelsniper.brush.dome: true + voxelsniper.brush.drain: true + voxelsniper.brush.ellipse: true + voxelsniper.brush.ellipsoid: true + voxelsniper.brush.eraser: true + voxelsniper.brush.erode: true + voxelsniper.brush.extrude: true + voxelsniper.brush.filldown: true + voxelsniper.brush.flatocean: true + voxelsniper.brush.heatray: true + voxelsniper.brush.jaggedline: true + voxelsniper.brush.line: true + voxelsniper.brush.move: true + voxelsniper.brush.ocean: true + voxelsniper.brush.overlay: true + voxelsniper.brush.pull: true + voxelsniper.brush.randomerode: true + voxelsniper.brush.ring: true + voxelsniper.brush.rot2d: true + voxelsniper.brush.rot2dvert: true + voxelsniper.brush.rot3d: true + voxelsniper.brush.ruler: true + voxelsniper.brush.scanner: true + voxelsniper.brush.set: true + voxelsniper.brush.setredstoneflip: true + voxelsniper.brush.setredstonerotate: true + voxelsniper.brush.shellball: true + voxelsniper.brush.shellset: true + voxelsniper.brush.shellvoxel: true + voxelsniper.brush.signoverwrite: true + voxelsniper.brush.snipe: true + voxelsniper.brush.snowcone: true + voxelsniper.brush.spiralstaircase: true + voxelsniper.brush.splatterball: true + voxelsniper.brush.splatterdisc: true + voxelsniper.brush.splatteroverlay: true + voxelsniper.brush.splattervoxel: true + voxelsniper.brush.splattervoxeldisc: true + voxelsniper.brush.spline: true + voxelsniper.brush.stamp: true + voxelsniper.brush.stencil: true + voxelsniper.brush.stencillist: true + voxelsniper.brush.threepointcircle: true + voxelsniper.brush.triangle: true + voxelsniper.brush.underlay: true + voxelsniper.brush.voltmeter: true + voxelsniper.brush.voxel: true + voxelsniper.brush.voxeldisc: true + voxelsniper.brush.voxeldiscface: true + voxelsniper.brush.warp: true + voxelsniper.goto: true + voxelsniper.sniper: true \ No newline at end of file diff --git a/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java b/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java index f90cd007..05710880 100644 --- a/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java +++ b/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java @@ -6,7 +6,6 @@ import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.object.BytePair; import com.boydti.fawe.object.FaweChunk; -import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; @@ -17,8 +16,10 @@ import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.internal.Constants; +import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -35,20 +36,32 @@ import net.minecraft.server.v1_8_R3.EntityPlayer; import net.minecraft.server.v1_8_R3.EntityTracker; import net.minecraft.server.v1_8_R3.EntityTrackerEntry; import net.minecraft.server.v1_8_R3.EntityTypes; +import net.minecraft.server.v1_8_R3.EnumDifficulty; import net.minecraft.server.v1_8_R3.LongHashMap; +import net.minecraft.server.v1_8_R3.MinecraftServer; import net.minecraft.server.v1_8_R3.NBTTagCompound; import net.minecraft.server.v1_8_R3.NibbleArray; import net.minecraft.server.v1_8_R3.PacketPlayOutEntityDestroy; import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk; import net.minecraft.server.v1_8_R3.PlayerChunkMap; +import net.minecraft.server.v1_8_R3.ServerNBTManager; import net.minecraft.server.v1_8_R3.TileEntity; +import net.minecraft.server.v1_8_R3.WorldData; +import net.minecraft.server.v1_8_R3.WorldManager; import net.minecraft.server.v1_8_R3.WorldServer; +import net.minecraft.server.v1_8_R3.WorldSettings; +import net.minecraft.server.v1_8_R3.WorldType; import org.bukkit.Bukkit; import org.bukkit.Chunk; -import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.WorldCreator; import org.bukkit.craftbukkit.v1_8_R3.CraftChunk; +import org.bukkit.craftbukkit.v1_8_R3.CraftServer; +import org.bukkit.craftbukkit.v1_8_R3.CraftWorld; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.generator.ChunkGenerator; public class BukkitQueue18R3 extends BukkitQueue_0 { @@ -76,6 +89,17 @@ public class BukkitQueue18R3 extends BukkitQueue_0 fs = (CharFaweChunk) fc; CraftChunk chunk = (CraftChunk) fs.getChunk(); net.minecraft.server.v1_8_R3.Chunk nmsChunk = chunk.getHandle(); + nmsChunk.f(true); // Modified + nmsChunk.mustSave = true; net.minecraft.server.v1_8_R3.World nmsWorld = nmsChunk.getWorld(); try { final boolean flag = getWorld().getEnvironment() == World.Environment.NORMAL; @@ -478,167 +504,150 @@ public class BukkitQueue18R3 extends BukkitQueue_0 fc = (CharFaweChunk) chunk; - CraftChunk craftChunk = (CraftChunk) fc.getChunk(); - net.minecraft.server.v1_8_R3.Chunk nmsChunk = craftChunk.getHandle(); - if (!craftChunk.isLoaded()) { - return false; - } - ChunkSection[] sections = nmsChunk.getSections(); - final boolean flag = craftChunk.getWorld().getEnvironment() == World.Environment.NORMAL; - if (mode == RelightMode.ALL) { - for (int i = 0; i < sections.length; i++) { - ChunkSection section = sections[i]; - if (section != null) { - section.a(new NibbleArray()); - if (flag) { - section.b(new NibbleArray()); - } + public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) { + if (mode == RelightMode.ALL) { + for (int i = 0; i < sections.length; i++) { + ChunkSection section = sections[i]; + if (section != null) { + section.a(new NibbleArray()); + if (sky) { + section.b(new NibbleArray()); } } } - if (flag) { - if (mode == RelightMode.ALL) { - nmsChunk.initLighting(); - } else { - int i = nmsChunk.g(); - for (int x = 0; x < 16; ++x) { - for (int z = 0; z < 16; ++z) { - int l = 15; - int y = i + 16 - 1; - do { - int opacity = nmsChunk.getTypeAbs(x, y, z).p(); - if (opacity == 0 && l != 15) { - opacity = 1; - } - l -= opacity; - if (l > 0) { - ChunkSection section = sections[y >> 4]; - if (section != null) { - section.a(x, y & 15, z, l); - } - } - --y; - } while (y > 0 && l > 0); - } - } - } - } - - if (fc.getTotalRelight() == 0 && mode == RelightMode.MINIMAL) { - return true; - } - net.minecraft.server.v1_8_R3.World nmsWorld = nmsChunk.getWorld(); - - int X = fc.getX() << 4; - int Z = fc.getZ() << 4; - - for (int j = 0; j < sections.length; j++) { - ChunkSection section = sections[j]; - if (section == null) { - continue; - } - if (((fc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (fc.getCount(j) == 0 && mode != RelightMode.ALL) || ((fc.getCount(j) >= 4096) && (fc.getAir(j) == 0)) || fc.getAir(j) == 4096) { - continue; - } - char[] array = section.getIdArray(); - if (array == null) { - continue; - } - if (mode == RelightMode.ALL) { - for (int k = array.length - 1; k >= 0; k--) { - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (isSurrounded(sections, x, y, z)) { - continue; - } - pos.c(X + x, y, Z + z); - nmsWorld.x(pos); - } - continue; - } - for (int k = array.length - 1; k >= 0; k--) { - final int i = array[k]; - final short id = (short) (i >> 4); - switch (id) { // Lighting - case 0: - continue; - default: - if (mode == RelightMode.MINIMAL) { - continue; - } - if (PseudoRandom.random.random(3) != 0) { - continue; - } - case 10: - case 11: - case 39: - case 40: - case 50: - case 51: - case 62: - case 74: - case 76: - case 89: - case 122: - case 124: - case 130: - case 138: - case 169: - case 213: - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (isSurrounded(sections, x, y, z)) { - continue; - } - pos.c(X + x, y, Z + z); - nmsWorld.x(pos); - } - } - } - return true; - } catch (Throwable e) { - if (Thread.currentThread() == Fawe.get().getMainThread()) { - MainUtil.handleError(e); - } } - return false; + return true; } - public boolean isSurrounded(ChunkSection[] sections, int x, int y, int z) { - return isSolid(getId(sections, x, y + 1, z)) - && isSolid(getId(sections, x + 1, y - 1, z)) - && isSolid(getId(sections, x - 1, y, z)) - && isSolid(getId(sections, x, y, z + 1)) - && isSolid(getId(sections, x, y, z - 1)); + @Override + public World createWorld(final WorldCreator creator) { + final String name = creator.name(); + ChunkGenerator generator = creator.generator(); + final CraftServer server = (CraftServer) Bukkit.getServer(); + final MinecraftServer console = server.getServer(); + final File folder = new File(server.getWorldContainer(), name); + final World world = server.getWorld(name); + final WorldType type = WorldType.getType(creator.type().getName()); + final boolean generateStructures = creator.generateStructures(); + if (world != null) { + return world; + } + if (folder.exists() && !folder.isDirectory()) { + throw new IllegalArgumentException("File exists with the name '" + name + "' and isn't a folder"); + } + if (generator == null) { + generator = server.getGenerator(name); + } + int dimension = 10 + console.worlds.size(); + boolean used = false; + do { + for (final WorldServer ws : console.worlds) { + used = (ws.dimension == dimension); + if (used) { + ++dimension; + break; + } + } + } while (used); + final boolean hardcore = false; + ServerNBTManager sdm = new ServerNBTManager(server.getWorldContainer(), name, true); + WorldData worlddata = sdm.getWorldData(); + final WorldSettings worldSettings; + if (worlddata == null) { + worldSettings = new WorldSettings(creator.seed(), WorldSettings.EnumGamemode.getById(server.getDefaultGameMode().getValue()), generateStructures, hardcore, type); + worldSettings.setGeneratorSettings(creator.generatorSettings()); + worlddata = new WorldData(worldSettings, name); + } else { + worldSettings = null; + } + worlddata.checkName(name); + final WorldServer internal = (WorldServer)new WorldServer(console, sdm, worlddata, dimension, console.methodProfiler, creator.environment(), generator).b(); + startSet(true); // Temporarily allow async chunk load since the world isn't added yet + internal.a(worldSettings); + endSet(true); + internal.scoreboard = server.getScoreboardManager().getMainScoreboard().getHandle(); + internal.tracker = new EntityTracker(internal); + internal.addIWorldAccess(new WorldManager(console, internal)); + internal.worldData.setDifficulty(EnumDifficulty.EASY); + internal.setSpawnFlags(true, true); + if (generator != null) { + internal.getWorld().getPopulators().addAll(generator.getDefaultPopulators(internal.getWorld())); + } + // Add the world + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(World value) { + console.worlds.add(internal); + server.getPluginManager().callEvent(new WorldInitEvent(internal.getWorld())); + server.getPluginManager().callEvent(new WorldLoadEvent(internal.getWorld())); + this.value = internal.getWorld(); + } + }); } - public boolean isSolid(int i) { - return Material.getMaterial(i).isOccluding(); + @Override + public boolean initLighting(Chunk chunk, ChunkSection[] sections, RelightMode mode) { + net.minecraft.server.v1_8_R3.Chunk c = ((CraftChunk) chunk).getHandle(); + if (mode == RelightMode.ALL) { + c.initLighting(); + } else { + int i = c.g(); + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + int l = 15; + int y = i + 16 - 1; + do { + int opacity = c.getTypeAbs(x, y, z).p(); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ChunkSection section = sections[y >> 4]; + if (section != null) { + section.a(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + return true; } - public int getId(ChunkSection[] sections, int x, int y, int z) { - if (x < 0 || x > 15 || z < 0 || z > 15) { - return 1; + + @Override + public int getSkyLight(ChunkSection[] sections, int x, int y, int z) { + ChunkSection section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 15; } - if (y < 0 || y > 255) { - return 1; - } - int i = FaweCache.CACHE_I[y][x][z]; - ChunkSection section = sections[i]; + return section.d(x, y & 15, z); + } + + @Override + public int getEmmittedLight(ChunkSection[] sections, int x, int y, int z) { + ChunkSection section = sections[FaweCache.CACHE_I[y][x][z]]; if (section == null) { return 0; } - char[] array = section.getIdArray(); - int j = FaweCache.CACHE_J[y][x][z]; - return array[j] >> 4; + return section.e(x, y & 15, z); + } + + @Override + public void relight(int x, int y, int z) { + pos.c(x, y, z); + nmsWorld.x(pos); + } + + private WorldServer nmsWorld; + + @Override + public World getImpWorld() { + World world = super.getImpWorld(); + this.nmsWorld = ((CraftWorld) world).getHandle(); + return super.getImpWorld(); } @Override diff --git a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java index 48586504..81d4e125 100644 --- a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java +++ b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java @@ -6,7 +6,6 @@ import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.object.BytePair; import com.boydti.fawe.object.FaweChunk; -import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; @@ -18,9 +17,11 @@ import com.sk89q.jnbt.LongTag; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.internal.Constants; +import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -44,22 +45,36 @@ import net.minecraft.server.v1_9_R2.EntityPlayer; import net.minecraft.server.v1_9_R2.EntityTracker; import net.minecraft.server.v1_9_R2.EntityTrackerEntry; import net.minecraft.server.v1_9_R2.EntityTypes; +import net.minecraft.server.v1_9_R2.EnumDifficulty; import net.minecraft.server.v1_9_R2.IBlockData; +import net.minecraft.server.v1_9_R2.IDataManager; +import net.minecraft.server.v1_9_R2.MinecraftServer; import net.minecraft.server.v1_9_R2.NBTTagCompound; import net.minecraft.server.v1_9_R2.NibbleArray; import net.minecraft.server.v1_9_R2.PacketPlayOutEntityDestroy; import net.minecraft.server.v1_9_R2.PacketPlayOutMapChunk; import net.minecraft.server.v1_9_R2.PlayerChunk; import net.minecraft.server.v1_9_R2.PlayerChunkMap; +import net.minecraft.server.v1_9_R2.ServerNBTManager; import net.minecraft.server.v1_9_R2.TileEntity; +import net.minecraft.server.v1_9_R2.WorldData; +import net.minecraft.server.v1_9_R2.WorldManager; import net.minecraft.server.v1_9_R2.WorldServer; +import net.minecraft.server.v1_9_R2.WorldSettings; +import net.minecraft.server.v1_9_R2.WorldType; +import org.bukkit.Bukkit; import org.bukkit.Chunk; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.World.Environment; +import org.bukkit.WorldCreator; import org.bukkit.block.Biome; import org.bukkit.craftbukkit.v1_9_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_9_R2.CraftServer; +import org.bukkit.craftbukkit.v1_9_R2.CraftWorld; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.generator.ChunkGenerator; public class BukkitQueue_1_9_R1 extends BukkitQueue_0 { @@ -187,142 +202,161 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0 0) { - ChunkSection section = sections[y >> 4]; - if (section != null) { - section.a(x, y & 15, z, l); - } - } - --y; - } while (y > 0 && l > 0); - } - } - } + } while (used); + final boolean hardcore = false; + final IDataManager sdm = new ServerNBTManager(server.getWorldContainer(), name, true, server.getHandle().getServer().getDataConverterManager()); + WorldData worlddata = sdm.getWorldData(); + final WorldSettings worldSettings; + if (worlddata == null) { + worldSettings = new WorldSettings(creator.seed(), WorldSettings.EnumGamemode.getById(server.getDefaultGameMode().getValue()), generateStructures, hardcore, type); + worldSettings.setGeneratorSettings(creator.generatorSettings()); + worlddata = new WorldData(worldSettings, name); + } else { + worldSettings = null; + } + worlddata.checkName(name); + final WorldServer internal = (WorldServer)new WorldServer(console, sdm, worlddata, dimension, console.methodProfiler, creator.environment(), generator).b(); + startSet(true); // Temporarily allow async chunk load since the world isn't added yet + internal.a(worldSettings); + endSet(true); + internal.scoreboard = server.getScoreboardManager().getMainScoreboard().getHandle(); + internal.tracker = new EntityTracker(internal); + internal.addIWorldAccess(new WorldManager(console, internal)); + internal.worldData.setDifficulty(EnumDifficulty.EASY); + internal.setSpawnFlags(true, true); + if (generator != null) { + internal.getWorld().getPopulators().addAll(generator.getDefaultPopulators(internal.getWorld())); + } + // Add the world + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(World value) { + console.worlds.add(internal); + server.getPluginManager().callEvent(new WorldInitEvent(internal.getWorld())); + server.getPluginManager().callEvent(new WorldLoadEvent(internal.getWorld())); + this.value = internal.getWorld(); } - if (((bc.getTotalRelight() == 0) && mode == RelightMode.MINIMAL)) { - return true; - } - if (mode == RelightMode.ALL) { - bc = getPrevious(bc, c.getSections(), null, null, null, true); - } - int total = bc.getTotalCount(); - net.minecraft.server.v1_9_R2.World w = c.world; - final int X = chunk.getX() << 4; - final int Z = chunk.getZ() << 4; - for (int j = sections.length - 1; j >= 0; j--) { - final Object section = sections[j]; - if (section == null) { - continue; - } - if (((bc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0)) || bc.getAir(j) == 4096) { - continue; - } - final char[] array = bc.getIdArray(j); - if (array == null) { - continue; - } - if (mode == RelightMode.ALL) { - for (int k = array.length - 1; k >= 0; k--) { - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { - continue; - } - pos.c(X + x, y, Z + z); - w.w(pos); - } - continue; - } - for (int k = array.length - 1; k >= 0; k--) { - final int i = array[k]; - final short id = (short) (i >> 4); - switch (id) { // Lighting - case 0: - continue; - default: - if (mode == RelightMode.MINIMAL) { - continue; - } - if (PseudoRandom.random.random(3) != 0) { - continue; - } - case 10: - case 11: - case 39: - case 40: - case 50: - case 51: - case 62: - case 74: - case 76: - case 89: - case 122: - case 124: - case 130: - case 138: - case 169: - case 213: - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { - continue; - } - pos.c(X + x, y, Z + z); - w.w(pos); - } - } - } - return true; - } catch (Throwable e) { - if (Thread.currentThread() == Fawe.get().getMainThread()) { - MainUtil.handleError(e); + }); + } + + @Override + public void setFullbright(ChunkSection[] sections) { + for (int i = 0; i < sections.length; i++) { + ChunkSection section = sections[i]; + if (section != null) { + byte[] bytes = section.getSkyLightArray().asBytes(); + Arrays.fill(bytes, Byte.MAX_VALUE); } } - return false; + } + + @Override + public boolean removeLighting(ChunkSection[] sections, RelightMode mode, boolean sky) { + if (mode == RelightMode.ALL) { + for (int i = 0; i < sections.length; i++) { + ChunkSection section = sections[i]; + if (section != null) { + section.a(new NibbleArray()); + if (sky) { + section.b(new NibbleArray()); + } + } + } + } + return true; + } + + @Override + public boolean initLighting(Chunk chunk, ChunkSection[] sections, RelightMode mode) { + net.minecraft.server.v1_9_R2.Chunk c = ((CraftChunk) chunk).getHandle(); + if (mode == RelightMode.ALL) { + c.initLighting(); + } else { + int i = c.g(); + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + int l = 15; + int y = i + 16 - 1; + do { + int opacity = c.a(x, y, z).c(); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ChunkSection section = sections[y >> 4]; + if (section != null) { + section.a(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + return true; + } + + + @Override + public int getSkyLight(ChunkSection[] sections, int x, int y, int z) { + ChunkSection section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 15; + } + return section.b(x, y & 15, z); + } + + @Override + public int getEmmittedLight(ChunkSection[] sections, int x, int y, int z) { + ChunkSection section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 0; + } + return section.c(x, y & 15, z); + } + + @Override + public void relight(int x, int y, int z) { + pos.c(x, y, z); + nmsWorld.w(pos); + } + + private WorldServer nmsWorld; + + @Override + public World getImpWorld() { + World world = super.getImpWorld(); + this.nmsWorld = ((CraftWorld) world).getHandle(); + return super.getImpWorld(); } public boolean isSurrounded(final char[][] sections, final int x, final int y, final int z) { @@ -333,12 +367,8 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0 clazzChunk = nmsChunk.getClass(); diff --git a/core/src/main/java/com/boydti/fawe/FaweAPI.java b/core/src/main/java/com/boydti/fawe/FaweAPI.java index 29232a23..8dad9510 100644 --- a/core/src/main/java/com/boydti/fawe/FaweAPI.java +++ b/core/src/main/java/com/boydti/fawe/FaweAPI.java @@ -2,6 +2,8 @@ package com.boydti.fawe; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.example.NMSMappedFaweQueue; +import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FaweQueue; @@ -24,12 +26,14 @@ import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.ShortTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.World; import java.io.File; @@ -368,6 +372,86 @@ public class FaweAPI { queue.fixLighting(queue.getFaweChunk(chunk.getX(), chunk.getZ()), mode); } + /** + * Fix the lighting in a selection
+ * - First removes all lighting, then relights + * - Relights in parallel (if enabled) for best performance
+ * - Also resends chunks
+ * @param world + * @param selection (assumes cuboid) + * @return + */ + public static int fixLighting(String world, Region selection) { + final Vector bot = selection.getMinimumPoint(); + final Vector top = selection.getMaximumPoint(); + + final int minX = bot.getBlockX() >> 4; + final int minZ = bot.getBlockZ() >> 4; + + final int maxX = top.getBlockX() >> 4; + final int maxZ = top.getBlockZ() >> 4; + + int count = 0; + final FaweQueue queue = SetQueue.IMP.getNewQueue(world, true, false); + // Remove existing lighting first + if (queue instanceof NMSMappedFaweQueue) { + final NMSMappedFaweQueue nmsQueue = (NMSMappedFaweQueue) queue; + boolean sky = nmsQueue.hasSky(); + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z <= maxZ; z ++) { + if (!nmsQueue.isChunkLoaded(x, z)) { + final int xf = x; + final int zf = z; + if (!TaskManager.IMP.syncWhenFree(new RunnableVal() { + @Override + public void run(Boolean value) { + this.value = nmsQueue.loadChunk(nmsQueue.getWorld(), xf, zf, false); + } + })) { + continue; + } + } + Object sections = nmsQueue.getCachedSections(nmsQueue.getWorld(), x, z); + nmsQueue.removeLighting(sections, FaweQueue.RelightMode.ALL, sky); + } + } + } + ArrayList threads = new ArrayList<>(); + for (int X = 0; X < 2; X++) { + for (int Z = 0; Z < 2; Z++) { + for (int x = minX + X; x <= maxX; x += 2) { + for (int z = minZ + Z; z <= maxZ; z += 2) { + final FaweChunk chunk = queue.getFaweChunk(x, z); + if (Settings.LIGHTING.ASYNC) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + queue.fixLightingSafe(chunk, FaweQueue.RelightMode.ALL); + queue.sendChunk(chunk, FaweQueue.RelightMode.NONE); + } + }); + thread.start(); + threads.add(thread); + } else { + queue.fixLightingSafe(chunk, FaweQueue.RelightMode.ALL); + queue.sendChunk(chunk, FaweQueue.RelightMode.NONE); + } + count++; + } + } + for (Thread thread : threads) { + try { + thread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + threads.clear(); + } + } + return count; + } + /** * If a schematic is too large to be pasted normally
* - Skips any block history diff --git a/core/src/main/java/com/boydti/fawe/FaweCache.java b/core/src/main/java/com/boydti/fawe/FaweCache.java index 1861038f..708037bc 100644 --- a/core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/core/src/main/java/com/boydti/fawe/FaweCache.java @@ -245,6 +245,138 @@ public class FaweCache { CACHE_COLOR[getCombined(35, 15)] = new Color(0, 0, 0); // Black } + public static LightType getLight(int id) { + switch (id) { // Lighting + case 0: + case 6: + case 27: + case 28: + case 31: + case 32: + case 37: + case 38: + case 55: + case 59: + case 65: + case 66: + case 69: + case 75: + case 77: + case 78: + case 83: + case 90: + case 93: + case 94: + case 104: + case 105: + case 106: + case 111: + case 115: + case 119: + case 127: + case 131: + case 132: + case 140: + case 141: + case 142: + case 143: + case 144: + case 149: + case 150: + case 157: + case 171: + case 175: + case 198: + case 199: + case 200: + case 207: + case 209: + case 217: + return LightType.TRANSPARENT; + case 39: + case 40: + case 50: + case 51: + case 76: + return LightType.TRANSPARENT_EMIT; + case 10: + case 11: + case 62: + case 74: + case 89: + case 122: + case 124: + case 130: + case 138: + case 169: + case 213: + return LightType.SOLID_EMIT; + default: + return LightType.OCCLUDING; + } + } + + public enum LightType { + TRANSPARENT, OCCLUDING, SOLID_EMIT, TRANSPARENT_EMIT + } + + public static boolean isTransparent(int id) { + switch (id) { + case 0: + case 6: + case 27: + case 28: + case 31: + case 32: + case 37: + case 38: + case 39: + case 40: + case 50: + case 51: + case 55: + case 59: + case 65: + case 66: + case 69: + case 75: + case 76: + case 77: + case 78: + case 83: + case 90: + case 93: + case 94: + case 104: + case 105: + case 106: + case 111: + case 115: + case 119: + case 127: + case 131: + case 132: + case 140: + case 141: + case 142: + case 143: + case 144: + case 149: + case 150: + case 157: + case 171: + case 175: + case 198: + case 199: + case 200: + case 207: + case 209: + case 217: + return true; + default: return false; + } + } + /** * Check if an id might have data * @param id diff --git a/core/src/main/java/com/boydti/fawe/command/FixLighting.java b/core/src/main/java/com/boydti/fawe/command/FixLighting.java index 07c55f96..564db9cc 100644 --- a/core/src/main/java/com/boydti/fawe/command/FixLighting.java +++ b/core/src/main/java/com/boydti/fawe/command/FixLighting.java @@ -1,11 +1,10 @@ package com.boydti.fawe.command; +import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.FaweQueue; -import com.boydti.fawe.util.SetQueue; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; @@ -29,25 +28,8 @@ public class FixLighting extends FaweCommand { if (selection == null) { selection = new CuboidRegion(new Vector(cx - 8, 0, cz - 8).multiply(16), new Vector(cx + 8, 0, cz + 8).multiply(16)); } - final Vector bot = selection.getMinimumPoint(); - final Vector top = selection.getMaximumPoint(); - - final int minX = bot.getBlockX() >> 4; - final int minZ = bot.getBlockZ() >> 4; - - final int maxX = top.getBlockX() >> 4; - final int maxZ = top.getBlockZ() >> 4; - - int count = 0; - FaweQueue queue = SetQueue.IMP.getNewQueue(loc.world, true, false); - for (int x = minX; x <= maxX; x++) { - for (int z = minZ; z <= maxZ; z++) { - queue.sendChunk(queue.getFaweChunk(x, z), FaweQueue.RelightMode.ALL); - count++; - } - } + int count = FaweAPI.fixLighting(loc.world, selection); BBC.FIX_LIGHTING_SELECTION.send(player, count); - return true; } } diff --git a/core/src/main/java/com/boydti/fawe/config/Settings.java b/core/src/main/java/com/boydti/fawe/config/Settings.java index 33e3524c..466c93f4 100644 --- a/core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/core/src/main/java/com/boydti/fawe/config/Settings.java @@ -202,10 +202,21 @@ public class Settings extends Config { } public static class LIGHTING { - @Comment("If chunk lighting should be done asynchronously") + @Comment({ + "If chunk lighting should be done asynchronously:", + " - Async lighting is less intense, but more likely to glitch" + }) public static boolean ASYNC = true; - @Comment("If all lighting should be fixed in a chunk that is edited") - public static boolean FIX_ALL = true; + @Comment({ + "The relighting mode to use:", + " - 0 = None (Do no relighting)", + " - 1 = Shadowless (Removes shadows)", + " - 2 = Minimal (Relight changed light sources)", + " - 3 = Fullbright (Relight changed light sources, remove shadows)", + " - 4 = Optimal (Relight changed light sources and changed blocks)", + " - 5 = All (Slowly relight every blocks)" + }) + public static int MODE = 4; } public static void save(File file) { diff --git a/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java b/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java index adebbedf..0e9a3829 100644 --- a/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java +++ b/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java @@ -120,6 +120,15 @@ public abstract class CharFaweChunk extends FaweChunk { return this.biomes; } + public int getCombinedId(int x, int y, int z) { + short i = FaweCache.CACHE_I[y][x][z]; + char[] array = getIdArray(i); + if (array == null) { + return 0; + } + return array[FaweCache.CACHE_J[y][x][z]]; + } + public HashMap tiles; @Override diff --git a/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java b/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java index f9164cf6..620a8051 100644 --- a/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java @@ -69,7 +69,7 @@ public abstract class MappedFaweQueue extends FaweQueue { super(world); } - public abstract WORLD getWorld(String world); + public abstract WORLD getImpWorld(); public abstract boolean isChunkLoaded(WORLD world, int x, int z); @@ -93,7 +93,7 @@ public abstract class MappedFaweQueue extends FaweQueue { if (impWorld != null) { return impWorld; } - return impWorld = getWorld(super.getWorldName()); + return impWorld = getImpWorld(); } @Override diff --git a/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java b/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java index 66540a0d..e5e2402a 100644 --- a/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java @@ -1,5 +1,7 @@ package com.boydti.fawe.example; +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweQueue; @@ -29,7 +31,7 @@ public abstract class NMSMappedFaweQueue ex @Override public void sendChunk(final FaweChunk fc, RelightMode mode) { if (mode == null) { - mode = Settings.LIGHTING.FIX_ALL ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL; + mode = FaweQueue.RelightMode.values()[Settings.LIGHTING.MODE]; } final RelightMode finalMode = mode; TaskManager.IMP.taskSoonMain(new Runnable() { @@ -56,6 +58,197 @@ public abstract class NMSMappedFaweQueue ex }, Settings.LIGHTING.ASYNC); } + public abstract boolean hasSky(); + + public abstract void setFullbright(CHUNKSECTION sections); + + public abstract boolean removeLighting(CHUNKSECTION sections, RelightMode mode, boolean hasSky); + + public abstract boolean initLighting(CHUNK chunk, CHUNKSECTION sections, RelightMode mode); + + public boolean isSurrounded(final char[][] sections, final int x, final int y, final int z) { + return this.isSolid(this.getId(sections, x, y + 1, z)) + && this.isSolid(this.getId(sections, x + 1, y - 1, z)) + && this.isSolid(this.getId(sections, x - 1, y, z)) + && this.isSolid(this.getId(sections, x, y, z + 1)) + && this.isSolid(this.getId(sections, x, y, z - 1)); + } + + public boolean isSolid(final int id) { + return !FaweCache.isTransparent(id); + } + + public int getId(final char[][] sections, final int x, final int y, final int z) { + if ((x < 0) || (x > 15) || (z < 0) || (z > 15)) { + return 1; + } + if ((y < 0) || (y > 255)) { + return 1; + } + final int i = FaweCache.CACHE_I[y][x][z]; + final char[] section = sections[i]; + if (section == null) { + return 0; + } + final int j = FaweCache.CACHE_J[y][x][z]; + return section[j] >> 4; + } + + public abstract void relight(int x, int y, int z); + + public abstract int getSkyLight(CHUNKSECTION sections, int x, int y, int z); + + public abstract int getEmmittedLight(CHUNKSECTION sections, int x, int y, int z); + + public int getLight(CHUNKSECTION sections, int x, int y, int z) { + if (!hasSky()) { + return getEmmittedLight(sections, x, y, z); + } + return Math.max(getSkyLight(sections, x, y, z), getEmmittedLight(sections, x, y, z)); + } + + @Override + public boolean fixLighting(FaweChunk fc, RelightMode mode) { + if (mode == RelightMode.NONE) { + return true; + } + try { + boolean async = Fawe.get().getMainThread() != Thread.currentThread(); + int cx = fc.getX(); + int cz = fc.getZ(); + if (!isChunkLoaded(cx, cz)) { + if (async) { + return false; + } + loadChunk(getWorld(), cx, cz, false); + } + CHUNKSECTION sections = getCachedSections(getWorld(), cx, cz); + boolean hasSky = hasSky(); + if (mode.ordinal() < 3) { + if (hasSky) { + setFullbright(sections); + } + } + CHUNK impChunk = (CHUNK) fc.getChunk(); + removeLighting(sections, mode, hasSky); + if (hasSky) { + initLighting(impChunk, sections, mode); + } + if (mode == RelightMode.SHADOWLESS) { + return true; + } + CharFaweChunk bc = (CharFaweChunk) fc; + if (((bc.getTotalRelight() != 0) || mode.ordinal() > 3)) { + if (mode == RelightMode.ALL) { + bc = getPrevious(bc, sections, null, null, null, true); + } + int total = bc.getTotalCount(); + final int X = cx << 4; + final int Z = cz << 4; + for (int j = 15; j >= 0; j--) { + if (((bc.getRelight(j) == 0) && mode.ordinal() <= 3) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0)) || bc.getAir(j) == 4096) { + continue; + } + final char[] array = bc.getIdArray(j); + if (array == null) { + continue; + } + switch (mode) { + case ALL: { + for (int k = 4095; k >= 0; k--) { + final int x = FaweCache.CACHE_X[j][k]; + final int y = FaweCache.CACHE_Y[j][k]; + if (y == 0) { + continue; + } + final int z = FaweCache.CACHE_Z[j][k]; + final int i = array[k]; + final short id = (short) (i >> 4); + switch (FaweCache.getLight(id)) { + case OCCLUDING: + if (y == 0 || !FaweCache.isTransparent(bc.getCombinedId(x, y - 1, z) >> 4)) { + continue; + } + break; + case TRANSPARENT_EMIT: + case SOLID_EMIT: + if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { + continue; + } + break; + case TRANSPARENT: + if (y >= 255) { + continue; + } + int light = getSkyLight(sections, x, y, z); + if (light != 0) { + continue; + } + break; + } + relight(X + x, y, Z + z); + } + break; + } + case OPTIMAL: { + for (int k = 4095; k >= 0; k--) { + final int x = FaweCache.CACHE_X[j][k]; + final int y = FaweCache.CACHE_Y[j][k]; + if (y == 0) { + continue; + } + final int z = FaweCache.CACHE_Z[j][k]; + final int i = array[k]; + final short id = (short) (i >> 4); + switch (FaweCache.getLight(id)) { + case OCCLUDING: + if (y == 0 || !FaweCache.isTransparent(bc.getCombinedId(x, y - 1, z) >> 4)) { + continue; + } + break; + case TRANSPARENT_EMIT: + case SOLID_EMIT: + if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { + continue; + } + break; + case TRANSPARENT: + continue; + } + relight(X + x, y, Z + z); + } + break; + } + case FULLBRIGHT: + case MINIMAL: { + for (int k = 4095; k >= 0; k--) { + final int x = FaweCache.CACHE_X[j][k]; + final int y = FaweCache.CACHE_Y[j][k]; + final int z = FaweCache.CACHE_Z[j][k]; + final int i = array[k]; + final short id = (short) (i >> 4); + switch (FaweCache.getLight(id)) { + case TRANSPARENT: + case OCCLUDING: + continue; + case TRANSPARENT_EMIT: + case SOLID_EMIT: + if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { + continue; + } + } + relight(X + x, y, Z + z); + } + break; + } + } + } + } + return true; + } catch (Throwable ignore) {} + return false; + } + public abstract void refreshChunk(WORLD world, CHUNK chunk); public abstract CharFaweChunk getPrevious(CharFaweChunk fs, CHUNKSECTION sections, Map tiles, Collection[] entities, Set createdEntities, boolean all) throws Exception; diff --git a/core/src/main/java/com/boydti/fawe/object/FaweChunk.java b/core/src/main/java/com/boydti/fawe/object/FaweChunk.java index 375eb390..74bcdc31 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweChunk.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweChunk.java @@ -82,7 +82,7 @@ public abstract class FaweChunk { * Fix the lighting in this chunk */ public void fixLighting() { - parent.fixLighting(this, Settings.LIGHTING.FIX_ALL ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL); + parent.fixLighting(this, FaweQueue.RelightMode.values()[Settings.LIGHTING.MODE]); } /** diff --git a/core/src/main/java/com/boydti/fawe/object/FaweCommand.java b/core/src/main/java/com/boydti/fawe/object/FaweCommand.java index 079744ea..f57d72f7 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweCommand.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweCommand.java @@ -23,9 +23,16 @@ public abstract class FaweCommand { public boolean executeSafe(final FawePlayer player, final String... args) { try { - if (player == null || !safe) { + if (!safe) { execute(player, args); return true; + } else if (player == null) { + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + execute(player, args); + } + }); } else { if (player.getMeta("fawe_action") != null) { BBC.WORLDEDIT_COMMAND_LIMIT.send(player); diff --git a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java index 27b3edfa..96c22852 100644 --- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -104,6 +104,9 @@ public abstract class FawePlayer { public FawePlayer(final T parent) { this.parent = parent; Fawe.get().register(this); + if (Settings.CLIPBOARD.USE_DISK) { + loadClipboardFromDisk(); + } if (getSession() == null || getPlayer() == null || session.getSize() != 0 || !Settings.HISTORY.USE_DISK) { return; } @@ -117,7 +120,6 @@ public abstract class FawePlayer { loadSessionsFromDisk(world); } } - loadClipboardFromDisk(); } catch (Exception e) { MainUtil.handleError(e); Fawe.debug("Failed to load history for: " + getName()); @@ -125,7 +127,8 @@ public abstract class FawePlayer { } public void loadClipboardFromDisk() { - File file = new File(Fawe.imp().getDirectory(), "clipboard" + File.separator + getUUID()); + System.out.println("LOADING CLIPBOARD FROM DISK"); + File file = new File(Fawe.imp().getDirectory(), "clipboard" + File.separator + getUUID() + ".bd"); try { if (file.exists() && file.length() > 5) { DiskOptimizedClipboard doc = new DiskOptimizedClipboard(file); @@ -143,6 +146,8 @@ public abstract class FawePlayer { ClipboardHolder holder = new ClipboardHolder(clip, worldData); getSession().setClipboard(holder); } + } else { + System.out.println("FILE: " + file.exists() + " | " + file + " | " + file.length()); } } catch (Exception ignore) { Fawe.debug("====== INVALID CLIPBOARD ======"); diff --git a/core/src/main/java/com/boydti/fawe/object/FaweQueue.java b/core/src/main/java/com/boydti/fawe/object/FaweQueue.java index 27b60abe..dd4e6f50 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweQueue.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MemUtil; @@ -36,7 +37,9 @@ public abstract class FaweQueue { public enum RelightMode { NONE, + SHADOWLESS, MINIMAL, + FULLBRIGHT, OPTIMAL, ALL, } @@ -129,6 +132,25 @@ public abstract class FaweQueue { public abstract void setChunk(final FaweChunk chunk); + public boolean fixLightingSafe(final FaweChunk chunk, final RelightMode mode) { + if (Settings.LIGHTING.ASYNC || Fawe.get().isMainThread()) { + try { + if (fixLighting(chunk, mode)) { + return true; + } + if (Fawe.get().isMainThread()) { + return false; + } + } catch (Throwable ignore) {} + } + return TaskManager.IMP.syncWhenFree(new RunnableVal() { + @Override + public void run(Boolean value) { + this.value = fixLighting(chunk, mode); + } + }); + } + public abstract boolean fixLighting(final FaweChunk chunk, RelightMode mode); public abstract boolean isChunkLoaded(final int x, final int z); diff --git a/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index 41b9b476..316d3a6f 100644 --- a/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -31,7 +31,7 @@ import java.util.UUID; /** * A clipboard with disk backed storage. (lower memory + loads on crash) * - Uses an auto closable RandomAccessFile for getting / setting id / data - * - I don't know how to reduce nbt / entities to O(1) complexity, so it is stored in memory. + * - I don't know how to reduce nbt / entities to O(2) complexity, so it is stored in memory. * * TODO load on join */ @@ -69,9 +69,10 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { long size = (raf.length() - HEADER_SIZE) >> 1; raf.seek(2); last = -1; - width = raf.readChar(); - height = raf.readChar(); - length = raf.readChar(); + width = (int) raf.readChar(); + height = (int) raf.readChar(); + length = (int) raf.readChar(); + System.out.println("DIMENSIONS " + width + " | " + height + " | " + length); area = width * length; autoCloseTask(); } @@ -151,11 +152,13 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { length = dimensions.getBlockZ(); long size = width * height * length * 2l + HEADER_SIZE; raf.setLength(size); - raf.seek(1); - last = -0; + raf.seek(2); + last = -1; + System.out.println("SET DIMENSIONS: " + width + "," + height + "," + length); raf.writeChar(width); raf.writeChar(height); raf.writeChar(length); + raf.flush(); } catch (IOException e) { MainUtil.handleError(e); } @@ -198,8 +201,9 @@ public class DiskOptimizedClipboard extends FaweClipboard implements Closeable { if (raf.length() != size) { raf.setLength(size); // write length etc - raf.seek(1); + raf.seek(2); last = -1; + System.out.println("SET DIMENSIONS 2: " + width + "," + height + "," + length); raf.writeChar(width); raf.writeChar(height); raf.writeChar(length); diff --git a/core/src/main/java/com/boydti/fawe/util/FaweTimer.java b/core/src/main/java/com/boydti/fawe/util/FaweTimer.java index f1479a14..63215644 100644 --- a/core/src/main/java/com/boydti/fawe/util/FaweTimer.java +++ b/core/src/main/java/com/boydti/fawe/util/FaweTimer.java @@ -6,7 +6,8 @@ public class FaweTimer implements Runnable { private int historyIndex = 0; private long lastPoll = System.currentTimeMillis(); private long tickStart = System.currentTimeMillis(); - private final long tickInterval = 50; + private final long tickInterval = 5; + private final double millisPer20Interval = tickInterval * 50 * 20; private long tick = 0; private long tickMod = 0; @@ -24,7 +25,7 @@ public class FaweTimer implements Runnable { { timeSpent = 1; } - double tps = tickInterval * 1000000.0 / timeSpent; + double tps = millisPer20Interval / timeSpent; history[historyIndex++] = tps; if (historyIndex >= history.length) { historyIndex = 0; @@ -66,7 +67,7 @@ public class FaweTimer implements Runnable { } return false; } - if (getTickMillis() < 50 || getTPS() < tps) { + if (getTickMillis() > 100 || getTPS() < tps) { skip = 10; skipTick = tick; } diff --git a/core/src/main/java/com/boydti/fawe/util/MathMan.java b/core/src/main/java/com/boydti/fawe/util/MathMan.java index 72b60541..58ef02d9 100644 --- a/core/src/main/java/com/boydti/fawe/util/MathMan.java +++ b/core/src/main/java/com/boydti/fawe/util/MathMan.java @@ -47,6 +47,10 @@ public class MathMan { return (((long)x) << 32) | (y & 0xffffffffL); } + public static long chunkXZ2Int(int x, int z) { + return (long)x & 4294967295L | ((long)z & 4294967295L) << 32; + } + public static int unpairIntX(long pair) { return (int)(pair >> 32); } diff --git a/core/src/main/java/com/boydti/fawe/util/SetQueue.java b/core/src/main/java/com/boydti/fawe/util/SetQueue.java index c577313d..ea69b679 100644 --- a/core/src/main/java/com/boydti/fawe/util/SetQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/SetQueue.java @@ -62,7 +62,7 @@ public class SetQueue { @Override public void run() { while (!tasks.isEmpty() && Fawe.get().getTimer().isAbove(18.5)) { - Runnable task = tasks.poll(); + tasks.poll().run(); } if (inactiveQueues.isEmpty() && activeQueues.isEmpty()) { lastSuccess = System.currentTimeMillis(); diff --git a/core/src/main/java/com/boydti/fawe/util/TaskManager.java b/core/src/main/java/com/boydti/fawe/util/TaskManager.java index 166e0f9d..90e8eac4 100644 --- a/core/src/main/java/com/boydti/fawe/util/TaskManager.java +++ b/core/src/main/java/com/boydti/fawe/util/TaskManager.java @@ -236,6 +236,10 @@ public abstract class TaskManager { } } + public T syncWhenFree(final RunnableVal function) { + return syncWhenFree(function, Integer.MAX_VALUE); + } + /** * Run a task on the main thread when the TPS is high enough, and wait for execution to finish:
* - Useful if you need to access something from the Bukkit API from another thread
diff --git a/forge110/build.gradle b/forge110/build.gradle new file mode 100644 index 00000000..31ad1fa2 --- /dev/null +++ b/forge110/build.gradle @@ -0,0 +1,88 @@ +buildscript { + repositories { + jcenter() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven {url = "https://oss.sonatype.org/content/repositories/snapshots/"} + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' + } +} + +apply plugin: 'net.minecraftforge.gradle.forge' +apply plugin: 'com.github.johnrengelman.shadow' + +dependencies { + compile project(':core') + compile 'org.spongepowered:spongeapi:4.+' + compile 'org.mcstats.sponge:metrics:R8-SNAPSHOT' + compile 'com.sk89q.worldedit:worldedit-forge-mc1.8.9:6.1.1' +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +repositories { + maven { + name = 'forge' + url = 'http://files.minecraftforge.net/maven' + } + maven { + name = "Sponge" + url = "https://repo.spongepowered.org/maven" + } + maven { + name = "Sponge Metrics" + url = "http://repo.mcstats.org/content/repositories/releases/" + } +} +minecraft { + version = "2014" + mappings = "snapshot_20160629" + runDir = 'run' +} + +project.archivesBaseName = "${project.archivesBaseName}-mc${minecraft.version}" + +processResources { + from(sourceSets.main.resources.srcDirs) { + expand 'version': project.version, + 'mcVersion': project.minecraft.version + exclude 'mcmod.info' + } +} + +shadowJar { + relocate 'org.yaml.snakeyaml', 'com.boydti.fawe.yaml' + dependencies { + include(dependency(':core')) + include(dependency('org.yaml:snakeyaml:1.16')) + } + archiveName = "${parent.name}-${project.name}-${parent.version}.jar" + destinationDir = file '../target' +} +shadowJar.doLast { + task -> + ant.checksum file: task.archivePath +} + + +reobf { + shadowJar { + mappingType = 'SEARGE' + } +} + +task deobfJar(type: Jar) { + from sourceSets.main.output + classifier = 'dev' +} + +artifacts { + archives deobfJar +} + +build.dependsOn(shadowJar) diff --git a/forge110/src/main/java/com/boydti/fawe/forge/FaweForge.java b/forge110/src/main/java/com/boydti/fawe/forge/FaweForge.java new file mode 100644 index 00000000..7382c121 --- /dev/null +++ b/forge110/src/main/java/com/boydti/fawe/forge/FaweForge.java @@ -0,0 +1,161 @@ +package com.boydti.fawe.forge; + + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.IFawe; +import com.boydti.fawe.forge.v0.ForgeQueue_All; +import com.boydti.fawe.object.FaweCommand; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.regions.FaweMaskManager; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.wrappers.WorldWrapper; +import com.mojang.authlib.GameProfile; +import com.sk89q.worldedit.forge.ForgeWorld; +import com.sk89q.worldedit.world.World; +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; +import javax.management.InstanceAlreadyExistsException; +import net.minecraft.command.ServerCommandManager; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fml.common.FMLCommonHandler; +import org.apache.logging.log4j.Logger; + +public class FaweForge implements IFawe { + + private final ForgeMain parent; + private final File directory; + private final Logger logger; + + public FaweForge(ForgeMain plugin, Logger logger, File directory) { + this.parent = plugin; + this.logger = logger; + this.directory = directory; + try { + Fawe.set(this); + } catch (InstanceAlreadyExistsException e) { + MainUtil.handleError(e); + } + } + + @Override + public void debug(String s) { + logger.debug(s); + } + + @Override + public File getDirectory() { + return directory; + } + + @Override + public void setupCommand(String label, FaweCommand cmd) { + if (TaskManager.IMP != null) { + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + ServerCommandManager scm = (ServerCommandManager) FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager(); + scm.registerCommand(new ForgeCommand(label, cmd)); + } + }); + } + } + + @Override + public FawePlayer wrap(Object obj) { + EntityPlayerMP player = null; + if (obj instanceof String) { + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + player = server.getPlayerList().getPlayerByUsername((String) obj); + } else if (obj instanceof UUID) { + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + player = server.getPlayerList().getPlayerByUUID((UUID) obj); + } else if (obj instanceof EntityPlayerMP) { + player = (EntityPlayerMP) obj; + } + if (player == null) { + return null; + } + FawePlayer existing = Fawe.get().getCachedPlayer(player.getName()); + return existing != null ? existing : new ForgePlayer(player); + } + + @Override + public void setupVault() { + // Do nothing + } + + @Override + public TaskManager getTaskManager() { + return new com.boydti.fawe.forge.ForgeTaskMan(512); + } + + @Override + public int[] getVersion() { + String[] version = FMLCommonHandler.instance().getMinecraftServerInstance().getMinecraftVersion().split("\\."); + return new int[] {Integer.parseInt(version[0]), Integer.parseInt(version[1]), Integer.parseInt(version[2])}; + } + + @Override + public String getWorldName(World world) { + if (world instanceof WorldWrapper) { + world = ((WorldWrapper) world).getParent(); + } + return ((ForgeWorld) world).getWorld().getWorldInfo().getWorldName(); + } + + @Override + public FaweQueue getNewQueue(String world, boolean dontCareIfFast) { + return new ForgeQueue_All(world); + } + + @Override + public Collection getMaskManagers() { + return new ArrayList<>(); + } + + @Override + public void startMetrics() { + try { + com.boydti.fawe.forge.ForgeMetrics metrics = new com.boydti.fawe.forge.ForgeMetrics("FastAsyncWorldEdit", "3.5.1"); + metrics.start(); + debug("[FAWE] &6Metrics enabled."); + } catch (Throwable e) { + debug("[FAWE] &cFailed to load up metrics."); + } + } + + @Override + public String getPlatform() { + return "forge"; + } + + @Override + public UUID getUUID(String name) { + try { + GameProfile profile = FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerProfileCache().getGameProfileForUsername(name); + return profile.getId(); + } catch (Throwable e) { + return null; + } + } + + @Override + public String getName(UUID uuid) { + try { + GameProfile profile = FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerProfileCache().getProfileByUUID(uuid); + return profile.getName(); + } catch (Throwable e) { + return null; + } + } + + @Override + public Object getBlocksHubApi() { + return null; + } +} diff --git a/forge110/src/main/java/com/boydti/fawe/forge/ForgeCommand.java b/forge110/src/main/java/com/boydti/fawe/forge/ForgeCommand.java new file mode 100644 index 00000000..f9ba9d22 --- /dev/null +++ b/forge110/src/main/java/com/boydti/fawe/forge/ForgeCommand.java @@ -0,0 +1,42 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.object.FaweCommand; +import com.boydti.fawe.object.FawePlayer; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; + +public class ForgeCommand extends CommandBase { + + private final String name; + private final FaweCommand cmd; + + public ForgeCommand(String name, FaweCommand cmd) { + this.name = name; + this.cmd = cmd; + } + + @Override + public String getCommandName() { + return name; + } + + @Override + public String getCommandUsage(ICommandSender iCommandSender) { + return "/" + name; + } + + @Override + public void execute(MinecraftServer minecraftServer, ICommandSender sender, String[] args) throws CommandException { + if ((sender instanceof EntityPlayerMP)) { + EntityPlayerMP player = (EntityPlayerMP) sender; + if (player.worldObj.isRemote) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + cmd.executeSafe(fp, args); + } + } +} diff --git a/forge110/src/main/java/com/boydti/fawe/forge/ForgeMain.java b/forge110/src/main/java/com/boydti/fawe/forge/ForgeMain.java new file mode 100644 index 00000000..c9faabe4 --- /dev/null +++ b/forge110/src/main/java/com/boydti/fawe/forge/ForgeMain.java @@ -0,0 +1,75 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FawePlayer; +import java.io.File; +import java.util.List; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerStoppingEvent; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; +import org.apache.logging.log4j.Logger; + +@Mod(modid = "com.boydti.fawe", name = "FastAsyncWorldEdit", version = "3.5.1", acceptableRemoteVersions = "*") +public class ForgeMain { + private static com.boydti.fawe.forge.FaweForge IMP; + private Logger logger; + + @Mod.EventHandler + public void preInit(FMLPreInitializationEvent event) { + this.logger = event.getModLog(); + File directory = new File(event.getModConfigurationDirectory() + File.separator + "FastAsyncWorldEdit"); + MinecraftForge.EVENT_BUS.register(this); + FMLCommonHandler.instance().bus().register(this); + this.IMP = new com.boydti.fawe.forge.FaweForge(this, event.getModLog(), directory); + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onPlayerQuit(PlayerEvent.PlayerLoggedOutEvent event) { + if (event.player.worldObj.isRemote) { + return; + } + handleQuit((EntityPlayerMP) event.player); + } + + @Mod.EventHandler + public void serverStopping(FMLServerStoppingEvent event) { + for (EntityPlayerMP player : (List)FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayerList()) { + handleQuit(player); + } + } + + public void handleQuit(EntityPlayerMP player) { + FawePlayer fp = FawePlayer.wrap(player); + fp.unregister(); + Fawe.get().unregister(player.getName()); + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onPlayerChangedWorld(EntityJoinWorldEvent event) { + Entity entity = event.getEntity(); + if (!(entity instanceof EntityPlayerMP)) { + return; + } + EntityPlayerMP player = (EntityPlayerMP) entity; + if (player.worldObj.isRemote) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + if (fp.getMeta("lastWorld") != event.getWorld()) { + fp.setMeta("lastWorld", event.getWorld()); + if (Settings.HISTORY.USE_DISK) { + fp.getSession().clearHistory(); + fp.loadSessionsFromDisk(fp.getWorld()); + } + } + } +} diff --git a/forge110/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge110/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java new file mode 100644 index 00000000..7115b1a3 --- /dev/null +++ b/forge110/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -0,0 +1,481 @@ +/* + * Copyright 2011-2013 Tyler Blair. All rights reserved. + * Ported to Minecraft Forge by Mike Primm + * 1.7.x update by Dries007 + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.boydti.fawe.forge; + +import com.boydti.fawe.util.MainUtil; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.UUID; +import java.util.zip.GZIPOutputStream; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +public class ForgeMetrics { + + /** + * The current revision number + */ + private final static int REVISION = 7; + + /** + * The base url of the metrics domain + */ + private static final String BASE_URL = "http://report.mcstats.org"; + + /** + * The url used to report a server's status + */ + private static final String REPORT_URL = "/plugin/%s"; + + /** + * Interval of time to ping (in minutes) + */ + private static final int PING_INTERVAL = 15; + + /** + * The mod this metrics submits for + */ + private final String modName; + + private final String modVersion; + + /** + * The metrics configuration file + */ + private final Configuration configuration; + + /** + * The metrics configuration file + */ + private final File configurationFile; + + /** + * Unique server id + */ + private final String guid; + + /** + * Debug mode + */ + private final boolean debug; + + private Thread thread = null; + private boolean firstPost = true; + int tickCount; + + public ForgeMetrics(final String modName, final String modVersion) throws IOException { + if (modName == null || modVersion == null) { + throw new IllegalArgumentException("modName and modVersion cannot be null"); + } + + this.modName = modName; + this.modVersion = modVersion; + + // load the config + configurationFile = getConfigFile(); + configuration = new Configuration(configurationFile); + + // Get values, and add some defaults, if needed + configuration.get(Configuration.CATEGORY_GENERAL, "opt-out", false, "Set to true to disable all reporting"); + guid = configuration.get(Configuration.CATEGORY_GENERAL, "guid", UUID.randomUUID().toString(), "Server unique ID").getString(); + debug = configuration.get(Configuration.CATEGORY_GENERAL, "debug", false, "Set to true for verbose debug").getBoolean(false); + configuration.save(); + } + + /** + * Start measuring statistics. This will immediately create an async + * repeating task as the plugin and send the initial data to the metrics + * backend, and then after that it will post in increments of PING_INTERVAL + * * 1200 ticks. + * + * @return True if statistics measuring is running, otherwise false. + */ + public boolean start() { + // Did we opt out? + if (isOptOut()) { + return false; + } + + FMLCommonHandler.instance().bus().register(this); + + return true; + } + + @SubscribeEvent + public void tick(TickEvent.ServerTickEvent tick) { + if (tick.phase != TickEvent.Phase.END) return; + + if (tickCount++ % (PING_INTERVAL * 1200) != 0) return; + + if (thread == null) { + thread = new Thread(new Runnable() { + public void run() { + try { + // Disable Task, if it is running and the server owner decided + // to opt-out + if (isOptOut()) { + FMLCommonHandler.instance().bus().unregister(ForgeMetrics.this); + return; + } + // We use the inverse of firstPost because if it + // is the first time we are posting, + // it is not a interval ping, so it evaluates to + // FALSE + // Each time thereafter it will evaluate to + // TRUE, i.e PING! + postPlugin(!firstPost); + // After the first post we set firstPost to + // false + // Each post thereafter will be a ping + firstPost = false; + } catch (IOException e) { + if (debug) { + FMLLog.info("[Metrics] Exception - %s", e.getMessage()); + } + } finally { + thread = null; + } + } + }); + thread.start(); + } + } + + /** + * Stop processing + */ + public void stop() { + } + + /** + * Has the server owner denied plugin metrics? + * + * @return true if metrics should be opted out of it + */ + public boolean isOptOut() { + // Reload the metrics file + configuration.load(); + return configuration.get(Configuration.CATEGORY_GENERAL, "opt-out", false).getBoolean(false); + } + + /** + * Enables metrics for the server by setting "opt-out" to false in the + * config file and starting the metrics task. + * + * @throws java.io.IOException + */ + public void enable() throws IOException { + // Check if the server owner has already set opt-out, if not, set it. + if (isOptOut()) { + configuration.getCategory(Configuration.CATEGORY_GENERAL).get("opt-out").set("false"); + configuration.save(); + } + // Enable Task, if it is not running + FMLCommonHandler.instance().bus().register(this); + } + + /** + * Disables metrics for the server by setting "opt-out" to true in the + * config file and canceling the metrics task. + * + * @throws java.io.IOException + */ + public void disable() throws IOException { + // Check if the server owner has already set opt-out, if not, set it. + if (!isOptOut()) { + configuration.getCategory(Configuration.CATEGORY_GENERAL).get("opt-out").set("true"); + configuration.save(); + } + FMLCommonHandler.instance().bus().unregister(this); + } + + /** + * Gets the File object of the config file that should be used to store data + * such as the GUID and opt-out status + * + * @return the File object for the config file + */ + public File getConfigFile() { + return new File(Loader.instance().getConfigDir(), "PluginMetrics.cfg"); + } + + /** + * Generic method that posts a plugin to the metrics website + */ + private void postPlugin(final boolean isPing) throws IOException { + // Server software specific section + String pluginName = modName; + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + boolean onlineMode = server.isServerInOnlineMode(); + String pluginVersion = modVersion; + String serverVersion; + if (server.isDedicatedServer()) { + serverVersion = "MinecraftForge (MC: " + server.getMinecraftVersion() + ")"; + } else { + serverVersion = "MinecraftForgeSSP (MC: " + server.getMinecraftVersion() + ")"; + } + int playersOnline = server.getCurrentPlayerCount(); + + // END server software specific section -- all code below does not use any code outside of this class / Java + + // Construct the post data + StringBuilder json = new StringBuilder(1024); + json.append('{'); + + // The plugin's description file containg all of the plugin data such as name, version, author, etc + appendJSONPair(json, "guid", guid); + appendJSONPair(json, "plugin_version", pluginVersion); + appendJSONPair(json, "server_version", serverVersion); + appendJSONPair(json, "players_online", Integer.toString(playersOnline)); + + // New data as of R6 + String osname = System.getProperty("os.name"); + String osarch = System.getProperty("os.arch"); + String osversion = System.getProperty("os.version"); + String java_version = System.getProperty("java.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + // normalize os arch .. amd64 -> x86_64 + if (osarch.equals("amd64")) { + osarch = "x86_64"; + } + + appendJSONPair(json, "osname", osname); + appendJSONPair(json, "osarch", osarch); + appendJSONPair(json, "osversion", osversion); + appendJSONPair(json, "cores", Integer.toString(coreCount)); + appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0"); + appendJSONPair(json, "java_version", java_version); + + // If we're pinging, append it + if (isPing) { + appendJSONPair(json, "ping", "1"); + } + + // close json + json.append('}'); + + // Create the url + URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName))); + + // Connect to the website + URLConnection connection; + + // Mineshafter creates a socks proxy, so we can safely bypass it + // It does not reroute POST requests so we need to go around it + if (isMineshafterPresent()) { + connection = url.openConnection(Proxy.NO_PROXY); + } else { + connection = url.openConnection(); + } + + + byte[] uncompressed = json.toString().getBytes(); + byte[] compressed = gzip(json.toString()); + + // Headers + connection.addRequestProperty("User-Agent", "MCStats/" + REVISION); + connection.addRequestProperty("Content-Type", "application/json"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", Integer.toString(compressed.length)); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + + connection.setDoOutput(true); + + // Write the data + OutputStream os = connection.getOutputStream(); + os.write(compressed); + os.flush(); + + // Now read the response + final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String response = reader.readLine(); + + // close resources + os.close(); + reader.close(); + + if (response == null || response.startsWith("ERR") || response.startsWith("7")) { + if (response == null) { + response = "null"; + } else if (response.startsWith("7")) { + response = response.substring(response.startsWith("7,") ? 2 : 1); + } + + throw new IOException(response); + } + } + + /** + * GZip compress a string of bytes + * + * @param input + * @return + */ + public static byte[] gzip(String input) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream gzos = null; + + try { + gzos = new GZIPOutputStream(baos); + gzos.write(input.getBytes("UTF-8")); + } catch (IOException e) { + MainUtil.handleError(e); + } finally { + if (gzos != null) try { + gzos.close(); + } catch (IOException ignore) { + } + } + + return baos.toByteArray(); + } + + /** + * Check if mineshafter is present. If it is, we need to bypass it to send POST requests + * + * @return true if mineshafter is installed on the server + */ + private boolean isMineshafterPresent() { + try { + Class.forName("mineshafter.MineServer"); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * Appends a json encoded key/value pair to the given string builder. + * + * @param json + * @param key + * @param value + * @throws java.io.UnsupportedEncodingException + */ + private static void appendJSONPair(StringBuilder json, String key, String value) throws UnsupportedEncodingException { + boolean isValueNumeric = false; + + try { + if (value.equals("0") || !value.endsWith("0")) { + Double.parseDouble(value); + isValueNumeric = true; + } + } catch (NumberFormatException e) { + isValueNumeric = false; + } + + if (json.charAt(json.length() - 1) != '{') { + json.append(','); + } + + json.append(escapeJSON(key)); + json.append(':'); + + if (isValueNumeric) { + json.append(value); + } else { + json.append(escapeJSON(value)); + } + } + + /** + * Escape a string to create a valid JSON string + * + * @param text + * @return + */ + private static String escapeJSON(String text) { + StringBuilder builder = new StringBuilder(); + + builder.append('"'); + for (int index = 0; index < text.length(); index++) { + char chr = text.charAt(index); + + switch (chr) { + case '"': + case '\\': + builder.append('\\'); + builder.append(chr); + break; + case '\b': + builder.append("\\b"); + break; + case '\t': + builder.append("\\t"); + break; + case '\n': + builder.append("\\n"); + break; + case '\r': + builder.append("\\r"); + break; + default: + if (chr < ' ') { + String t = "000" + Integer.toHexString(chr); + builder.append("\\u" + t.substring(t.length() - 4)); + } else { + builder.append(chr); + } + break; + } + } + builder.append('"'); + + return builder.toString(); + } + + /** + * Encode text as UTF-8 + * + * @param text the text to encode + * @return the encoded text, as UTF-8 + */ + private static String urlEncode(final String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, "UTF-8"); + } + +} \ No newline at end of file diff --git a/forge110/src/main/java/com/boydti/fawe/forge/ForgePlayer.java b/forge110/src/main/java/com/boydti/fawe/forge/ForgePlayer.java new file mode 100644 index 00000000..dab16474 --- /dev/null +++ b/forge110/src/main/java/com/boydti/fawe/forge/ForgePlayer.java @@ -0,0 +1,78 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FaweLocation; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.wrappers.PlayerWrapper; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.forge.ForgeWorldEdit; +import java.util.UUID; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentBase; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.world.World; + +public class ForgePlayer extends FawePlayer { + public ForgePlayer(EntityPlayerMP parent) { + super(parent); + } + + @Override + public void sendTitle(String head, String sub) { // Not supported + Settings.QUEUE.PROGRESS.DISPLAY = false; + } + + @Override + public void resetTitle() { // Not supported + Settings.QUEUE.PROGRESS.DISPLAY = false; + } + + @Override + public String getName() { + return parent.getName(); + } + + @Override + public UUID getUUID() { + return parent.getUniqueID(); + } + + @Override + public boolean hasPermission(String perm) { + Object meta = getMeta(perm); + return meta instanceof Boolean ? (boolean) meta : ForgeWorldEdit.inst.getPermissionsProvider().hasPermission(parent, perm); + } + + @Override + public void setPermission(String perm, boolean flag) { + setMeta(perm, flag); + } + + @Override + public void sendMessage(String msg) { + for (String part : msg.split("\n")) { + part = BBC.color(part); + TextComponentBase text = new TextComponentString(part); + this.parent.addChatMessage(text); + } + } + + @Override + public void executeCommand(String substring) { + throw new UnsupportedOperationException("NOT IMPLEMENTED"); + } + + @Override + public FaweLocation getLocation() { + World world = parent.worldObj; + BlockPos pos = parent.getPosition(); + return new FaweLocation(world.getWorldInfo().getWorldName(), pos.getX(), pos.getY(), pos.getZ()); + } + + @Override + public Player getPlayer() { + return PlayerWrapper.wrap(ForgeWorldEdit.inst.wrap(this.parent)); + } +} diff --git a/forge110/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge110/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java new file mode 100644 index 00000000..d1940818 --- /dev/null +++ b/forge110/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java @@ -0,0 +1,168 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.TaskManager; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +public class ForgeTaskMan extends TaskManager { + + private final ConcurrentLinkedDeque syncTasks = new ConcurrentLinkedDeque<>(); + private final ConcurrentLinkedDeque asyncTasks = new ConcurrentLinkedDeque<>(); + + private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(); + + + private final AtomicInteger taskId = new AtomicInteger(); + private final ExecutorService executor; + + public ForgeTaskMan(int size) { + this.executor = Executors.newFixedThreadPool(size); + FMLCommonHandler.instance().bus().register(this); + } + + + @Override + public int repeat(final Runnable r, final int interval) { + if (r == null) { + return -1; + } + int id = taskId.incrementAndGet(); + taskIdMap.put(id, r); + task(new Runnable() { + @Override + public void run() { + if (!taskIdMap.containsKey(id)) { + return; + } + try { + r.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + later(this, interval); + } + }); + return id; + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onServerTick(TickEvent.ServerTickEvent event) { + Fawe.get().setMainThread(); + int asyncSize = asyncTasks.size(); + for (int i = 0; i < asyncSize; i++) { + Runnable item = asyncTasks.poll(); + if (item != null) { + async(item); + } + } + int syncSize = syncTasks.size(); + for (int i = 0; i < syncSize; i++) { + Runnable item = syncTasks.poll(); + if (item != null) { + try { + item.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + } + } + + @Override + public int repeatAsync(Runnable r, int interval) { + if (r == null) { + return -1; + } + int id = taskId.incrementAndGet(); + taskIdMap.put(id, r); + async(new Runnable() { + @Override + public void run() { + if (!taskIdMap.containsKey(id)) { + return; + } + try { + r.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + laterAsync(this, interval); + } + }); + return id; + } + + @Override + public void async(Runnable r) { + if (r == null) { + return; + } + executor.execute(r); + } + + @Override + public void task(Runnable r) { + if (r == null) { + return; + } + syncTasks.add(r); + } + + @Override + public void later(Runnable r, int delay) { + if (r == null) { + return; + } + AtomicInteger remaining = new AtomicInteger(delay); + task(new Runnable() { + @Override + public void run() { + if (remaining.decrementAndGet() <= 0) { + try { + r.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + return; + } + task(this); + } + }); + } + + @Override + public void laterAsync(Runnable r, int delay) { + if (r == null) { + return; + } + AtomicInteger remaining = new AtomicInteger(delay); + task(new Runnable() { + @Override + public void run() { + if (remaining.decrementAndGet() <= 0) { + try { + async(r); + } catch (Throwable e) { + MainUtil.handleError(e); + } + return; + } + task(this); + } + }); + } + + @Override + public void cancel(int task) { + taskIdMap.remove(task); + } +} diff --git a/forge110/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java b/forge110/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java new file mode 100644 index 00000000..82f591af --- /dev/null +++ b/forge110/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java @@ -0,0 +1,114 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.util.MainUtil; +import java.lang.reflect.Field; +import net.minecraft.block.Block; +import net.minecraft.util.BitArray; +import net.minecraft.world.World; +import net.minecraft.world.chunk.BlockStateContainer; +import net.minecraft.world.chunk.BlockStatePaletteRegistry; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IBlockStatePalette; + +public class ForgeChunk_All extends CharFaweChunk { + + public BlockStateContainer[] sectionPalettes; + + /** + * A FaweSections object represents a chunk and the blocks that you wish to change in it. + * + * @param parent + * @param x + * @param z + */ + public ForgeChunk_All(FaweQueue parent, int x, int z) { + super(parent, x, z); + } + + @Override + public Chunk getNewChunk() { + World world = ((ForgeQueue_All) getParent()).getWorld(); + return world.getChunkProvider().provideChunk(getX(), getZ()); + } + + @Override + public CharFaweChunk copy(boolean shallow) { + ForgeChunk_All value = (ForgeChunk_All) super.copy(shallow); + if (sectionPalettes != null) { + value.sectionPalettes = new BlockStateContainer[16]; + try { + Field fieldBits = BlockStateContainer.class.getDeclaredField("storage"); + fieldBits.setAccessible(true); + Field fieldPalette = BlockStateContainer.class.getDeclaredField("palette"); + fieldPalette.setAccessible(true); + Field fieldSize = BlockStateContainer.class.getDeclaredField("bits"); + fieldSize.setAccessible(true); + for (int i = 0; i < sectionPalettes.length; i++) { + BlockStateContainer current = sectionPalettes[i]; + if (current == null) { + continue; + } + // Clone palette + IBlockStatePalette currentPalette = (IBlockStatePalette) fieldPalette.get(current); + if (!(currentPalette instanceof BlockStatePaletteRegistry)) { + current.onResize(128, null); + } + BlockStateContainer paletteBlock = new BlockStateContainer(); + currentPalette = (IBlockStatePalette) fieldPalette.get(current); + if (!(currentPalette instanceof BlockStatePaletteRegistry)) { + throw new RuntimeException("Palette must be global!"); + } + fieldPalette.set(paletteBlock, currentPalette); + // Clone size + fieldSize.set(paletteBlock, fieldSize.get(current)); + // Clone palette + BitArray currentBits = (BitArray) fieldBits.get(current); + BitArray newBits = new BitArray(1, 0); + for (Field field : BitArray.class.getDeclaredFields()) { + field.setAccessible(true); + Object currentValue = field.get(currentBits); + if (currentValue instanceof long[]) { + currentValue = ((long[]) currentValue).clone(); + } + field.set(newBits, currentValue); + } + fieldBits.set(paletteBlock, newBits); + value.sectionPalettes[i] = paletteBlock; + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + return value; + } + + public void optimize() { + if (sectionPalettes != null) { + return; + } + char[][] arrays = getCombinedIdArrays(); + char lastChar = Character.MAX_VALUE; + for (int layer = 0; layer < 16; layer++) { + if (getCount(layer) > 0) { + if (sectionPalettes == null) { + sectionPalettes = new BlockStateContainer[16]; + } + BlockStateContainer palette = new BlockStateContainer(); + char[] blocks = getIdArray(layer); + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + char combinedId = blocks[FaweCache.CACHE_J[y][x][z]]; + if (combinedId > 1) { + palette.set(x, y, z, Block.getBlockById(combinedId >> 4).getStateFromMeta(combinedId & 0xF)); + } + } + } + } + } + } + } +} diff --git a/forge110/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java b/forge110/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java new file mode 100644 index 00000000..7134fbaa --- /dev/null +++ b/forge110/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java @@ -0,0 +1,702 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.example.NMSMappedFaweQueue; +import com.boydti.fawe.object.BytePair; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityTracker; +import net.minecraft.entity.EntityTrackerEntry; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.play.server.SPacketChunkData; +import net.minecraft.network.play.server.SPacketDestroyEntities; +import net.minecraft.server.management.PlayerChunkMap; +import net.minecraft.server.management.PlayerChunkMapEntry; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BitArray; +import net.minecraft.util.ClassInheritanceMultiMap; +import net.minecraft.util.IntHashMap; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.BlockStateContainer; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IBlockStatePalette; +import net.minecraft.world.chunk.IChunkGenerator; +import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.chunk.NibbleArray; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import net.minecraft.world.gen.ChunkProviderServer; +import net.minecraftforge.fml.common.FMLCommonHandler; + +public class ForgeQueue_All extends NMSMappedFaweQueue { + + private Method methodFromNative; + private Method methodToNative; + + public ForgeQueue_All(String world) { + super(world); + try { + Class converter = Class.forName("com.sk89q.worldedit.forge.NBTConverter"); + this.methodFromNative = converter.getDeclaredMethod("toNative", Tag.class); + this.methodToNative = converter.getDeclaredMethod("fromNative", NBTBase.class); + methodFromNative.setAccessible(true); + methodToNative.setAccessible(true); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + private BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(0, 0, 0); + + @Override + public CompoundTag getTileEntity(Chunk chunk, int x, int y, int z) { + Map tiles = chunk.getTileEntityMap(); + pos.setPos(x, y, z); + TileEntity tile = tiles.get(pos); + return tile != null ? getTag(tile) : null; + } + + public CompoundTag getTag(TileEntity tile) { + try { + NBTTagCompound tag = new NBTTagCompound(); + tile.readFromNBT(tag); // readTagIntoEntity + return (CompoundTag) methodToNative.invoke(null, tag); + } catch (Exception e) { + MainUtil.handleError(e); + return null; + } + } + + @Override + public Chunk getChunk(World world, int x, int z) { + Chunk chunk = world.getChunkProvider().provideChunk(x, z); + if (chunk != null && !chunk.isLoaded()) { + chunk.onChunkLoad(); + } + return chunk; + } + + @Override + public boolean isChunkLoaded(int x, int z) { + return getWorld().getChunkProvider().getLoadedChunk(x, z) != null; + } + + @Override + public boolean regenerateChunk(World world, int x, int z) { + IChunkProvider provider = world.getChunkProvider(); + if (!(provider instanceof ChunkProviderServer)) { + return false; + } + + + try { + ChunkProviderServer chunkServer = (ChunkProviderServer) provider; + IChunkGenerator gen = chunkServer.chunkGenerator; + long pos = ChunkPos.chunkXZ2Int(x, z); + Chunk mcChunk; + if (chunkServer.chunkExists(x, z)) { + mcChunk = chunkServer.loadChunk(x, z); + mcChunk.onChunkUnload(); + } + PlayerChunkMap playerManager = ((WorldServer) getWorld()).getPlayerChunkMap(); + List oldWatchers = null; + if (chunkServer.chunkExists(x, z)) { + mcChunk = chunkServer.loadChunk(x, z); + PlayerChunkMapEntry entry = playerManager.getEntry(x, z); + if (entry != null) { + Field fieldPlayers = PlayerChunkMap.class.getDeclaredField("players"); + fieldPlayers.setAccessible(true); + oldWatchers = (List) fieldPlayers.get(entry); + playerManager.removeEntry(entry); + } + mcChunk.onChunkUnload(); + } + try { + Field droppedChunksSetField = chunkServer.getClass().getDeclaredField("field_73248_b"); + droppedChunksSetField.setAccessible(true); + Set droppedChunksSet = (Set) droppedChunksSetField.get(chunkServer); + droppedChunksSet.remove(pos); + } catch (Throwable e) { + MainUtil.handleError(e); + } + chunkServer.id2ChunkMap.remove(pos); + mcChunk = gen.provideChunk(x, z); + chunkServer.id2ChunkMap.put(pos, mcChunk); + if (mcChunk != null) { + mcChunk.onChunkLoad(); + mcChunk.populateChunk(chunkServer, chunkServer.chunkGenerator); + } + if (oldWatchers != null) { + for (EntityPlayerMP player : oldWatchers) { + playerManager.addPlayer(player); + } + } + return true; + } catch (Throwable t) { + MainUtil.handleError(t); + return false; + } + } + + @Override + public boolean loadChunk(World world, int x, int z, boolean generate) { + return getCachedSections(world, x, z) != null; + } + + @Override + public ExtendedBlockStorage[] getCachedSections(World world, int x, int z) { + Chunk chunk = world.getChunkProvider().provideChunk(x, z); + if (chunk != null && !chunk.isLoaded()) { + chunk.onChunkLoad(); + } + return chunk == null ? null : chunk.getBlockStorageArray(); + } + + @Override + public BlockStateContainer getCachedSection(ExtendedBlockStorage[] chunk, int cy) { + ExtendedBlockStorage value = chunk[cy]; + return value == null ? null : value.getData(); + } + + @Override + public int getCombinedId4Data(BlockStateContainer ls, int x, int y, int z) { + IBlockState ibd = lastSection.get(x & 15, y & 15, z & 15); + Block block = ibd.getBlock(); + int id = Block.getIdFromBlock(block); + if (FaweCache.hasData(id)) { + return (id << 4) + block.getMetaFromState(ibd); + } else { + return id << 4; + } + } + + @Override + public boolean isChunkLoaded(World world, int x, int z) { + return world.getChunkProvider().getLoadedChunk(x, z) != null; + } + + public void setCount(int tickingBlockCount, int nonEmptyBlockCount, ExtendedBlockStorage section) throws NoSuchFieldException, IllegalAccessException { + Class clazz = section.getClass(); + Field fieldTickingBlockCount = clazz.getDeclaredField("field_76683_c"); + Field fieldNonEmptyBlockCount = clazz.getDeclaredField("field_76682_b"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + fieldTickingBlockCount.set(section, tickingBlockCount); + fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount); + } + + @Override + public CharFaweChunk getPrevious(CharFaweChunk fs, ExtendedBlockStorage[] sections, Map tilesGeneric, Collection[] entitiesGeneric, Set createdEntities, boolean all) throws Exception { + Map tiles = (Map) tilesGeneric; + ClassInheritanceMultiMap[] entities = (ClassInheritanceMultiMap[]) entitiesGeneric; + CharFaweChunk previous = (CharFaweChunk) getFaweChunk(fs.getX(), fs.getZ()); + char[][] idPrevious = new char[16][]; + for (int layer = 0; layer < sections.length; layer++) { + if (fs.getCount(layer) != 0 || all) { + ExtendedBlockStorage section = sections[layer]; + if (section != null) { + short solid = 0; + char[] previousLayer = idPrevious[layer] = new char[4096]; + BlockStateContainer blocks = section.getData(); + for (int j = 0; j < 4096; j++) { + int x = FaweCache.CACHE_X[0][j]; + int y = FaweCache.CACHE_Y[0][j]; + int z = FaweCache.CACHE_Z[0][j]; + IBlockState ibd = blocks.get(x, y, z); + Block block = ibd.getBlock(); + int combined = Block.getIdFromBlock(block); + if (FaweCache.hasData(combined)) { + combined = (combined << 4) + block.getMetaFromState(ibd); + } else { + combined = combined << 4; + } + if (combined > 1) { + solid++; + } + previousLayer[j] = (char) combined; + } + previous.count[layer] = solid; + previous.air[layer] = (short) (4096 - solid); + } + } + } + previous.ids = idPrevious; + if (tiles != null) { + for (Map.Entry entry : tiles.entrySet()) { + TileEntity tile = entry.getValue(); + NBTTagCompound tag = new NBTTagCompound(); + tile.readFromNBT(tag); // readTileEntityIntoTag + BlockPos pos = entry.getKey(); + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(null, tag); + previous.setTile(pos.getX(), pos.getY(), pos.getZ(), nativeTag); + } + } + if (entities != null) { + for (Collection entityList : entities) { + for (Entity ent : entityList) { + if (ent instanceof EntityPlayer || (!createdEntities.isEmpty() && createdEntities.contains(ent.getUniqueID()))) { + continue; + } + int x = ((int) Math.round(ent.posX) & 15); + int z = ((int) Math.round(ent.posZ) & 15); + int y = (int) Math.round(ent.posY); + int i = FaweCache.CACHE_I[y][x][z]; + char[] array = fs.getIdArray(i); + if (array == null) { + continue; + } + int j = FaweCache.CACHE_J[y][x][z]; + if (array[j] != 0) { + String id = EntityList.getEntityString(ent); + if (id != null) { + NBTTagCompound tag = ent.getEntityData(); // readEntityIntoTag + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(null, tag); + Map map = ReflectionUtils.getMap(nativeTag.getValue()); + map.put("Id", new StringTag(id)); + previous.setEntity(nativeTag); + } + } + } + } + } + return previous; + } + + private final IBlockState air = Blocks.AIR.getDefaultState(); + + @Override + public boolean setComponents(FaweChunk fc, RunnableVal changeTask) { + ForgeChunk_All fs = (ForgeChunk_All) fc; + net.minecraft.world.chunk.Chunk nmsChunk = fs.getChunk(); + nmsChunk.setModified(true); + net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); + try { + boolean flag = !nmsWorld.provider.getHasNoSky(); + // Sections + ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); + Map tiles = nmsChunk.getTileEntityMap(); + ClassInheritanceMultiMap[] entities = nmsChunk.getEntityLists(); + + + // Remove entities + for (int i = 0; i < 16; i++) { + int count = fs.getCount(i); + if (count == 0) { + continue; + } else if (count >= 4096) { + entities[i] = new ClassInheritanceMultiMap<>(Entity.class); + } else { + char[] array = fs.getIdArray(i); + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entity instanceof EntityPlayer) { + continue; + } + int x = ((int) Math.round(entity.posX) & 15); + int z = ((int) Math.round(entity.posZ) & 15); + int y = (int) Math.round(entity.posY); + if (array == null) { + continue; + } + if (y < 0 || y > 255 || array[FaweCache.CACHE_J[y][x][z]] != 0) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Set entities + Set createdEntities = new HashSet<>(); + Set entitiesToSpawn = fs.getEntities(); + for (CompoundTag nativeTag : entitiesToSpawn) { + Map entityTagMap = nativeTag.getValue(); + StringTag idTag = (StringTag) entityTagMap.get("Id"); + ListTag posTag = (ListTag) entityTagMap.get("Pos"); + ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + if (idTag == null || posTag == null || rotTag == null) { + Fawe.debug("Unknown entity tag: " + nativeTag); + continue; + } + double x = posTag.getDouble(0); + double y = posTag.getDouble(1); + double z = posTag.getDouble(2); + float yaw = rotTag.getFloat(0); + float pitch = rotTag.getFloat(1); + String id = idTag.getValue(); + NBTTagCompound tag = (NBTTagCompound)methodFromNative.invoke(null, nativeTag); + Entity entity = EntityList.createEntityFromNBT(tag, nmsWorld); + if (entity != null) { + entity.setPositionAndRotation(x, y, z, yaw, pitch); + nmsWorld.spawnEntityInWorld(entity); + } + } + // Run change task if applicable + if (changeTask != null) { + CharFaweChunk previous = getPrevious(fs, sections, tiles, entities, createdEntities, false); + changeTask.run(previous); + } + // Trim tiles + Set> entryset = tiles.entrySet(); + Iterator> iterator = entryset.iterator(); + while (iterator.hasNext()) { + Map.Entry tile = iterator.next(); + BlockPos pos = tile.getKey(); + int lx = pos.getX() & 15; + int ly = pos.getY(); + int lz = pos.getZ() & 15; + int j = FaweCache.CACHE_I[ly][lx][lz]; + char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + int k = FaweCache.CACHE_J[ly][lx][lz]; + if (array[k] != 0) { + tile.getValue().invalidate();; + iterator.remove(); + } + } + HashSet entsToRemove = fs.getEntityRemoves(); + if (entsToRemove.size() > 0) { + for (int i = 0; i < entities.length; i++) { + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entsToRemove.contains(entity.getUniqueID())) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Efficiently merge sections + for (int j = 0; j < sections.length; j++) { + int count = fs.getCount(j); + if (count == 0) { + continue; + } + final char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + ExtendedBlockStorage section = sections[j]; + if (section == null) { + if (fs.sectionPalettes != null && fs.sectionPalettes[j] != null) { + section = sections[j] = new ExtendedBlockStorage(j << 4, flag); + setPalette(section, fs.sectionPalettes[j]); + setCount(0, count - fs.getAir(j), section); + } else { + sections[j] = new ExtendedBlockStorage(j << 4, flag); + } + } else if (count >= 4096) { + if (fs.sectionPalettes != null && fs.sectionPalettes[j] != null) { + setPalette(section, fs.sectionPalettes[j]); + setCount(0, count - fs.getAir(j), section); + continue; + } else { + sections[j] = new ExtendedBlockStorage(j << 4, flag); + } + } + BlockStateContainer nibble = section.getData(); + Field fieldBits = BlockStateContainer.class.getDeclaredField("storage"); + fieldBits.setAccessible(true); + BitArray bits = (BitArray) fieldBits.get(nibble); + + Field fieldPalette = BlockStateContainer.class.getDeclaredField("palette"); + fieldPalette.setAccessible(true); + IBlockStatePalette palette = (IBlockStatePalette) fieldPalette.get(nibble); + int nonEmptyBlockCount = 0; + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + char combinedId = array[FaweCache.CACHE_J[y][x][z]]; + switch (combinedId) { + case 0: + IBlockState existing = nibble.get(x, y, z); + if (existing != air) { + nonEmptyBlockCount++; + } + continue; + case 1: + nibble.set(x, y, z, air); + continue; + default: + nonEmptyBlockCount++; + nibble.set(x, y, z, Block.getBlockById(combinedId >> 4).getStateFromMeta(combinedId & 0xF)); + } + } + } + } + setCount(0, nonEmptyBlockCount, section); + } + // Set biomes + int[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + int biome = array[z]; + if (biome == 0) { + continue; + } + nmsChunk.getBiomeArray()[((z & 0xF) << 4 | x & 0xF)] = (byte) biome; + } + } + } + // Set tiles + Map tilesToSpawn = fs.getTiles(); + int bx = fs.getX() << 4; + int bz = fs.getZ() << 4; + + for (Map.Entry entry : tilesToSpawn.entrySet()) { + CompoundTag nativeTag = entry.getValue(); + BytePair pair = entry.getKey(); + BlockPos pos = new BlockPos(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity != null) { + NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(null, nativeTag); + tileEntity.readFromNBT(tag); // ReadTagIntoTile + } + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + int[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + int biome = array[z]; + if (biome == 0) { + continue; + } + nmsChunk.getBiomeArray()[((z & 0xF) << 4 | x & 0xF)] = (byte) biome; + } + } + } + sendChunk(fs, null); + return true; + } + + public void setPalette(ExtendedBlockStorage section, BlockStateContainer palette) throws NoSuchFieldException, IllegalAccessException { + Field fieldSection = ExtendedBlockStorage.class.getDeclaredField("data"); + fieldSection.setAccessible(true); + fieldSection.set(section, palette); + } + + @Override + public void refreshChunk(World world, net.minecraft.world.chunk.Chunk nmsChunk) { + if (!nmsChunk.isLoaded()) { + return; + } + try { + ChunkPos pos = nmsChunk.getChunkCoordIntPair(); + WorldServer w = (WorldServer) nmsChunk.getWorld(); + PlayerChunkMap chunkMap = w.getPlayerChunkMap(); + int x = pos.chunkXPos; + int z = pos.chunkZPos; + if (!chunkMap.contains(x, z)) { + return; + } + EntityTracker tracker = w.getEntityTracker(); + HashSet players = new HashSet<>(); + for (EntityPlayer player : w.playerEntities) { + if (player instanceof EntityPlayerMP) { + if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) { + players.add((EntityPlayerMP) player); + } + } + } + if (players.size() == 0) { + return; + } + HashSet entities = new HashSet<>(); + ClassInheritanceMultiMap[] entitieSlices = nmsChunk.getEntityLists(); + IntHashMap entries = null; + for (Field field : tracker.getClass().getDeclaredFields()) { + if (field.getType() == IntHashMap.class) { + field.setAccessible(true); + entries = (IntHashMap) field.get(tracker); + } + } + for (ClassInheritanceMultiMap slice : entitieSlices) { + if (slice == null) { + continue; + } + for (Entity ent : slice) { + EntityTrackerEntry entry = entries != null ? entries.lookup(ent.getEntityId()) : null; + if (entry == null) { + continue; + } + entities.add(entry); + SPacketDestroyEntities packet = new SPacketDestroyEntities(ent.getEntityId()); + for (EntityPlayerMP player : players) { + player.connection.sendPacket(packet); + } + } + } + // Send chunks + SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535); + for (EntityPlayerMP player : players) { + player.connection.sendPacket(packet); + } + // send ents + for (EntityTrackerEntry entry : entities) { + try { + TaskManager.IMP.later(new Runnable() { + @Override + public void run() { + for (EntityPlayerMP player : players) { + boolean result = entry.trackingPlayers.remove(player); + if (result && entry.getTrackedEntity() != player) { + entry.updatePlayerEntity(player); + } + } + } + }, 2); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + + @Override + public FaweChunk getFaweChunk(int x, int z) { + return new ForgeChunk_All(this, x, z); + } + + @Override + public boolean removeLighting(ExtendedBlockStorage[] sections, RelightMode mode, boolean sky) { + if (mode == RelightMode.ALL) { + for (int i = 0; i < sections.length; i++) { + ExtendedBlockStorage section = sections[i]; + if (section != null) { + section.setBlocklightArray(new NibbleArray()); + if (sky) { + section.setSkylightArray(new NibbleArray()); + } + } + } + } + return true; + } + + @Override + public boolean hasSky() { + return nmsWorld.provider.getHasNoSky(); + } + + @Override + public boolean initLighting(Chunk nmsChunk, ExtendedBlockStorage[] sections, RelightMode mode) { + if (mode == RelightMode.ALL) { + nmsChunk.generateSkylightMap(); + } else { + int i = nmsChunk.getTopFilledSegment(); + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + int l = 15; + int y = i + 16 - 1; + do { + int opacity = nmsChunk.getBlockLightOpacity(new BlockPos(x, y, z)); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ExtendedBlockStorage section = sections[y >> 4]; + if (section != null) { + section.setExtSkylightValue(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + return true; + } + + @Override + public void setFullbright(ExtendedBlockStorage[] sections) { + for (int i = 0; i < sections.length; i++) { + ExtendedBlockStorage section = sections[i]; + if (section != null) { + byte[] bytes = section.getSkylightArray().getData(); + Arrays.fill(bytes, (byte) 255); + } + } + } + + @Override + public int getSkyLight(ExtendedBlockStorage[] sections, int x, int y, int z) { + ExtendedBlockStorage section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 15; + } + return section.getExtSkylightValue(x, y & 15, z); + } + + @Override + public int getEmmittedLight(ExtendedBlockStorage[] sections, int x, int y, int z) { + ExtendedBlockStorage section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 0; + } + return section.getExtBlocklightValue(x, y & 15, z); + } + + @Override + public void relight(int x, int y, int z) { + pos.setPos(x, y, z); + nmsWorld.checkLight(pos); + } + + private WorldServer nmsWorld; + + @Override + public World getImpWorld() { + WorldServer[] worlds = FMLCommonHandler.instance().getMinecraftServerInstance().worldServers; + for (WorldServer ws : worlds) { + if (ws.getWorldInfo().getWorldName().equals(getWorldName())) { + return nmsWorld = ws; + } + } + return null; + } +} diff --git a/forge110/src/main/resources/config.yml b/forge110/src/main/resources/config.yml new file mode 100644 index 00000000..e69de29b diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java b/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java index a84dce1b..fbad5c97 100644 --- a/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java +++ b/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java @@ -7,7 +7,6 @@ import com.boydti.fawe.example.NMSMappedFaweQueue; import com.boydti.fawe.object.BytePair; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.IntegerPair; -import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; @@ -20,6 +19,7 @@ import com.sk89q.jnbt.Tag; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityTracker; @@ -132,17 +131,6 @@ public class ForgeQueue_All extends NMSMappedFaweQueue fc, RelightMode mode) { - if (mode == RelightMode.NONE) { - return true; - } - try { - ForgeChunk_All bc = (ForgeChunk_All) fc; - net.minecraft.world.chunk.Chunk nmsChunk = bc.getChunk(); - if (!nmsChunk.isChunkLoaded) { - return false; - } - World nmsWorld = nmsChunk.worldObj; - boolean flag = !nmsWorld.provider.hasNoSky; - ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); - if (mode == RelightMode.ALL) { - for (int i = 0; i < sections.length; i++) { - ExtendedBlockStorage section = sections[i]; - if (section != null) { - section.setBlocklightArray(new NibbleArray(4096, 4)); - if (flag) { - section.setSkylightArray(new NibbleArray(4096, 4)); - } + public boolean removeLighting(ExtendedBlockStorage[] sections, RelightMode mode, boolean sky) { + if (mode == RelightMode.ALL) { + for (int i = 0; i < sections.length; i++) { + ExtendedBlockStorage section = sections[i]; + if (section != null) { + section.setBlocklightArray(new NibbleArray(4096, 4)); + if (sky) { + section.setSkylightArray(new NibbleArray(4096, 4)); } } } - if (flag) { - if (mode == RelightMode.ALL) { - nmsChunk.generateSkylightMap(); - } else { - int i = nmsChunk.getTopFilledSegment(); - for (int x = 0; x < 16; ++x) { - for (int z = 0; z < 16; ++z) { - int l = 15; - int y = i + 16 - 1; - do { - int opacity = nmsChunk.func_150808_b(x, y, z); - if (opacity == 0 && l != 15) { - opacity = 1; - } - l -= opacity; - if (l > 0) { - ExtendedBlockStorage section = sections[y >> 4]; - if (section != null) { - section.setExtSkylightValue(x, y & 15, z, l); - } - } - --y; - } while (y > 0 && l > 0); - } - } - } - } - if (bc.getTotalRelight() == 0 && mode == RelightMode.MINIMAL) { - return true; - } - int X = fc.getX() << 4; - int Z = fc.getZ() << 4; - - for (int j = 0; j < sections.length; j++) { - ExtendedBlockStorage section = sections[j]; - if (section == null) { - continue; - } - if (((bc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0)) || bc.getAir(j) == 4096) { - continue; - } - byte[] array = section.getBlockLSBArray(); - if (mode == RelightMode.ALL) { - for (int k = array.length - 1; k >= 0; k--) { - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (isSurrounded(sections, x, y, z)) { - continue; - } - nmsWorld.func_147451_t(X + x, y, Z + z); - } - continue; - } - for (int k = array.length - 1; k >= 0; k--) { - final int i = array[k]; - final short id = (short) (i >> 4); - switch (id) { // Lighting - case 0: - continue; - default: - if (mode == RelightMode.MINIMAL) { - continue; - } - if (PseudoRandom.random.random(3) != 0) { - continue; - } - case 10: - case 11: - case 39: - case 40: - case 50: - case 51: - case 62: - case 74: - case 76: - case 89: - case 122: - case 124: - case 130: - case 138: - case 169: - case 213: - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (isSurrounded(sections, x, y, z)) { - continue; - } - nmsWorld.func_147451_t(X + x, y, Z + z); - } - } - } - return true; - } catch (Throwable e) { - if (Thread.currentThread() == Fawe.get().getMainThread()) { - MainUtil.handleError(e); - } } - return false; + return true; } - public boolean isSurrounded(ExtendedBlockStorage[] sections, int x, int y, int z) { - return isSolid(getId(sections, x, y + 1, z)) - && isSolid(getId(sections, x + 1, y - 1, z)) - && isSolid(getId(sections, x - 1, y, z)) - && isSolid(getId(sections, x, y, z + 1)) - && isSolid(getId(sections, x, y, z - 1)); + @Override + public boolean hasSky() { + return nmsWorld.provider.hasNoSky; } - public boolean isSolid(int i) { - return i != 0 && Block.getBlockById(i).isOpaqueCube(); + @Override + public boolean initLighting(Chunk nmsChunk, ExtendedBlockStorage[] sections, RelightMode mode) { + if (mode == RelightMode.ALL) { + nmsChunk.generateSkylightMap(); + } else { + int i = nmsChunk.getTopFilledSegment(); + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + int l = 15; + int y = i + 16 - 1; + do { + int opacity = nmsChunk.func_150808_b(x, y, z); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ExtendedBlockStorage section = sections[y >> 4]; + if (section != null) { + section.setExtSkylightValue(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + return true; } - public int getId(ExtendedBlockStorage[] sections, int x, int y, int z) { - if (x < 0 || x > 15 || z < 0 || z > 15) { - return 1; + @Override + public void setFullbright(ExtendedBlockStorage[] sections) { + for (int i = 0; i < sections.length; i++) { + ExtendedBlockStorage section = sections[i]; + if (section != null) { + byte[] bytes = section.getSkylightArray().data; + Arrays.fill(bytes, (byte) 255); + } } - if (y < 0 || y > 255) { - return 1; + } + + @Override + public int getSkyLight(ExtendedBlockStorage[] sections, int x, int y, int z) { + ExtendedBlockStorage section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 15; } - int i = FaweCache.CACHE_I[y][x][z]; - ExtendedBlockStorage section = sections[i]; + return section.getExtSkylightValue(x, y & 15, z); + } + + @Override + public int getEmmittedLight(ExtendedBlockStorage[] sections, int x, int y, int z) { + ExtendedBlockStorage section = sections[FaweCache.CACHE_I[y][x][z]]; if (section == null) { return 0; } - byte[] array = section.getBlockLSBArray(); - int j = FaweCache.CACHE_J[y][x][z]; - return array[j]; + return section.getExtBlocklightValue(x, y & 15, z); + } + + @Override + public void relight(int x, int y, int z) { + nmsWorld.func_147451_t(x, y, z); + } + + private WorldServer nmsWorld; + + @Override + public World getImpWorld() { + WorldServer[] worlds = MinecraftServer.getServer().worldServers; + for (WorldServer ws : worlds) { + if (ws.provider.getDimensionName().equals(getWorldName())) { + return nmsWorld = ws; + } + } + return null; } } diff --git a/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java b/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java index afcc5201..85e6baa6 100644 --- a/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java +++ b/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java @@ -6,7 +6,6 @@ import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.example.NMSMappedFaweQueue; import com.boydti.fawe.object.BytePair; import com.boydti.fawe.object.FaweChunk; -import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; @@ -19,13 +18,13 @@ import com.sk89q.jnbt.Tag; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.UUID; -import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityTracker; @@ -104,16 +103,6 @@ public class ForgeQueue_All extends NMSMappedFaweQueue fc, RelightMode mode) { - if (mode == RelightMode.NONE) { - return true; - } - try { - CharFaweChunk bc = (CharFaweChunk) fc; - Chunk nmsChunk = bc.getChunk(); - if (!nmsChunk.isLoaded()) { - return false; - } - World nmsWorld = nmsChunk.getWorld(); - boolean flag = !nmsWorld.provider.getHasNoSky(); - ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); - if (mode == RelightMode.ALL) { - for (int i = 0; i < sections.length; i++) { - ExtendedBlockStorage section = sections[i]; - if (section != null) { - section.setBlocklightArray(new NibbleArray()); - if (flag) { - section.setSkylightArray(new NibbleArray()); - } - } - } - } - if (flag) { - if (mode == RelightMode.ALL) { - nmsChunk.generateSkylightMap(); - } else { - int i = nmsChunk.getTopFilledSegment(); - for (int x = 0; x < 16; ++x) { - for (int z = 0; z < 16; ++z) { - int l = 15; - int y = i + 16 - 1; - do { - int opacity = nmsChunk.getBlockLightOpacity(new BlockPos(x, y, z)); - if (opacity == 0 && l != 15) { - opacity = 1; - } - l -= opacity; - if (l > 0) { - ExtendedBlockStorage section = sections[y >> 4]; - if (section != null) { - section.setExtSkylightValue(x, y & 15, z, l); - } - } - --y; - } while (y > 0 && l > 0); - } - } - } - } - if (bc.getTotalRelight() == 0 && mode == RelightMode.MINIMAL) { - return true; - } - int X = fc.getX() << 4; - int Z = fc.getZ() << 4; - - for (int j = 0; j < sections.length; j++) { - ExtendedBlockStorage section = sections[j]; - if (section == null) { - continue; - } - if (((bc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0)) || bc.getAir(j) == 4096) { - continue; - } - char[] array = section.getData(); - if (mode == RelightMode.ALL) { - for (int k = array.length - 1; k >= 0; k--) { - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (isSurrounded(sections, x, y, z)) { - continue; - } - pos.set(X + x, y, Z + z); - nmsWorld.checkLight(pos); - } - continue; - } - for (int k = array.length - 1; k >= 0; k--) { - final int i = array[k]; - final short id = (short) (i >> 4); - switch (id) { // Lighting - case 0: - continue; - default: - if (mode == RelightMode.MINIMAL) { - continue; - } - if (PseudoRandom.random.random(3) != 0) { - continue; - } - case 10: - case 11: - case 39: - case 40: - case 50: - case 51: - case 62: - case 74: - case 76: - case 89: - case 122: - case 124: - case 130: - case 138: - case 169: - case 213: - final int x = FaweCache.CACHE_X[j][k]; - final int y = FaweCache.CACHE_Y[j][k]; - final int z = FaweCache.CACHE_Z[j][k]; - if (isSurrounded(sections, x, y, z)) { - continue; - } - pos.set(X + x, y, Z + z); - nmsWorld.checkLight(pos); - } - } - } - return true; - } catch (Throwable e) { - if (Thread.currentThread() == Fawe.get().getMainThread()) { - MainUtil.handleError(e); - } - } - return false; - } - @Override public CharFaweChunk getPrevious(CharFaweChunk fs, ExtendedBlockStorage[] sections, Map tilesGeneric, Collection[] entitiesGeneric, Set createdEntities, boolean all) throws Exception { Map tiles = (Map) tilesGeneric; @@ -385,6 +245,8 @@ public class ForgeQueue_All extends NMSMappedFaweQueue changeTask) { CharFaweChunk fs = (CharFaweChunk) fc; net.minecraft.world.chunk.Chunk nmsChunk = fs.getChunk(); + nmsChunk.setModified(true); + nmsChunk.setHasEntities(true); net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); try { boolean flag = !nmsWorld.provider.getHasNoSky(); @@ -670,18 +532,6 @@ public class ForgeQueue_All extends NMSMappedFaweQueue 15 || z < 0 || z > 15) { return 1; @@ -698,4 +548,103 @@ public class ForgeQueue_All extends NMSMappedFaweQueue> 4; } + + @Override + public boolean removeLighting(ExtendedBlockStorage[] sections, RelightMode mode, boolean sky) { + if (mode == RelightMode.ALL) { + for (int i = 0; i < sections.length; i++) { + ExtendedBlockStorage section = sections[i]; + if (section != null) { + section.setBlocklightArray(new NibbleArray()); + if (sky) { + section.setSkylightArray(new NibbleArray()); + } + } + } + } + return true; + } + + @Override + public boolean hasSky() { + return nmsWorld.provider.getHasNoSky(); + } + + @Override + public boolean initLighting(Chunk nmsChunk, ExtendedBlockStorage[] sections, RelightMode mode) { + if (mode == RelightMode.ALL) { + nmsChunk.generateSkylightMap(); + } else { + int i = nmsChunk.getTopFilledSegment(); + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + int l = 15; + int y = i + 16 - 1; + do { + int opacity = nmsChunk.getBlockLightOpacity(new BlockPos(x, y, z)); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ExtendedBlockStorage section = sections[y >> 4]; + if (section != null) { + section.setExtSkylightValue(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + return true; + } + + @Override + public void setFullbright(ExtendedBlockStorage[] sections) { + for (int i = 0; i < sections.length; i++) { + ExtendedBlockStorage section = sections[i]; + if (section != null) { + byte[] bytes = section.getSkylightArray().getData(); + Arrays.fill(bytes, (byte) 255); + } + } + } + + @Override + public int getSkyLight(ExtendedBlockStorage[] sections, int x, int y, int z) { + ExtendedBlockStorage section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 15; + } + return section.getExtSkylightValue(x, y & 15, z); + } + + @Override + public int getEmmittedLight(ExtendedBlockStorage[] sections, int x, int y, int z) { + ExtendedBlockStorage section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 0; + } + return section.getExtBlocklightValue(x, y & 15, z); + } + + @Override + public void relight(int x, int y, int z) { + pos.set(x, y, z); + nmsWorld.checkLight(pos); + } + + private WorldServer nmsWorld; + + @Override + public World getImpWorld() { + WorldServer[] worlds = MinecraftServer.getServer().worldServers; + for (WorldServer ws : worlds) { + if (ws.provider.getDimensionName().equals(getWorldName())) { + return nmsWorld = ws; + } + } + return null; + } } diff --git a/forge194/build.gradle b/forge194/build.gradle new file mode 100644 index 00000000..e0befc13 --- /dev/null +++ b/forge194/build.gradle @@ -0,0 +1,86 @@ +buildscript { + repositories { + jcenter() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven {url = "https://oss.sonatype.org/content/repositories/snapshots/"} + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' + } +} + +apply plugin: 'net.minecraftforge.gradle.forge' +apply plugin: 'com.github.johnrengelman.shadow' + +dependencies { + compile project(':core') + compile 'org.spongepowered:spongeapi:4.+' + compile 'org.mcstats.sponge:metrics:R8-SNAPSHOT' + compile 'com.sk89q.worldedit:worldedit-forge-mc1.8.9:6.1.1' +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +repositories { + maven { + name = 'forge' + url = 'http://files.minecraftforge.net/maven' + } + maven { + name = "Sponge" + url = "https://repo.spongepowered.org/maven" + } + maven { + name = "Sponge Metrics" + url = "http://repo.mcstats.org/content/repositories/releases/" + } +} +minecraft { + version = "1965" + mappings = "stable_26" + runDir = 'run' +} + +project.archivesBaseName = "${project.archivesBaseName}-mc${minecraft.version}" + +processResources { + from(sourceSets.main.resources.srcDirs) { + expand 'version': project.version, + 'mcVersion': project.minecraft.version + exclude 'mcmod.info' + } +} + +shadowJar { + relocate 'org.yaml.snakeyaml', 'com.boydti.fawe.yaml' + dependencies { + include(dependency(':core')) + include(dependency('org.yaml:snakeyaml:1.16')) + } + archiveName = "${parent.name}-${project.name}-${parent.version}.jar" + destinationDir = file '../target' +} +shadowJar.doLast { + task -> + ant.checksum file: task.archivePath +} + + +reobf { + shadowJar { + mappingType = 'SEARGE' + } +} + +task deobfJar(type: Jar) { + from sourceSets.main.output + classifier = 'dev' +} + +artifacts { + archives deobfJar +} \ No newline at end of file diff --git a/forge194/src/main/java/com/boydti/fawe/forge/FaweForge.java b/forge194/src/main/java/com/boydti/fawe/forge/FaweForge.java new file mode 100644 index 00000000..7382c121 --- /dev/null +++ b/forge194/src/main/java/com/boydti/fawe/forge/FaweForge.java @@ -0,0 +1,161 @@ +package com.boydti.fawe.forge; + + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.IFawe; +import com.boydti.fawe.forge.v0.ForgeQueue_All; +import com.boydti.fawe.object.FaweCommand; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.regions.FaweMaskManager; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.wrappers.WorldWrapper; +import com.mojang.authlib.GameProfile; +import com.sk89q.worldedit.forge.ForgeWorld; +import com.sk89q.worldedit.world.World; +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; +import javax.management.InstanceAlreadyExistsException; +import net.minecraft.command.ServerCommandManager; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fml.common.FMLCommonHandler; +import org.apache.logging.log4j.Logger; + +public class FaweForge implements IFawe { + + private final ForgeMain parent; + private final File directory; + private final Logger logger; + + public FaweForge(ForgeMain plugin, Logger logger, File directory) { + this.parent = plugin; + this.logger = logger; + this.directory = directory; + try { + Fawe.set(this); + } catch (InstanceAlreadyExistsException e) { + MainUtil.handleError(e); + } + } + + @Override + public void debug(String s) { + logger.debug(s); + } + + @Override + public File getDirectory() { + return directory; + } + + @Override + public void setupCommand(String label, FaweCommand cmd) { + if (TaskManager.IMP != null) { + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + ServerCommandManager scm = (ServerCommandManager) FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager(); + scm.registerCommand(new ForgeCommand(label, cmd)); + } + }); + } + } + + @Override + public FawePlayer wrap(Object obj) { + EntityPlayerMP player = null; + if (obj instanceof String) { + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + player = server.getPlayerList().getPlayerByUsername((String) obj); + } else if (obj instanceof UUID) { + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + player = server.getPlayerList().getPlayerByUUID((UUID) obj); + } else if (obj instanceof EntityPlayerMP) { + player = (EntityPlayerMP) obj; + } + if (player == null) { + return null; + } + FawePlayer existing = Fawe.get().getCachedPlayer(player.getName()); + return existing != null ? existing : new ForgePlayer(player); + } + + @Override + public void setupVault() { + // Do nothing + } + + @Override + public TaskManager getTaskManager() { + return new com.boydti.fawe.forge.ForgeTaskMan(512); + } + + @Override + public int[] getVersion() { + String[] version = FMLCommonHandler.instance().getMinecraftServerInstance().getMinecraftVersion().split("\\."); + return new int[] {Integer.parseInt(version[0]), Integer.parseInt(version[1]), Integer.parseInt(version[2])}; + } + + @Override + public String getWorldName(World world) { + if (world instanceof WorldWrapper) { + world = ((WorldWrapper) world).getParent(); + } + return ((ForgeWorld) world).getWorld().getWorldInfo().getWorldName(); + } + + @Override + public FaweQueue getNewQueue(String world, boolean dontCareIfFast) { + return new ForgeQueue_All(world); + } + + @Override + public Collection getMaskManagers() { + return new ArrayList<>(); + } + + @Override + public void startMetrics() { + try { + com.boydti.fawe.forge.ForgeMetrics metrics = new com.boydti.fawe.forge.ForgeMetrics("FastAsyncWorldEdit", "3.5.1"); + metrics.start(); + debug("[FAWE] &6Metrics enabled."); + } catch (Throwable e) { + debug("[FAWE] &cFailed to load up metrics."); + } + } + + @Override + public String getPlatform() { + return "forge"; + } + + @Override + public UUID getUUID(String name) { + try { + GameProfile profile = FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerProfileCache().getGameProfileForUsername(name); + return profile.getId(); + } catch (Throwable e) { + return null; + } + } + + @Override + public String getName(UUID uuid) { + try { + GameProfile profile = FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerProfileCache().getProfileByUUID(uuid); + return profile.getName(); + } catch (Throwable e) { + return null; + } + } + + @Override + public Object getBlocksHubApi() { + return null; + } +} diff --git a/forge194/src/main/java/com/boydti/fawe/forge/ForgeCommand.java b/forge194/src/main/java/com/boydti/fawe/forge/ForgeCommand.java new file mode 100644 index 00000000..f9ba9d22 --- /dev/null +++ b/forge194/src/main/java/com/boydti/fawe/forge/ForgeCommand.java @@ -0,0 +1,42 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.object.FaweCommand; +import com.boydti.fawe.object.FawePlayer; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; + +public class ForgeCommand extends CommandBase { + + private final String name; + private final FaweCommand cmd; + + public ForgeCommand(String name, FaweCommand cmd) { + this.name = name; + this.cmd = cmd; + } + + @Override + public String getCommandName() { + return name; + } + + @Override + public String getCommandUsage(ICommandSender iCommandSender) { + return "/" + name; + } + + @Override + public void execute(MinecraftServer minecraftServer, ICommandSender sender, String[] args) throws CommandException { + if ((sender instanceof EntityPlayerMP)) { + EntityPlayerMP player = (EntityPlayerMP) sender; + if (player.worldObj.isRemote) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + cmd.executeSafe(fp, args); + } + } +} diff --git a/forge194/src/main/java/com/boydti/fawe/forge/ForgeMain.java b/forge194/src/main/java/com/boydti/fawe/forge/ForgeMain.java new file mode 100644 index 00000000..c9faabe4 --- /dev/null +++ b/forge194/src/main/java/com/boydti/fawe/forge/ForgeMain.java @@ -0,0 +1,75 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FawePlayer; +import java.io.File; +import java.util.List; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerStoppingEvent; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.PlayerEvent; +import org.apache.logging.log4j.Logger; + +@Mod(modid = "com.boydti.fawe", name = "FastAsyncWorldEdit", version = "3.5.1", acceptableRemoteVersions = "*") +public class ForgeMain { + private static com.boydti.fawe.forge.FaweForge IMP; + private Logger logger; + + @Mod.EventHandler + public void preInit(FMLPreInitializationEvent event) { + this.logger = event.getModLog(); + File directory = new File(event.getModConfigurationDirectory() + File.separator + "FastAsyncWorldEdit"); + MinecraftForge.EVENT_BUS.register(this); + FMLCommonHandler.instance().bus().register(this); + this.IMP = new com.boydti.fawe.forge.FaweForge(this, event.getModLog(), directory); + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onPlayerQuit(PlayerEvent.PlayerLoggedOutEvent event) { + if (event.player.worldObj.isRemote) { + return; + } + handleQuit((EntityPlayerMP) event.player); + } + + @Mod.EventHandler + public void serverStopping(FMLServerStoppingEvent event) { + for (EntityPlayerMP player : (List)FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayerList()) { + handleQuit(player); + } + } + + public void handleQuit(EntityPlayerMP player) { + FawePlayer fp = FawePlayer.wrap(player); + fp.unregister(); + Fawe.get().unregister(player.getName()); + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onPlayerChangedWorld(EntityJoinWorldEvent event) { + Entity entity = event.getEntity(); + if (!(entity instanceof EntityPlayerMP)) { + return; + } + EntityPlayerMP player = (EntityPlayerMP) entity; + if (player.worldObj.isRemote) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + if (fp.getMeta("lastWorld") != event.getWorld()) { + fp.setMeta("lastWorld", event.getWorld()); + if (Settings.HISTORY.USE_DISK) { + fp.getSession().clearHistory(); + fp.loadSessionsFromDisk(fp.getWorld()); + } + } + } +} diff --git a/forge194/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge194/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java new file mode 100644 index 00000000..7115b1a3 --- /dev/null +++ b/forge194/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -0,0 +1,481 @@ +/* + * Copyright 2011-2013 Tyler Blair. All rights reserved. + * Ported to Minecraft Forge by Mike Primm + * 1.7.x update by Dries007 + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.boydti.fawe.forge; + +import com.boydti.fawe.util.MainUtil; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.UUID; +import java.util.zip.GZIPOutputStream; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +public class ForgeMetrics { + + /** + * The current revision number + */ + private final static int REVISION = 7; + + /** + * The base url of the metrics domain + */ + private static final String BASE_URL = "http://report.mcstats.org"; + + /** + * The url used to report a server's status + */ + private static final String REPORT_URL = "/plugin/%s"; + + /** + * Interval of time to ping (in minutes) + */ + private static final int PING_INTERVAL = 15; + + /** + * The mod this metrics submits for + */ + private final String modName; + + private final String modVersion; + + /** + * The metrics configuration file + */ + private final Configuration configuration; + + /** + * The metrics configuration file + */ + private final File configurationFile; + + /** + * Unique server id + */ + private final String guid; + + /** + * Debug mode + */ + private final boolean debug; + + private Thread thread = null; + private boolean firstPost = true; + int tickCount; + + public ForgeMetrics(final String modName, final String modVersion) throws IOException { + if (modName == null || modVersion == null) { + throw new IllegalArgumentException("modName and modVersion cannot be null"); + } + + this.modName = modName; + this.modVersion = modVersion; + + // load the config + configurationFile = getConfigFile(); + configuration = new Configuration(configurationFile); + + // Get values, and add some defaults, if needed + configuration.get(Configuration.CATEGORY_GENERAL, "opt-out", false, "Set to true to disable all reporting"); + guid = configuration.get(Configuration.CATEGORY_GENERAL, "guid", UUID.randomUUID().toString(), "Server unique ID").getString(); + debug = configuration.get(Configuration.CATEGORY_GENERAL, "debug", false, "Set to true for verbose debug").getBoolean(false); + configuration.save(); + } + + /** + * Start measuring statistics. This will immediately create an async + * repeating task as the plugin and send the initial data to the metrics + * backend, and then after that it will post in increments of PING_INTERVAL + * * 1200 ticks. + * + * @return True if statistics measuring is running, otherwise false. + */ + public boolean start() { + // Did we opt out? + if (isOptOut()) { + return false; + } + + FMLCommonHandler.instance().bus().register(this); + + return true; + } + + @SubscribeEvent + public void tick(TickEvent.ServerTickEvent tick) { + if (tick.phase != TickEvent.Phase.END) return; + + if (tickCount++ % (PING_INTERVAL * 1200) != 0) return; + + if (thread == null) { + thread = new Thread(new Runnable() { + public void run() { + try { + // Disable Task, if it is running and the server owner decided + // to opt-out + if (isOptOut()) { + FMLCommonHandler.instance().bus().unregister(ForgeMetrics.this); + return; + } + // We use the inverse of firstPost because if it + // is the first time we are posting, + // it is not a interval ping, so it evaluates to + // FALSE + // Each time thereafter it will evaluate to + // TRUE, i.e PING! + postPlugin(!firstPost); + // After the first post we set firstPost to + // false + // Each post thereafter will be a ping + firstPost = false; + } catch (IOException e) { + if (debug) { + FMLLog.info("[Metrics] Exception - %s", e.getMessage()); + } + } finally { + thread = null; + } + } + }); + thread.start(); + } + } + + /** + * Stop processing + */ + public void stop() { + } + + /** + * Has the server owner denied plugin metrics? + * + * @return true if metrics should be opted out of it + */ + public boolean isOptOut() { + // Reload the metrics file + configuration.load(); + return configuration.get(Configuration.CATEGORY_GENERAL, "opt-out", false).getBoolean(false); + } + + /** + * Enables metrics for the server by setting "opt-out" to false in the + * config file and starting the metrics task. + * + * @throws java.io.IOException + */ + public void enable() throws IOException { + // Check if the server owner has already set opt-out, if not, set it. + if (isOptOut()) { + configuration.getCategory(Configuration.CATEGORY_GENERAL).get("opt-out").set("false"); + configuration.save(); + } + // Enable Task, if it is not running + FMLCommonHandler.instance().bus().register(this); + } + + /** + * Disables metrics for the server by setting "opt-out" to true in the + * config file and canceling the metrics task. + * + * @throws java.io.IOException + */ + public void disable() throws IOException { + // Check if the server owner has already set opt-out, if not, set it. + if (!isOptOut()) { + configuration.getCategory(Configuration.CATEGORY_GENERAL).get("opt-out").set("true"); + configuration.save(); + } + FMLCommonHandler.instance().bus().unregister(this); + } + + /** + * Gets the File object of the config file that should be used to store data + * such as the GUID and opt-out status + * + * @return the File object for the config file + */ + public File getConfigFile() { + return new File(Loader.instance().getConfigDir(), "PluginMetrics.cfg"); + } + + /** + * Generic method that posts a plugin to the metrics website + */ + private void postPlugin(final boolean isPing) throws IOException { + // Server software specific section + String pluginName = modName; + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + boolean onlineMode = server.isServerInOnlineMode(); + String pluginVersion = modVersion; + String serverVersion; + if (server.isDedicatedServer()) { + serverVersion = "MinecraftForge (MC: " + server.getMinecraftVersion() + ")"; + } else { + serverVersion = "MinecraftForgeSSP (MC: " + server.getMinecraftVersion() + ")"; + } + int playersOnline = server.getCurrentPlayerCount(); + + // END server software specific section -- all code below does not use any code outside of this class / Java + + // Construct the post data + StringBuilder json = new StringBuilder(1024); + json.append('{'); + + // The plugin's description file containg all of the plugin data such as name, version, author, etc + appendJSONPair(json, "guid", guid); + appendJSONPair(json, "plugin_version", pluginVersion); + appendJSONPair(json, "server_version", serverVersion); + appendJSONPair(json, "players_online", Integer.toString(playersOnline)); + + // New data as of R6 + String osname = System.getProperty("os.name"); + String osarch = System.getProperty("os.arch"); + String osversion = System.getProperty("os.version"); + String java_version = System.getProperty("java.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + // normalize os arch .. amd64 -> x86_64 + if (osarch.equals("amd64")) { + osarch = "x86_64"; + } + + appendJSONPair(json, "osname", osname); + appendJSONPair(json, "osarch", osarch); + appendJSONPair(json, "osversion", osversion); + appendJSONPair(json, "cores", Integer.toString(coreCount)); + appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0"); + appendJSONPair(json, "java_version", java_version); + + // If we're pinging, append it + if (isPing) { + appendJSONPair(json, "ping", "1"); + } + + // close json + json.append('}'); + + // Create the url + URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName))); + + // Connect to the website + URLConnection connection; + + // Mineshafter creates a socks proxy, so we can safely bypass it + // It does not reroute POST requests so we need to go around it + if (isMineshafterPresent()) { + connection = url.openConnection(Proxy.NO_PROXY); + } else { + connection = url.openConnection(); + } + + + byte[] uncompressed = json.toString().getBytes(); + byte[] compressed = gzip(json.toString()); + + // Headers + connection.addRequestProperty("User-Agent", "MCStats/" + REVISION); + connection.addRequestProperty("Content-Type", "application/json"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", Integer.toString(compressed.length)); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + + connection.setDoOutput(true); + + // Write the data + OutputStream os = connection.getOutputStream(); + os.write(compressed); + os.flush(); + + // Now read the response + final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String response = reader.readLine(); + + // close resources + os.close(); + reader.close(); + + if (response == null || response.startsWith("ERR") || response.startsWith("7")) { + if (response == null) { + response = "null"; + } else if (response.startsWith("7")) { + response = response.substring(response.startsWith("7,") ? 2 : 1); + } + + throw new IOException(response); + } + } + + /** + * GZip compress a string of bytes + * + * @param input + * @return + */ + public static byte[] gzip(String input) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream gzos = null; + + try { + gzos = new GZIPOutputStream(baos); + gzos.write(input.getBytes("UTF-8")); + } catch (IOException e) { + MainUtil.handleError(e); + } finally { + if (gzos != null) try { + gzos.close(); + } catch (IOException ignore) { + } + } + + return baos.toByteArray(); + } + + /** + * Check if mineshafter is present. If it is, we need to bypass it to send POST requests + * + * @return true if mineshafter is installed on the server + */ + private boolean isMineshafterPresent() { + try { + Class.forName("mineshafter.MineServer"); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * Appends a json encoded key/value pair to the given string builder. + * + * @param json + * @param key + * @param value + * @throws java.io.UnsupportedEncodingException + */ + private static void appendJSONPair(StringBuilder json, String key, String value) throws UnsupportedEncodingException { + boolean isValueNumeric = false; + + try { + if (value.equals("0") || !value.endsWith("0")) { + Double.parseDouble(value); + isValueNumeric = true; + } + } catch (NumberFormatException e) { + isValueNumeric = false; + } + + if (json.charAt(json.length() - 1) != '{') { + json.append(','); + } + + json.append(escapeJSON(key)); + json.append(':'); + + if (isValueNumeric) { + json.append(value); + } else { + json.append(escapeJSON(value)); + } + } + + /** + * Escape a string to create a valid JSON string + * + * @param text + * @return + */ + private static String escapeJSON(String text) { + StringBuilder builder = new StringBuilder(); + + builder.append('"'); + for (int index = 0; index < text.length(); index++) { + char chr = text.charAt(index); + + switch (chr) { + case '"': + case '\\': + builder.append('\\'); + builder.append(chr); + break; + case '\b': + builder.append("\\b"); + break; + case '\t': + builder.append("\\t"); + break; + case '\n': + builder.append("\\n"); + break; + case '\r': + builder.append("\\r"); + break; + default: + if (chr < ' ') { + String t = "000" + Integer.toHexString(chr); + builder.append("\\u" + t.substring(t.length() - 4)); + } else { + builder.append(chr); + } + break; + } + } + builder.append('"'); + + return builder.toString(); + } + + /** + * Encode text as UTF-8 + * + * @param text the text to encode + * @return the encoded text, as UTF-8 + */ + private static String urlEncode(final String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, "UTF-8"); + } + +} \ No newline at end of file diff --git a/forge194/src/main/java/com/boydti/fawe/forge/ForgePlayer.java b/forge194/src/main/java/com/boydti/fawe/forge/ForgePlayer.java new file mode 100644 index 00000000..dab16474 --- /dev/null +++ b/forge194/src/main/java/com/boydti/fawe/forge/ForgePlayer.java @@ -0,0 +1,78 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FaweLocation; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.wrappers.PlayerWrapper; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.forge.ForgeWorldEdit; +import java.util.UUID; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextComponentBase; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.world.World; + +public class ForgePlayer extends FawePlayer { + public ForgePlayer(EntityPlayerMP parent) { + super(parent); + } + + @Override + public void sendTitle(String head, String sub) { // Not supported + Settings.QUEUE.PROGRESS.DISPLAY = false; + } + + @Override + public void resetTitle() { // Not supported + Settings.QUEUE.PROGRESS.DISPLAY = false; + } + + @Override + public String getName() { + return parent.getName(); + } + + @Override + public UUID getUUID() { + return parent.getUniqueID(); + } + + @Override + public boolean hasPermission(String perm) { + Object meta = getMeta(perm); + return meta instanceof Boolean ? (boolean) meta : ForgeWorldEdit.inst.getPermissionsProvider().hasPermission(parent, perm); + } + + @Override + public void setPermission(String perm, boolean flag) { + setMeta(perm, flag); + } + + @Override + public void sendMessage(String msg) { + for (String part : msg.split("\n")) { + part = BBC.color(part); + TextComponentBase text = new TextComponentString(part); + this.parent.addChatMessage(text); + } + } + + @Override + public void executeCommand(String substring) { + throw new UnsupportedOperationException("NOT IMPLEMENTED"); + } + + @Override + public FaweLocation getLocation() { + World world = parent.worldObj; + BlockPos pos = parent.getPosition(); + return new FaweLocation(world.getWorldInfo().getWorldName(), pos.getX(), pos.getY(), pos.getZ()); + } + + @Override + public Player getPlayer() { + return PlayerWrapper.wrap(ForgeWorldEdit.inst.wrap(this.parent)); + } +} diff --git a/forge194/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge194/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java new file mode 100644 index 00000000..d1940818 --- /dev/null +++ b/forge194/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java @@ -0,0 +1,168 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.TaskManager; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +public class ForgeTaskMan extends TaskManager { + + private final ConcurrentLinkedDeque syncTasks = new ConcurrentLinkedDeque<>(); + private final ConcurrentLinkedDeque asyncTasks = new ConcurrentLinkedDeque<>(); + + private final ConcurrentHashMap taskIdMap = new ConcurrentHashMap<>(); + + + private final AtomicInteger taskId = new AtomicInteger(); + private final ExecutorService executor; + + public ForgeTaskMan(int size) { + this.executor = Executors.newFixedThreadPool(size); + FMLCommonHandler.instance().bus().register(this); + } + + + @Override + public int repeat(final Runnable r, final int interval) { + if (r == null) { + return -1; + } + int id = taskId.incrementAndGet(); + taskIdMap.put(id, r); + task(new Runnable() { + @Override + public void run() { + if (!taskIdMap.containsKey(id)) { + return; + } + try { + r.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + later(this, interval); + } + }); + return id; + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onServerTick(TickEvent.ServerTickEvent event) { + Fawe.get().setMainThread(); + int asyncSize = asyncTasks.size(); + for (int i = 0; i < asyncSize; i++) { + Runnable item = asyncTasks.poll(); + if (item != null) { + async(item); + } + } + int syncSize = syncTasks.size(); + for (int i = 0; i < syncSize; i++) { + Runnable item = syncTasks.poll(); + if (item != null) { + try { + item.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + } + } + + @Override + public int repeatAsync(Runnable r, int interval) { + if (r == null) { + return -1; + } + int id = taskId.incrementAndGet(); + taskIdMap.put(id, r); + async(new Runnable() { + @Override + public void run() { + if (!taskIdMap.containsKey(id)) { + return; + } + try { + r.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + laterAsync(this, interval); + } + }); + return id; + } + + @Override + public void async(Runnable r) { + if (r == null) { + return; + } + executor.execute(r); + } + + @Override + public void task(Runnable r) { + if (r == null) { + return; + } + syncTasks.add(r); + } + + @Override + public void later(Runnable r, int delay) { + if (r == null) { + return; + } + AtomicInteger remaining = new AtomicInteger(delay); + task(new Runnable() { + @Override + public void run() { + if (remaining.decrementAndGet() <= 0) { + try { + r.run(); + } catch (Throwable e) { + MainUtil.handleError(e); + } + return; + } + task(this); + } + }); + } + + @Override + public void laterAsync(Runnable r, int delay) { + if (r == null) { + return; + } + AtomicInteger remaining = new AtomicInteger(delay); + task(new Runnable() { + @Override + public void run() { + if (remaining.decrementAndGet() <= 0) { + try { + async(r); + } catch (Throwable e) { + MainUtil.handleError(e); + } + return; + } + task(this); + } + }); + } + + @Override + public void cancel(int task) { + taskIdMap.remove(task); + } +} diff --git a/forge194/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java b/forge194/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java new file mode 100644 index 00000000..82f591af --- /dev/null +++ b/forge194/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java @@ -0,0 +1,114 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.util.MainUtil; +import java.lang.reflect.Field; +import net.minecraft.block.Block; +import net.minecraft.util.BitArray; +import net.minecraft.world.World; +import net.minecraft.world.chunk.BlockStateContainer; +import net.minecraft.world.chunk.BlockStatePaletteRegistry; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IBlockStatePalette; + +public class ForgeChunk_All extends CharFaweChunk { + + public BlockStateContainer[] sectionPalettes; + + /** + * A FaweSections object represents a chunk and the blocks that you wish to change in it. + * + * @param parent + * @param x + * @param z + */ + public ForgeChunk_All(FaweQueue parent, int x, int z) { + super(parent, x, z); + } + + @Override + public Chunk getNewChunk() { + World world = ((ForgeQueue_All) getParent()).getWorld(); + return world.getChunkProvider().provideChunk(getX(), getZ()); + } + + @Override + public CharFaweChunk copy(boolean shallow) { + ForgeChunk_All value = (ForgeChunk_All) super.copy(shallow); + if (sectionPalettes != null) { + value.sectionPalettes = new BlockStateContainer[16]; + try { + Field fieldBits = BlockStateContainer.class.getDeclaredField("storage"); + fieldBits.setAccessible(true); + Field fieldPalette = BlockStateContainer.class.getDeclaredField("palette"); + fieldPalette.setAccessible(true); + Field fieldSize = BlockStateContainer.class.getDeclaredField("bits"); + fieldSize.setAccessible(true); + for (int i = 0; i < sectionPalettes.length; i++) { + BlockStateContainer current = sectionPalettes[i]; + if (current == null) { + continue; + } + // Clone palette + IBlockStatePalette currentPalette = (IBlockStatePalette) fieldPalette.get(current); + if (!(currentPalette instanceof BlockStatePaletteRegistry)) { + current.onResize(128, null); + } + BlockStateContainer paletteBlock = new BlockStateContainer(); + currentPalette = (IBlockStatePalette) fieldPalette.get(current); + if (!(currentPalette instanceof BlockStatePaletteRegistry)) { + throw new RuntimeException("Palette must be global!"); + } + fieldPalette.set(paletteBlock, currentPalette); + // Clone size + fieldSize.set(paletteBlock, fieldSize.get(current)); + // Clone palette + BitArray currentBits = (BitArray) fieldBits.get(current); + BitArray newBits = new BitArray(1, 0); + for (Field field : BitArray.class.getDeclaredFields()) { + field.setAccessible(true); + Object currentValue = field.get(currentBits); + if (currentValue instanceof long[]) { + currentValue = ((long[]) currentValue).clone(); + } + field.set(newBits, currentValue); + } + fieldBits.set(paletteBlock, newBits); + value.sectionPalettes[i] = paletteBlock; + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + return value; + } + + public void optimize() { + if (sectionPalettes != null) { + return; + } + char[][] arrays = getCombinedIdArrays(); + char lastChar = Character.MAX_VALUE; + for (int layer = 0; layer < 16; layer++) { + if (getCount(layer) > 0) { + if (sectionPalettes == null) { + sectionPalettes = new BlockStateContainer[16]; + } + BlockStateContainer palette = new BlockStateContainer(); + char[] blocks = getIdArray(layer); + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + char combinedId = blocks[FaweCache.CACHE_J[y][x][z]]; + if (combinedId > 1) { + palette.set(x, y, z, Block.getBlockById(combinedId >> 4).getStateFromMeta(combinedId & 0xF)); + } + } + } + } + } + } + } +} diff --git a/forge194/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java b/forge194/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java new file mode 100644 index 00000000..7134fbaa --- /dev/null +++ b/forge194/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java @@ -0,0 +1,702 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.example.NMSMappedFaweQueue; +import com.boydti.fawe.object.BytePair; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityTracker; +import net.minecraft.entity.EntityTrackerEntry; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.play.server.SPacketChunkData; +import net.minecraft.network.play.server.SPacketDestroyEntities; +import net.minecraft.server.management.PlayerChunkMap; +import net.minecraft.server.management.PlayerChunkMapEntry; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BitArray; +import net.minecraft.util.ClassInheritanceMultiMap; +import net.minecraft.util.IntHashMap; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.BlockStateContainer; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IBlockStatePalette; +import net.minecraft.world.chunk.IChunkGenerator; +import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.chunk.NibbleArray; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import net.minecraft.world.gen.ChunkProviderServer; +import net.minecraftforge.fml.common.FMLCommonHandler; + +public class ForgeQueue_All extends NMSMappedFaweQueue { + + private Method methodFromNative; + private Method methodToNative; + + public ForgeQueue_All(String world) { + super(world); + try { + Class converter = Class.forName("com.sk89q.worldedit.forge.NBTConverter"); + this.methodFromNative = converter.getDeclaredMethod("toNative", Tag.class); + this.methodToNative = converter.getDeclaredMethod("fromNative", NBTBase.class); + methodFromNative.setAccessible(true); + methodToNative.setAccessible(true); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + private BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(0, 0, 0); + + @Override + public CompoundTag getTileEntity(Chunk chunk, int x, int y, int z) { + Map tiles = chunk.getTileEntityMap(); + pos.setPos(x, y, z); + TileEntity tile = tiles.get(pos); + return tile != null ? getTag(tile) : null; + } + + public CompoundTag getTag(TileEntity tile) { + try { + NBTTagCompound tag = new NBTTagCompound(); + tile.readFromNBT(tag); // readTagIntoEntity + return (CompoundTag) methodToNative.invoke(null, tag); + } catch (Exception e) { + MainUtil.handleError(e); + return null; + } + } + + @Override + public Chunk getChunk(World world, int x, int z) { + Chunk chunk = world.getChunkProvider().provideChunk(x, z); + if (chunk != null && !chunk.isLoaded()) { + chunk.onChunkLoad(); + } + return chunk; + } + + @Override + public boolean isChunkLoaded(int x, int z) { + return getWorld().getChunkProvider().getLoadedChunk(x, z) != null; + } + + @Override + public boolean regenerateChunk(World world, int x, int z) { + IChunkProvider provider = world.getChunkProvider(); + if (!(provider instanceof ChunkProviderServer)) { + return false; + } + + + try { + ChunkProviderServer chunkServer = (ChunkProviderServer) provider; + IChunkGenerator gen = chunkServer.chunkGenerator; + long pos = ChunkPos.chunkXZ2Int(x, z); + Chunk mcChunk; + if (chunkServer.chunkExists(x, z)) { + mcChunk = chunkServer.loadChunk(x, z); + mcChunk.onChunkUnload(); + } + PlayerChunkMap playerManager = ((WorldServer) getWorld()).getPlayerChunkMap(); + List oldWatchers = null; + if (chunkServer.chunkExists(x, z)) { + mcChunk = chunkServer.loadChunk(x, z); + PlayerChunkMapEntry entry = playerManager.getEntry(x, z); + if (entry != null) { + Field fieldPlayers = PlayerChunkMap.class.getDeclaredField("players"); + fieldPlayers.setAccessible(true); + oldWatchers = (List) fieldPlayers.get(entry); + playerManager.removeEntry(entry); + } + mcChunk.onChunkUnload(); + } + try { + Field droppedChunksSetField = chunkServer.getClass().getDeclaredField("field_73248_b"); + droppedChunksSetField.setAccessible(true); + Set droppedChunksSet = (Set) droppedChunksSetField.get(chunkServer); + droppedChunksSet.remove(pos); + } catch (Throwable e) { + MainUtil.handleError(e); + } + chunkServer.id2ChunkMap.remove(pos); + mcChunk = gen.provideChunk(x, z); + chunkServer.id2ChunkMap.put(pos, mcChunk); + if (mcChunk != null) { + mcChunk.onChunkLoad(); + mcChunk.populateChunk(chunkServer, chunkServer.chunkGenerator); + } + if (oldWatchers != null) { + for (EntityPlayerMP player : oldWatchers) { + playerManager.addPlayer(player); + } + } + return true; + } catch (Throwable t) { + MainUtil.handleError(t); + return false; + } + } + + @Override + public boolean loadChunk(World world, int x, int z, boolean generate) { + return getCachedSections(world, x, z) != null; + } + + @Override + public ExtendedBlockStorage[] getCachedSections(World world, int x, int z) { + Chunk chunk = world.getChunkProvider().provideChunk(x, z); + if (chunk != null && !chunk.isLoaded()) { + chunk.onChunkLoad(); + } + return chunk == null ? null : chunk.getBlockStorageArray(); + } + + @Override + public BlockStateContainer getCachedSection(ExtendedBlockStorage[] chunk, int cy) { + ExtendedBlockStorage value = chunk[cy]; + return value == null ? null : value.getData(); + } + + @Override + public int getCombinedId4Data(BlockStateContainer ls, int x, int y, int z) { + IBlockState ibd = lastSection.get(x & 15, y & 15, z & 15); + Block block = ibd.getBlock(); + int id = Block.getIdFromBlock(block); + if (FaweCache.hasData(id)) { + return (id << 4) + block.getMetaFromState(ibd); + } else { + return id << 4; + } + } + + @Override + public boolean isChunkLoaded(World world, int x, int z) { + return world.getChunkProvider().getLoadedChunk(x, z) != null; + } + + public void setCount(int tickingBlockCount, int nonEmptyBlockCount, ExtendedBlockStorage section) throws NoSuchFieldException, IllegalAccessException { + Class clazz = section.getClass(); + Field fieldTickingBlockCount = clazz.getDeclaredField("field_76683_c"); + Field fieldNonEmptyBlockCount = clazz.getDeclaredField("field_76682_b"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + fieldTickingBlockCount.set(section, tickingBlockCount); + fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount); + } + + @Override + public CharFaweChunk getPrevious(CharFaweChunk fs, ExtendedBlockStorage[] sections, Map tilesGeneric, Collection[] entitiesGeneric, Set createdEntities, boolean all) throws Exception { + Map tiles = (Map) tilesGeneric; + ClassInheritanceMultiMap[] entities = (ClassInheritanceMultiMap[]) entitiesGeneric; + CharFaweChunk previous = (CharFaweChunk) getFaweChunk(fs.getX(), fs.getZ()); + char[][] idPrevious = new char[16][]; + for (int layer = 0; layer < sections.length; layer++) { + if (fs.getCount(layer) != 0 || all) { + ExtendedBlockStorage section = sections[layer]; + if (section != null) { + short solid = 0; + char[] previousLayer = idPrevious[layer] = new char[4096]; + BlockStateContainer blocks = section.getData(); + for (int j = 0; j < 4096; j++) { + int x = FaweCache.CACHE_X[0][j]; + int y = FaweCache.CACHE_Y[0][j]; + int z = FaweCache.CACHE_Z[0][j]; + IBlockState ibd = blocks.get(x, y, z); + Block block = ibd.getBlock(); + int combined = Block.getIdFromBlock(block); + if (FaweCache.hasData(combined)) { + combined = (combined << 4) + block.getMetaFromState(ibd); + } else { + combined = combined << 4; + } + if (combined > 1) { + solid++; + } + previousLayer[j] = (char) combined; + } + previous.count[layer] = solid; + previous.air[layer] = (short) (4096 - solid); + } + } + } + previous.ids = idPrevious; + if (tiles != null) { + for (Map.Entry entry : tiles.entrySet()) { + TileEntity tile = entry.getValue(); + NBTTagCompound tag = new NBTTagCompound(); + tile.readFromNBT(tag); // readTileEntityIntoTag + BlockPos pos = entry.getKey(); + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(null, tag); + previous.setTile(pos.getX(), pos.getY(), pos.getZ(), nativeTag); + } + } + if (entities != null) { + for (Collection entityList : entities) { + for (Entity ent : entityList) { + if (ent instanceof EntityPlayer || (!createdEntities.isEmpty() && createdEntities.contains(ent.getUniqueID()))) { + continue; + } + int x = ((int) Math.round(ent.posX) & 15); + int z = ((int) Math.round(ent.posZ) & 15); + int y = (int) Math.round(ent.posY); + int i = FaweCache.CACHE_I[y][x][z]; + char[] array = fs.getIdArray(i); + if (array == null) { + continue; + } + int j = FaweCache.CACHE_J[y][x][z]; + if (array[j] != 0) { + String id = EntityList.getEntityString(ent); + if (id != null) { + NBTTagCompound tag = ent.getEntityData(); // readEntityIntoTag + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(null, tag); + Map map = ReflectionUtils.getMap(nativeTag.getValue()); + map.put("Id", new StringTag(id)); + previous.setEntity(nativeTag); + } + } + } + } + } + return previous; + } + + private final IBlockState air = Blocks.AIR.getDefaultState(); + + @Override + public boolean setComponents(FaweChunk fc, RunnableVal changeTask) { + ForgeChunk_All fs = (ForgeChunk_All) fc; + net.minecraft.world.chunk.Chunk nmsChunk = fs.getChunk(); + nmsChunk.setModified(true); + net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); + try { + boolean flag = !nmsWorld.provider.getHasNoSky(); + // Sections + ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); + Map tiles = nmsChunk.getTileEntityMap(); + ClassInheritanceMultiMap[] entities = nmsChunk.getEntityLists(); + + + // Remove entities + for (int i = 0; i < 16; i++) { + int count = fs.getCount(i); + if (count == 0) { + continue; + } else if (count >= 4096) { + entities[i] = new ClassInheritanceMultiMap<>(Entity.class); + } else { + char[] array = fs.getIdArray(i); + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entity instanceof EntityPlayer) { + continue; + } + int x = ((int) Math.round(entity.posX) & 15); + int z = ((int) Math.round(entity.posZ) & 15); + int y = (int) Math.round(entity.posY); + if (array == null) { + continue; + } + if (y < 0 || y > 255 || array[FaweCache.CACHE_J[y][x][z]] != 0) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Set entities + Set createdEntities = new HashSet<>(); + Set entitiesToSpawn = fs.getEntities(); + for (CompoundTag nativeTag : entitiesToSpawn) { + Map entityTagMap = nativeTag.getValue(); + StringTag idTag = (StringTag) entityTagMap.get("Id"); + ListTag posTag = (ListTag) entityTagMap.get("Pos"); + ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + if (idTag == null || posTag == null || rotTag == null) { + Fawe.debug("Unknown entity tag: " + nativeTag); + continue; + } + double x = posTag.getDouble(0); + double y = posTag.getDouble(1); + double z = posTag.getDouble(2); + float yaw = rotTag.getFloat(0); + float pitch = rotTag.getFloat(1); + String id = idTag.getValue(); + NBTTagCompound tag = (NBTTagCompound)methodFromNative.invoke(null, nativeTag); + Entity entity = EntityList.createEntityFromNBT(tag, nmsWorld); + if (entity != null) { + entity.setPositionAndRotation(x, y, z, yaw, pitch); + nmsWorld.spawnEntityInWorld(entity); + } + } + // Run change task if applicable + if (changeTask != null) { + CharFaweChunk previous = getPrevious(fs, sections, tiles, entities, createdEntities, false); + changeTask.run(previous); + } + // Trim tiles + Set> entryset = tiles.entrySet(); + Iterator> iterator = entryset.iterator(); + while (iterator.hasNext()) { + Map.Entry tile = iterator.next(); + BlockPos pos = tile.getKey(); + int lx = pos.getX() & 15; + int ly = pos.getY(); + int lz = pos.getZ() & 15; + int j = FaweCache.CACHE_I[ly][lx][lz]; + char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + int k = FaweCache.CACHE_J[ly][lx][lz]; + if (array[k] != 0) { + tile.getValue().invalidate();; + iterator.remove(); + } + } + HashSet entsToRemove = fs.getEntityRemoves(); + if (entsToRemove.size() > 0) { + for (int i = 0; i < entities.length; i++) { + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entsToRemove.contains(entity.getUniqueID())) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Efficiently merge sections + for (int j = 0; j < sections.length; j++) { + int count = fs.getCount(j); + if (count == 0) { + continue; + } + final char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + ExtendedBlockStorage section = sections[j]; + if (section == null) { + if (fs.sectionPalettes != null && fs.sectionPalettes[j] != null) { + section = sections[j] = new ExtendedBlockStorage(j << 4, flag); + setPalette(section, fs.sectionPalettes[j]); + setCount(0, count - fs.getAir(j), section); + } else { + sections[j] = new ExtendedBlockStorage(j << 4, flag); + } + } else if (count >= 4096) { + if (fs.sectionPalettes != null && fs.sectionPalettes[j] != null) { + setPalette(section, fs.sectionPalettes[j]); + setCount(0, count - fs.getAir(j), section); + continue; + } else { + sections[j] = new ExtendedBlockStorage(j << 4, flag); + } + } + BlockStateContainer nibble = section.getData(); + Field fieldBits = BlockStateContainer.class.getDeclaredField("storage"); + fieldBits.setAccessible(true); + BitArray bits = (BitArray) fieldBits.get(nibble); + + Field fieldPalette = BlockStateContainer.class.getDeclaredField("palette"); + fieldPalette.setAccessible(true); + IBlockStatePalette palette = (IBlockStatePalette) fieldPalette.get(nibble); + int nonEmptyBlockCount = 0; + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + char combinedId = array[FaweCache.CACHE_J[y][x][z]]; + switch (combinedId) { + case 0: + IBlockState existing = nibble.get(x, y, z); + if (existing != air) { + nonEmptyBlockCount++; + } + continue; + case 1: + nibble.set(x, y, z, air); + continue; + default: + nonEmptyBlockCount++; + nibble.set(x, y, z, Block.getBlockById(combinedId >> 4).getStateFromMeta(combinedId & 0xF)); + } + } + } + } + setCount(0, nonEmptyBlockCount, section); + } + // Set biomes + int[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + int biome = array[z]; + if (biome == 0) { + continue; + } + nmsChunk.getBiomeArray()[((z & 0xF) << 4 | x & 0xF)] = (byte) biome; + } + } + } + // Set tiles + Map tilesToSpawn = fs.getTiles(); + int bx = fs.getX() << 4; + int bz = fs.getZ() << 4; + + for (Map.Entry entry : tilesToSpawn.entrySet()) { + CompoundTag nativeTag = entry.getValue(); + BytePair pair = entry.getKey(); + BlockPos pos = new BlockPos(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity != null) { + NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(null, nativeTag); + tileEntity.readFromNBT(tag); // ReadTagIntoTile + } + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + int[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + int biome = array[z]; + if (biome == 0) { + continue; + } + nmsChunk.getBiomeArray()[((z & 0xF) << 4 | x & 0xF)] = (byte) biome; + } + } + } + sendChunk(fs, null); + return true; + } + + public void setPalette(ExtendedBlockStorage section, BlockStateContainer palette) throws NoSuchFieldException, IllegalAccessException { + Field fieldSection = ExtendedBlockStorage.class.getDeclaredField("data"); + fieldSection.setAccessible(true); + fieldSection.set(section, palette); + } + + @Override + public void refreshChunk(World world, net.minecraft.world.chunk.Chunk nmsChunk) { + if (!nmsChunk.isLoaded()) { + return; + } + try { + ChunkPos pos = nmsChunk.getChunkCoordIntPair(); + WorldServer w = (WorldServer) nmsChunk.getWorld(); + PlayerChunkMap chunkMap = w.getPlayerChunkMap(); + int x = pos.chunkXPos; + int z = pos.chunkZPos; + if (!chunkMap.contains(x, z)) { + return; + } + EntityTracker tracker = w.getEntityTracker(); + HashSet players = new HashSet<>(); + for (EntityPlayer player : w.playerEntities) { + if (player instanceof EntityPlayerMP) { + if (chunkMap.isPlayerWatchingChunk((EntityPlayerMP) player, x, z)) { + players.add((EntityPlayerMP) player); + } + } + } + if (players.size() == 0) { + return; + } + HashSet entities = new HashSet<>(); + ClassInheritanceMultiMap[] entitieSlices = nmsChunk.getEntityLists(); + IntHashMap entries = null; + for (Field field : tracker.getClass().getDeclaredFields()) { + if (field.getType() == IntHashMap.class) { + field.setAccessible(true); + entries = (IntHashMap) field.get(tracker); + } + } + for (ClassInheritanceMultiMap slice : entitieSlices) { + if (slice == null) { + continue; + } + for (Entity ent : slice) { + EntityTrackerEntry entry = entries != null ? entries.lookup(ent.getEntityId()) : null; + if (entry == null) { + continue; + } + entities.add(entry); + SPacketDestroyEntities packet = new SPacketDestroyEntities(ent.getEntityId()); + for (EntityPlayerMP player : players) { + player.connection.sendPacket(packet); + } + } + } + // Send chunks + SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65535); + for (EntityPlayerMP player : players) { + player.connection.sendPacket(packet); + } + // send ents + for (EntityTrackerEntry entry : entities) { + try { + TaskManager.IMP.later(new Runnable() { + @Override + public void run() { + for (EntityPlayerMP player : players) { + boolean result = entry.trackingPlayers.remove(player); + if (result && entry.getTrackedEntity() != player) { + entry.updatePlayerEntity(player); + } + } + } + }, 2); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + + @Override + public FaweChunk getFaweChunk(int x, int z) { + return new ForgeChunk_All(this, x, z); + } + + @Override + public boolean removeLighting(ExtendedBlockStorage[] sections, RelightMode mode, boolean sky) { + if (mode == RelightMode.ALL) { + for (int i = 0; i < sections.length; i++) { + ExtendedBlockStorage section = sections[i]; + if (section != null) { + section.setBlocklightArray(new NibbleArray()); + if (sky) { + section.setSkylightArray(new NibbleArray()); + } + } + } + } + return true; + } + + @Override + public boolean hasSky() { + return nmsWorld.provider.getHasNoSky(); + } + + @Override + public boolean initLighting(Chunk nmsChunk, ExtendedBlockStorage[] sections, RelightMode mode) { + if (mode == RelightMode.ALL) { + nmsChunk.generateSkylightMap(); + } else { + int i = nmsChunk.getTopFilledSegment(); + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + int l = 15; + int y = i + 16 - 1; + do { + int opacity = nmsChunk.getBlockLightOpacity(new BlockPos(x, y, z)); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ExtendedBlockStorage section = sections[y >> 4]; + if (section != null) { + section.setExtSkylightValue(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + return true; + } + + @Override + public void setFullbright(ExtendedBlockStorage[] sections) { + for (int i = 0; i < sections.length; i++) { + ExtendedBlockStorage section = sections[i]; + if (section != null) { + byte[] bytes = section.getSkylightArray().getData(); + Arrays.fill(bytes, (byte) 255); + } + } + } + + @Override + public int getSkyLight(ExtendedBlockStorage[] sections, int x, int y, int z) { + ExtendedBlockStorage section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 15; + } + return section.getExtSkylightValue(x, y & 15, z); + } + + @Override + public int getEmmittedLight(ExtendedBlockStorage[] sections, int x, int y, int z) { + ExtendedBlockStorage section = sections[FaweCache.CACHE_I[y][x][z]]; + if (section == null) { + return 0; + } + return section.getExtBlocklightValue(x, y & 15, z); + } + + @Override + public void relight(int x, int y, int z) { + pos.setPos(x, y, z); + nmsWorld.checkLight(pos); + } + + private WorldServer nmsWorld; + + @Override + public World getImpWorld() { + WorldServer[] worlds = FMLCommonHandler.instance().getMinecraftServerInstance().worldServers; + for (WorldServer ws : worlds) { + if (ws.getWorldInfo().getWorldName().equals(getWorldName())) { + return nmsWorld = ws; + } + } + return null; + } +} diff --git a/forge194/src/main/resources/config.yml b/forge194/src/main/resources/config.yml new file mode 100644 index 00000000..e69de29b diff --git a/settings.gradle b/settings.gradle index 4291e29c..272ff13c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ rootProject.name = 'FastAsyncWorldEdit' -include 'core', 'bukkit0', 'bukkit19', 'bukkit110', 'bukkit18', 'forge189', 'forge1710', 'sponge' +include 'core', 'bukkit0', 'bukkit1710', 'bukkit18', 'bukkit19', 'bukkit110', 'forge1710', 'forge189', 'forge194', 'forge110'