diff --git a/.gitignore b/.gitignore index 1f97b0ed..02e1e77c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,11 @@ /target/ /.gradle /.idea -/forge/build -forge/.gradle/gradle.log *.log +gradle.log /lib -/core/build \ No newline at end of file +/core/build +/forge189/build +/forge1710/build +/sponge/build +/bukkit/build \ No newline at end of file diff --git a/build.gradle b/build.gradle index 6503c89e..9b87a444 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { } group = 'com.boydti.fawe' -version = '3.3.21' +version = '3.4.0' description = """FastAsyncWorldEdit""" subprojects { @@ -35,7 +35,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://repo.techcable.net/content/groups/public/"} 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/"} diff --git a/bukkit/build/resources/main/plugin.yml b/bukkit/build/resources/main/plugin.yml index fc0da240..ca4b079f 100644 --- a/bukkit/build/resources/main/plugin.yml +++ b/bukkit/build/resources/main/plugin.yml @@ -1,6 +1,6 @@ name: FastAsyncWorldEdit main: com.boydti.fawe.bukkit.FaweBukkit -version: 3.3.21 +version: 3.4.0 description: Fast Async WorldEdit plugin authors: [Empire92] loadbefore: [WorldEdit] diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 85425644..aeb35fb7 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -28,13 +28,12 @@ import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.world.World; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -121,15 +120,6 @@ public class FaweBukkit extends JavaPlugin implements IFawe, Listener { debug("&6Metrics enabled."); } - @Override - public Set getPlayers() { - HashSet players = new HashSet<>(); - for (Player player : Bukkit.getServer().getOnlinePlayers()) { - players.add(wrap(player)); - } - return players; - } - /** * Kinda a really messy class I just copied over from an old project
* - Still works, so cbf cleaning it up
@@ -239,6 +229,11 @@ public class FaweBukkit extends JavaPlugin implements IFawe, Listener { return new BukkitQueue_All(world); } + @Override + public String getWorldName(World world) { + return world.getName(); + } + /** * The EditSessionWrapper should have the same functionality as the normal EditSessionWrapper but with some optimizations */ @@ -358,7 +353,7 @@ public class FaweBukkit extends JavaPlugin implements IFawe, Listener { FawePlayer fp = FawePlayer.wrap(player); if (Settings.STORE_HISTORY_ON_DISK) { fp.getSession().clearHistory(); - fp.loadSessionFromDisk(fp.getWorld()); + fp.loadSessionsFromDisk(fp.getWorld()); } } diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/logging/LoggingExtent.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/logging/LoggingExtent.java index 76fe933f..0b820053 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/logging/LoggingExtent.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/logging/LoggingExtent.java @@ -43,7 +43,6 @@ public class LoggingExtent extends AbstractDelegateExtent { * @param changeSet the change set * @param api * @param player - * @param thread */ public LoggingExtent(final Extent extent, final ChangeSet changeSet, final FawePlayer player, final IBlocksHubApi api) { super(extent); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue_1_8.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue_1_8.java index a36ab31a..0069981b 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue_1_8.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue_1_8.java @@ -246,7 +246,9 @@ public class BukkitQueue_1_8 extends BukkitQueue_All { } return true; } catch (final Throwable e) { - e.printStackTrace(); + if (Thread.currentThread() == Fawe.get().getMainThread()) { + e.printStackTrace(); + } } return false; } diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index fc0da240..ca4b079f 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: FastAsyncWorldEdit main: com.boydti.fawe.bukkit.FaweBukkit -version: 3.3.21 +version: 3.4.0 description: Fast Async WorldEdit plugin authors: [Empire92] loadbefore: [WorldEdit] diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index a6488539..b23fa9df 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -44,6 +44,7 @@ import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; +import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -154,14 +155,11 @@ public class Fawe { this.setupConfigs(); MainUtil.deleteOlder(new File(IMP.getDirectory(), "history"), TimeUnit.DAYS.toMillis(Settings.DELETE_HISTORY_AFTER_DAYS)); - this.setupCommands(); - - // TODO command event - queue? - TaskManager.IMP = this.IMP.getTaskManager(); if (Settings.METRICS) { this.IMP.startMetrics(); } + this.setupCommands(); // Delayed setup TaskManager.IMP.later(new Runnable() { @@ -337,6 +335,10 @@ public class Fawe { return players.get(name); } + public Collection getCachedPlayers() { + return players.values(); + } + /* * TODO FIXME * - Async packet sending diff --git a/core/src/main/java/com/boydti/fawe/FaweAPI.java b/core/src/main/java/com/boydti/fawe/FaweAPI.java index 23631178..2afcb296 100644 --- a/core/src/main/java/com/boydti/fawe/FaweAPI.java +++ b/core/src/main/java/com/boydti/fawe/FaweAPI.java @@ -87,6 +87,15 @@ public class FaweAPI { return SetQueue.IMP.getNewQueue(worldName, autoqueue); } + public static World getWorld(String worldName) { + for (World current : WorldEdit.getInstance().getServer().getWorlds()) { + if (Fawe.imp().getWorldName(current).equals(worldName)) { + return current; + } + } + return null; + } + /** * Get a list of supported protection plugin masks. * @return Set of FaweMaskManager @@ -155,13 +164,7 @@ public class FaweAPI { } String worldName = path[path.length - 3]; String uuidString = path[path.length - 2]; - World world = null; - for (World current : WorldEdit.getInstance().getServer().getWorlds()) { - if (current.getName().equals(worldName)) { - world = current; - break; - } - } + World world = getWorld(worldName); if (world == null) { throw new IllegalArgumentException("Corresponding world does not exist: " + worldName); } diff --git a/core/src/main/java/com/boydti/fawe/IFawe.java b/core/src/main/java/com/boydti/fawe/IFawe.java index cca2efbc..2baa9c48 100644 --- a/core/src/main/java/com/boydti/fawe/IFawe.java +++ b/core/src/main/java/com/boydti/fawe/IFawe.java @@ -7,9 +7,9 @@ import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.world.World; import java.io.File; import java.util.Collection; -import java.util.Set; import java.util.UUID; public interface IFawe { @@ -31,14 +31,14 @@ public interface IFawe { public FaweQueue getNewQueue(String world); + public String getWorldName(World world); + public EditSessionWrapper getEditSessionWrapper(final EditSession session); public Collection getMaskManagers(); public void startMetrics(); - public Set getPlayers(); - public String getPlatform(); public UUID getUUID(String name); diff --git a/core/src/main/java/com/boydti/fawe/command/Rollback.java b/core/src/main/java/com/boydti/fawe/command/Rollback.java index fe1db5ce..17747bee 100644 --- a/core/src/main/java/com/boydti/fawe/command/Rollback.java +++ b/core/src/main/java/com/boydti/fawe/command/Rollback.java @@ -1,6 +1,7 @@ package com.boydti.fawe.command; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FaweLocation; @@ -158,7 +159,7 @@ public class Rollback extends FaweCommand { } } FaweLocation origin = player.getLocation(); - List edits = MainUtil.getBDFiles(origin, user, radius, time, shallow); + List edits = FaweAPI.getBDFiles(origin, user, radius, time, shallow); if (edits == null) { player.sendMessage("&cToo broad, try refining your search!"); return; diff --git a/core/src/main/java/com/boydti/fawe/command/Stream.java b/core/src/main/java/com/boydti/fawe/command/Stream.java index 50f9c695..5412a9a0 100644 --- a/core/src/main/java/com/boydti/fawe/command/Stream.java +++ b/core/src/main/java/com/boydti/fawe/command/Stream.java @@ -30,7 +30,7 @@ public class Stream extends FaweCommand { BBC.SCHEMATIC_NOT_FOUND.send(player, args[0]); return false; } - FaweAPI.streamSchematicAsync(file, player.getLocation()); + FaweAPI.streamSchematic(file, player.getLocation()); BBC.SCHEMATIC_PASTING.send(player); return true; } diff --git a/core/src/main/java/com/boydti/fawe/configuration/TypeDescription.java b/core/src/main/java/com/boydti/fawe/configuration/TypeDescription.java new file mode 100644 index 00000000..4c383076 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/TypeDescription.java @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2008, http://www.snakeyaml.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.yaml.snakeyaml; + +import java.util.HashMap; +import java.util.Map; + +import org.yaml.snakeyaml.nodes.Tag; + +/** + * Provides additional runtime information necessary to create a custom Java + * instance. + */ +public final class TypeDescription { + private final Class type; + private Tag tag; + private Map> listProperties; + private Map> keyProperties; + private Map> valueProperties; + + public TypeDescription(Class clazz, Tag tag) { + this.type = clazz; + this.tag = tag; + listProperties = new HashMap>(); + keyProperties = new HashMap>(); + valueProperties = new HashMap>(); + } + + public TypeDescription(Class clazz, String tag) { + this(clazz, new Tag(tag)); + } + + public TypeDescription(Class clazz) { + this(clazz, (Tag) null); + } + + /** + * Get tag which shall be used to load or dump the type (class). + * + * @return tag to be used. It may be a tag for Language-Independent Types + * (http://www.yaml.org/type/) + */ + public Tag getTag() { + return tag; + } + + /** + * Set tag to be used to load or dump the type (class). + * + * @param tag + * local or global tag + */ + public void setTag(Tag tag) { + this.tag = tag; + } + + public void setTag(String tag) { + setTag(new Tag(tag)); + } + + /** + * Get represented type (class) + * + * @return type (class) to be described. + */ + public Class getType() { + return type; + } + + /** + * Specify that the property is a type-safe List. + * + * @param property + * name of the JavaBean property + * @param type + * class of List values + */ + public void putListPropertyType(String property, Class type) { + listProperties.put(property, type); + } + + /** + * Get class of List values for provided JavaBean property. + * + * @param property + * property name + * @return class of List values + */ + public Class getListPropertyType(String property) { + return listProperties.get(property); + } + + /** + * Specify that the property is a type-safe Map. + * + * @param property + * property name of this JavaBean + * @param key + * class of keys in Map + * @param value + * class of values in Map + */ + public void putMapPropertyType(String property, Class key, + Class value) { + keyProperties.put(property, key); + valueProperties.put(property, value); + } + + /** + * Get keys type info for this JavaBean + * + * @param property + * property name of this JavaBean + * @return class of keys in the Map + */ + public Class getMapKeyType(String property) { + return keyProperties.get(property); + } + + /** + * Get values type info for this JavaBean + * + * @param property + * property name of this JavaBean + * @return class of values in the Map + */ + public Class getMapValueType(String property) { + return valueProperties.get(property); + } + + @Override + public String toString() { + return "TypeDescription for " + getType() + " (tag='" + getTag() + "')"; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/Yaml.java b/core/src/main/java/com/boydti/fawe/configuration/Yaml.java new file mode 100644 index 00000000..5c4559cb --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/Yaml.java @@ -0,0 +1,661 @@ +/** + * Copyright (c) 2008, http://www.snakeyaml.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.yaml.snakeyaml; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; + +import org.yaml.snakeyaml.DumperOptions.FlowStyle; +import org.yaml.snakeyaml.composer.Composer; +import org.yaml.snakeyaml.constructor.BaseConstructor; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.emitter.Emitable; +import org.yaml.snakeyaml.emitter.Emitter; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.events.Event; +import org.yaml.snakeyaml.introspector.BeanAccess; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.Tag; +import org.yaml.snakeyaml.parser.Parser; +import org.yaml.snakeyaml.parser.ParserImpl; +import org.yaml.snakeyaml.reader.StreamReader; +import org.yaml.snakeyaml.reader.UnicodeReader; +import org.yaml.snakeyaml.representer.Representer; +import org.yaml.snakeyaml.resolver.Resolver; +import org.yaml.snakeyaml.serializer.Serializer; + +/** + * Public YAML interface. Each Thread must have its own instance. + */ +public class Yaml { + protected final Resolver resolver; + private String name; + protected BaseConstructor constructor; + protected Representer representer; + protected DumperOptions dumperOptions; + + /** + * Create Yaml instance. It is safe to create a few instances and use them + * in different Threads. + */ + public Yaml() { + this(new Constructor(), new Representer(), new DumperOptions(), new Resolver()); + } + + /** + * Create Yaml instance. + * + * @param dumperOptions + * DumperOptions to configure outgoing objects + */ + public Yaml(DumperOptions dumperOptions) { + this(new Constructor(), new Representer(), dumperOptions); + } + + /** + * Create Yaml instance. It is safe to create a few instances and use them + * in different Threads. + * + * @param representer + * Representer to emit outgoing objects + */ + public Yaml(Representer representer) { + this(new Constructor(), representer); + } + + /** + * Create Yaml instance. It is safe to create a few instances and use them + * in different Threads. + * + * @param constructor + * BaseConstructor to construct incoming documents + */ + public Yaml(BaseConstructor constructor) { + this(constructor, new Representer()); + } + + /** + * Create Yaml instance. It is safe to create a few instances and use them + * in different Threads. + * + * @param constructor + * BaseConstructor to construct incoming documents + * @param representer + * Representer to emit outgoing objects + */ + public Yaml(BaseConstructor constructor, Representer representer) { + this(constructor, representer, new DumperOptions()); + } + + /** + * Create Yaml instance. It is safe to create a few instances and use them + * in different Threads. + * + * @param representer + * Representer to emit outgoing objects + * @param dumperOptions + * DumperOptions to configure outgoing objects + */ + public Yaml(Representer representer, DumperOptions dumperOptions) { + this(new Constructor(), representer, dumperOptions, new Resolver()); + } + + /** + * Create Yaml instance. It is safe to create a few instances and use them + * in different Threads. + * + * @param constructor + * BaseConstructor to construct incoming documents + * @param representer + * Representer to emit outgoing objects + * @param dumperOptions + * DumperOptions to configure outgoing objects + */ + public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions) { + this(constructor, representer, dumperOptions, new Resolver()); + } + + /** + * Create Yaml instance. It is safe to create a few instances and use them + * in different Threads. + * + * @param constructor + * BaseConstructor to construct incoming documents + * @param representer + * Representer to emit outgoing objects + * @param dumperOptions + * DumperOptions to configure outgoing objects + * @param resolver + * Resolver to detect implicit type + */ + public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions, + Resolver resolver) { + if (!constructor.isExplicitPropertyUtils()) { + constructor.setPropertyUtils(representer.getPropertyUtils()); + } else if (!representer.isExplicitPropertyUtils()) { + representer.setPropertyUtils(constructor.getPropertyUtils()); + } + this.constructor = constructor; + representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle()); + representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle()); + representer.getPropertyUtils().setAllowReadOnlyProperties( + dumperOptions.isAllowReadOnlyProperties()); + representer.setTimeZone(dumperOptions.getTimeZone()); + this.representer = representer; + this.dumperOptions = dumperOptions; + this.resolver = resolver; + this.name = "Yaml:" + System.identityHashCode(this); + } + + /** + * Serialize a Java object into a YAML String. + * + * @param data + * Java object to be Serialized to YAML + * @return YAML String + */ + public String dump(Object data) { + List list = new ArrayList(1); + list.add(data); + return dumpAll(list.iterator()); + } + + /** + * Produce the corresponding representation tree for a given Object. + * + * @see Figure 3.1. Processing + * Overview + * @param data + * instance to build the representation tree for + * @return representation tree + */ + public Node represent(Object data) { + return representer.represent(data); + } + + /** + * Serialize a sequence of Java objects into a YAML String. + * + * @param data + * Iterator with Objects + * @return YAML String with all the objects in proper sequence + */ + public String dumpAll(Iterator data) { + StringWriter buffer = new StringWriter(); + dumpAll(data, buffer, null); + return buffer.toString(); + } + + /** + * Serialize a Java object into a YAML stream. + * + * @param data + * Java object to be serialized to YAML + * @param output + * stream to write to + */ + public void dump(Object data, Writer output) { + List list = new ArrayList(1); + list.add(data); + dumpAll(list.iterator(), output, null); + } + + /** + * Serialize a sequence of Java objects into a YAML stream. + * + * @param data + * Iterator with Objects + * @param output + * stream to write to + */ + public void dumpAll(Iterator data, Writer output) { + dumpAll(data, output, null); + } + + private void dumpAll(Iterator data, Writer output, Tag rootTag) { + Serializer serializer = new Serializer(new Emitter(output, dumperOptions), resolver, + dumperOptions, rootTag); + try { + serializer.open(); + while (data.hasNext()) { + Node node = representer.represent(data.next()); + serializer.serialize(node); + } + serializer.close(); + } catch (IOException e) { + throw new YAMLException(e); + } + } + + /** + *

+ * Serialize a Java object into a YAML string. Override the default root tag + * with rootTag. + *

+ * + *

+ * This method is similar to Yaml.dump(data) except that the + * root tag for the whole document is replaced with the given tag. This has + * two main uses. + *

+ * + *

+ * First, if the root tag is replaced with a standard YAML tag, such as + * Tag.MAP, then the object will be dumped as a map. The root + * tag will appear as !!map, or blank (implicit !!map). + *

+ * + *

+ * Second, if the root tag is replaced by a different custom tag, then the + * document appears to be a different type when loaded. For example, if an + * instance of MyClass is dumped with the tag !!YourClass, then it will be + * handled as an instance of YourClass when loaded. + *

+ * + * @param data + * Java object to be serialized to YAML + * @param rootTag + * the tag for the whole YAML document. The tag should be Tag.MAP + * for a JavaBean to make the tag disappear (to use implicit tag + * !!map). If null is provided then the standard tag + * with the full class name is used. + * @param flowStyle + * flow style for the whole document. See Chapter 10. Collection + * Styles http://yaml.org/spec/1.1/#id930798. If + * null is provided then the flow style from + * DumperOptions is used. + * + * @return YAML String + */ + public String dumpAs(Object data, Tag rootTag, FlowStyle flowStyle) { + FlowStyle oldStyle = representer.getDefaultFlowStyle(); + if (flowStyle != null) { + representer.setDefaultFlowStyle(flowStyle); + } + List list = new ArrayList(1); + list.add(data); + StringWriter buffer = new StringWriter(); + dumpAll(list.iterator(), buffer, rootTag); + representer.setDefaultFlowStyle(oldStyle); + return buffer.toString(); + } + + /** + *

+ * Serialize a Java object into a YAML string. Override the default root tag + * with Tag.MAP. + *

+ *

+ * This method is similar to Yaml.dump(data) except that the + * root tag for the whole document is replaced with Tag.MAP tag + * (implicit !!map). + *

+ *

+ * Block Mapping is used as the collection style. See 10.2.2. Block Mappings + * (http://yaml.org/spec/1.1/#id934537) + *

+ * + * @param data + * Java object to be serialized to YAML + * @return YAML String + */ + public String dumpAsMap(Object data) { + return dumpAs(data, Tag.MAP, FlowStyle.BLOCK); + } + + /** + * Serialize the representation tree into Events. + * + * @see Processing Overview + * @param data + * representation tree + * @return Event list + */ + public List serialize(Node data) { + SilentEmitter emitter = new SilentEmitter(); + Serializer serializer = new Serializer(emitter, resolver, dumperOptions, null); + try { + serializer.open(); + serializer.serialize(data); + serializer.close(); + } catch (IOException e) { + throw new YAMLException(e); + } + return emitter.getEvents(); + } + + private static class SilentEmitter implements Emitable { + private List events = new ArrayList(100); + + public List getEvents() { + return events; + } + + public void emit(Event event) throws IOException { + events.add(event); + } + } + + /** + * Parse the only YAML document in a String and produce the corresponding + * Java object. (Because the encoding in known BOM is not respected.) + * + * @param yaml + * YAML data to load from (BOM must not be present) + * @return parsed object + */ + public Object load(String yaml) { + return loadFromReader(new StreamReader(yaml), Object.class); + } + + /** + * Parse the only YAML document in a stream and produce the corresponding + * Java object. + * + * @param io + * data to load from (BOM is respected and removed) + * @return parsed object + */ + public Object load(InputStream io) { + return loadFromReader(new StreamReader(new UnicodeReader(io)), Object.class); + } + + /** + * Parse the only YAML document in a stream and produce the corresponding + * Java object. + * + * @param io + * data to load from (BOM must not be present) + * @return parsed object + */ + public Object load(Reader io) { + return loadFromReader(new StreamReader(io), Object.class); + } + + /** + * Parse the only YAML document in a stream and produce the corresponding + * Java object. + * + * @param + * Class is defined by the second argument + * @param io + * data to load from (BOM must not be present) + * @param type + * Class of the object to be created + * @return parsed object + */ + @SuppressWarnings("unchecked") + public T loadAs(Reader io, Class type) { + return (T) loadFromReader(new StreamReader(io), type); + } + + /** + * Parse the only YAML document in a String and produce the corresponding + * Java object. (Because the encoding in known BOM is not respected.) + * + * @param + * Class is defined by the second argument + * @param yaml + * YAML data to load from (BOM must not be present) + * @param type + * Class of the object to be created + * @return parsed object + */ + @SuppressWarnings("unchecked") + public T loadAs(String yaml, Class type) { + return (T) loadFromReader(new StreamReader(yaml), type); + } + + /** + * Parse the only YAML document in a stream and produce the corresponding + * Java object. + * + * @param + * Class is defined by the second argument + * @param input + * data to load from (BOM is respected and removed) + * @param type + * Class of the object to be created + * @return parsed object + */ + @SuppressWarnings("unchecked") + public T loadAs(InputStream input, Class type) { + return (T) loadFromReader(new StreamReader(new UnicodeReader(input)), type); + } + + private Object loadFromReader(StreamReader sreader, Class type) { + Composer composer = new Composer(new ParserImpl(sreader), resolver); + constructor.setComposer(composer); + return constructor.getSingleData(type); + } + + /** + * Parse all YAML documents in a String and produce corresponding Java + * objects. The documents are parsed only when the iterator is invoked. + * + * @param yaml + * YAML data to load from (BOM must not be present) + * @return an iterator over the parsed Java objects in this String in proper + * sequence + */ + public Iterable loadAll(Reader yaml) { + Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); + constructor.setComposer(composer); + Iterator result = new Iterator() { + public boolean hasNext() { + return constructor.checkData(); + } + + public Object next() { + return constructor.getData(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + return new YamlIterable(result); + } + + private static class YamlIterable implements Iterable { + private Iterator iterator; + + public YamlIterable(Iterator iterator) { + this.iterator = iterator; + } + + public Iterator iterator() { + return iterator; + } + } + + /** + * Parse all YAML documents in a String and produce corresponding Java + * objects. (Because the encoding in known BOM is not respected.) The + * documents are parsed only when the iterator is invoked. + * + * @param yaml + * YAML data to load from (BOM must not be present) + * @return an iterator over the parsed Java objects in this String in proper + * sequence + */ + public Iterable loadAll(String yaml) { + return loadAll(new StringReader(yaml)); + } + + /** + * Parse all YAML documents in a stream and produce corresponding Java + * objects. The documents are parsed only when the iterator is invoked. + * + * @param yaml + * YAML data to load from (BOM is respected and ignored) + * @return an iterator over the parsed Java objects in this stream in proper + * sequence + */ + public Iterable loadAll(InputStream yaml) { + return loadAll(new UnicodeReader(yaml)); + } + + /** + * Parse the first YAML document in a stream and produce the corresponding + * representation tree. (This is the opposite of the represent() method) + * + * @see Figure 3.1. Processing + * Overview + * @param yaml + * YAML document + * @return parsed root Node for the specified YAML document + */ + public Node compose(Reader yaml) { + Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); + constructor.setComposer(composer); + return composer.getSingleNode(); + } + + /** + * Parse all YAML documents in a stream and produce corresponding + * representation trees. + * + * @see Processing Overview + * @param yaml + * stream of YAML documents + * @return parsed root Nodes for all the specified YAML documents + */ + public Iterable composeAll(Reader yaml) { + final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); + constructor.setComposer(composer); + Iterator result = new Iterator() { + public boolean hasNext() { + return composer.checkNode(); + } + + public Node next() { + return composer.getNode(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + return new NodeIterable(result); + } + + private static class NodeIterable implements Iterable { + private Iterator iterator; + + public NodeIterable(Iterator iterator) { + this.iterator = iterator; + } + + public Iterator iterator() { + return iterator; + } + } + + /** + * Add an implicit scalar detector. If an implicit scalar value matches the + * given regexp, the corresponding tag is assigned to the scalar. + * + * @param tag + * tag to assign to the node + * @param regexp + * regular expression to match against + * @param first + * a sequence of possible initial characters or null (which means + * any). + */ + public void addImplicitResolver(Tag tag, Pattern regexp, String first) { + resolver.addImplicitResolver(tag, regexp, first); + } + + @Override + public String toString() { + return name; + } + + /** + * Get a meaningful name. It simplifies debugging in a multi-threaded + * environment. If nothing is set explicitly the address of the instance is + * returned. + * + * @return human readable name + */ + public String getName() { + return name; + } + + /** + * Set a meaningful name to be shown in toString() + * + * @param name + * human readable name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Parse a YAML stream and produce parsing events. + * + * @see Processing Overview + * @param yaml + * YAML document(s) + * @return parsed events + */ + public Iterable parse(Reader yaml) { + final Parser parser = new ParserImpl(new StreamReader(yaml)); + Iterator result = new Iterator() { + public boolean hasNext() { + return parser.peekEvent() != null; + } + + public Event next() { + return parser.getEvent(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + return new EventIterable(result); + } + + private static class EventIterable implements Iterable { + private Iterator iterator; + + public EventIterable(Iterator iterator) { + this.iterator = iterator; + } + + public Iterator iterator() { + return iterator; + } + } + + public void setBeanAccess(BeanAccess beanAccess) { + constructor.getPropertyUtils().setBeanAccess(beanAccess); + representer.getPropertyUtils().setBeanAccess(beanAccess); + } + +} diff --git a/core/src/main/java/com/boydti/fawe/object/FaweLocation.java b/core/src/main/java/com/boydti/fawe/object/FaweLocation.java index 509ddb31..fb21a170 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweLocation.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweLocation.java @@ -1,6 +1,6 @@ package com.boydti.fawe.object; -import com.sk89q.worldedit.WorldEdit; +import com.boydti.fawe.FaweAPI; import com.sk89q.worldedit.world.World; /** @@ -36,12 +36,7 @@ public class FaweLocation { } public World getWorld() { - for (World world : WorldEdit.getInstance().getServer().getWorlds()) { - if (world.getName().equals(this.world)) { - return world; - } - } - return null; + return FaweAPI.getWorld(world); } @Override 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 0339007f..fa678dea 100644 --- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -1,10 +1,13 @@ package com.boydti.fawe.object; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.changeset.DiskStorageHistory; +import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.WEManager; +import com.boydti.fawe.wrappers.PlayerWrapper; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; @@ -50,16 +53,27 @@ public abstract class FawePlayer { } if (obj instanceof Player) { Player actor = (Player) obj; - try { - Field fieldBasePlayer = actor.getClass().getDeclaredField("basePlayer"); - fieldBasePlayer.setAccessible(true); - Player player = (Player) fieldBasePlayer.get(actor); - Field fieldPlayer = player.getClass().getDeclaredField("player"); - fieldPlayer.setAccessible(true); - return Fawe.imp().wrap(fieldPlayer.get(player)); - } catch (Throwable e) { - e.printStackTrace(); - return Fawe.imp().wrap(actor.getName()); + if (obj.getClass().getSimpleName().equals("PlayerProxy")) { + try { + Field fieldBasePlayer = actor.getClass().getDeclaredField("basePlayer"); + fieldBasePlayer.setAccessible(true); + Player player = (Player) fieldBasePlayer.get(actor); + return wrap(player); + } catch (Throwable e) { + e.printStackTrace(); + return Fawe.imp().wrap(actor.getName()); + } + } else if (obj instanceof PlayerWrapper){ + return wrap(((PlayerWrapper) obj).getParent()); + } else { + try { + Field fieldPlayer = actor.getClass().getDeclaredField("player"); + fieldPlayer.setAccessible(true); + return wrap(fieldPlayer.get(actor)); + } catch (Throwable e) { + e.printStackTrace(); + return Fawe.imp().wrap(actor.getName()); + } } } return Fawe.imp().wrap(obj); @@ -92,13 +106,7 @@ public abstract class FawePlayer { * @return */ public World getWorld() { - String currentWorldName = getLocation().world; - for (World world : WorldEdit.getInstance().getServer().getWorlds()) { - if (world.getName().equals(currentWorldName)) { - return world; - } - } - return null; + return FaweAPI.getWorld(getLocation().world); } /** @@ -106,30 +114,39 @@ public abstract class FawePlayer { * - Usually already called when a player joins or changes world * @param world */ - public void loadSessionsFromDisk(World world) { + public void loadSessionsFromDisk(final World world) { if (world == null) { return; } - UUID uuid = getUUID(); - List editIds = new ArrayList<>(); - File folder = new File(Fawe.imp().getDirectory(), "history" + File.separator + world.getName() + File.separator + uuid); - if (folder.isDirectory()) { - for (File file : folder.listFiles()) { - if (file.getName().endsWith(".bd")) { - int index = Integer.parseInt(file.getName().split("\\.")[0]); - editIds.add(index); + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + UUID uuid = getUUID(); + List editIds = new ArrayList<>(); + File folder = new File(Fawe.imp().getDirectory(), "history" + File.separator + world.getName() + File.separator + uuid); + if (folder.isDirectory()) { + for (File file : folder.listFiles()) { + if (file.getName().endsWith(".bd")) { + int index = Integer.parseInt(file.getName().split("\\.")[0]); + editIds.add(index); + } + } + } + Collections.sort(editIds); + if (editIds.size() > 0) { + Fawe.debug(BBC.PREFIX.s() + " Indexing " + editIds.size() + " history objects for " + getName()); + for (int index : editIds) { + DiskStorageHistory set = new DiskStorageHistory(world, uuid, index); + EditSession edit = set.toEditSession(getPlayer()); + if (world.equals(getWorld())) { + session.remember(edit); + } else { + return; + } + } } } - } - Collections.sort(editIds); - if (editIds.size() > 0) { - Fawe.debug(BBC.PREFIX.s() + " Indexing " + editIds.size() + " history objects for " + getName()); - for (int index : editIds) { - DiskStorageHistory set = new DiskStorageHistory(world, uuid, index); - EditSession edit = set.toEditSession(getPlayer()); - session.remember(edit); - } - } + }); } /** diff --git a/core/src/main/java/com/boydti/fawe/util/MainUtil.java b/core/src/main/java/com/boydti/fawe/util/MainUtil.java index aa578f6c..e3e2a36a 100644 --- a/core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -27,7 +27,7 @@ public class MainUtil { } public static void sendAdmin(final String s) { - for (final FawePlayer player : Fawe.imp().getPlayers()) { + for (final FawePlayer player : Fawe.get().getCachedPlayers()) { if (player.hasPermission("fawe.admin")) { player.sendMessage(s); } diff --git a/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java b/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java index 9aab988f..1f2172d4 100644 --- a/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java +++ b/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java @@ -34,6 +34,10 @@ public class PlayerWrapper implements Player { this.parent = parent; } + public Player getParent() { + return parent; + } + @Override public World getWorld() { return new WorldWrapper((AbstractWorld) parent.getWorld()); diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 3b35e26a..2ed6b255 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -235,7 +235,7 @@ public class EditSession implements Extent { return; } this.actor = event.getActor(); - this.queue = SetQueue.IMP.getNewQueue(world.getName(), true); + this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), true); // Set the world of the event to the actual world (workaround for CoreProtect) try { Class eventClass = event.getClass(); @@ -266,8 +266,7 @@ public class EditSession implements Extent { } this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, actor.getUniqueId()) : new MemoryOptimizedHistory(actor); Extent extent; - final String name = actor.getName(); - final FawePlayer fp = FawePlayer.wrap(name); + final FawePlayer fp = FawePlayer.wrap(actor); final LocalSession session = fp.getSession(); this.fastmode = session.hasFastMode(); if (fp.hasWorldEditBypass()) { @@ -372,6 +371,12 @@ public class EditSession implements Extent { final Extent toReturn = event.getExtent(); if (toReturn != extent) { String className = toReturn.getClass().getName().toLowerCase(); + if (className.contains("coreprotect")) { + Fawe.debug("&cUnsafe extent detected: " + toReturn.getClass().getCanonicalName() + " !"); + Fawe.debug("&8 - &7Use BlocksHub instead"); + Fawe.debug("&8 - &7Or use FAWE rollback"); + return extent; + } for (String allowed : Settings.ALLOWED_3RDPARTY_EXTENTS) { if (className.contains(allowed.toLowerCase())) { return toReturn; diff --git a/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeEditSessionWrapper_0.java b/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeEditSessionWrapper_0.java deleted file mode 100644 index 3c94c214..00000000 --- a/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeEditSessionWrapper_0.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.boydti.fawe.forge.v0; - -import com.boydti.fawe.object.EditSessionWrapper; -import com.sk89q.worldedit.EditSession; - -/** - * Created by Jesse on 4/2/2016. - */ -public class SpongeEditSessionWrapper_0 extends EditSessionWrapper { - public SpongeEditSessionWrapper_0(EditSession session) { - super(session); - } -} diff --git a/forge1710/build.gradle b/forge1710/build.gradle new file mode 100644 index 00000000..4efa2409 --- /dev/null +++ b/forge1710/build.gradle @@ -0,0 +1,77 @@ +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:1.2-SNAPSHOT' + } +} + +apply plugin: 'forge' +apply plugin: 'com.github.johnrengelman.shadow' + +dependencies { + compile project(':core') + compile 'org.mcstats.sponge:metrics:R8-SNAPSHOT' + compile 'com.sk89q.worldedit:worldedit-forge-mc1.7.10:6.1.1-20151030.011615-19' + compile 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' +} + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 + +repositories { + maven { + name = 'forge' + url = 'http://files.minecraftforge.net/maven' + } +} +minecraft { + version = "10.13.4.1614-1.7.10" + 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}.jar" + destinationDir = file '../target' +} +shadowJar.doLast { + task -> + ant.checksum file: task.archivePath +} + + +reobf.reobf(shadowJar) { spec -> + spec.classpath = sourceSets.main.compileClasspath; +} + +task deobfJar(type: Jar) { + from sourceSets.main.output + classifier = 'dev' +} + +artifacts { + archives deobfJar +} + +build.dependsOn(shadowJar) diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/FaweForge.java b/forge1710/src/main/java/com/boydti/fawe/forge/FaweForge.java new file mode 100644 index 00000000..da28286f --- /dev/null +++ b/forge1710/src/main/java/com/boydti/fawe/forge/FaweForge.java @@ -0,0 +1,159 @@ +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.EditSessionWrapper; +import com.boydti.fawe.object.FaweCommand; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.regions.FaweMaskManager; +import com.boydti.fawe.util.FaweQueue; +import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.wrappers.WorldWrapper; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.forge.ForgeWorld; +import com.sk89q.worldedit.world.World; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +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 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) { + e.printStackTrace(); + } + } + + @Override + public void debug(String s) { + logger.error(s); + } + + @Override + public File getDirectory() { + return directory; + } + + @Override + public void setupCommand(final String label, final FaweCommand cmd) { + if (TaskManager.IMP != null) { + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + ServerCommandManager scm = (ServerCommandManager) MinecraftServer.getServer().getCommandManager(); + scm.registerCommand(new ForgeCommand(label, cmd)); + } + }); + } + } + + @Override + public FawePlayer wrap(Object obj) { + EntityPlayerMP player = null; + if (obj instanceof String) { + MinecraftServer server = MinecraftServer.getServer(); + List list = server.getConfigurationManager().getPlayerList((String) obj); + player = list.size() == 1 ? list.get(0) : null; + } else if (obj instanceof EntityPlayerMP) { + player = (EntityPlayerMP) obj; + } + if (player == null) { + return null; + } + FawePlayer existing = Fawe.get().getCachedPlayer(player.getCommandSenderName()); + return existing != null ? existing : new ForgePlayer(player); + } + + @Override + public void setupWEListener() { + // Do nothing + } + + @Override + public void setupVault() { + // Do nothing + } + + @Override + public TaskManager getTaskManager() { + return new ForgeTaskMan(512); + } + + @Override + public int[] getVersion() { + String[] version = MinecraftServer.getServer().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().provider.getDimensionName(); + } + + @Override + public FaweQueue getNewQueue(String world) { + return new ForgeQueue_All(world); + } + + @Override + public EditSessionWrapper getEditSessionWrapper(EditSession session) { + return new EditSessionWrapper(session); + } + + @Override + public Collection getMaskManagers() { + return new ArrayList<>(); + } + + @Override + public void startMetrics() { + try { + ForgeMetrics metrics = new ForgeMetrics("FastAsyncWorldEdit", "3.4.0"); + 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 { + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)); + } catch (Throwable e) { + return null; + } + } + + @Override + public String getName(UUID uuid) { + return uuid.toString(); + } +} diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/ForgeCommand.java b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeCommand.java new file mode 100644 index 00000000..c8ddb42c --- /dev/null +++ b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeCommand.java @@ -0,0 +1,41 @@ +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; + +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 processCommand(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/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMain.java b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMain.java new file mode 100644 index 00000000..65b7e0ad --- /dev/null +++ b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMain.java @@ -0,0 +1,76 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FawePlayer; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.event.FMLServerStoppingEvent; +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent; +import java.io.File; +import java.util.List; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import org.apache.logging.log4j.Logger; + +@Mod(modid = "com.boydti.fawe", name = "FastAsyncWorldEdit", version = "3.4.0", acceptableRemoteVersions = "*") +public class ForgeMain { + private static 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(), 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)MinecraftServer.getServer().getConfigurationManager().playerEntityList) { + handleQuit(player); + } + } + + public void handleQuit(EntityPlayerMP player) { + FawePlayer fp = FawePlayer.wrap(player); + fp.unregister(); + Fawe.get().unregister(player.getCommandSenderName()); + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onPlayerChangedWorld(EntityJoinWorldEvent event) { + Entity entity = event.entity; + if (!(entity instanceof EntityPlayerMP)) { + return; + } + EntityPlayerMP player = (EntityPlayerMP) entity; + if (player.worldObj.isRemote) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + if (fp.getMeta("lastWorld") != event.world) { + fp.setMeta("lastWorld", event.world); + if (Settings.STORE_HISTORY_ON_DISK) { + fp.getSession().clearHistory(); + fp.loadSessionsFromDisk(fp.getWorld()); + } + } + } +} diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java new file mode 100644 index 00000000..9d9f582b --- /dev/null +++ b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -0,0 +1,479 @@ +/* + * 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 cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.TickEvent; +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; + +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; + boolean onlineMode = MinecraftServer.getServer().isServerInOnlineMode(); + String pluginVersion = modVersion; + String serverVersion; + if (MinecraftServer.getServer().isDedicatedServer()) { + serverVersion = "MinecraftForge (MC: " + MinecraftServer.getServer().getMinecraftVersion() + ")"; + } else { + serverVersion = "MinecraftForgeSSP (MC: " + MinecraftServer.getServer().getMinecraftVersion() + ")"; + } + int playersOnline = MinecraftServer.getServer().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) { + e.printStackTrace(); + } 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/forge1710/src/main/java/com/boydti/fawe/forge/ForgePlayer.java b/forge1710/src/main/java/com/boydti/fawe/forge/ForgePlayer.java new file mode 100644 index 00000000..45c50bcc --- /dev/null +++ b/forge1710/src/main/java/com/boydti/fawe/forge/ForgePlayer.java @@ -0,0 +1,66 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.object.FaweLocation; +import com.boydti.fawe.object.FawePlayer; +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.ChatComponentText; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; + +public class ForgePlayer extends FawePlayer { + public ForgePlayer(EntityPlayerMP parent) { + super(parent); + } + + @Override + public String getName() { + return parent.getCommandSenderName(); + } + + @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 = EnumChatFormatting.getTextWithoutFormattingCodes(msg); + ChatComponentText component = new ChatComponentText(part); + component.getChatStyle().setColor(EnumChatFormatting.LIGHT_PURPLE); + this.parent.addChatMessage(component); + } + } + + @Override + public void executeCommand(String substring) { + throw new UnsupportedOperationException("NOT IMPLEMENTED"); + } + + @Override + public FaweLocation getLocation() { + World world = parent.worldObj; + ChunkCoordinates pos = parent.getPlayerCoordinates(); + return new FaweLocation(world.provider.getDimensionName(), pos.posX, pos.posY, pos.posZ); + } + + @Override + public Player getPlayer() { + return ForgeWorldEdit.inst.wrap(this.parent); + } +} diff --git a/forge1710/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java new file mode 100644 index 00000000..ed946eda --- /dev/null +++ b/forge1710/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java @@ -0,0 +1,165 @@ +package com.boydti.fawe.forge; + +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 cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.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; + } + final 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) { + e.printStackTrace(); + } + later(this, interval); + } + }); + return id; + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onServerTick(TickEvent.ServerTickEvent event) { + 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) { + e.printStackTrace(); + } + } + } + } + + @Override + public int repeatAsync(final Runnable r, final int interval) { + if (r == null) { + return -1; + } + final 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) { + e.printStackTrace(); + } + 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(final Runnable r, final int delay) { + if (r == null) { + return; + } + final AtomicInteger remaining = new AtomicInteger(delay); + task(new Runnable() { + @Override + public void run() { + if (remaining.decrementAndGet() <= 0) { + try { + r.run(); + } catch (Throwable e) { + e.printStackTrace(); + } + return; + } + task(this); + } + }); + } + + @Override + public void laterAsync(final Runnable r, final int delay) { + if (r == null) { + return; + } + final AtomicInteger remaining = new AtomicInteger(delay); + task(new Runnable() { + @Override + public void run() { + if (remaining.decrementAndGet() <= 0) { + try { + async(r); + } catch (Throwable e) { + e.printStackTrace(); + } + return; + } + task(this); + } + }); + } + + @Override + public void cancel(int task) { + taskIdMap.remove(task); + } +} 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 new file mode 100644 index 00000000..1b4b4425 --- /dev/null +++ b/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java @@ -0,0 +1,254 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.util.FaweQueue; +import com.sk89q.worldedit.world.biome.BaseBiome; +import java.util.Arrays; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.NibbleArray; + +public class ForgeChunk_All extends FaweChunk { + + public byte[][] ids; + public NibbleArray[] datas; + + public short[] count; + public short[] air; + public short[] relight; + public byte[][] biomes; + public Chunk chunk; + + public ForgeChunk_All(FaweQueue parent, int x, int z) { + super(parent, x, z); + this.ids = new byte[16][]; + this.datas = new NibbleArray[16]; + this.count = new short[16]; + this.air = new short[16]; + this.relight = new short[16]; + } + + + @Override + public Chunk getChunk() { + if (this.chunk == null) { + World world = ((ForgeQueue_All) getParent()).getWorld(); + this.chunk = world.getChunkProvider().provideChunk(getX(), getZ()); + } + return this.chunk; + } + + @Override + public void setLoc(final FaweQueue parent, int x, int z) { + super.setLoc(parent, x, z); + this.chunk = null; + } + + /** + * Get the number of block changes in a specified section. + * @param i + * @return + */ + public int getCount(int i) { + return this.count[i]; + } + + public int getAir(int i) { + return this.air[i]; + } + + public void setCount(int i, short value) { + this.count[i] = value; + } + + /** + * Get the number of block changes in a specified section. + * @param i + * @return + */ + public int getRelight(int i) { + return this.relight[i]; + } + + public int getTotalCount() { + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.count[i]; + } + return total; + } + + public int getTotalRelight() { + if (getTotalCount() == 0) { + Arrays.fill(this.count, (short) 1); + Arrays.fill(this.relight, Short.MAX_VALUE); + return Short.MAX_VALUE; + } + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.relight[i]; + } + return total; + } + + /** + * Get the raw data for a section. + * @param i + * @return + */ + public byte[] getIdArray(int i) { + return this.ids[i]; + } + + public NibbleArray getDataArray(int i) { + return datas[i]; + } + + @Override + public void setBlock(int x, int y, int z, int id, byte data) { + int i = FaweCache.CACHE_I[y][x][z]; + int j = FaweCache.CACHE_J[y][x][z]; + byte[] vs = this.ids[i]; + if (vs == null) { + vs = this.ids[i] = new byte[4096]; + this.count[i]++; + } else if (vs[j] == 0) { + this.count[i]++; + } + switch (id) { + case 0: + this.air[i]++; + vs[j] = -1; + return; + case 10: + case 11: + case 39: + case 40: + case 51: + case 74: + case 89: + case 122: + case 124: + case 138: + case 169: + 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 8: + case 9: + case 73: + case 78: + 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 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: + vs[j] = (byte) (id); + return; + case 130: + case 76: + case 62: + this.relight[i]++; + case 54: + case 146: + case 61: + case 65: + case 68: + case 50: + if (data < 2) { + data = 2; + } + default: + vs[j] = (byte) id; + NibbleArray dataArray = datas[i]; + if (dataArray == null) { + datas[i] = dataArray = new NibbleArray(4096, 4); + } + dataArray.set(x, y & 15, z, data); + return; + } + } + + @Override + public void setBiome(int x, int z, BaseBiome biome) { + if (this.biomes == null) { + this.biomes = new byte[16][]; + } + byte[] index = this.biomes[x]; + if (index == null) { + index = this.biomes[x] = new byte[16]; + } + index[z] = (byte) biome.getId(); + } + + @Override + public FaweChunk clone() { + ForgeChunk_All toReturn = new ForgeChunk_All(getParent(), getX(), getZ()); + toReturn.air = this.air.clone(); + toReturn.count = this.count.clone(); + toReturn.relight = this.relight.clone(); + toReturn.ids = new byte[this.ids.length][]; + for (int i = 0; i < this.ids.length; i++) { + byte[] matrix = this.ids[i]; + if (matrix != null) { + toReturn.ids[i] = new byte[matrix.length]; + System.arraycopy(matrix, 0, toReturn.ids[i], 0, matrix.length); + } + } + toReturn.datas = new NibbleArray[16]; + for (int i = 0; i < this.datas.length; i++) { + if (datas[i] != null) { + toReturn.datas[i] = datas[i]; + } + } + return toReturn; + } +} 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 new file mode 100644 index 00000000..5ebcff02 --- /dev/null +++ b/forge1710/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java @@ -0,0 +1,597 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.forge.ForgePlayer; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.IntegerPair; +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.object.exception.FaweException; +import com.boydti.fawe.util.FaweQueue; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.world.biome.BaseBiome; +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingDeque; +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.play.server.S21PacketChunkData; +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.LongHashMap; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.ChunkPosition; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.Chunk; +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; + +public class ForgeQueue_All extends FaweQueue { + + private World forgeWorld; + + private ConcurrentHashMap> blocks = new ConcurrentHashMap<>(); + private LinkedBlockingDeque> chunks = new LinkedBlockingDeque<>(); + + public ForgeQueue_All(final String world) { + super(world); + } + + @Override + public boolean isChunkLoaded(int x, int z) { + return getWorld().getChunkProvider().chunkExists(x, z); + } + + public World getWorld() { + if (forgeWorld != null) { + return forgeWorld; + } + WorldServer[] worlds = MinecraftServer.getServer().worldServers; + for (WorldServer ws : worlds) { + if (ws.provider.getDimensionName().equals(world)) { + return forgeWorld = ws; + } + } + return null; + } + + @Override + public boolean regenerateChunk(int x, int z) { + try { + IChunkProvider provider = getWorld().getChunkProvider(); + if (!(provider instanceof ChunkProviderServer)) { + return false; + } + ChunkProviderServer chunkServer = (ChunkProviderServer) provider; + + Chunk mcChunk; + if (chunkServer.chunkExists(x, z)) { + mcChunk = chunkServer.loadChunk(x, z); + mcChunk.onChunkUnload(); + } + Field u; + try { + u = ChunkProviderServer.class.getDeclaredField("field_73248_b"); // chunksToUnload + } catch(NoSuchFieldException e) { + u = ChunkProviderServer.class.getDeclaredField("chunksToUnload"); + } + u.setAccessible(true); + Set unloadQueue = (Set) u.get(chunkServer); + Field m; + try { + m = ChunkProviderServer.class.getDeclaredField("field_73244_f"); // loadedChunkHashMap + } catch(NoSuchFieldException e) { + m = ChunkProviderServer.class.getDeclaredField("loadedChunkHashMap"); + } + m.setAccessible(true); + LongHashMap loadedMap = (LongHashMap) m.get(chunkServer); + Field lc; + try { + lc = ChunkProviderServer.class.getDeclaredField("field_73245_g"); // loadedChunkHashMap + } catch(NoSuchFieldException e) { + lc = ChunkProviderServer.class.getDeclaredField("loadedChunks"); + } + lc.setAccessible(true); + @SuppressWarnings("unchecked") List loaded = (List) lc.get(chunkServer); + Field p; + try { + p = ChunkProviderServer.class.getDeclaredField("field_73246_d"); // currentChunkProvider + } catch(NoSuchFieldException e) { + p = ChunkProviderServer.class.getDeclaredField("currentChunkProvider"); + } + p.setAccessible(true); + IChunkProvider chunkProvider = (IChunkProvider) p.get(chunkServer); + long pos = ChunkCoordIntPair.chunkXZ2Int(x, z); + if (chunkServer.chunkExists(x, z)) { + mcChunk = chunkServer.loadChunk(x, z); + mcChunk.onChunkUnload(); + } + unloadQueue.remove(pos); + loadedMap.remove(pos); + mcChunk = chunkProvider.provideChunk(x, z); + loadedMap.add(pos, mcChunk); + loaded.add(mcChunk); + if (mcChunk != null) { + mcChunk.onChunkLoad(); + mcChunk.populateChunk(chunkProvider, chunkProvider, x, z); + } + } catch (Throwable t) { + t.printStackTrace(); + return false; + } + return true; + } + + @Override + public void addTask(int x, int z, Runnable runnable) { + long pair = (long) (x) << 32 | (z) & 0xFFFFFFFFL; + FaweChunk result = this.blocks.get(pair); + if (result == null) { + result = this.getChunk(x, z); + result.addTask(runnable); + FaweChunk previous = this.blocks.put(pair, result); + if (previous == null) { + chunks.add(result); + return; + } + this.blocks.put(pair, previous); + result = previous; + } + result.addTask(runnable); + } + + private int lcx = Integer.MIN_VALUE; + private int lcz = Integer.MIN_VALUE; + private int lcy = Integer.MIN_VALUE; + private net.minecraft.world.chunk.Chunk lc; + private ExtendedBlockStorage ls; + + private final RunnableVal loadChunk = new RunnableVal() { + @Override + public void run(IntegerPair loc) { + Chunk chunk = getWorld().getChunkProvider().provideChunk(loc.x, loc.z); + if (chunk != null && !chunk.isChunkLoaded) { + chunk.onChunkLoad(); + } + + } + }; + + @Override + public int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { + if (y < 0 || y > 255) { + return 0; + } + int cx = x >> 4; + int cz = z >> 4; + int cy = y >> 4; + if (cx != lcx || cz != lcz) { + World world = getWorld(); + lcx = cx; + lcz = cz; + IChunkProvider provider = world.getChunkProvider(); + Chunk chunk; + if (!provider.chunkExists(cx, cz)) { + boolean sync = Thread.currentThread() == Fawe.get().getMainThread(); + if (sync) { + chunk = provider.provideChunk(cx, cz); + } else if (Settings.CHUNK_WAIT > 0) { + loadChunk.value = new IntegerPair(cx, cz); + TaskManager.IMP.sync(loadChunk, Settings.CHUNK_WAIT); + if (!provider.chunkExists(cx, cz)) { + throw new FaweException.FaweChunkLoadException(); + } + chunk = provider.provideChunk(cx, cz); + } else { + return 0; + } + } else { + chunk = provider.provideChunk(cx, cz); + } + lc = chunk; + ls = lc.getBlockStorageArray()[cy]; + } else if (cy != lcy) { + if (lc == null) { + return 0; + } + ls = lc.getBlockStorageArray()[cy]; + } + if (ls == null) { + ls = null; + return 0; + } + byte[] ids = ls.getBlockLSBArray(); + NibbleArray datasNibble = ls.getBlockMSBArray(); + int i = FaweCache.CACHE_J[y & 15][x & 15][z & 15]; + int combined = (ids[i] << 4) + (datasNibble == null ? 0 : datasNibble.get(x & 15, y & 15, z & 15)); + return combined; + } + + private FaweChunk lastChunk; + private int lastX = Integer.MIN_VALUE; + private int lastZ = Integer.MIN_VALUE; + + @Override + public boolean setBlock(int x, int y, int z, short id, byte data) { + if ((y > 255) || (y < 0)) { + return false; + } + int cx = x >> 4; + int cz = z >> 4; + if (cx != lastX || cz != lastZ) { + lastX = cx; + lastZ = cz; + long pair = (long) (cx) << 32 | (cz) & 0xFFFFFFFFL; + lastChunk = this.blocks.get(pair); + if (lastChunk == null) { + lastChunk = this.getChunk(x >> 4, z >> 4); + lastChunk.setBlock(x & 15, y, z & 15, id, data); + FaweChunk previous = this.blocks.put(pair, lastChunk); + if (previous == null) { + chunks.add(lastChunk); + return true; + } + this.blocks.put(pair, previous); + lastChunk = previous; + } + } + lastChunk.setBlock(x & 15, y, z & 15, id, data); + return true; + } + + @Override + public boolean setBiome(int x, int z, BaseBiome biome) { + long pair = (long) (x >> 4) << 32 | (z >> 4) & 0xFFFFFFFFL; + FaweChunk result = this.blocks.get(pair); + if (result == null) { + result = this.getChunk(x >> 4, z >> 4); + FaweChunk previous = this.blocks.put(pair, result); + if (previous != null) { + this.blocks.put(pair, previous); + result = previous; + } else { + chunks.add(result); + } + } + result.setBiome(x & 15, z & 15, biome); + return true; + } + + @Override + public FaweChunk next() { + lastX = Integer.MIN_VALUE; + lastZ = Integer.MIN_VALUE; + try { + if (this.blocks.size() == 0) { + return null; + } + synchronized (blocks) { + FaweChunk chunk = chunks.poll(); + if (chunk != null) { + blocks.remove(chunk.longHash()); + this.execute(chunk); + return chunk; + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } + + @Override + public int size() { + return chunks.size(); + } + + private LinkedBlockingDeque> toUpdate = new LinkedBlockingDeque<>(); + + public boolean execute(FaweChunk fc) { + if (fc == null) { + return false; + } + // Load chunk + Chunk chunk = fc.getChunk(); + if (!chunk.isChunkLoaded) { + chunk.onChunkLoad(); + } + // Set blocks / entities / biome + if (!this.setComponents(fc)) { + return false; + } + fc.executeTasks(); + return true; + } + + @Override + public void clear() { + this.blocks.clear(); + } + + @Override + public void setChunk(FaweChunk chunk) { + FaweChunk previous = this.blocks.put(chunk.longHash(), (FaweChunk) chunk); + if (previous != null) { + chunks.remove(previous); + } + chunks.add((FaweChunk) chunk); + } + + public void sendChunk(final FaweChunk fc) { + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + final boolean result = fixLighting(fc, Settings.FIX_ALL_LIGHTING) || !Settings.ASYNC_LIGHTING; + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + if (!result) { + fixLighting(fc, Settings.FIX_ALL_LIGHTING); + } + Chunk chunk = fc.getChunk(); + if (!chunk.isChunkLoaded) { + return; + } + World world = chunk.worldObj; + ChunkCoordIntPair pos = chunk.getChunkCoordIntPair(); + int cx = pos.chunkXPos; + int cz = pos.chunkZPos; + for (FawePlayer fp : Fawe.get().getCachedPlayers()) { + ForgePlayer forgePlayer = (ForgePlayer) fp; + EntityPlayerMP player = forgePlayer.parent; + if (!player.worldObj.equals(world)) { + continue; + } + int view = MinecraftServer.getServer().getConfigurationManager().getViewDistance(); + EntityPlayerMP nmsPlayer = (EntityPlayerMP) player; + ChunkCoordinates loc = player.getPlayerCoordinates(); + int px = loc.posX >> 4; + int pz = loc.posZ >> 4; + int dx = Math.abs(cx - (loc.posX >> 4)); + int dz = Math.abs(cz - (loc.posZ >> 4)); + if ((dx > view) || (dz > view)) { + continue; + } + NetHandlerPlayServer con = nmsPlayer.playerNetServerHandler; + con.sendPacket(new S21PacketChunkData(chunk, false, 65535)); + // Try sending true, 0 first + // Try bulk chunk packet + } + } + }, false); + } + }, Settings.ASYNC_LIGHTING); + } + + public boolean setComponents(FaweChunk fc) { + ForgeChunk_All fs = (ForgeChunk_All) fc; + Chunk forgeChunk = fc.getChunk(); + net.minecraft.world.World nmsWorld = forgeChunk.worldObj; + try { + boolean flag = !nmsWorld.provider.hasNoSky; + // Sections + ExtendedBlockStorage[] sections = forgeChunk.getBlockStorageArray(); + Map tiles = forgeChunk.chunkTileEntityMap; + List[] entities = forgeChunk.entityLists; + // Trim tiles + Set> entryset = tiles.entrySet(); + Iterator> iterator = entryset.iterator(); + while (iterator.hasNext()) { + Map.Entry tile = iterator.next(); + ChunkPosition pos = tile.getKey(); + int lx = pos.chunkPosX & 15; + int ly = pos.chunkPosY; + int lz = pos.chunkPosZ & 15; + int j = FaweCache.CACHE_I[ly][lx][lz]; + int k = FaweCache.CACHE_J[ly][lx][lz]; + byte[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + if (array[k] != 0) { + iterator.remove(); + } + } + // Efficiently merge sections + for (int j = 0; j < sections.length; j++) { + if (fs.getCount(j) == 0) { + continue; + } + byte[] newIdArray = fs.getIdArray(j); + if (newIdArray == null) { + continue; + } + NibbleArray newDataArray = fs.getDataArray(j); + ExtendedBlockStorage section = sections[j]; + if ((section == null) || (fs.getCount(j) >= 4096)) { + sections[j] = section = new ExtendedBlockStorage(j << 4, !getWorld().provider.hasNoSky); + section.setBlockLSBArray(newIdArray); + section.setBlockMetadataArray(newDataArray); + continue; + } + // id + data << 8 + + byte[] currentIdArray = section.getBlockLSBArray(); + NibbleArray currentDataArray = section.getMetadataArray(); + boolean data = currentDataArray != null; + if (!data) { + section.setBlockMetadataArray(newDataArray); + } + boolean fill = true; + for (int k = 0; k < newIdArray.length; k++) { + byte n = newIdArray[k]; + switch (n) { + case 0: + fill = false; + continue; + case -1: + fill = false; + currentIdArray[k] = 0; + continue; + default: + currentIdArray[k] = n; + if (data) { + int x = FaweCache.CACHE_X[j][k]; + int y = FaweCache.CACHE_Y[j][k]; + int z = FaweCache.CACHE_Z[j][k]; + int newData = newDataArray == null ? 0 : newDataArray.get(x, y, z); + int currentData = currentDataArray == null ? 0 : currentDataArray.get(x, y, z); + if (newData != currentData) { + currentDataArray.set(x, y, z, newData); + } + } + continue; + } + } + if (fill) { + fs.setCount(j, Short.MAX_VALUE); + } + } +// // Clear + } catch (Throwable e) { + e.printStackTrace(); + } + byte[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + byte[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + byte biome = array[z]; + if (biome == 0) { + continue; + } + forgeChunk.getBiomeArray()[((z & 0xF) << 4 | x & 0xF)] = biome; + } + } + } + sendChunk(fs); + return true; + } + + @Override + public FaweChunk getChunk(int x, int z) { + return new ForgeChunk_All(this, x, z); + } + + @Override + public boolean fixLighting(FaweChunk chunk, boolean fixAll) { + try { + ForgeChunk_All fc = (ForgeChunk_All) chunk; + Chunk forgeChunk = fc.getChunk(); + if (!forgeChunk.isChunkLoaded) { + forgeChunk.onChunkLoad(); + } + forgeChunk.generateSkylightMap(); + if (fc.getTotalRelight() == 0 && !fixAll) { + return true; + } + ExtendedBlockStorage[] sections = forgeChunk.getBlockStorageArray(); + net.minecraft.world.World nmsWorld = forgeChunk.worldObj; + + 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 ((fc.getRelight(j) == 0 && !fixAll) || fc.getCount(j) == 0 || (fc.getCount(j) >= 4096 && fc.getAir(j) == 0)) { + continue; + } + byte[] array = section.getBlockLSBArray(); + int l = PseudoRandom.random.random(2); + for (int k = 0; k < array.length; k++) { + int i = array[k]; + if (i < 16) { + continue; + } + short id = (short) (i); + switch (id) { // Lighting + default: + if (!fixAll) { + continue; + } + if ((k & 1) == l) { + l = 1 - l; + 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: + int x = FaweCache.CACHE_X[j][k]; + int y = FaweCache.CACHE_Y[j][k]; + 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()) { + e.printStackTrace(); + } + } + return false; + } + + 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)); + } + + public boolean isSolid(int i) { + return i != 0 && Block.getBlockById(i).isOpaqueCube(); + } + + public int getId(ExtendedBlockStorage[] sections, int x, int y, int z) { + if (x < 0 || x > 15 || z < 0 || z > 15) { + return 1; + } + if (y < 0 || y > 255) { + return 1; + } + int i = FaweCache.CACHE_I[y][x][z]; + ExtendedBlockStorage section = sections[i]; + if (section == null) { + return 0; + } + byte[] array = section.getBlockLSBArray(); + int j = FaweCache.CACHE_J[y][x][z]; + return array[j]; + } +} diff --git a/forge/src/main/resources/config.yml b/forge1710/src/main/resources/config.yml similarity index 100% rename from forge/src/main/resources/config.yml rename to forge1710/src/main/resources/config.yml diff --git a/forge1710/src/main/resources/mcmod.info b/forge1710/src/main/resources/mcmod.info new file mode 100644 index 00000000..ac36169a --- /dev/null +++ b/forge1710/src/main/resources/mcmod.info @@ -0,0 +1,10 @@ +[{ + "modid": "com.boydti.fawe", + "name": "FastAsyncWorldEdit", + "description": "Extreme WorldEdit optimizations, no lag, low memory usage, area + tile + entity limits, block logging + rollback", + "version": "3.4.0", + "mcVersion": "1.7.10", + "dependencies": [ + "WorldEdit" + ] +}] \ No newline at end of file diff --git a/forge/build.gradle b/forge189/build.gradle similarity index 83% rename from forge/build.gradle rename to forge189/build.gradle index 5798bfbf..89b63c58 100644 --- a/forge/build.gradle +++ b/forge189/build.gradle @@ -5,15 +5,14 @@ buildscript { name = "forge" url = "http://files.minecraftforge.net/maven" } - maven {url = "http://repo.minecrell.net/snapshots"} + maven {url = "https://oss.sonatype.org/content/repositories/snapshots/"} } dependencies { - classpath 'net.minecrell:VanillaGradle:2.0.3-SNAPSHOT' classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT' } } -apply plugin: 'net.minecrell.vanilla.server.library' +apply plugin: 'net.minecraftforge.gradle.forge' apply plugin: 'com.github.johnrengelman.shadow' dependencies { @@ -41,7 +40,7 @@ repositories { } } minecraft { - version = "1.8.9" + version = "1855" mappings = "stable_22" runDir = 'run' } @@ -52,15 +51,16 @@ 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.mcstats.sponge:metrics:R8-SNAPSHOT')) + include(dependency('org.yaml:snakeyaml:1.16')) } - relocate 'org.mcstats', 'com.boydti.fawe.stats' archiveName = "${parent.name}-${project.name}.jar" destinationDir = file '../target' } @@ -69,6 +69,7 @@ shadowJar.doLast { ant.checksum file: task.archivePath } + reobf { shadowJar { mappingType = 'SEARGE' @@ -80,4 +81,8 @@ task deobfJar(type: Jar) { classifier = 'dev' } +artifacts { + archives deobfJar +} + build.dependsOn(shadowJar) diff --git a/forge189/src/main/java/com/boydti/fawe/forge/FaweForge.java b/forge189/src/main/java/com/boydti/fawe/forge/FaweForge.java new file mode 100644 index 00000000..13920b8e --- /dev/null +++ b/forge189/src/main/java/com/boydti/fawe/forge/FaweForge.java @@ -0,0 +1,166 @@ +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.EditSessionWrapper; +import com.boydti.fawe.object.FaweCommand; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.regions.FaweMaskManager; +import com.boydti.fawe.util.FaweQueue; +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.UUID; +import javax.management.InstanceAlreadyExistsException; +import net.minecraft.command.ServerCommandManager; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +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) { + e.printStackTrace(); + } + } + + @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) MinecraftServer.getServer().getCommandManager(); + scm.registerCommand(new ForgeCommand(label, cmd)); + } + }); + } + } + + @Override + public FawePlayer wrap(Object obj) { + EntityPlayerMP player = null; + if (obj instanceof String) { + MinecraftServer server = MinecraftServer.getServer(); + player = server.getConfigurationManager().getPlayerByUsername((String) obj); + } else if (obj instanceof UUID) { + MinecraftServer server = MinecraftServer.getServer(); + player = server.getConfigurationManager().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 setupWEListener() { + // Do nothing + } + + @Override + public void setupVault() { + // Do nothing + } + + @Override + public TaskManager getTaskManager() { + return new com.boydti.fawe.forge.ForgeTaskMan(512); + } + + @Override + public int[] getVersion() { + String[] version = MinecraftServer.getServer().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().provider.getDimensionName(); + } + + @Override + public FaweQueue getNewQueue(String world) { + return new ForgeQueue_All(world); + } + + @Override + public EditSessionWrapper getEditSessionWrapper(EditSession session) { + return new EditSessionWrapper(session); + } + + @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.4.0"); + 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 = MinecraftServer.getServer().getPlayerProfileCache().getGameProfileForUsername(name); + return profile.getId(); + } catch (Throwable e) { + return null; + } + } + + @Override + public String getName(UUID uuid) { + try { + GameProfile profile = MinecraftServer.getServer().getPlayerProfileCache().getProfileByUUID(uuid); + return profile.getName(); + } catch (Throwable e) { + return null; + } + } +} diff --git a/forge189/src/main/java/com/boydti/fawe/forge/ForgeCommand.java b/forge189/src/main/java/com/boydti/fawe/forge/ForgeCommand.java new file mode 100644 index 00000000..c8ddb42c --- /dev/null +++ b/forge189/src/main/java/com/boydti/fawe/forge/ForgeCommand.java @@ -0,0 +1,41 @@ +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; + +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 processCommand(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/forge189/src/main/java/com/boydti/fawe/forge/ForgeMain.java b/forge189/src/main/java/com/boydti/fawe/forge/ForgeMain.java new file mode 100644 index 00000000..cf156683 --- /dev/null +++ b/forge189/src/main/java/com/boydti/fawe/forge/ForgeMain.java @@ -0,0 +1,76 @@ +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.minecraft.server.MinecraftServer; +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.4.0", 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)MinecraftServer.getServer().getConfigurationManager().playerEntityList) { + 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.entity; + if (!(entity instanceof EntityPlayerMP)) { + return; + } + EntityPlayerMP player = (EntityPlayerMP) entity; + if (player.worldObj.isRemote) { + return; + } + FawePlayer fp = FawePlayer.wrap(player); + if (fp.getMeta("lastWorld") != event.world) { + fp.setMeta("lastWorld", event.world); + if (Settings.STORE_HISTORY_ON_DISK) { + fp.getSession().clearHistory(); + fp.loadSessionsFromDisk(fp.getWorld()); + } + } + } +} diff --git a/forge189/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java b/forge189/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java new file mode 100644 index 00000000..2811a35b --- /dev/null +++ b/forge189/src/main/java/com/boydti/fawe/forge/ForgeMetrics.java @@ -0,0 +1,479 @@ +/* + * 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 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; + boolean onlineMode = MinecraftServer.getServer().isServerInOnlineMode(); + String pluginVersion = modVersion; + String serverVersion; + if (MinecraftServer.getServer().isDedicatedServer()) { + serverVersion = "MinecraftForge (MC: " + MinecraftServer.getServer().getMinecraftVersion() + ")"; + } else { + serverVersion = "MinecraftForgeSSP (MC: " + MinecraftServer.getServer().getMinecraftVersion() + ")"; + } + int playersOnline = MinecraftServer.getServer().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) { + e.printStackTrace(); + } 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/forge189/src/main/java/com/boydti/fawe/forge/ForgePlayer.java b/forge189/src/main/java/com/boydti/fawe/forge/ForgePlayer.java new file mode 100644 index 00000000..fdcdbb79 --- /dev/null +++ b/forge189/src/main/java/com/boydti/fawe/forge/ForgePlayer.java @@ -0,0 +1,66 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.object.FaweLocation; +import com.boydti.fawe.object.FawePlayer; +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.BlockPos; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.world.World; + +public class ForgePlayer extends FawePlayer { + public ForgePlayer(EntityPlayerMP parent) { + super(parent); + } + + @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 = EnumChatFormatting.getTextWithoutFormattingCodes(msg); + ChatComponentText component = new ChatComponentText(part); + component.getChatStyle().setColor(EnumChatFormatting.LIGHT_PURPLE); + this.parent.addChatMessage(component); + } + } + + @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.provider.getDimensionName(), pos.getX(), pos.getY(), pos.getZ()); + } + + @Override + public Player getPlayer() { + return ForgeWorldEdit.inst.wrap(this.parent); + } +} diff --git a/forge189/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java b/forge189/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java new file mode 100644 index 00000000..1edbdbab --- /dev/null +++ b/forge189/src/main/java/com/boydti/fawe/forge/ForgeTaskMan.java @@ -0,0 +1,165 @@ +package com.boydti.fawe.forge; + +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) { + e.printStackTrace(); + } + later(this, interval); + } + }); + return id; + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public void onServerTick(TickEvent.ServerTickEvent event) { + 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) { + e.printStackTrace(); + } + } + } + } + + @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) { + e.printStackTrace(); + } + 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) { + e.printStackTrace(); + } + 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) { + e.printStackTrace(); + } + return; + } + task(this); + } + }); + } + + @Override + public void cancel(int task) { + taskIdMap.remove(task); + } +} 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 new file mode 100644 index 00000000..0ac64089 --- /dev/null +++ b/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeChunk_All.java @@ -0,0 +1,235 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.util.FaweQueue; +import com.sk89q.worldedit.world.biome.BaseBiome; +import java.util.Arrays; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; + +public class ForgeChunk_All extends FaweChunk { + + public char[][] ids; + public short[] count; + public short[] air; + public short[] relight; + public byte[][] biomes; + public Chunk chunk; + + public ForgeChunk_All(FaweQueue parent, int x, int z) { + super(parent, x, z); + this.ids = new char[16][]; + this.count = new short[16]; + this.air = new short[16]; + this.relight = new short[16]; + } + + + @Override + public Chunk getChunk() { + if (this.chunk == null) { + World world = ((ForgeQueue_All) getParent()).getWorld(); + this.chunk = world.getChunkProvider().provideChunk(getX(), getZ()); + } + return this.chunk; + } + + @Override + public void setLoc(final FaweQueue parent, int x, int z) { + super.setLoc(parent, x, z); + this.chunk = null; + } + + /** + * Get the number of block changes in a specified section. + * @param i + * @return + */ + public int getCount(int i) { + return this.count[i]; + } + + public int getAir(int i) { + return this.air[i]; + } + + public void setCount(int i, short value) { + this.count[i] = value; + } + + /** + * Get the number of block changes in a specified section. + * @param i + * @return + */ + public int getRelight(int i) { + return this.relight[i]; + } + + public int getTotalCount() { + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.count[i]; + } + return total; + } + + public int getTotalRelight() { + if (getTotalCount() == 0) { + Arrays.fill(this.count, (short) 1); + Arrays.fill(this.relight, Short.MAX_VALUE); + return Short.MAX_VALUE; + } + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.relight[i]; + } + return total; + } + + /** + * Get the raw data for a section. + * @param i + * @return + */ + public char[] getIdArray(int i) { + return this.ids[i]; + } + + @Override + public void setBlock(int x, int y, int z, int id, byte data) { + int i = FaweCache.CACHE_I[y][x][z]; + int j = FaweCache.CACHE_J[y][x][z]; + char[] vs = this.ids[i]; + if (vs == null) { + vs = this.ids[i] = new char[4096]; + this.count[i]++; + } else if (vs[j] == 0) { + this.count[i]++; + } + switch (id) { + case 0: + this.air[i]++; + vs[j] = (char) 1; + return; + case 10: + case 11: + case 39: + case 40: + case 51: + case 74: + case 89: + case 122: + case 124: + case 138: + case 169: + 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 8: + case 9: + case 73: + case 78: + 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 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: + vs[j] = (char) (id << 4); + return; + case 130: + case 76: + case 62: + this.relight[i]++; + case 54: + case 146: + case 61: + case 65: + case 68: + case 50: + if (data < 2) { + data = 2; + } + default: + vs[j] = (char) ((id << 4) + data); + return; + } + } + + @Override + public void setBiome(int x, int z, BaseBiome biome) { + if (this.biomes == null) { + this.biomes = new byte[16][]; + } + byte[] index = this.biomes[x]; + if (index == null) { + index = this.biomes[x] = new byte[16]; + } + index[z] = (byte) biome.getId(); + } + + @Override + public FaweChunk clone() { + ForgeChunk_All toReturn = new ForgeChunk_All(getParent(), getX(), getZ()); + toReturn.air = this.air.clone(); + toReturn.count = this.count.clone(); + toReturn.relight = this.relight.clone(); + toReturn.ids = new char[this.ids.length][]; + for (int i = 0; i < this.ids.length; i++) { + char[] matrix = this.ids[i]; + if (matrix != null) { + toReturn.ids[i] = new char[matrix.length]; + System.arraycopy(matrix, 0, toReturn.ids[i], 0, matrix.length); + } + } + return toReturn; + } +} 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 new file mode 100644 index 00000000..fa8a4182 --- /dev/null +++ b/forge189/src/main/java/com/boydti/fawe/forge/v0/ForgeQueue_All.java @@ -0,0 +1,541 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.forge.ForgePlayer; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.IntegerPair; +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.object.exception.FaweException; +import com.boydti.fawe.util.FaweQueue; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.world.biome.BaseBiome; +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingDeque; +import net.minecraft.block.Block; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.play.server.S21PacketChunkData; +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ClassInheritanceMultiMap; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import net.minecraft.world.gen.ChunkProviderServer; + +public class ForgeQueue_All extends FaweQueue { + + private World forgeWorld; + + private ConcurrentHashMap> blocks = new ConcurrentHashMap<>(); + private LinkedBlockingDeque> chunks = new LinkedBlockingDeque<>(); + + public ForgeQueue_All(final String world) { + super(world); + } + + @Override + public boolean isChunkLoaded(int x, int z) { + return getWorld().getChunkProvider().chunkExists(x, z); + } + + public World getWorld() { + if (forgeWorld != null) { + return forgeWorld; + } + WorldServer[] worlds = MinecraftServer.getServer().worldServers; + for (WorldServer ws : worlds) { + if (ws.provider.getDimensionName().equals(world)) { + return forgeWorld = ws; + } + } + return null; + } + + @Override + public boolean regenerateChunk(int x, int z) { + IChunkProvider provider = getWorld().getChunkProvider(); + if (!(provider instanceof ChunkProviderServer)) { + return false; + } + ChunkProviderServer chunkServer = (ChunkProviderServer) provider; + IChunkProvider chunkProvider = chunkServer.serverChunkGenerator; + + long pos = ChunkCoordIntPair.chunkXZ2Int(x, z); + Chunk mcChunk; + if (chunkServer.chunkExists(x, z)) { + mcChunk = chunkServer.loadChunk(x, z); + 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) { + e.printStackTrace(); + } + chunkServer.id2ChunkMap.remove(pos); + mcChunk = chunkProvider.provideChunk(x, z); + chunkServer.id2ChunkMap.add(pos, mcChunk); + chunkServer.loadedChunks.add(mcChunk); + if (mcChunk != null) { + mcChunk.onChunkLoad(); + mcChunk.populateChunk(chunkProvider, chunkProvider, x, z); + } + return true; + } + + @Override + public void addTask(int x, int z, Runnable runnable) { + long pair = (long) (x) << 32 | (z) & 0xFFFFFFFFL; + FaweChunk result = this.blocks.get(pair); + if (result == null) { + result = this.getChunk(x, z); + result.addTask(runnable); + FaweChunk previous = this.blocks.put(pair, result); + if (previous == null) { + chunks.add(result); + return; + } + this.blocks.put(pair, previous); + result = previous; + } + result.addTask(runnable); + } + + private int lcx = Integer.MIN_VALUE; + private int lcz = Integer.MIN_VALUE; + private int lcy = Integer.MIN_VALUE; + private net.minecraft.world.chunk.Chunk lc; + private char[] ls; + + private final RunnableVal loadChunk = new RunnableVal() { + @Override + public void run(IntegerPair loc) { + getWorld().getChunkProvider().provideChunk(loc.x, loc.z); + } + }; + + @Override + public int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { + if (y < 0 || y > 255) { + return 0; + } + int cx = x >> 4; + int cz = z >> 4; + int cy = y >> 4; + if (cx != lcx || cz != lcz) { + World world = getWorld(); + lcx = cx; + lcz = cz; + IChunkProvider provider = world.getChunkProvider(); + Chunk chunk; + if (!provider.chunkExists(lcx, lcz)) { + boolean sync = Thread.currentThread() == Fawe.get().getMainThread(); + if (sync) { + chunk = provider.provideChunk(cx, cz); + } else if (Settings.CHUNK_WAIT > 0) { + loadChunk.value = new IntegerPair(cx, cz); + TaskManager.IMP.sync(loadChunk, Settings.CHUNK_WAIT); + if (!provider.chunkExists(cx, cz)) { + throw new FaweException.FaweChunkLoadException(); + } + chunk = provider.provideChunk(cx, cz); + } else { + return 0; + } + } else { + chunk = provider.provideChunk(cx, cz); + } + lc = chunk; + } else if (cy == lcy) { + return ls != null ? ls[FaweCache.CACHE_J[y][x & 15][z & 15]] : 0; + } + ExtendedBlockStorage storage = lc.getBlockStorageArray()[cy]; + if (storage == null) { + ls = null; + return 0; + } + ls = storage.getData(); + return ls[FaweCache.CACHE_J[y][x & 15][z & 15]]; + } + + private FaweChunk lastChunk; + private int lastX = Integer.MIN_VALUE; + private int lastZ = Integer.MIN_VALUE; + + @Override + public boolean setBlock(int x, int y, int z, short id, byte data) { + if ((y > 255) || (y < 0)) { + return false; + } + int cx = x >> 4; + int cz = z >> 4; + if (cx != lastX || cz != lastZ) { + lastX = cx; + lastZ = cz; + long pair = (long) (cx) << 32 | (cz) & 0xFFFFFFFFL; + lastChunk = this.blocks.get(pair); + if (lastChunk == null) { + lastChunk = this.getChunk(x >> 4, z >> 4); + lastChunk.setBlock(x & 15, y, z & 15, id, data); + FaweChunk previous = this.blocks.put(pair, lastChunk); + if (previous == null) { + chunks.add(lastChunk); + return true; + } + this.blocks.put(pair, previous); + lastChunk = previous; + } + } + lastChunk.setBlock(x & 15, y, z & 15, id, data); + return true; + } + + @Override + public boolean setBiome(int x, int z, BaseBiome biome) { + long pair = (long) (x >> 4) << 32 | (z >> 4) & 0xFFFFFFFFL; + FaweChunk result = this.blocks.get(pair); + if (result == null) { + result = this.getChunk(x >> 4, z >> 4); + FaweChunk previous = this.blocks.put(pair, result); + if (previous != null) { + this.blocks.put(pair, previous); + result = previous; + } else { + chunks.add(result); + } + } + result.setBiome(x & 15, z & 15, biome); + return true; + } + + @Override + public FaweChunk next() { + lastX = Integer.MIN_VALUE; + lastZ = Integer.MIN_VALUE; + try { + if (this.blocks.size() == 0) { + return null; + } + synchronized (blocks) { + FaweChunk chunk = chunks.poll(); + if (chunk != null) { + blocks.remove(chunk.longHash()); + this.execute(chunk); + return chunk; + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } + + @Override + public int size() { + return chunks.size(); + } + + private LinkedBlockingDeque> toUpdate = new LinkedBlockingDeque<>(); + + public boolean execute(FaweChunk fc) { + if (fc == null) { + return false; + } + // Load chunk + Chunk chunk = fc.getChunk(); + if (!chunk.isLoaded()) { + chunk.onChunkLoad(); + } + // Set blocks / entities / biome + if (!this.setComponents(fc)) { + return false; + } + fc.executeTasks(); + return true; + } + + @Override + public void clear() { + this.blocks.clear(); + } + + @Override + public void setChunk(FaweChunk chunk) { + FaweChunk previous = this.blocks.put(chunk.longHash(), (FaweChunk) chunk); + if (previous != null) { + chunks.remove(previous); + } + chunks.add((FaweChunk) chunk); + } + + public void sendChunk(FaweChunk fc) { + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + final boolean result = fixLighting(fc, Settings.FIX_ALL_LIGHTING) || !Settings.ASYNC_LIGHTING; + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + if (!result) { + fixLighting(fc, Settings.FIX_ALL_LIGHTING); + } + Chunk chunk = fc.getChunk(); + if (!chunk.isLoaded()) { + return; + } + World world = chunk.getWorld(); + ChunkCoordIntPair pos = chunk.getChunkCoordIntPair(); + int cx = pos.chunkXPos; + int cz = pos.chunkZPos; + for (FawePlayer fp : Fawe.get().getCachedPlayers()) { + ForgePlayer forgePlayer = (ForgePlayer) fp; + EntityPlayerMP player = forgePlayer.parent; + if (!player.worldObj.equals(world)) { + continue; + } + int view = MinecraftServer.getServer().getConfigurationManager().getViewDistance(); + EntityPlayerMP nmsPlayer = (EntityPlayerMP) player; + BlockPos loc = player.getPosition(); + int px = loc.getX() >> 4; + int pz = loc.getZ() >> 4; + int dx = Math.abs(cx - (loc.getX() >> 4)); + int dz = Math.abs(cz - (loc.getZ() >> 4)); + if ((dx > view) || (dz > view)) { + continue; + } + NetHandlerPlayServer con = nmsPlayer.playerNetServerHandler; + con.sendPacket(new S21PacketChunkData(chunk, false, 65535)); + // Try sending true, 0 first + // Try bulk chunk packet + } + } + }, false); + } + }, Settings.ASYNC_LIGHTING); + } + + public boolean setComponents(FaweChunk fc) { + ForgeChunk_All fs = (ForgeChunk_All) fc; + Chunk forgeChunk = fc.getChunk(); + net.minecraft.world.World nmsWorld = forgeChunk.getWorld(); + try { + boolean flag = !nmsWorld.provider.getHasNoSky(); + // Sections + ExtendedBlockStorage[] sections = forgeChunk.getBlockStorageArray(); + Map tiles = forgeChunk.getTileEntityMap(); + ClassInheritanceMultiMap[] entities = forgeChunk.getEntityLists(); + // 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]; + int k = FaweCache.CACHE_J[ly][lx][lz]; + char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + if (array[k] != 0) { + iterator.remove(); + } + } + // Trim entities + for (int i = 0; i < 16; i++) { + if ((entities[i] != null) && (fs.getCount(i) >= 4096)) { + entities[i] = new ClassInheritanceMultiMap<>(Entity.class); + } + } + // Efficiently merge sections + for (int j = 0; j < sections.length; j++) { + if (fs.getCount(j) == 0) { + continue; + } + char[] newArray = fs.getIdArray(j); + if (newArray == null) { + continue; + } + ExtendedBlockStorage section = sections[j]; + if ((section == null) || (fs.getCount(j) >= 4096)) { + section = new ExtendedBlockStorage(j << 4, flag); + section.setData(newArray); + sections[j] = section; + continue; + } + char[] currentArray = section.getData(); + boolean fill = true; + for (int k = 0; k < newArray.length; k++) { + char n = newArray[k]; + switch (n) { + case 0: + fill = false; + continue; + case 1: + fill = false; + currentArray[k] = 0; + continue; + default: + currentArray[k] = n; + continue; + } + } + if (fill) { + fs.setCount(j, Short.MAX_VALUE); + } + } +// // Clear + } catch (Throwable e) { + e.printStackTrace(); + } + byte[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + byte[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + byte biome = array[z]; + if (biome == 0) { + continue; + } + forgeChunk.getBiomeArray()[((z & 0xF) << 4 | x & 0xF)] = biome; + } + } + } + sendChunk(fs); + return true; + } + + @Override + public FaweChunk getChunk(int x, int z) { + return new ForgeChunk_All(this, x, z); + } + + @Override + public boolean fixLighting(FaweChunk chunk, boolean fixAll) { + try { + ForgeChunk_All fc = (ForgeChunk_All) chunk; + Chunk forgeChunk = fc.getChunk(); + if (!forgeChunk.isLoaded()) { + forgeChunk.onChunkLoad(); + } + forgeChunk.generateSkylightMap(); + if (fc.getTotalRelight() == 0 && !fixAll) { + return true; + } + ExtendedBlockStorage[] sections = forgeChunk.getBlockStorageArray(); + net.minecraft.world.World nmsWorld = forgeChunk.getWorld(); + + 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 ((fc.getRelight(j) == 0 && !fixAll) || fc.getCount(j) == 0 || (fc.getCount(j) >= 4096 && fc.getAir(j) == 0)) { + continue; + } + char[] array = section.getData(); + int l = PseudoRandom.random.random(2); + for (int k = 0; k < array.length; k++) { + int i = array[k]; + if (i < 16) { + continue; + } + short id = (short) (i >> 4); + switch (id) { // Lighting + default: + if (!fixAll) { + continue; + } + if ((k & 1) == l) { + l = 1 - l; + 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: + int x = FaweCache.CACHE_X[j][k]; + int y = FaweCache.CACHE_Y[j][k]; + int z = FaweCache.CACHE_Z[j][k]; + if (isSurrounded(sections, x, y, z)) { + continue; + } + BlockPos pos = new BlockPos(X + x, y, Z + z); + nmsWorld.checkLight(pos); + } + } + } + return true; + } catch (Throwable e) { + if (Thread.currentThread() == Fawe.get().getMainThread()) { + e.printStackTrace(); + } + } + return false; + } + + 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)); + } + + public boolean isSolid(int i) { + return i != 0 && Block.getBlockById(i).isOpaqueCube(); + } + + public int getId(ExtendedBlockStorage[] sections, int x, int y, int z) { + if (x < 0 || x > 15 || z < 0 || z > 15) { + return 1; + } + if (y < 0 || y > 255) { + return 1; + } + int i = FaweCache.CACHE_I[y][x][z]; + ExtendedBlockStorage section = sections[i]; + if (section == null) { + return 0; + } + char[] array = section.getData(); + int j = FaweCache.CACHE_J[y][x][z]; + return array[j] >> 4; + } +} diff --git a/forge189/src/main/resources/config.yml b/forge189/src/main/resources/config.yml new file mode 100644 index 00000000..e69de29b diff --git a/gradle.properties b/gradle.properties index 0ff1aa36..68da8d19 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,5 @@ +#org.gradle.java.home=C:/PROGRA~2/Java/jdk1.7.0_79 +#org.gradle.java.home=C:/PROGRA~1/Java/jdk1.8.0_51 org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.configureondemand=true diff --git a/pom.xml b/pom.xml index fa993689..fad6913b 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ UTF-8 FastAsyncWorldEdit - 3.3.21 + 3.4.0 FastAsyncWorldEdit jar diff --git a/settings.gradle b/settings.gradle index 32327a97..6de5dbef 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ rootProject.name = 'FastAsyncWorldEdit' -include 'core', 'bukkit', 'forge' \ No newline at end of file +include 'core', 'bukkit', 'forge189', 'forge1710', 'sponge' diff --git a/sponge/build.gradle b/sponge/build.gradle new file mode 100644 index 00000000..35f67203 --- /dev/null +++ b/sponge/build.gradle @@ -0,0 +1,94 @@ +buildscript { + repositories { + jcenter() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = 'minecrell' + url = 'http://repo.minecrell.net/releases' + } + maven {url = "https://oss.sonatype.org/content/repositories/snapshots/"} + maven {url = "http://repo.minecrell.net/snapshots"} + } + dependencies { + classpath 'net.minecrell:VanillaGradle:2.0.3_1' + classpath 'net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT' + } +} + +apply plugin: 'net.minecrell.vanilla.server.library' +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 = "1.8.9" + mappings = "stable_22" + 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}.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/forge/src/main/java/com/boydti/fawe/forge/SpongeCommand.java b/sponge/src/main/java/com/boydti/fawe/SpongeCommand.java similarity index 98% rename from forge/src/main/java/com/boydti/fawe/forge/SpongeCommand.java rename to sponge/src/main/java/com/boydti/fawe/SpongeCommand.java index f7f1c139..3085a4c1 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/SpongeCommand.java +++ b/sponge/src/main/java/com/boydti/fawe/SpongeCommand.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.forge; +package com.boydti.fawe; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; diff --git a/forge/src/main/java/com/boydti/fawe/forge/FaweSponge.java b/sponge/src/main/java/com/boydti/fawe/sponge/FaweSponge.java similarity index 89% rename from forge/src/main/java/com/boydti/fawe/forge/FaweSponge.java rename to sponge/src/main/java/com/boydti/fawe/sponge/FaweSponge.java index 9e6fb047..5d79a258 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/FaweSponge.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/FaweSponge.java @@ -1,10 +1,10 @@ -package com.boydti.fawe.forge; +package com.boydti.fawe.sponge; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; import com.boydti.fawe.config.BBC; -import com.boydti.fawe.forge.v0.SpongeEditSessionWrapper_0; -import com.boydti.fawe.forge.v1_8.SpongeQueue_1_8; +import com.boydti.fawe.SpongeCommand; +import com.boydti.fawe.v1_8.SpongeQueue_1_8; import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; @@ -13,12 +13,10 @@ import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.forge.ForgeWorldEdit; +import com.sk89q.worldedit.world.World; import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; import org.spongepowered.api.Sponge; import org.spongepowered.api.entity.living.player.Player; @@ -118,9 +116,14 @@ public class FaweSponge implements IFawe { return new SpongeQueue_1_8(world); } + @Override + public String getWorldName(World world) { + return world.getName(); + } + @Override public EditSessionWrapper getEditSessionWrapper(EditSession session) { - return new SpongeEditSessionWrapper_0(session); + return new EditSessionWrapper(session); } @Override @@ -134,20 +137,11 @@ public class FaweSponge implements IFawe { SpongeMetrics metrics = new SpongeMetrics(Sponge.getGame(), Sponge.getPluginManager().fromInstance(plugin).get()); metrics.start(); debug("[FAWE] &6Metrics enabled."); - } catch (IOException e) { + } catch (Throwable e) { debug("[FAWE] &cFailed to load up metrics."); } } - @Override - public Set getPlayers() { - HashSet players = new HashSet<>(); - for (Player player : Sponge.getServer().getOnlinePlayers()) { - players.add(wrap(player)); - } - return players; - } - @Override public String getPlatform() { return "sponge"; diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongeMain.java b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeMain.java similarity index 83% rename from forge/src/main/java/com/boydti/fawe/forge/SpongeMain.java rename to sponge/src/main/java/com/boydti/fawe/sponge/SpongeMain.java index a6634366..3dd96e5c 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/SpongeMain.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeMain.java @@ -1,10 +1,10 @@ -package com.boydti.fawe.forge; +package com.boydti.fawe.sponge; import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FawePlayer; import com.google.inject.Inject; -import com.sk89q.worldedit.WorldEdit; import org.slf4j.Logger; import org.spongepowered.api.Game; import org.spongepowered.api.Server; @@ -18,7 +18,7 @@ import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.profile.GameProfileManager; import org.spongepowered.api.world.World; -@Plugin(id = "com.boydti.fawe", name = "FastAsyncWorldEdit", description = "Lagless WorldEdit, Area restrictions, Memory mangement, Block logging", url = "https://github.com/boy0001/FastAsyncWorldedit", version = "3.3.21") +@Plugin(id = "com.boydti.fawe", name = "FastAsyncWorldEdit", description = "Lagless WorldEdit, Area restrictions, Memory mangement, Block logging", url = "https://github.com/boy0001/FastAsyncWorldedit", version = "3.4.0", authors = "Empire92") public class SpongeMain { public PluginContainer plugin; @@ -70,13 +70,10 @@ public class SpongeMain { if (!from.equals(to)) { Player player = event.getTargetEntity(); FawePlayer fp = FawePlayer.wrap(player); - for (com.sk89q.worldedit.world.World world : WorldEdit.getInstance().getServer().getWorlds()) { - if (world.getName().equals(to.getName())) { - fp.getSession().clearHistory(); - fp.loadSessionFromDisk(world); - return; - } - } + com.sk89q.worldedit.world.World world = FaweAPI.getWorld(to.getName()); + fp.getSession().clearHistory(); + fp.loadSessionsFromDisk(world); + return; } } } diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongeMetrics.java b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeMetrics.java similarity index 99% rename from forge/src/main/java/com/boydti/fawe/forge/SpongeMetrics.java rename to sponge/src/main/java/com/boydti/fawe/sponge/SpongeMetrics.java index e4450885..ff0aee37 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/SpongeMetrics.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeMetrics.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.forge; +package com.boydti.fawe.sponge; /* * Copyright 2011-2013 Tyler Blair. All rights reserved. diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongePlayer.java b/sponge/src/main/java/com/boydti/fawe/sponge/SpongePlayer.java similarity index 91% rename from forge/src/main/java/com/boydti/fawe/forge/SpongePlayer.java rename to sponge/src/main/java/com/boydti/fawe/sponge/SpongePlayer.java index 46e19315..c9780bc2 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/SpongePlayer.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/SpongePlayer.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.forge; +package com.boydti.fawe.sponge; import com.boydti.fawe.Fawe; import com.boydti.fawe.config.BBC; @@ -58,9 +58,4 @@ public class SpongePlayer extends FawePlayer { public com.sk89q.worldedit.entity.Player getPlayer() { return (com.sk89q.worldedit.entity.Player) Fawe. imp().getWorldEditPlugin().wrap((EntityPlayerMP) this.parent); } - - @Override - public boolean hasWorldEditBypass() { - return hasPermission("fawe.bypass") || getMeta("fawe.bypass") == Boolean.TRUE; - } } diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongeTaskMan.java b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeTaskMan.java similarity index 98% rename from forge/src/main/java/com/boydti/fawe/forge/SpongeTaskMan.java rename to sponge/src/main/java/com/boydti/fawe/sponge/SpongeTaskMan.java index e592c8ee..2f4c41ec 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/SpongeTaskMan.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeTaskMan.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.forge; +package com.boydti.fawe.sponge; import com.boydti.fawe.util.TaskManager; import java.util.HashMap; diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongeUtil.java b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeUtil.java similarity index 98% rename from forge/src/main/java/com/boydti/fawe/forge/SpongeUtil.java rename to sponge/src/main/java/com/boydti/fawe/sponge/SpongeUtil.java index 11999824..2b738bc3 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/SpongeUtil.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/SpongeUtil.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.forge; +package com.boydti.fawe.sponge; import com.boydti.fawe.Fawe; import com.sk89q.worldedit.world.biome.BiomeData; diff --git a/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeQueue_0.java b/sponge/src/main/java/com/boydti/fawe/v0/SpongeQueue_0.java similarity index 98% rename from forge/src/main/java/com/boydti/fawe/forge/v0/SpongeQueue_0.java rename to sponge/src/main/java/com/boydti/fawe/v0/SpongeQueue_0.java index e3cda2a1..040f6c09 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeQueue_0.java +++ b/sponge/src/main/java/com/boydti/fawe/v0/SpongeQueue_0.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.forge.v0; +package com.boydti.fawe.v0; import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.util.FaweQueue; @@ -10,9 +10,6 @@ import org.spongepowered.api.Sponge; import org.spongepowered.api.world.Chunk; import org.spongepowered.api.world.World; -/** - * Created by Jesse on 4/2/2016. - */ public abstract class SpongeQueue_0 extends FaweQueue { /** diff --git a/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeChunk_1_8.java b/sponge/src/main/java/com/boydti/fawe/v1_8/SpongeChunk_1_8.java similarity index 99% rename from forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeChunk_1_8.java rename to sponge/src/main/java/com/boydti/fawe/v1_8/SpongeChunk_1_8.java index 46b68d64..2b86a2c9 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeChunk_1_8.java +++ b/sponge/src/main/java/com/boydti/fawe/v1_8/SpongeChunk_1_8.java @@ -1,4 +1,4 @@ -package com.boydti.fawe.forge.v1_8; +package com.boydti.fawe.v1_8; import com.boydti.fawe.FaweCache; import com.boydti.fawe.object.FaweChunk; diff --git a/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeQueue_1_8.java b/sponge/src/main/java/com/boydti/fawe/v1_8/SpongeQueue_1_8.java similarity index 82% rename from forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeQueue_1_8.java rename to sponge/src/main/java/com/boydti/fawe/v1_8/SpongeQueue_1_8.java index 33f1f399..6768c00b 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeQueue_1_8.java +++ b/sponge/src/main/java/com/boydti/fawe/v1_8/SpongeQueue_1_8.java @@ -1,13 +1,14 @@ -package com.boydti.fawe.forge.v1_8; +package com.boydti.fawe.v1_8; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.forge.SpongeUtil; -import com.boydti.fawe.forge.v0.SpongeQueue_0; +import com.boydti.fawe.sponge.SpongeUtil; +import com.boydti.fawe.v0.SpongeQueue_0; 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.object.exception.FaweException; import com.boydti.fawe.util.TaskManager; import com.flowpowered.math.vector.Vector3i; @@ -18,7 +19,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.concurrent.LinkedBlockingDeque; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayerMP; @@ -44,24 +44,20 @@ public class SpongeQueue_1_8 extends SpongeQueue_0 { public SpongeQueue_1_8(String world) { super(world); - TaskManager.IMP.repeat(() -> { - synchronized (loadQueue) { - while (loadQueue.size() > 0) { - IntegerPair loc = loadQueue.poll(); - if (spongeWorld == null) { - spongeWorld = Sponge.getServer().getWorld(world).get(); - } - Chunk chunk = spongeWorld.getChunk(loc.x, 0, loc.z).orElse(null); - if (chunk == null || !chunk.isLoaded()) { - spongeWorld.loadChunk(loc.x, 0, loc.z, true); - } - } - loadQueue.notifyAll(); - } - }, 1); } - private LinkedBlockingDeque loadQueue = new LinkedBlockingDeque<>(); + private final RunnableVal loadChunk = new RunnableVal() { + @Override + public void run(IntegerPair loc) { + if (spongeWorld == null) { + spongeWorld = Sponge.getServer().getWorld(world).get(); + } + Chunk chunk = spongeWorld.getChunk(loc.x, 0, loc.z).orElse(null); + if (chunk == null || !chunk.isLoaded()) { + spongeWorld.loadChunk(loc.x, 0, loc.z, true); + } + } + }; @Override public int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { @@ -83,14 +79,8 @@ public class SpongeQueue_1_8 extends SpongeQueue_0 { if (sync) { chunk = spongeWorld.loadChunk(cx, 0, cz, true).orElse(null); } else if (Settings.CHUNK_WAIT > 0) { - synchronized (loadQueue) { - loadQueue.add(new IntegerPair(cx, cz)); - try { - loadQueue.wait(Settings.CHUNK_WAIT); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + loadChunk.value = new IntegerPair(cx, cz); + TaskManager.IMP.sync(loadChunk, Settings.CHUNK_WAIT); chunk = spongeWorld.getChunk(cx, 0, cz).orElse(null); if (chunk == null || !chunk.isLoaded()) { throw new FaweException.FaweChunkLoadException(); @@ -125,35 +115,48 @@ public class SpongeQueue_1_8 extends SpongeQueue_0 { } public void sendChunk(FaweChunk fc) { - fixLighting(fc, Settings.FIX_ALL_LIGHTING); - Chunk chunk = fc.getChunk(); - if (!chunk.isLoaded()) { - return; - } - World world = chunk.getWorld(); - Vector3i pos = chunk.getBlockMin(); - int cx = pos.getX() >> 4; - int cz = pos.getZ() >> 4; - for (Player player : Sponge.getServer().getOnlinePlayers()) { - if (!player.getWorld().equals(world)) { - continue; + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + final boolean result = fixLighting(fc, Settings.FIX_ALL_LIGHTING) || !Settings.ASYNC_LIGHTING; + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + if (!result) { + fixLighting(fc, Settings.FIX_ALL_LIGHTING); + } + Chunk chunk = fc.getChunk(); + if (!chunk.isLoaded()) { + return; + } + World world = chunk.getWorld(); + Vector3i pos = chunk.getBlockMin(); + int cx = pos.getX() >> 4; + int cz = pos.getZ() >> 4; + for (Player player : Sponge.getServer().getOnlinePlayers()) { + if (!player.getWorld().equals(world)) { + continue; + } + int view = player.getViewDistance(); + EntityPlayerMP nmsPlayer = (EntityPlayerMP) player; + Location loc = player.getLocation(); + int px = loc.getBlockX() >> 4; + int pz = loc.getBlockZ() >> 4; + int dx = Math.abs(cx - (loc.getBlockX() >> 4)); + int dz = Math.abs(cz - (loc.getBlockZ() >> 4)); + if ((dx > view) || (dz > view)) { + continue; + } + NetHandlerPlayServer con = nmsPlayer.playerNetServerHandler; + net.minecraft.world.chunk.Chunk nmsChunk = (net.minecraft.world.chunk.Chunk) chunk; + con.sendPacket(new S21PacketChunkData(nmsChunk, false, 65535)); + // Try sending true, 0 first + // Try bulk chunk packet + } + } + }, false); } - int view = player.getViewDistance(); - EntityPlayerMP nmsPlayer = (EntityPlayerMP) player; - Location loc = player.getLocation(); - int px = loc.getBlockX() >> 4; - int pz = loc.getBlockZ() >> 4; - int dx = Math.abs(cx - (loc.getBlockX() >> 4)); - int dz = Math.abs(cz - (loc.getBlockZ() >> 4)); - if ((dx > view) || (dz > view)) { - continue; - } - NetHandlerPlayServer con = nmsPlayer.playerNetServerHandler; - net.minecraft.world.chunk.Chunk nmsChunk = (net.minecraft.world.chunk.Chunk) chunk; - con.sendPacket(new S21PacketChunkData(nmsChunk, false, 65535)); - // Try sending true, 0 first - // Try bulk chunk packet - } + }, Settings.ASYNC_LIGHTING); } private int lcx = Integer.MIN_VALUE; @@ -256,12 +259,7 @@ public class SpongeQueue_1_8 extends SpongeQueue_0 { } } } - TaskManager.IMP.later(new Runnable() { - @Override - public void run() { - sendChunk(fs); - } - }, 1); + sendChunk(fs); return true; } @@ -350,7 +348,9 @@ public class SpongeQueue_1_8 extends SpongeQueue_0 { } return true; } catch (Throwable e) { - e.printStackTrace(); + if (Thread.currentThread() == Fawe.get().getMainThread()) { + e.printStackTrace(); + } } return false; } diff --git a/sponge/src/main/resources/config.yml b/sponge/src/main/resources/config.yml new file mode 100644 index 00000000..e69de29b