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 index c7db0436..56dd6cbb 100644 --- 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 @@ -153,6 +153,8 @@ public class ForgeChunk_All extends CharFaweChunk { @Override public ForgeChunk_All call() { net.minecraft.world.chunk.Chunk nmsChunk = this.getChunk(); + int bx = this.getX() << 4; + int bz = this.getZ() << 4; nmsChunk.setModified(true); net.minecraft.world.World nmsWorld = getParent().getWorld(); try { @@ -209,11 +211,17 @@ public class ForgeChunk_All extends CharFaweChunk { float yaw = rotTag.getFloat(0); float pitch = rotTag.getFloat(1); String id = idTag.getValue(); - NBTTagCompound tag = (NBTTagCompound)ForgeQueue_All.methodFromNative.invoke(null, nativeTag); - Entity entity = EntityList.createEntityFromNBT(tag, nmsWorld); - if (entity != null) { - entity.setPositionAndRotation(x, y, z, yaw, pitch); - nmsWorld.spawnEntityInWorld(entity); + if (id != null) { + Entity entity = EntityList.createEntityByName(id, nmsWorld); + if (entity != null) { + NBTTagCompound tag = (NBTTagCompound) ForgeQueue_All.methodFromNative.invoke(null, nativeTag); + tag.removeTag("UUIDMost"); + tag.removeTag("UUIDLeast"); + System.out.println(tag); + entity.readFromNBT(tag); + entity.setPositionAndRotation(x, y, z, yaw, pitch); + nmsWorld.spawnEntityInWorld(entity); + } } } // Run change task if applicable @@ -281,6 +289,8 @@ public class ForgeChunk_All extends CharFaweChunk { sections[j] = section = new ExtendedBlockStorage(j << 4, flag); } } + IBlockState existing; + int by = j << 4; BlockStateContainer nibble = section.getData(); int nonEmptyBlockCount = 0; for (int y = 0; y < 16; y++) { @@ -289,22 +299,32 @@ public class ForgeChunk_All extends CharFaweChunk { char combinedId = array[FaweCache.CACHE_J[y][z][x]]; switch (combinedId) { case 0: - IBlockState existing = nibble.get(x, y, z); - if (existing != ForgeQueue_All.air) { - nonEmptyBlockCount++; - } continue; case 1: + existing = nibble.get(x, y, z); + if (existing != ForgeQueue_All.air) { + if (existing.getLightValue() > 0) { + getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); + } + nonEmptyBlockCount--; + } nibble.set(x, y, z, ForgeQueue_All.air); continue; default: - nonEmptyBlockCount++; + existing = nibble.get(x, y, z); + if (existing != ForgeQueue_All.air) { + if (existing.getLightValue() > 0) { + getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); + } + } else { + nonEmptyBlockCount++; + } nibble.set(x, y, z, Block.getBlockById(combinedId >> 4).getStateFromMeta(combinedId & 0xF)); } } } } - getParent().setCount(0, nonEmptyBlockCount, section); + getParent().setCount(0, getParent().getNonEmptyBlockCount(section) + nonEmptyBlockCount, section); } // Set biomes int[][] biomes = this.biomes; @@ -325,8 +345,6 @@ public class ForgeChunk_All extends CharFaweChunk { } // Set tiles Map tilesToSpawn = this.getTiles(); - int bx = this.getX() << 4; - int bz = this.getZ() << 4; for (Map.Entry entry : tilesToSpawn.entrySet()) { CompoundTag nativeTag = entry.getValue(); 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 index bb5c0b4d..998ac8bd 100644 --- 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 @@ -50,31 +50,35 @@ import net.minecraftforge.common.DimensionManager; public class ForgeQueue_All extends NMSMappedFaweQueue { - protected static Method methodFromNative; - protected static Method methodToNative; + protected final static Method methodFromNative; + protected final static Method methodToNative; + protected final static Field fieldTickingBlockCount; + protected final static Field fieldNonEmptyBlockCount; + + static { + try { + Class converter = Class.forName("com.sk89q.worldedit.forge.NBTConverter"); + methodFromNative = converter.getDeclaredMethod("toNative", Tag.class); + methodToNative = converter.getDeclaredMethod("fromNative", NBTBase.class); + methodFromNative.setAccessible(true); + methodToNative.setAccessible(true); + + fieldTickingBlockCount = ExtendedBlockStorage.class.getDeclaredField("field_76683_c"); + fieldNonEmptyBlockCount = ExtendedBlockStorage.class.getDeclaredField("field_76682_b"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } public ForgeQueue_All(com.sk89q.worldedit.world.World world) { super(world); - init(); + getImpWorld(); } public ForgeQueue_All(String world) { super(world); - init(); - } - - private void init() { - if (methodFromNative == null) { - 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); - } - } getImpWorld(); } @@ -152,7 +156,7 @@ public class ForgeQueue_All extends NMSMappedFaweQueue) fieldPlayers.get(entry); playerManager.removeEntry(entry); @@ -222,12 +226,11 @@ public class ForgeQueue_All extends NMSMappedFaweQueue 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); } diff --git a/forge111/build.gradle b/forge111/build.gradle new file mode 100644 index 00000000..e2eeb0bf --- /dev/null +++ b/forge111/build.gradle @@ -0,0 +1,87 @@ +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:3.1.0-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 = "1.11-13.19.0.2157" + mappings = "snapshot_20161123" + 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/forge111/src/main/java/com/boydti/fawe/forge/FaweForge.java b/forge111/src/main/java/com/boydti/fawe/forge/FaweForge.java new file mode 100644 index 00000000..39135328 --- /dev/null +++ b/forge111/src/main/java/com/boydti/fawe/forge/FaweForge.java @@ -0,0 +1,174 @@ +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.EditSession; +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.HashMap; +import java.util.Map; +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 net.minecraftforge.fml.common.ModMetadata; +import org.apache.logging.log4j.Logger; + +public class FaweForge implements IFawe { + + private final ForgeMain parent; + private final File directory; + private final Logger logger; + private final ModMetadata mod; + + public FaweForge(ForgeMain plugin, Logger logger, ModMetadata mod, File directory) { + this.parent = plugin; + this.logger = logger; + this.directory = directory; + this.mod = mod; + 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; + } + + private HashMap commands = new HashMap<>(); + + @Override + public void setupCommand(String label, FaweCommand cmd) { + this.commands.put(label, cmd); + } + + public void insertCommands() { + for (Map.Entry entry : commands.entrySet()) { + ServerCommandManager scm = (ServerCommandManager) FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager(); + scm.registerCommand(new ForgeCommand(entry.getKey(), entry.getValue())); + } + } + + @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 String getWorldName(World world) { + if (world instanceof WorldWrapper) { + return getWorldName(((WorldWrapper) world).getParent()); + } + else if (world instanceof EditSession) { + return getWorldName(((EditSession) world).getWorld()); + } + return getWorldName(((ForgeWorld) world).getWorld()); + + } + + public String getWorldName(net.minecraft.world.World w) { + return w.getWorldInfo().getWorldName() + ";" + w.provider.getDimension(); + } + + @Override + public FaweQueue getNewQueue(World world, boolean dontCareIfFast) { + return new ForgeQueue_All(world); + } + + @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(); + } 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/forge111/src/main/java/com/boydti/fawe/forge/ForgeCommand.java b/forge111/src/main/java/com/boydti/fawe/forge/ForgeCommand.java new file mode 100644 index 00000000..c4cd4883 --- /dev/null +++ b/forge111/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 getName() { + return name; + } + + @Override + public String getUsage(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.world.isRemote) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + cmd.executeSafe(fp, args); + } + } +} diff --git a/forge111/src/main/java/com/boydti/fawe/forge/ForgeMain.java b/forge111/src/main/java/com/boydti/fawe/forge/ForgeMain.java new file mode 100644 index 00000000..c35b6d8c --- /dev/null +++ b/forge111/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.object.FawePlayer; +import java.io.File; +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.FMLServerStartingEvent; +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 FaweForge(this, event.getModLog(), event.getModMetadata(), directory); + } + + @Mod.EventHandler + public void serverLoad(FMLServerStartingEvent event) { + IMP.insertCommands(); + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onPlayerQuit(PlayerEvent.PlayerLoggedOutEvent event) { + if (event.player.world.isRemote) { + return; + } + handleQuit((EntityPlayerMP) event.player); + } + + @Mod.EventHandler + public void serverStopping(FMLServerStoppingEvent event) { + for (EntityPlayerMP player : FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayers()) { + 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.world.isRemote) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + if (fp.getMeta("lastWorld") != event.getWorld()) { + fp.setMeta("lastWorld", event.getWorld()); + } + } +} diff --git a/forge111/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge111/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java new file mode 100644 index 00000000..3142ebc7 --- /dev/null +++ b/forge111/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -0,0 +1,476 @@ +/* + * 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.object.io.FastByteArrayOutputStream; +import com.boydti.fawe.object.io.PGZIPOutputStream; +import com.boydti.fawe.util.MainUtil; +import java.io.*; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.UUID; +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) { + FastByteArrayOutputStream baos = new FastByteArrayOutputStream(); + PGZIPOutputStream gzos = null; + + try { + gzos = new PGZIPOutputStream(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/forge111/src/main/java/com/boydti/fawe/forge/ForgePlayer.java b/forge111/src/main/java/com/boydti/fawe/forge/ForgePlayer.java new file mode 100644 index 00000000..2105b6b1 --- /dev/null +++ b/forge111/src/main/java/com/boydti/fawe/forge/ForgePlayer.java @@ -0,0 +1,77 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.Fawe; +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.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) { + msg = BBC.color(msg); + for (String line : msg.split("\n")) { + this.parent.sendMessage(new TextComponentString(line)); + } + } + + @Override + public void executeCommand(String substring) { + throw new UnsupportedOperationException("NOT IMPLEMENTED"); + } + + @Override + public FaweLocation getLocation() { + World world = parent.world; + BlockPos pos = parent.getPosition(); + return new FaweLocation(Fawe.imp().getWorldName(world), pos.getX(), pos.getY(), pos.getZ()); + } + + @Override + public Player getPlayer() { + return PlayerWrapper.wrap(ForgeWorldEdit.inst.wrap(this.parent)); + } +} diff --git a/forge111/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge111/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java new file mode 100644 index 00000000..39eb1155 --- /dev/null +++ b/forge111/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<>(8, 0.9f, 1); + + + 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/forge111/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java b/forge111/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java new file mode 100644 index 00000000..b3c00016 --- /dev/null +++ b/forge111/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java @@ -0,0 +1,393 @@ +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.object.BytePair; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.MathMan; +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.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +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.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BitArray; +import net.minecraft.util.ClassInheritanceMultiMap; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +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; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; + +public class ForgeChunk_All extends CharFaweChunk { + + public BlockStateContainer[] sectionPalettes; + + public static Map entityKeys; + + /** + * 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); + } + + public ForgeChunk_All(FaweQueue parent, int x, int z, char[][] ids, short[] count, short[] air, byte[] heightMap) { + super(parent, x, z, ids, count, air, heightMap); + } + + @Override + public CharFaweChunk copy(boolean shallow) { + ForgeChunk_All copy; + if (shallow) { + copy = new ForgeChunk_All(getParent(), getX(), getZ(), ids, count, air, heightMap); + copy.biomes = biomes; + copy.chunk = chunk; + } else { + copy = new ForgeChunk_All(getParent(), getX(), getZ(), (char[][]) MainUtil.copyNd(ids), count.clone(), air.clone(), heightMap.clone()); + copy.biomes = biomes; + copy.chunk = chunk; + copy.biomes = biomes.clone(); + copy.chunk = chunk; + } + if (sectionPalettes != null) { + copy.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); + copy.sectionPalettes[i] = paletteBlock; + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + return copy; + } + + @Override + public Chunk getNewChunk() { + World world = ((ForgeQueue_All) getParent()).getWorld(); + return world.getChunkProvider().provideChunk(getX(), getZ()); + } + + 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][z][x]]; + if (combinedId > 1) { + palette.set(x, y, z, Block.getBlockById(combinedId >> 4).getStateFromMeta(combinedId & 0xF)); + } + } + } + } + } + } + } + + @Override + public ForgeChunk_All call() { + net.minecraft.world.chunk.Chunk nmsChunk = this.getChunk(); + int bx = this.getX() << 4; + int bz = this.getZ() << 4; + nmsChunk.setModified(true); + net.minecraft.world.World nmsWorld = getParent().getWorld(); + try { + boolean flag = !nmsWorld.provider.hasNoSky(); + // Sections + ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); + Map tiles = nmsChunk.getTileEntityMap(); + ClassInheritanceMultiMap[] entities = nmsChunk.getEntityLists(); + + // Set heightmap + getParent().setHeightMap(this, heightMap); + + // Remove entities + for (int i = 0; i < 16; i++) { + int count = this.getCount(i); + if (count == 0) { + continue; + } else if (count >= 4096) { + entities[i] = new ClassInheritanceMultiMap<>(Entity.class); + } else { + char[] array = this.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][z][x]] != 0) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Set entities + Set createdEntities = new HashSet<>(); + Set entitiesToSpawn = this.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(); + if (entityKeys == null) { + entityKeys = new HashMap<>(); + for (ResourceLocation key : EntityList.getEntityNameList()) { + String currentId = EntityList.getTranslationName(key); + entityKeys.put(currentId, key); + entityKeys.put(key.getResourcePath(), key); + } + } + ResourceLocation entityKey = entityKeys.get(id); + if (entityKey != null) { + Entity entity = EntityList.createEntityByIDFromName(entityKey, nmsWorld); + if (entity != null) { + NBTTagCompound tag = (NBTTagCompound)ForgeQueue_All.methodFromNative.invoke(null, nativeTag); + entity.readFromNBT(tag); + tag.removeTag("UUIDMost"); + tag.removeTag("UUIDLeast"); + entity.setPositionAndRotation(x, y, z, yaw, pitch); + nmsWorld.spawnEntity(entity); + } + } + + } + // Run change task if applicable + if (getParent().getChangeTask() != null) { + CharFaweChunk previous = getParent().getPrevious(this, sections, tiles, entities, createdEntities, false); + getParent().getChangeTask().run(previous, this); + } + // 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][lz][lx]; + char[] array = this.getIdArray(j); + if (array == null) { + continue; + } + int k = FaweCache.CACHE_J[ly][lz][lx]; + if (array[k] != 0) { + tile.getValue().invalidate();; + iterator.remove(); + } + } + HashSet entsToRemove = this.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 = this.getCount(j); + if (count == 0) { + continue; + } + final char[] array = this.getIdArray(j); + if (array == null) { + continue; + } + ExtendedBlockStorage section = sections[j]; + if (section == null) { + if (this.sectionPalettes != null && this.sectionPalettes[j] != null) { + section = sections[j] = new ExtendedBlockStorage(j << 4, flag); + getParent().setPalette(section, this.sectionPalettes[j]); + getParent().setCount(0, count - this.getAir(j), section); + continue; + } else { + sections[j] = section = new ExtendedBlockStorage(j << 4, flag); + } + } else if (count >= 4096) { + if (this.sectionPalettes != null && this.sectionPalettes[j] != null) { + getParent().setPalette(section, this.sectionPalettes[j]); + getParent().setCount(0, count - this.getAir(j), section); + continue; + } else { + sections[j] = section = new ExtendedBlockStorage(j << 4, flag); + } + } + IBlockState existing; + int by = j << 4; + BlockStateContainer nibble = section.getData(); + 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][z][x]]; + switch (combinedId) { + case 0: + continue; + case 1: + existing = nibble.get(x, y, z); + if (existing != ForgeQueue_All.air) { + if (existing.getLightValue() > 0) { + getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); + } + nonEmptyBlockCount--; + } + nibble.set(x, y, z, ForgeQueue_All.air); + continue; + default: + existing = nibble.get(x, y, z); + if (existing != ForgeQueue_All.air) { + if (existing.getLightValue() > 0) { + getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); + } + } else { + nonEmptyBlockCount++; + } + nibble.set(x, y, z, Block.getBlockById(combinedId >> 4).getStateFromMeta(combinedId & 0xF)); + } + } + } + } + getParent().setCount(0, getParent().getNonEmptyBlockCount(section) + nonEmptyBlockCount, section); + } + // Set biomes + int[][] biomes = this.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 = this.getTiles(); + + for (Map.Entry entry : tilesToSpawn.entrySet()) { + CompoundTag nativeTag = entry.getValue(); + BytePair pair = entry.getKey(); + BlockPos pos = new BlockPos(MathMan.unpair16x((byte) pair.get0()) + bx, pair.get1() & 0xFF, MathMan.unpair16y((byte) pair.get0()) + bz); // Set pos + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity != null) { + NBTTagCompound tag = (NBTTagCompound) ForgeQueue_All.methodFromNative.invoke(null, nativeTag); + tileEntity.readFromNBT(tag); // ReadTagIntoTile + } + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + int[][] biomes = this.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; + } + } + } + return this; + } +} diff --git a/forge111/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java b/forge111/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java new file mode 100644 index 00000000..b735a0a4 --- /dev/null +++ b/forge111/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java @@ -0,0 +1,487 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.example.NMSMappedFaweQueue; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Collection; +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.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.server.management.PlayerChunkMap; +import net.minecraft.server.management.PlayerChunkMapEntry; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ClassInheritanceMultiMap; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.EnumSkyBlock; +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.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.common.DimensionManager; + +public class ForgeQueue_All extends NMSMappedFaweQueue { + + protected final static Method methodFromNative; + protected final static Method methodToNative; + protected final static Field fieldTickingBlockCount; + protected final static Field fieldNonEmptyBlockCount; + + static { + try { + Class converter = Class.forName("com.sk89q.worldedit.forge.NBTConverter"); + methodFromNative = converter.getDeclaredMethod("toNative", Tag.class); + methodToNative = converter.getDeclaredMethod("fromNative", NBTBase.class); + methodFromNative.setAccessible(true); + methodToNative.setAccessible(true); + + fieldTickingBlockCount = ExtendedBlockStorage.class.getDeclaredField("field_76683_c"); + fieldNonEmptyBlockCount = ExtendedBlockStorage.class.getDeclaredField("field_76682_b"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + public ForgeQueue_All(com.sk89q.worldedit.world.World world) { + super(world); + getImpWorld(); + } + + public ForgeQueue_All(String world) { + super(world); + getImpWorld(); + } + + @Override + public void setHeightMap(FaweChunk chunk, byte[] heightMap) { + Chunk forgeChunk = (Chunk) chunk.getChunk(); + if (forgeChunk != null) { + int[] otherMap = forgeChunk.getHeightMap(); + for (int i = 0; i < heightMap.length; i++) { + int newHeight = heightMap[i] & 0xFF; + int currentHeight = otherMap[i]; + if (newHeight > currentHeight) { + otherMap[i] = newHeight; + } + } + } + } + + protected 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.writeToNBT(tag); // readTagIntoEntity + CompoundTag result = (CompoundTag) methodToNative.invoke(null, tag); + return result; + } 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.asLong(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 = PlayerChunkMapEntry.class.getDeclaredField("field_187283_c"); + 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 ExtendedBlockStorage getCachedSection(ExtendedBlockStorage[] chunk, int cy) { + return chunk[cy]; + } + + @Override + public int getCombinedId4Data(ExtendedBlockStorage section, int x, int y, int z) { + IBlockState ibd = section.getData().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 int getNonEmptyBlockCount(ExtendedBlockStorage section) throws IllegalAccessException { + return (int) fieldNonEmptyBlockCount.get(section); + } + + public void setCount(int tickingBlockCount, int nonEmptyBlockCount, ExtendedBlockStorage section) throws NoSuchFieldException, IllegalAccessException { + 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 = previous.getCombinedIdArrays(); + 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); + } + } + } + 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][z][x]; + char[] array = fs.getIdArray(i); + if (array == null) { + continue; + } + int j = FaweCache.CACHE_J[y][z][x]; + 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; + } + + protected final static IBlockState air = Blocks.AIR.getDefaultState(); + + 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(FaweChunk fc) { + ForgeChunk_All fs = (ForgeChunk_All) fc; + ensureChunkLoaded(fc.getX(), fc.getZ()); + Chunk nmsChunk = fs.getChunk(); + 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; + PlayerChunkMapEntry chunkMapEntry = chunkMap.getEntry(x, z); + if (chunkMapEntry == null) { + return; + } + final ArrayDeque players = new ArrayDeque<>(); + chunkMapEntry.hasPlayerMatching(input -> { + players.add(input); + return false; + }); + int mask = fc.getBitMask(); + if (mask == 0 || mask == 65535 && hasEntities(nmsChunk)) { + SPacketChunkData packet = new SPacketChunkData(nmsChunk, 65280); + for (EntityPlayerMP player : players) { + player.connection.sendPacket(packet); + } + mask = 255; + } + SPacketChunkData packet = new SPacketChunkData(nmsChunk, mask); + for (EntityPlayerMP player : players) { + player.connection.sendPacket(packet); + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + + public boolean hasEntities(Chunk nmsChunk) { + ClassInheritanceMultiMap[] entities = nmsChunk.getEntityLists(); + for (int i = 0; i < entities.length; i++) { + ClassInheritanceMultiMap slice = entities[i]; + if (slice != null && !slice.isEmpty()) { + return true; + } + } + return false; + } + + + @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.hasNoSky(); + } + + @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 void relight(int x, int y, int z) { + pos.setPos(x, y, z); + nmsWorld.checkLight(pos); + } + + protected WorldServer nmsWorld; + + @Override + public World getImpWorld() { + if (nmsWorld != null || getWorldName() == null) { + return nmsWorld; + } + String[] split = getWorldName().split(";"); + int id = Integer.parseInt(split[split.length - 1]); + nmsWorld = DimensionManager.getWorld(id); + return nmsWorld; + } + + @Override + public void setSkyLight(ExtendedBlockStorage section, int x, int y, int z, int value) { + section.getSkylightArray().set(x & 15, y & 15, z & 15, value); + } + + @Override + public void setBlockLight(ExtendedBlockStorage section, int x, int y, int z, int value) { + section.getBlocklightArray().set(x & 15, y & 15, z & 15, value); + } + + @Override + public int getSkyLight(ExtendedBlockStorage section, int x, int y, int z) { + return section.getExtSkylightValue(x & 15, y & 15, z & 15); + } + + @Override + public int getEmmittedLight(ExtendedBlockStorage section, int x, int y, int z) { + return section.getExtBlocklightValue(x & 15, y & 15, z & 15); + } + + @Override + public int getOpacity(ExtendedBlockStorage section, int x, int y, int z) { + BlockStateContainer dataPalette = section.getData(); + IBlockState ibd = dataPalette.get(x & 15, y & 15, z & 15); + return ibd.getLightOpacity(); + } + + @Override + public int getBrightness(ExtendedBlockStorage section, int x, int y, int z) { + BlockStateContainer dataPalette = section.getData(); + IBlockState ibd = dataPalette.get(x & 15, y & 15, z & 15); + return ibd.getLightValue(); + } + + @Override + public int getOpacityBrightnessPair(ExtendedBlockStorage section, int x, int y, int z) { + BlockStateContainer dataPalette = section.getData(); + IBlockState ibd = dataPalette.get(x & 15, y & 15, z & 15); + return MathMan.pair16(ibd.getLightOpacity(), ibd.getLightValue()); + } + + @Override + public void relightBlock(int x, int y, int z) { + pos.setPos(x, y, z); + nmsWorld.checkLightFor(EnumSkyBlock.BLOCK, pos); + } + + @Override + public void relightSky(int x, int y, int z) { + pos.setPos(x, y, z); + nmsWorld.checkLightFor(EnumSkyBlock.SKY, pos); + } + + @Override + public File getSaveFolder() { + return new File(((WorldServer) getWorld()).getChunkSaveLocation(), "region"); + } +} diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java b/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java index 22b0e218..fbb7ac36 100644 --- a/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java +++ b/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java @@ -187,9 +187,12 @@ public class ForgeChunk_All extends CharFaweChunk { float yaw = rotTag.getFloat(0); float pitch = rotTag.getFloat(1); String id = idTag.getValue(); - NBTTagCompound tag = (NBTTagCompound)ForgeQueue_All.methodFromNative.invoke(null, nativeTag); - Entity entity = EntityList.createEntityFromNBT(tag, nmsWorld); + Entity entity = EntityList.createEntityByName(id, nmsWorld); if (entity != null) { + NBTTagCompound tag = (NBTTagCompound) ForgeQueue_All.methodFromNative.invoke(null, nativeTag); + tag.removeTag("UUIDMost"); + tag.removeTag("UUIDLeast"); + entity.readFromNBT(tag); entity.setPositionAndRotation(x, y, z, yaw, pitch); nmsWorld.spawnEntityInWorld(entity); } diff --git a/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java b/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java index a3223982..5ba09c6a 100644 --- a/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java +++ b/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java @@ -71,6 +71,8 @@ public class ForgeChunk_All extends CharFaweChunk { @Override public ForgeChunk_All call() { net.minecraft.world.chunk.Chunk nmsChunk = this.getChunk(); + int bx = this.getX() << 4; + int bz = this.getZ() << 4; nmsChunk.setModified(true); nmsChunk.setHasEntities(true); net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); @@ -128,9 +130,12 @@ public class ForgeChunk_All extends CharFaweChunk { float yaw = rotTag.getFloat(0); float pitch = rotTag.getFloat(1); String id = idTag.getValue(); - NBTTagCompound tag = (NBTTagCompound) ForgeQueue_All.methodFromNative.invoke(null, nativeTag); - Entity entity = EntityList.createEntityFromNBT(tag, nmsWorld); + Entity entity = EntityList.createEntityByName(id, nmsWorld); if (entity != null) { + NBTTagCompound tag = (NBTTagCompound) ForgeQueue_All.methodFromNative.invoke(null, nativeTag); + tag.removeTag("UUIDMost"); + tag.removeTag("UUIDLeast"); + entity.readFromNBT(tag); entity.setPositionAndRotation(x, y, z, yaw, pitch); nmsWorld.spawnEntityInWorld(entity); } @@ -196,26 +201,40 @@ public class ForgeChunk_All extends CharFaweChunk { char[] currentArray = section.getData(); boolean fill = true; int solid = 0; + char existingId; for (int k = 0; k < newArray.length; k++) { char n = newArray[k]; switch (n) { case 0: - fill = false; continue; case 1: - fill = false; - if (currentArray[k] > 1) { - solid++; + existingId = currentArray[k]; + if (existingId > 1) { + if (FaweCache.hasLight(existingId)) { + int x = FaweCache.CACHE_X[j][k]; + int y = FaweCache.CACHE_Y[j][k]; + int z = FaweCache.CACHE_Z[j][k]; + getParent().getRelighter().addLightUpdate(bx + x, y, bz + z); + } + solid--; + currentArray[k] = 0; } - currentArray[k] = 0; continue; default: - solid++; + existingId = currentArray[k]; + if (existingId <= 1) { + solid++; + } else if (FaweCache.hasLight(existingId)) { + int x = FaweCache.CACHE_X[j][k]; + int y = FaweCache.CACHE_Y[j][k]; + int z = FaweCache.CACHE_Z[j][k]; + getParent().getRelighter().addLightUpdate(bx + x, y, bz + z); + } currentArray[k] = n; continue; } } - getParent().setCount(0, solid, section); + getParent().setCount(0, getParent().getNonEmptyBlockCount(section) + solid, section); if (fill) { this.setCount(j, Short.MAX_VALUE); } @@ -240,9 +259,6 @@ public class ForgeChunk_All extends CharFaweChunk { } // Set tiles Map tilesToSpawn = this.getTiles(); - int bx = this.getX() << 4; - int bz = this.getZ() << 4; - for (Map.Entry entry : tilesToSpawn.entrySet()) { CompoundTag nativeTag = entry.getValue(); BytePair pair = entry.getKey(); 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 3491810b..625fa6ca 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 @@ -44,31 +44,34 @@ import net.minecraftforge.common.DimensionManager; public class ForgeQueue_All extends NMSMappedFaweQueue { - protected static Method methodFromNative; - protected static Method methodToNative; + protected final static Method methodFromNative; + protected final static Method methodToNative; + private static final Field fieldTickingBlockCount; + private static final Field fieldNonEmptyBlockCount; + + static { + try { + Class converter = Class.forName("com.sk89q.worldedit.forge.NBTConverter"); + methodFromNative = converter.getDeclaredMethod("toNative", Tag.class); + methodToNative = converter.getDeclaredMethod("fromNative", NBTBase.class); + methodFromNative.setAccessible(true); + methodToNative.setAccessible(true); + fieldTickingBlockCount = ExtendedBlockStorage.class.getDeclaredField("field_76683_c"); + fieldNonEmptyBlockCount = ExtendedBlockStorage.class.getDeclaredField("field_76682_b"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } public ForgeQueue_All(com.sk89q.worldedit.world.World world) { super(world); - init(); + getImpWorld(); } public ForgeQueue_All(String world) { super(world); - init(); - } - - private void init() { - if (methodFromNative == null) { - 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); - } - } getImpWorld(); } @@ -186,15 +189,14 @@ public class ForgeQueue_All extends NMSMappedFaweQueue 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); } + public int getNonEmptyBlockCount(ExtendedBlockStorage section) throws IllegalAccessException { + return (int) fieldNonEmptyBlockCount.get(section); + } + @Override public CharFaweChunk getPrevious(CharFaweChunk fs, ExtendedBlockStorage[] sections, Map tilesGeneric, Collection[] entitiesGeneric, Set createdEntities, boolean all) throws Exception { Map tiles = (Map) tilesGeneric; 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 index 11047ac2..ace36a9a 100644 --- 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 @@ -69,7 +69,7 @@ public class ForgeChunk_All extends CharFaweChunk { copy.biomes = biomes.clone(); copy.chunk = chunk; } - if (copy != null) { + if (sectionPalettes != null) { copy.sectionPalettes = new BlockStateContainer[16]; try { Field fieldBits = BlockStateContainer.class.getDeclaredField("storage"); @@ -153,8 +153,10 @@ public class ForgeChunk_All extends CharFaweChunk { @Override public ForgeChunk_All call() { net.minecraft.world.chunk.Chunk nmsChunk = this.getChunk(); + int bx = this.getX() << 4; + int bz = this.getZ() << 4; nmsChunk.setModified(true); - net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); + net.minecraft.world.World nmsWorld = getParent().getWorld(); try { boolean flag = !nmsWorld.provider.getHasNoSky(); // Sections @@ -209,9 +211,12 @@ public class ForgeChunk_All extends CharFaweChunk { float yaw = rotTag.getFloat(0); float pitch = rotTag.getFloat(1); String id = idTag.getValue(); - NBTTagCompound tag = (NBTTagCompound) ForgeQueue_All.methodFromNative.invoke(null, nativeTag); - Entity entity = EntityList.createEntityFromNBT(tag, nmsWorld); + Entity entity = EntityList.createEntityByName(id, nmsWorld); if (entity != null) { + NBTTagCompound tag = (NBTTagCompound) ForgeQueue_All.methodFromNative.invoke(null, nativeTag); + tag.removeTag("UUIDMost"); + tag.removeTag("UUIDLeast"); + entity.readFromNBT(tag); entity.setPositionAndRotation(x, y, z, yaw, pitch); nmsWorld.spawnEntityInWorld(entity); } @@ -281,6 +286,8 @@ public class ForgeChunk_All extends CharFaweChunk { sections[j] = section = new ExtendedBlockStorage(j << 4, flag); } } + IBlockState existing; + int by = j << 4; BlockStateContainer nibble = section.getData(); int nonEmptyBlockCount = 0; for (int y = 0; y < 16; y++) { @@ -289,22 +296,32 @@ public class ForgeChunk_All extends CharFaweChunk { char combinedId = array[FaweCache.CACHE_J[y][z][x]]; switch (combinedId) { case 0: - IBlockState existing = nibble.get(x, y, z); - if (existing != ForgeQueue_All.air) { - nonEmptyBlockCount++; - } continue; case 1: + existing = nibble.get(x, y, z); + if (existing != ForgeQueue_All.air) { + if (existing.getLightValue() > 0) { + getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); + } + nonEmptyBlockCount--; + } nibble.set(x, y, z, ForgeQueue_All.air); continue; default: - nonEmptyBlockCount++; + existing = nibble.get(x, y, z); + if (existing != ForgeQueue_All.air) { + if (existing.getLightValue() > 0) { + getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); + } + } else { + nonEmptyBlockCount++; + } nibble.set(x, y, z, Block.getBlockById(combinedId >> 4).getStateFromMeta(combinedId & 0xF)); } } } } - getParent().setCount(0, nonEmptyBlockCount, section); + getParent().setCount(0, getParent().getNonEmptyBlockCount(section) + nonEmptyBlockCount, section); } // Set biomes int[][] biomes = this.biomes; @@ -325,8 +342,6 @@ public class ForgeChunk_All extends CharFaweChunk { } // Set tiles Map tilesToSpawn = this.getTiles(); - int bx = this.getX() << 4; - int bz = this.getZ() << 4; for (Map.Entry entry : tilesToSpawn.entrySet()) { CompoundTag nativeTag = entry.getValue(); 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 index 74d9335a..998ac8bd 100644 --- 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 @@ -16,6 +16,7 @@ import java.lang.reflect.Method; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -49,36 +50,38 @@ import net.minecraftforge.common.DimensionManager; public class ForgeQueue_All extends NMSMappedFaweQueue { - protected static Method methodFromNative; - protected static Method methodToNative; + protected final static Method methodFromNative; + protected final static Method methodToNative; + protected final static Field fieldTickingBlockCount; + protected final static Field fieldNonEmptyBlockCount; + + static { + try { + Class converter = Class.forName("com.sk89q.worldedit.forge.NBTConverter"); + methodFromNative = converter.getDeclaredMethod("toNative", Tag.class); + methodToNative = converter.getDeclaredMethod("fromNative", NBTBase.class); + methodFromNative.setAccessible(true); + methodToNative.setAccessible(true); + + fieldTickingBlockCount = ExtendedBlockStorage.class.getDeclaredField("field_76683_c"); + fieldNonEmptyBlockCount = ExtendedBlockStorage.class.getDeclaredField("field_76682_b"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } public ForgeQueue_All(com.sk89q.worldedit.world.World world) { super(world); - init(); + getImpWorld(); } public ForgeQueue_All(String world) { super(world); - init(); - } - - private void init() { - if (methodFromNative == null) { - 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); - } - } getImpWorld(); } - protected BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(0, 0, 0); - @Override public void setHeightMap(FaweChunk chunk, byte[] heightMap) { Chunk forgeChunk = (Chunk) chunk.getChunk(); @@ -94,6 +97,8 @@ public class ForgeQueue_All extends NMSMappedFaweQueue tiles = chunk.getTileEntityMap(); @@ -106,7 +111,8 @@ public class ForgeQueue_All extends NMSMappedFaweQueue oldWatchers = null; if (chunkServer.chunkExists(x, z)) { mcChunk = chunkServer.loadChunk(x, z); + PlayerChunkMapEntry entry = playerManager.getEntry(x, z); + if (entry != null) { + Field fieldPlayers = PlayerChunkMapEntry.class.getDeclaredField("field_187283_c"); + fieldPlayers.setAccessible(true); + oldWatchers = (List) fieldPlayers.get(entry); + playerManager.removeEntry(entry); + } mcChunk.onChunkUnload(); } try { @@ -163,6 +178,11 @@ public class ForgeQueue_All extends NMSMappedFaweQueue 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); } @@ -351,6 +370,7 @@ public class ForgeQueue_All extends NMSMappedFaweQueue getFaweChunk(int x, int z) { return new ForgeChunk_All(this, x, z); @@ -403,7 +423,8 @@ public class ForgeQueue_All extends NMSMappedFaweQueue