diff --git a/patches/api/Expose-codepoint-limit-in-YamlConfigOptions-and-incr.patch b/patches/api/Expose-codepoint-limit-in-YamlConfigOptions-and-incr.patch
index b37b835d4d..4a225d3c5c 100644
--- a/patches/api/Expose-codepoint-limit-in-YamlConfigOptions-and-incr.patch
+++ b/patches/api/Expose-codepoint-limit-in-YamlConfigOptions-and-incr.patch
@@ -25,7 +25,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  public class YamlConfigurationOptions extends FileConfigurationOptions {
      private int indent = 2;
      private int width = 80;
-+    private int codePointLimit = 64 * 1024 * 1024; // 64 MB // Paper
++    private int codePointLimit = Integer.MAX_VALUE; // Paper - use upstream's default from YamlConfiguration
  
      protected YamlConfigurationOptions(@NotNull YamlConfiguration configuration) {
          super(configuration);
diff --git a/patches/api/Remove-upstream-snakeyaml-fix.patch b/patches/api/Remove-upstream-snakeyaml-fix.patch
deleted file mode 100644
index b2bac83ef2..0000000000
--- a/patches/api/Remove-upstream-snakeyaml-fix.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <jake.m.potrebic@gmail.com>
-Date: Wed, 5 Jan 2022 12:12:58 -0800
-Subject: [PATCH] Remove upstream snakeyaml fix
-
-See Server Patch: Fix saving configs with more long comments
-
-diff --git a/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java b/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java
-+++ b/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java
-@@ -0,0 +0,0 @@ public class YamlConfiguration extends FileConfiguration {
-         yamlLoaderOptions = new LoaderOptions();
-         yamlLoaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE); // SPIGOT-5881: Not ideal, but was default pre SnakeYAML 1.26
- 
--        yaml = new BukkitYaml(constructor, representer, yamlDumperOptions, yamlLoaderOptions);
-+        yaml = new /*BukkitYaml*/Yaml(constructor, representer, yamlDumperOptions, yamlLoaderOptions); // Paper - don't use upstream BukkitYaml fix, add the whole snakeyaml Emitter class itself with the fix
-     }
- 
-     @NotNull
-diff --git a/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java b/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java
-+++ b/src/test/java/org/bukkit/configuration/file/YamlConfigurationTest.java
-@@ -0,0 +0,0 @@ public class YamlConfigurationTest extends FileConfigurationTest {
-     }
- 
-     @Test
-+    @org.junit.Ignore // Paper - ignore test because our fix doesn't work in testing environment
-     public void test100Comments() throws InvalidConfigurationException {
-         StringBuilder commentBuilder = new StringBuilder();
-         for (int i = 0; i < 100; i++) {
diff --git a/patches/server/Configurable-cactus-bamboo-and-reed-growth-heights.patch b/patches/server/Configurable-cactus-bamboo-and-reed-growth-heights.patch
index f224bf8445..4ce612fed5 100644
--- a/patches/server/Configurable-cactus-bamboo-and-reed-growth-heights.patch
+++ b/patches/server/Configurable-cactus-bamboo-and-reed-growth-heights.patch
@@ -11,7 +11,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/world/level/block/BambooBlock.java
 +++ b/src/main/java/net/minecraft/world/level/block/BambooBlock.java
 @@ -0,0 +0,0 @@ public class BambooBlock extends Block implements BonemealableBlock {
-             if (random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.bambooModifier) * 3)) == 0 && world.isEmptyBlock(pos.above()) && world.getRawBrightness(pos.above(), 0) >= 9) { // Spigot
+             if (random.nextFloat() < (world.spigotConfig.bambooModifier / (100.0f * 3)) && world.isEmptyBlock(pos.above()) && world.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution
                  int i = this.getHeightBelowUpToMax(world, pos) + 1;
  
 -                if (i < 16) {
@@ -76,7 +76,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (i < world.paperConfig().maxGrowthHeight.cactus) { // Paper - Configurable growth height
                  int j = (Integer) state.getValue(CactusBlock.AGE);
  
-                 if (j >= (byte) range(3, ((100.0F / world.spigotConfig.cactusModifier) * 15) + 0.5F, 15)) { // Spigot
+                 int modifier = world.spigotConfig.cactusModifier; // Spigot - SPIGOT-7159: Better modifier resolution
 diff --git a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java b/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/level/block/SugarCaneBlock.java
@@ -89,4 +89,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +            if (i < world.paperConfig().maxGrowthHeight.reeds) { // Paper - Configurable growth height
                  int j = (Integer) state.getValue(SugarCaneBlock.AGE);
  
-                 if (j >= (byte) range(3, ((100.0F / world.spigotConfig.caneModifier) * 15) + 0.5F, 15)) { // Spigot
+                 int modifier = world.spigotConfig.caneModifier; // Spigot - SPIGOT-7159: Better modifier resolution
diff --git a/patches/server/Fix-kelp-modifier-changing-growth-for-other-crops.patch b/patches/server/Fix-kelp-modifier-changing-growth-for-other-crops.patch
index 7252ad3746..150d4128ba 100644
--- a/patches/server/Fix-kelp-modifier-changing-growth-for-other-crops.patch
+++ b/patches/server/Fix-kelp-modifier-changing-growth-for-other-crops.patch
@@ -38,25 +38,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
 +++ b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
 @@ -0,0 +0,0 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements
- 
-     @Override
-     public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
--        if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < (100.0D / world.spigotConfig.kelpModifier) * this.growPerTickProbability) { // Spigot
-+        // Paper start
-+        final int modifier;
-+        if (state.is(Blocks.TWISTING_VINES) || state.is(Blocks.TWISTING_VINES_PLANT)) {
-+            modifier = world.spigotConfig.twistingVinesModifier;
-+        } else if (state.is(Blocks.WEEPING_VINES) || state.is(Blocks.WEEPING_VINES_PLANT)) {
-+            modifier = world.spigotConfig.weepingVinesModifier;
-+        } else if (state.is(Blocks.CAVE_VINES) || state.is(Blocks.CAVE_VINES_PLANT)) {
-+            modifier = world.spigotConfig.caveVinesModifier;
-+        } else if (state.is(Blocks.KELP) || state.is(Blocks.KELP_PLANT)) {
-+            modifier = world.spigotConfig.kelpModifier;
-+        } else {
-+            modifier = 100; // Above cases are exhaustive as of 1.18
-+        }
-+        if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < (modifier / 100.0D) * this.growPerTickProbability) { // Spigot // Paper - fix growth modifier having the reverse effect
-+            // Paper end
              BlockPos blockposition1 = pos.relative(this.growthDirection);
  
              if (this.canGrowInto(world.getBlockState(blockposition1))) {
@@ -81,28 +62,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
 +++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
 @@ -0,0 +0,0 @@ public class SpigotWorldConfig
-     public int bambooModifier;
-     public int sweetBerryModifier;
-     public int kelpModifier;
-+    // Paper start
-+    public int twistingVinesModifier;
-+    public int weepingVinesModifier;
-+    public int caveVinesModifier;
-+    public int glowBerryModifier;
-+    // Paper end
+     public int twistingVinesModifier;
+     public int weepingVinesModifier;
+     public int caveVinesModifier;
++    public int glowBerryModifier; // Paper
      private int getAndValidateGrowth(String crop)
      {
          int modifier = this.getInt( "growth." + crop.toLowerCase(java.util.Locale.ENGLISH) + "-modifier", 100 );
 @@ -0,0 +0,0 @@ public class SpigotWorldConfig
-         this.bambooModifier = this.getAndValidateGrowth( "Bamboo" );
-         this.sweetBerryModifier = this.getAndValidateGrowth( "SweetBerry" );
-         this.kelpModifier = this.getAndValidateGrowth( "Kelp" );
-+        // Paper start
-+        this.twistingVinesModifier = this.getAndValidateGrowth("TwistingVines");
-+        this.weepingVinesModifier = this.getAndValidateGrowth("WeepingVines");
-+        this.caveVinesModifier = this.getAndValidateGrowth("CaveVines");
-+        this.glowBerryModifier = this.getAndValidateGrowth("GlowBerry");
-+        // Paper end
+         this.twistingVinesModifier = this.getAndValidateGrowth( "TwistingVines" );
+         this.weepingVinesModifier = this.getAndValidateGrowth( "WeepingVines" );
+         this.caveVinesModifier = this.getAndValidateGrowth( "CaveVines" );
++        this.glowBerryModifier = this.getAndValidateGrowth("GlowBerry"); // Paper
      }
  
      public double itemMerge;
diff --git a/patches/server/Fix-saving-configs-with-more-long-comments.patch b/patches/server/Fix-saving-configs-with-more-long-comments.patch
deleted file mode 100644
index c11ff2631f..0000000000
--- a/patches/server/Fix-saving-configs-with-more-long-comments.patch
+++ /dev/null
@@ -1,1702 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <jake.m.potrebic@gmail.com>
-Date: Sat, 1 Jan 2022 21:24:50 -0800
-Subject: [PATCH] Fix saving configs with more long comments
-
-This is a bug with snakeyaml
-PR: https://bitbucket.org/snakeyaml/snakeyaml/pull-requests/3
-Issue: https://bitbucket.org/snakeyaml/snakeyaml/issues/518/comments-could-cause-queue-full
-
-Added the entire Emitter class from snakeyaml because
-dev-imports doesn't work with non-mojang-added dependencies
-
-Replacement for upstream: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/commits/a7505b3cd0498baca152777767f0e4ddebbe4d1a
-
-diff --git a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
---- /dev/null
-+++ b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
-@@ -0,0 +0,0 @@
-+/**
-+ * Copyright (c) 2008, SnakeYAML
-+ *
-+ * 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.emitter;
-+
-+import org.yaml.snakeyaml.DumperOptions;
-+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
-+import org.yaml.snakeyaml.DumperOptions.Version;
-+import org.yaml.snakeyaml.comments.CommentEventsCollector;
-+import org.yaml.snakeyaml.comments.CommentLine;
-+import org.yaml.snakeyaml.comments.CommentType;
-+import org.yaml.snakeyaml.error.YAMLException;
-+import org.yaml.snakeyaml.events.AliasEvent;
-+import org.yaml.snakeyaml.events.CollectionEndEvent;
-+import org.yaml.snakeyaml.events.CollectionStartEvent;
-+import org.yaml.snakeyaml.events.CommentEvent;
-+import org.yaml.snakeyaml.events.DocumentEndEvent;
-+import org.yaml.snakeyaml.events.DocumentStartEvent;
-+import org.yaml.snakeyaml.events.Event;
-+import org.yaml.snakeyaml.events.Event.ID;
-+import org.yaml.snakeyaml.events.MappingEndEvent;
-+import org.yaml.snakeyaml.events.MappingStartEvent;
-+import org.yaml.snakeyaml.events.NodeEvent;
-+import org.yaml.snakeyaml.events.ScalarEvent;
-+import org.yaml.snakeyaml.events.SequenceEndEvent;
-+import org.yaml.snakeyaml.events.SequenceStartEvent;
-+import org.yaml.snakeyaml.events.StreamEndEvent;
-+import org.yaml.snakeyaml.events.StreamStartEvent;
-+import org.yaml.snakeyaml.nodes.Tag;
-+import org.yaml.snakeyaml.reader.StreamReader;
-+import org.yaml.snakeyaml.scanner.Constant;
-+import org.yaml.snakeyaml.util.ArrayStack;
-+
-+import java.io.IOException;
-+import java.io.Writer;
-+import java.util.ArrayDeque;
-+import java.util.HashMap;
-+import java.util.HashSet;
-+import java.util.Iterator;
-+import java.util.LinkedHashMap;
-+import java.util.List;
-+import java.util.Map;
-+import java.util.Queue;
-+import java.util.Set;
-+import java.util.TreeSet;
-+import java.util.concurrent.ArrayBlockingQueue;
-+import java.util.regex.Matcher;
-+import java.util.regex.Pattern;
-+
-+/**
-+ * <pre>
-+ * Emitter expects events obeying the following grammar:
-+ * stream ::= STREAM-START document* STREAM-END
-+ * document ::= DOCUMENT-START node DOCUMENT-END
-+ * node ::= SCALAR | sequence | mapping
-+ * sequence ::= SEQUENCE-START node* SEQUENCE-END
-+ * mapping ::= MAPPING-START (node node)* MAPPING-END
-+ * </pre>
-+ */
-+public final class Emitter implements Emitable {
-+    public static final int MIN_INDENT = 1;
-+    public static final int MAX_INDENT = 10;
-+    private static final char[] SPACE = {' '};
-+
-+    private static final Pattern SPACES_PATTERN = Pattern.compile("\\s");
-+    private static final Set<Character> INVALID_ANCHOR = new HashSet();
-+    static {
-+        INVALID_ANCHOR.add('[');
-+        INVALID_ANCHOR.add(']');
-+        INVALID_ANCHOR.add('{');
-+        INVALID_ANCHOR.add('}');
-+        INVALID_ANCHOR.add(',');
-+        INVALID_ANCHOR.add('*');
-+        INVALID_ANCHOR.add('&');
-+    }
-+
-+    private static final Map<Character, String> ESCAPE_REPLACEMENTS = new HashMap<Character, String>();
-+    static {
-+        ESCAPE_REPLACEMENTS.put('\0', "0");
-+        ESCAPE_REPLACEMENTS.put('\u0007', "a");
-+        ESCAPE_REPLACEMENTS.put('\u0008', "b");
-+        ESCAPE_REPLACEMENTS.put('\u0009', "t");
-+        ESCAPE_REPLACEMENTS.put('\n', "n");
-+        ESCAPE_REPLACEMENTS.put('\u000B', "v");
-+        ESCAPE_REPLACEMENTS.put('\u000C', "f");
-+        ESCAPE_REPLACEMENTS.put('\r', "r");
-+        ESCAPE_REPLACEMENTS.put('\u001B', "e");
-+        ESCAPE_REPLACEMENTS.put('"', "\"");
-+        ESCAPE_REPLACEMENTS.put('\\', "\\");
-+        ESCAPE_REPLACEMENTS.put('\u0085', "N");
-+        ESCAPE_REPLACEMENTS.put('\u00A0', "_");
-+        ESCAPE_REPLACEMENTS.put('\u2028', "L");
-+        ESCAPE_REPLACEMENTS.put('\u2029', "P");
-+    }
-+
-+    private final static Map<String, String> DEFAULT_TAG_PREFIXES = new LinkedHashMap<String, String>();
-+    static {
-+        DEFAULT_TAG_PREFIXES.put("!", "!");
-+        DEFAULT_TAG_PREFIXES.put(Tag.PREFIX, "!!");
-+    }
-+    // The stream should have the methods `write` and possibly `flush`.
-+    private final Writer stream;
-+
-+    // Encoding is defined by Writer (cannot be overridden by STREAM-START.)
-+    // private Charset encoding;
-+
-+    // Emitter is a state machine with a stack of states to handle nested
-+    // structures.
-+    private final ArrayStack<EmitterState> states;
-+    private EmitterState state;
-+
-+    // Current event and the event queue.
-+    private final Queue<Event> events;
-+    private Event event;
-+
-+    // The current indentation level and the stack of previous indents.
-+    private final ArrayStack<Integer> indents;
-+    private Integer indent;
-+
-+    // Flow level.
-+    private int flowLevel;
-+
-+    // Contexts.
-+    private boolean rootContext;
-+    private boolean mappingContext;
-+    private boolean simpleKeyContext;
-+
-+    //
-+    // Characteristics of the last emitted character:
-+    // - current position.
-+    // - is it a whitespace?
-+    // - is it an indention character
-+    // (indentation space, '-', '?', or ':')?
-+    // private int line; this variable is not used
-+    private int column;
-+    private boolean whitespace;
-+    private boolean indention;
-+    private boolean openEnded;
-+
-+    // Formatting details.
-+    private final Boolean canonical;
-+    // pretty print flow by adding extra line breaks
-+    private final Boolean prettyFlow;
-+
-+    private final boolean allowUnicode;
-+    private int bestIndent;
-+    private final int indicatorIndent;
-+    private final boolean indentWithIndicator;
-+    private int bestWidth;
-+    private final char[] bestLineBreak;
-+    private final boolean splitLines;
-+    private final int maxSimpleKeyLength;
-+    private final boolean emitComments;
-+
-+    // Tag prefixes.
-+    private Map<String, String> tagPrefixes;
-+
-+    // Prepared anchor and tag.
-+    private String preparedAnchor;
-+    private String preparedTag;
-+
-+    // Scalar analysis and style.
-+    private ScalarAnalysis analysis;
-+    private DumperOptions.ScalarStyle style;
-+
-+    // Comment processing
-+    private final CommentEventsCollector blockCommentsCollector;
-+    private final CommentEventsCollector inlineCommentsCollector;
-+
-+
-+    public Emitter(Writer stream, DumperOptions opts) {
-+        // The stream should have the methods `write` and possibly `flush`.
-+        this.stream = stream;
-+        // Emitter is a state machine with a stack of states to handle nested
-+        // structures.
-+        this.states = new ArrayStack<EmitterState>(100);
-+        this.state = new ExpectStreamStart();
-+        // Current event and the event queue.
-+        this.events = new ArrayDeque<>(100); // Paper - allow more than 100 events (or comments)
-+        // this.events = new ArrayBlockingQueue<>(100);
-+        this.event = null;
-+        // The current indentation level and the stack of previous indents.
-+        this.indents = new ArrayStack<Integer>(10);
-+        this.indent = null;
-+        // Flow level.
-+        this.flowLevel = 0;
-+        // Contexts.
-+        mappingContext = false;
-+        simpleKeyContext = false;
-+
-+        //
-+        // Characteristics of the last emitted character:
-+        // - current position.
-+        // - is it a whitespace?
-+        // - is it an indention character
-+        // (indentation space, '-', '?', or ':')?
-+        column = 0;
-+        whitespace = true;
-+        indention = true;
-+
-+        // Whether the document requires an explicit document indicator
-+        openEnded = false;
-+
-+        // Formatting details.
-+        this.canonical = opts.isCanonical();
-+        this.prettyFlow = opts.isPrettyFlow();
-+        this.allowUnicode = opts.isAllowUnicode();
-+        this.bestIndent = 2;
-+        if ((opts.getIndent() > MIN_INDENT) && (opts.getIndent() < MAX_INDENT)) {
-+            this.bestIndent = opts.getIndent();
-+        }
-+        this.indicatorIndent = opts.getIndicatorIndent();
-+        this.indentWithIndicator = opts.getIndentWithIndicator();
-+        this.bestWidth = 80;
-+        if (opts.getWidth() > this.bestIndent * 2) {
-+            this.bestWidth = opts.getWidth();
-+        }
-+        this.bestLineBreak = opts.getLineBreak().getString().toCharArray();
-+        this.splitLines = opts.getSplitLines();
-+        this.maxSimpleKeyLength = opts.getMaxSimpleKeyLength();
-+        this.emitComments = opts.isProcessComments();
-+
-+        // Tag prefixes.
-+        this.tagPrefixes = new LinkedHashMap<String, String>();
-+
-+        // Prepared anchor and tag.
-+        this.preparedAnchor = null;
-+        this.preparedTag = null;
-+
-+        // Scalar analysis and style.
-+        this.analysis = null;
-+        this.style = null;
-+
-+        // Comment processing
-+        this.blockCommentsCollector = new CommentEventsCollector(events,
-+            CommentType.BLANK_LINE, CommentType.BLOCK);
-+        this.inlineCommentsCollector = new CommentEventsCollector(events,
-+            CommentType.IN_LINE);
-+    }
-+
-+    public void emit(Event event) throws IOException {
-+        this.events.add(event);
-+        while (!needMoreEvents()) {
-+            this.event = this.events.poll();
-+            this.state.expect();
-+            this.event = null;
-+        }
-+    }
-+
-+    // In some cases, we wait for a few next events before emitting.
-+
-+    private boolean needMoreEvents() {
-+        if (events.isEmpty()) {
-+            return true;
-+        }
-+
-+        Iterator<Event> iter = events.iterator();
-+        Event event = iter.next(); // FIXME why without check ???
-+        while(event instanceof CommentEvent) {
-+            if (!iter.hasNext()) {
-+                return true;
-+            }
-+            event = iter.next();
-+        }
-+
-+        if (event instanceof DocumentStartEvent) {
-+            return needEvents(iter, 1);
-+        } else if (event instanceof SequenceStartEvent) {
-+            return needEvents(iter, 2);
-+        } else if (event instanceof MappingStartEvent) {
-+            return needEvents(iter, 3);
-+        } else if (event instanceof StreamStartEvent) {
-+            return needEvents(iter, 2);
-+        } else if (event instanceof StreamEndEvent) {
-+            return false;
-+        } else if (emitComments) {
-+            return needEvents(iter, 1);
-+        }
-+        return false;
-+    }
-+
-+    private boolean needEvents(Iterator<Event> iter, int count) {
-+        int level = 0;
-+        int actualCount = 0;
-+        while (iter.hasNext()) {
-+            Event event = iter.next();
-+            if (event instanceof CommentEvent) {
-+                continue;
-+            }
-+            actualCount++;
-+            if (event instanceof DocumentStartEvent || event instanceof CollectionStartEvent) {
-+                level++;
-+            } else if (event instanceof DocumentEndEvent || event instanceof CollectionEndEvent) {
-+                level--;
-+            } else if (event instanceof StreamEndEvent) {
-+                level = -1;
-+            }
-+            if (level < 0) {
-+                return false;
-+            }
-+        }
-+        return actualCount < count;
-+    }
-+
-+    private void increaseIndent(boolean flow, boolean indentless) {
-+        indents.push(indent);
-+        if (indent == null) {
-+            if (flow) {
-+                indent = bestIndent;
-+            } else {
-+                indent = 0;
-+            }
-+        } else if (!indentless) {
-+            this.indent += bestIndent;
-+        }
-+    }
-+
-+    // States
-+
-+    // Stream handlers.
-+
-+    private class ExpectStreamStart implements EmitterState {
-+        public void expect() throws IOException {
-+            if (event instanceof StreamStartEvent) {
-+                writeStreamStart();
-+                state = new ExpectFirstDocumentStart();
-+            } else {
-+                throw new EmitterException("expected StreamStartEvent, but got " + event);
-+            }
-+        }
-+    }
-+
-+    private class ExpectNothing implements EmitterState {
-+        public void expect() throws IOException {
-+            throw new EmitterException("expecting nothing, but got " + event);
-+        }
-+    }
-+
-+    // Document handlers.
-+
-+    private class ExpectFirstDocumentStart implements EmitterState {
-+        public void expect() throws IOException {
-+            new ExpectDocumentStart(true).expect();
-+        }
-+    }
-+
-+    private class ExpectDocumentStart implements EmitterState {
-+        private final boolean first;
-+
-+        public ExpectDocumentStart(boolean first) {
-+            this.first = first;
-+        }
-+
-+        public void expect() throws IOException {
-+            if (event instanceof DocumentStartEvent) {
-+                DocumentStartEvent ev = (DocumentStartEvent) event;
-+                if ((ev.getVersion() != null || ev.getTags() != null) && openEnded) {
-+                    writeIndicator("...", true, false, false);
-+                    writeIndent();
-+                }
-+                if (ev.getVersion() != null) {
-+                    String versionText = prepareVersion(ev.getVersion());
-+                    writeVersionDirective(versionText);
-+                }
-+                tagPrefixes = new LinkedHashMap<String, String>(DEFAULT_TAG_PREFIXES);
-+                if (ev.getTags() != null) {
-+                    Set<String> handles = new TreeSet<String>(ev.getTags().keySet());
-+                    for (String handle : handles) {
-+                        String prefix = ev.getTags().get(handle);
-+                        tagPrefixes.put(prefix, handle);
-+                        String handleText = prepareTagHandle(handle);
-+                        String prefixText = prepareTagPrefix(prefix);
-+                        writeTagDirective(handleText, prefixText);
-+                    }
-+                }
-+                boolean implicit = first && !ev.getExplicit() && !canonical
-+                    && ev.getVersion() == null
-+                    && (ev.getTags() == null || ev.getTags().isEmpty())
-+                    && !checkEmptyDocument();
-+                if (!implicit) {
-+                    writeIndent();
-+                    writeIndicator("---", true, false, false);
-+                    if (canonical) {
-+                        writeIndent();
-+                    }
-+                }
-+                state = new ExpectDocumentRoot();
-+            } else if (event instanceof StreamEndEvent) {
-+                writeStreamEnd();
-+                state = new ExpectNothing();
-+            } else if (event instanceof CommentEvent) {
-+                blockCommentsCollector.collectEvents(event);
-+                writeBlockComment();
-+                // state = state; remains unchanged
-+            } else {
-+                throw new EmitterException("expected DocumentStartEvent, but got " + event);
-+            }
-+        }
-+    }
-+
-+    private class ExpectDocumentEnd implements EmitterState {
-+        public void expect() throws IOException {
-+            event = blockCommentsCollector.collectEventsAndPoll(event);
-+            writeBlockComment();
-+            if (event instanceof DocumentEndEvent) {
-+                writeIndent();
-+                if (((DocumentEndEvent) event).getExplicit()) {
-+                    writeIndicator("...", true, false, false);
-+                    writeIndent();
-+                }
-+                flushStream();
-+                state = new ExpectDocumentStart(false);
-+            } else {
-+                throw new EmitterException("expected DocumentEndEvent, but got " + event);
-+            }
-+        }
-+    }
-+
-+    private class ExpectDocumentRoot implements EmitterState {
-+        public void expect() throws IOException {
-+            event = blockCommentsCollector.collectEventsAndPoll(event);
-+            if (!blockCommentsCollector.isEmpty()) {
-+                writeBlockComment();
-+                if (event instanceof DocumentEndEvent) {
-+                    new ExpectDocumentEnd().expect();
-+                    return;
-+                }
-+            }
-+            states.push(new ExpectDocumentEnd());
-+            expectNode(true, false, false);
-+        }
-+    }
-+
-+    // Node handlers.
-+
-+    private void expectNode(boolean root, boolean mapping, boolean simpleKey) throws IOException {
-+        rootContext = root;
-+        mappingContext = mapping;
-+        simpleKeyContext = simpleKey;
-+        if (event instanceof AliasEvent) {
-+            expectAlias();
-+        } else if (event instanceof ScalarEvent || event instanceof CollectionStartEvent) {
-+            processAnchor("&");
-+            processTag();
-+            if (event instanceof ScalarEvent) {
-+                expectScalar();
-+            } else if (event instanceof SequenceStartEvent) {
-+                if (flowLevel != 0 || canonical || ((SequenceStartEvent) event).isFlow()
-+                    || checkEmptySequence()) {
-+                    expectFlowSequence();
-+                } else {
-+                    expectBlockSequence();
-+                }
-+            } else {// MappingStartEvent
-+                if (flowLevel != 0 || canonical || ((MappingStartEvent) event).isFlow()
-+                    || checkEmptyMapping()) {
-+                    expectFlowMapping();
-+                } else {
-+                    expectBlockMapping();
-+                }
-+            }
-+        } else {
-+            throw new EmitterException("expected NodeEvent, but got " + event);
-+        }
-+    }
-+
-+    private void expectAlias() throws IOException {
-+        if (!(event instanceof AliasEvent)) {
-+            throw new EmitterException("Alias must be provided");
-+        }
-+        processAnchor("*");
-+        state = states.pop();
-+    }
-+
-+    private void expectScalar() throws IOException {
-+        increaseIndent(true, false);
-+        processScalar();
-+        indent = indents.pop();
-+        state = states.pop();
-+    }
-+
-+    // Flow sequence handlers.
-+
-+    private void expectFlowSequence() throws IOException {
-+        writeIndicator("[", true, true, false);
-+        flowLevel++;
-+        increaseIndent(true, false);
-+        if (prettyFlow) {
-+            writeIndent();
-+        }
-+        state = new ExpectFirstFlowSequenceItem();
-+    }
-+
-+    private class ExpectFirstFlowSequenceItem implements EmitterState {
-+        public void expect() throws IOException {
-+            if (event instanceof SequenceEndEvent) {
-+                indent = indents.pop();
-+                flowLevel--;
-+                writeIndicator("]", false, false, false);
-+                inlineCommentsCollector.collectEvents();
-+                writeInlineComments();
-+                state = states.pop();
-+            } else if (event instanceof CommentEvent) {
-+                blockCommentsCollector.collectEvents(event);
-+                writeBlockComment();
-+            } else {
-+                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
-+                    writeIndent();
-+                }
-+                states.push(new ExpectFlowSequenceItem());
-+                expectNode(false, false, false);
-+                event = inlineCommentsCollector.collectEvents(event);
-+                writeInlineComments();
-+            }
-+        }
-+    }
-+
-+    private class ExpectFlowSequenceItem implements EmitterState {
-+        public void expect() throws IOException {
-+            if (event instanceof SequenceEndEvent) {
-+                indent = indents.pop();
-+                flowLevel--;
-+                if (canonical) {
-+                    writeIndicator(",", false, false, false);
-+                    writeIndent();
-+                } else if (prettyFlow) {
-+                    writeIndent();
-+                }
-+                writeIndicator("]", false, false, false);
-+                inlineCommentsCollector.collectEvents();
-+                writeInlineComments();
-+                if (prettyFlow) {
-+                    writeIndent();
-+                }
-+                state = states.pop();
-+            } else if (event instanceof CommentEvent) {
-+                event = blockCommentsCollector.collectEvents(event);
-+            } else {
-+                writeIndicator(",", false, false, false);
-+                writeBlockComment();
-+                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
-+                    writeIndent();
-+                }
-+                states.push(new ExpectFlowSequenceItem());
-+                expectNode(false, false, false);
-+                event = inlineCommentsCollector.collectEvents(event);
-+                writeInlineComments();
-+            }
-+        }
-+    }
-+
-+    // Flow mapping handlers.
-+
-+    private void expectFlowMapping() throws IOException {
-+        writeIndicator("{", true, true, false);
-+        flowLevel++;
-+        increaseIndent(true, false);
-+        if (prettyFlow) {
-+            writeIndent();
-+        }
-+        state = new ExpectFirstFlowMappingKey();
-+    }
-+
-+    private class ExpectFirstFlowMappingKey implements EmitterState {
-+        public void expect() throws IOException {
-+            event = blockCommentsCollector.collectEventsAndPoll(event);
-+            writeBlockComment();
-+            if (event instanceof MappingEndEvent) {
-+                indent = indents.pop();
-+                flowLevel--;
-+                writeIndicator("}", false, false, false);
-+                inlineCommentsCollector.collectEvents();
-+                writeInlineComments();
-+                state = states.pop();
-+            } else {
-+                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
-+                    writeIndent();
-+                }
-+                if (!canonical && checkSimpleKey()) {
-+                    states.push(new ExpectFlowMappingSimpleValue());
-+                    expectNode(false, true, true);
-+                } else {
-+                    writeIndicator("?", true, false, false);
-+                    states.push(new ExpectFlowMappingValue());
-+                    expectNode(false, true, false);
-+                }
-+            }
-+        }
-+    }
-+
-+    private class ExpectFlowMappingKey implements EmitterState {
-+        public void expect() throws IOException {
-+            if (event instanceof MappingEndEvent) {
-+                indent = indents.pop();
-+                flowLevel--;
-+                if (canonical) {
-+                    writeIndicator(",", false, false, false);
-+                    writeIndent();
-+                }
-+                if (prettyFlow) {
-+                    writeIndent();
-+                }
-+                writeIndicator("}", false, false, false);
-+                inlineCommentsCollector.collectEvents();
-+                writeInlineComments();
-+                state = states.pop();
-+            } else {
-+                writeIndicator(",", false, false, false);
-+                event = blockCommentsCollector.collectEventsAndPoll(event);
-+                writeBlockComment();
-+                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
-+                    writeIndent();
-+                }
-+                if (!canonical && checkSimpleKey()) {
-+                    states.push(new ExpectFlowMappingSimpleValue());
-+                    expectNode(false, true, true);
-+                } else {
-+                    writeIndicator("?", true, false, false);
-+                    states.push(new ExpectFlowMappingValue());
-+                    expectNode(false, true, false);
-+                }
-+            }
-+        }
-+    }
-+
-+    private class ExpectFlowMappingSimpleValue implements EmitterState {
-+        public void expect() throws IOException {
-+            writeIndicator(":", false, false, false);
-+            event = inlineCommentsCollector.collectEventsAndPoll(event);
-+            writeInlineComments();
-+            states.push(new ExpectFlowMappingKey());
-+            expectNode(false, true, false);
-+            inlineCommentsCollector.collectEvents(event);
-+            writeInlineComments();
-+        }
-+    }
-+
-+    private class ExpectFlowMappingValue implements EmitterState {
-+        public void expect() throws IOException {
-+            if (canonical || (column > bestWidth) || prettyFlow) {
-+                writeIndent();
-+            }
-+            writeIndicator(":", true, false, false);
-+            event = inlineCommentsCollector.collectEventsAndPoll(event);
-+            writeInlineComments();
-+            states.push(new ExpectFlowMappingKey());
-+            expectNode(false, true, false);
-+            inlineCommentsCollector.collectEvents(event);
-+            writeInlineComments();
-+        }
-+    }
-+
-+    // Block sequence handlers.
-+
-+    private void expectBlockSequence() throws IOException {
-+        boolean indentless = mappingContext && !indention;
-+        increaseIndent(false, indentless);
-+        state = new ExpectFirstBlockSequenceItem();
-+    }
-+
-+    private class ExpectFirstBlockSequenceItem implements EmitterState {
-+        public void expect() throws IOException {
-+            new ExpectBlockSequenceItem(true).expect();
-+        }
-+    }
-+
-+    private class ExpectBlockSequenceItem implements EmitterState {
-+        private final boolean first;
-+
-+        public ExpectBlockSequenceItem(boolean first) {
-+            this.first = first;
-+        }
-+
-+        public void expect() throws IOException {
-+            if (!this.first && event instanceof SequenceEndEvent) {
-+                indent = indents.pop();
-+                state = states.pop();
-+            } else if( event instanceof CommentEvent) {
-+                blockCommentsCollector.collectEvents(event);
-+            } else {
-+                writeIndent();
-+                if (!indentWithIndicator || this.first) {
-+                    writeWhitespace(indicatorIndent);
-+                }
-+                writeIndicator("-", true, false, true);
-+                if (indentWithIndicator && this.first) {
-+                    indent += indicatorIndent;
-+                }
-+                if (!blockCommentsCollector.isEmpty()) {
-+                    increaseIndent(false, false);
-+                    writeBlockComment();
-+                    if(event instanceof ScalarEvent) {
-+                        analysis = analyzeScalar(((ScalarEvent)event).getValue());
-+                        if (!analysis.isEmpty()) {
-+                            writeIndent();
-+                        }
-+                    }
-+                    indent = indents.pop();
-+                }
-+                states.push(new ExpectBlockSequenceItem(false));
-+                expectNode(false, false, false);
-+                inlineCommentsCollector.collectEvents();
-+                writeInlineComments();
-+            }
-+        }
-+    }
-+
-+    // Block mapping handlers.
-+    private void expectBlockMapping() throws IOException {
-+        increaseIndent(false, false);
-+        state = new ExpectFirstBlockMappingKey();
-+    }
-+
-+    private class ExpectFirstBlockMappingKey implements EmitterState {
-+        public void expect() throws IOException {
-+            new ExpectBlockMappingKey(true).expect();
-+        }
-+    }
-+
-+    private class ExpectBlockMappingKey implements EmitterState {
-+        private final boolean first;
-+
-+        public ExpectBlockMappingKey(boolean first) {
-+            this.first = first;
-+        }
-+
-+        public void expect() throws IOException {
-+            event = blockCommentsCollector.collectEventsAndPoll(event);
-+            writeBlockComment();
-+            if (!this.first && event instanceof MappingEndEvent) {
-+                indent = indents.pop();
-+                state = states.pop();
-+            } else {
-+                writeIndent();
-+                if (checkSimpleKey()) {
-+                    states.push(new ExpectBlockMappingSimpleValue());
-+                    expectNode(false, true, true);
-+                } else {
-+                    writeIndicator("?", true, false, true);
-+                    states.push(new ExpectBlockMappingValue());
-+                    expectNode(false, true, false);
-+                }
-+            }
-+        }
-+    }
-+
-+    private boolean isFoldedOrLiteral(Event event) {
-+        if(!event.is(ID.Scalar)) {
-+            return false;
-+        }
-+        ScalarEvent scalarEvent = (ScalarEvent) event;
-+        ScalarStyle style = scalarEvent.getScalarStyle();
-+        return style == ScalarStyle.FOLDED || style == ScalarStyle.LITERAL;
-+    }
-+
-+    private class ExpectBlockMappingSimpleValue implements EmitterState {
-+        public void expect() throws IOException {
-+            writeIndicator(":", false, false, false);
-+            event = inlineCommentsCollector.collectEventsAndPoll(event);
-+            if(!isFoldedOrLiteral(event)) {
-+                if(writeInlineComments()) {
-+                    increaseIndent(true, false);
-+                    writeIndent();
-+                    indent = indents.pop();
-+                }
-+            }
-+            event = blockCommentsCollector.collectEventsAndPoll(event);
-+            if(!blockCommentsCollector.isEmpty()) {
-+                increaseIndent(true, false);
-+                writeBlockComment();
-+                writeIndent();
-+                indent = indents.pop();
-+            }
-+            states.push(new ExpectBlockMappingKey(false));
-+            expectNode(false, true, false);
-+            inlineCommentsCollector.collectEvents();
-+            writeInlineComments();
-+        }
-+    }
-+
-+    private class ExpectBlockMappingValue implements EmitterState {
-+        public void expect() throws IOException {
-+            writeIndent();
-+            writeIndicator(":", true, false, true);
-+            event = inlineCommentsCollector.collectEventsAndPoll(event);
-+            writeInlineComments();
-+            event = blockCommentsCollector.collectEventsAndPoll(event);
-+            writeBlockComment();
-+            states.push(new ExpectBlockMappingKey(false));
-+            expectNode(false, true, false);
-+            inlineCommentsCollector.collectEvents(event);
-+            writeInlineComments();
-+        }
-+    }
-+
-+    // Checkers.
-+
-+    private boolean checkEmptySequence() {
-+        return event instanceof SequenceStartEvent && !events.isEmpty() && events.peek() instanceof SequenceEndEvent;
-+    }
-+
-+    private boolean checkEmptyMapping() {
-+        return event instanceof MappingStartEvent && !events.isEmpty() && events.peek() instanceof MappingEndEvent;
-+    }
-+
-+    private boolean checkEmptyDocument() {
-+        if (!(event instanceof DocumentStartEvent) || events.isEmpty()) {
-+            return false;
-+        }
-+        Event event = events.peek();
-+        if (event instanceof ScalarEvent) {
-+            ScalarEvent e = (ScalarEvent) event;
-+            return e.getAnchor() == null && e.getTag() == null && e.getImplicit() != null && e
-+                .getValue().length() == 0;
-+        }
-+        return false;
-+    }
-+
-+    private boolean checkSimpleKey() {
-+        int length = 0;
-+        if (event instanceof NodeEvent && ((NodeEvent) event).getAnchor() != null) {
-+            if (preparedAnchor == null) {
-+                preparedAnchor = prepareAnchor(((NodeEvent) event).getAnchor());
-+            }
-+            length += preparedAnchor.length();
-+        }
-+        String tag = null;
-+        if (event instanceof ScalarEvent) {
-+            tag = ((ScalarEvent) event).getTag();
-+        } else if (event instanceof CollectionStartEvent) {
-+            tag = ((CollectionStartEvent) event).getTag();
-+        }
-+        if (tag != null) {
-+            if (preparedTag == null) {
-+                preparedTag = prepareTag(tag);
-+            }
-+            length += preparedTag.length();
-+        }
-+        if (event instanceof ScalarEvent) {
-+            if (analysis == null) {
-+                analysis = analyzeScalar(((ScalarEvent) event).getValue());
-+            }
-+            length += analysis.getScalar().length();
-+        }
-+        return length < maxSimpleKeyLength && (event instanceof AliasEvent
-+            || (event instanceof ScalarEvent && !analysis.isEmpty() && !analysis.isMultiline())
-+            || checkEmptySequence() || checkEmptyMapping());
-+    }
-+
-+    // Anchor, Tag, and Scalar processors.
-+
-+    private void processAnchor(String indicator) throws IOException {
-+        NodeEvent ev = (NodeEvent) event;
-+        if (ev.getAnchor() == null) {
-+            preparedAnchor = null;
-+            return;
-+        }
-+        if (preparedAnchor == null) {
-+            preparedAnchor = prepareAnchor(ev.getAnchor());
-+        }
-+        writeIndicator(indicator + preparedAnchor, true, false, false);
-+        preparedAnchor = null;
-+    }
-+
-+    private void processTag() throws IOException {
-+        String tag = null;
-+        if (event instanceof ScalarEvent) {
-+            ScalarEvent ev = (ScalarEvent) event;
-+            tag = ev.getTag();
-+            if (style == null) {
-+                style = chooseScalarStyle();
-+            }
-+            if ((!canonical || tag == null) && ((style == null && ev.getImplicit()
-+                .canOmitTagInPlainScalar()) || (style != null && ev.getImplicit()
-+                .canOmitTagInNonPlainScalar()))) {
-+                preparedTag = null;
-+                return;
-+            }
-+            if (ev.getImplicit().canOmitTagInPlainScalar() && tag == null) {
-+                tag = "!";
-+                preparedTag = null;
-+            }
-+        } else {
-+            CollectionStartEvent ev = (CollectionStartEvent) event;
-+            tag = ev.getTag();
-+            if ((!canonical || tag == null) && ev.getImplicit()) {
-+                preparedTag = null;
-+                return;
-+            }
-+        }
-+        if (tag == null) {
-+            throw new EmitterException("tag is not specified");
-+        }
-+        if (preparedTag == null) {
-+            preparedTag = prepareTag(tag);
-+        }
-+        writeIndicator(preparedTag, true, false, false);
-+        preparedTag = null;
-+    }
-+
-+    private DumperOptions.ScalarStyle chooseScalarStyle() {
-+        ScalarEvent ev = (ScalarEvent) event;
-+        if (analysis == null) {
-+            analysis = analyzeScalar(ev.getValue());
-+        }
-+        if (!ev.isPlain() && ev.getScalarStyle() == DumperOptions.ScalarStyle.DOUBLE_QUOTED || this.canonical) {
-+            return DumperOptions.ScalarStyle.DOUBLE_QUOTED;
-+        }
-+        if (ev.isPlain() && ev.getImplicit().canOmitTagInPlainScalar()) {
-+            if (!(simpleKeyContext && (analysis.isEmpty() || analysis.isMultiline()))
-+                && ((flowLevel != 0 && analysis.isAllowFlowPlain()) || (flowLevel == 0 && analysis.isAllowBlockPlain()))) {
-+                return null;
-+            }
-+        }
-+        if (!ev.isPlain() && (ev.getScalarStyle() == DumperOptions.ScalarStyle.LITERAL || ev.getScalarStyle() == DumperOptions.ScalarStyle.FOLDED)) {
-+            if (flowLevel == 0 && !simpleKeyContext && analysis.isAllowBlock()) {
-+                return ev.getScalarStyle();
-+            }
-+        }
-+        if (ev.isPlain() || ev.getScalarStyle() == DumperOptions.ScalarStyle.SINGLE_QUOTED) {
-+            if (analysis.isAllowSingleQuoted() && !(simpleKeyContext && analysis.isMultiline())) {
-+                return DumperOptions.ScalarStyle.SINGLE_QUOTED;
-+            }
-+        }
-+        return DumperOptions.ScalarStyle.DOUBLE_QUOTED;
-+    }
-+
-+    private void processScalar() throws IOException {
-+        ScalarEvent ev = (ScalarEvent) event;
-+        if (analysis == null) {
-+            analysis = analyzeScalar(ev.getValue());
-+        }
-+        if (style == null) {
-+            style = chooseScalarStyle();
-+        }
-+        boolean split = !simpleKeyContext && splitLines;
-+        if (style == null) {
-+            writePlain(analysis.getScalar(), split);
-+        } else {
-+            switch (style) {
-+                case DOUBLE_QUOTED:
-+                    writeDoubleQuoted(analysis.getScalar(), split);
-+                    break;
-+                case SINGLE_QUOTED:
-+                    writeSingleQuoted(analysis.getScalar(), split);
-+                    break;
-+                case FOLDED:
-+                    writeFolded(analysis.getScalar(), split);
-+                    break;
-+                case LITERAL:
-+                    writeLiteral(analysis.getScalar());
-+                    break;
-+                default:
-+                    throw new YAMLException("Unexpected style: " + style);
-+            }
-+        }
-+        analysis = null;
-+        style = null;
-+    }
-+
-+    // Analyzers.
-+
-+    private String prepareVersion(Version version) {
-+        if (version.major() != 1) {
-+            throw new EmitterException("unsupported YAML version: " + version);
-+        }
-+        return version.getRepresentation();
-+    }
-+
-+    private final static Pattern HANDLE_FORMAT = Pattern.compile("^![-_\\w]*!$");
-+
-+    private String prepareTagHandle(String handle) {
-+        if (handle.length() == 0) {
-+            throw new EmitterException("tag handle must not be empty");
-+        } else if (handle.charAt(0) != '!' || handle.charAt(handle.length() - 1) != '!') {
-+            throw new EmitterException("tag handle must start and end with '!': " + handle);
-+        } else if (!"!".equals(handle) && !HANDLE_FORMAT.matcher(handle).matches()) {
-+            throw new EmitterException("invalid character in the tag handle: " + handle);
-+        }
-+        return handle;
-+    }
-+
-+    private String prepareTagPrefix(String prefix) {
-+        if (prefix.length() == 0) {
-+            throw new EmitterException("tag prefix must not be empty");
-+        }
-+        StringBuilder chunks = new StringBuilder();
-+        int start = 0;
-+        int end = 0;
-+        if (prefix.charAt(0) == '!') {
-+            end = 1;
-+        }
-+        while (end < prefix.length()) {
-+            end++;
-+        }
-+        if (start < end) {
-+            chunks.append(prefix, start, end);
-+        }
-+        return chunks.toString();
-+    }
-+
-+    private String prepareTag(String tag) {
-+        if (tag.length() == 0) {
-+            throw new EmitterException("tag must not be empty");
-+        }
-+        if ("!".equals(tag)) {
-+            return tag;
-+        }
-+        String handle = null;
-+        String suffix = tag;
-+        // shall the tag prefixes be sorted as in PyYAML?
-+        for (String prefix : tagPrefixes.keySet()) {
-+            if (tag.startsWith(prefix) && ("!".equals(prefix) || prefix.length() < tag.length())) {
-+                handle = prefix;
-+            }
-+        }
-+        if (handle != null) {
-+            suffix = tag.substring(handle.length());
-+            handle = tagPrefixes.get(handle);
-+        }
-+
-+        int end = suffix.length();
-+        String suffixText = end > 0 ? suffix.substring(0, end) : "";
-+
-+        if (handle != null) {
-+            return handle + suffixText;
-+        }
-+        return "!<" + suffixText + ">";
-+    }
-+
-+    static String prepareAnchor(String anchor) {
-+        if (anchor.length() == 0) {
-+            throw new EmitterException("anchor must not be empty");
-+        }
-+        for (Character invalid : INVALID_ANCHOR) {
-+            if (anchor.indexOf(invalid) > -1) {
-+                throw new EmitterException("Invalid character '" + invalid + "' in the anchor: " + anchor);
-+            }
-+        }
-+        Matcher matcher = SPACES_PATTERN.matcher(anchor);
-+        if (matcher.find()) {
-+            throw new EmitterException("Anchor may not contain spaces: " + anchor);
-+        }
-+        return anchor;
-+    }
-+
-+    private ScalarAnalysis analyzeScalar(String scalar) {
-+        // Empty scalar is a special case.
-+        if (scalar.length() == 0) {
-+            return new ScalarAnalysis(scalar, true, false, false, true, true, false);
-+        }
-+        // Indicators and special characters.
-+        boolean blockIndicators = false;
-+        boolean flowIndicators = false;
-+        boolean lineBreaks = false;
-+        boolean specialCharacters = false;
-+
-+        // Important whitespace combinations.
-+        boolean leadingSpace = false;
-+        boolean leadingBreak = false;
-+        boolean trailingSpace = false;
-+        boolean trailingBreak = false;
-+        boolean breakSpace = false;
-+        boolean spaceBreak = false;
-+
-+        // Check document indicators.
-+        if (scalar.startsWith("---") || scalar.startsWith("...")) {
-+            blockIndicators = true;
-+            flowIndicators = true;
-+        }
-+        // First character or preceded by a whitespace.
-+        boolean preceededByWhitespace = true;
-+        boolean followedByWhitespace = scalar.length() == 1 || Constant.NULL_BL_T_LINEBR.has(scalar.codePointAt(1));
-+        // The previous character is a space.
-+        boolean previousSpace = false;
-+
-+        // The previous character is a break.
-+        boolean previousBreak = false;
-+
-+        int index = 0;
-+
-+        while (index < scalar.length()) {
-+            int c = scalar.codePointAt(index);
-+            // Check for indicators.
-+            if (index == 0) {
-+                // Leading indicators are special characters.
-+                if ("#,[]{}&*!|>'\"%@`".indexOf(c) != -1) {
-+                    flowIndicators = true;
-+                    blockIndicators = true;
-+                }
-+                if (c == '?' || c == ':') {
-+                    flowIndicators = true;
-+                    if (followedByWhitespace) {
-+                        blockIndicators = true;
-+                    }
-+                }
-+                if (c == '-' && followedByWhitespace) {
-+                    flowIndicators = true;
-+                    blockIndicators = true;
-+                }
-+            } else {
-+                // Some indicators cannot appear within a scalar as well.
-+                if (",?[]{}".indexOf(c) != -1) {
-+                    flowIndicators = true;
-+                }
-+                if (c == ':') {
-+                    flowIndicators = true;
-+                    if (followedByWhitespace) {
-+                        blockIndicators = true;
-+                    }
-+                }
-+                if (c == '#' && preceededByWhitespace) {
-+                    flowIndicators = true;
-+                    blockIndicators = true;
-+                }
-+            }
-+            // Check for line breaks, special, and unicode characters.
-+            boolean isLineBreak = Constant.LINEBR.has(c);
-+            if (isLineBreak) {
-+                lineBreaks = true;
-+            }
-+            if (!(c == '\n' || (0x20 <= c && c <= 0x7E))) {
-+                if (c == 0x85 || (c >= 0xA0 && c <= 0xD7FF)
-+                    || (c >= 0xE000 && c <= 0xFFFD)
-+                    || (c >= 0x10000 && c <= 0x10FFFF)) {
-+                    // unicode is used
-+                    if (!this.allowUnicode) {
-+                        specialCharacters = true;
-+                    }
-+                } else {
-+                    specialCharacters = true;
-+                }
-+            }
-+            // Detect important whitespace combinations.
-+            if (c == ' ') {
-+                if (index == 0) {
-+                    leadingSpace = true;
-+                }
-+                if (index == scalar.length() - 1) {
-+                    trailingSpace = true;
-+                }
-+                if (previousBreak) {
-+                    breakSpace = true;
-+                }
-+                previousSpace = true;
-+                previousBreak = false;
-+            } else if (isLineBreak) {
-+                if (index == 0) {
-+                    leadingBreak = true;
-+                }
-+                if (index == scalar.length() - 1) {
-+                    trailingBreak = true;
-+                }
-+                if (previousSpace) {
-+                    spaceBreak = true;
-+                }
-+                previousSpace = false;
-+                previousBreak = true;
-+            } else {
-+                previousSpace = false;
-+                previousBreak = false;
-+            }
-+
-+            // Prepare for the next character.
-+            index += Character.charCount(c);
-+            preceededByWhitespace = Constant.NULL_BL_T.has(c) || isLineBreak;
-+            followedByWhitespace = true;
-+            if (index + 1 < scalar.length()) {
-+                int nextIndex = index + Character.charCount(scalar.codePointAt(index));
-+                if (nextIndex < scalar.length()) {
-+                    followedByWhitespace = (Constant.NULL_BL_T.has(scalar.codePointAt(nextIndex))) || isLineBreak;
-+                }
-+            }
-+        }
-+        // Let's decide what styles are allowed.
-+        boolean allowFlowPlain = true;
-+        boolean allowBlockPlain = true;
-+        boolean allowSingleQuoted = true;
-+        boolean allowBlock = true;
-+        // Leading and trailing whitespaces are bad for plain scalars.
-+        if (leadingSpace || leadingBreak || trailingSpace || trailingBreak) {
-+            allowFlowPlain = allowBlockPlain = false;
-+        }
-+        // We do not permit trailing spaces for block scalars.
-+        if (trailingSpace) {
-+            allowBlock = false;
-+        }
-+        // Spaces at the beginning of a new line are only acceptable for block
-+        // scalars.
-+        if (breakSpace) {
-+            allowFlowPlain = allowBlockPlain = allowSingleQuoted = false;
-+        }
-+        // Spaces followed by breaks, as well as special character are only
-+        // allowed for double quoted scalars.
-+        if (spaceBreak || specialCharacters) {
-+            allowFlowPlain = allowBlockPlain = allowSingleQuoted = allowBlock = false;
-+        }
-+        // Although the plain scalar writer supports breaks, we never emit
-+        // multiline plain scalars in the flow context.
-+        if (lineBreaks) {
-+            allowFlowPlain = false;
-+        }
-+        // Flow indicators are forbidden for flow plain scalars.
-+        if (flowIndicators) {
-+            allowFlowPlain = false;
-+        }
-+        // Block indicators are forbidden for block plain scalars.
-+        if (blockIndicators) {
-+            allowBlockPlain = false;
-+        }
-+
-+        return new ScalarAnalysis(scalar, false, lineBreaks, allowFlowPlain, allowBlockPlain,
-+            allowSingleQuoted, allowBlock);
-+    }
-+
-+    // Writers.
-+
-+    void flushStream() throws IOException {
-+        stream.flush();
-+    }
-+
-+    void writeStreamStart() {
-+        // BOM is written by Writer.
-+    }
-+
-+    void writeStreamEnd() throws IOException {
-+        flushStream();
-+    }
-+
-+    void writeIndicator(String indicator, boolean needWhitespace, boolean whitespace,
-+                        boolean indentation) throws IOException {
-+        if (!this.whitespace && needWhitespace) {
-+            this.column++;
-+            stream.write(SPACE);
-+        }
-+        this.whitespace = whitespace;
-+        this.indention = this.indention && indentation;
-+        this.column += indicator.length();
-+        openEnded = false;
-+        stream.write(indicator);
-+    }
-+
-+    void writeIndent() throws IOException {
-+        int indent;
-+        if (this.indent != null) {
-+            indent = this.indent;
-+        } else {
-+            indent = 0;
-+        }
-+
-+        if (!this.indention || this.column > indent || (this.column == indent && !this.whitespace)) {
-+            writeLineBreak(null);
-+        }
-+
-+        writeWhitespace(indent - this.column);
-+    }
-+
-+    private void writeWhitespace(int length) throws IOException {
-+        if (length <= 0) {
-+            return;
-+        }
-+        this.whitespace = true;
-+        char[] data = new char[length];
-+        for (int i = 0; i < data.length; i++) {
-+            data[i] = ' ';
-+        }
-+        this.column += length;
-+        stream.write(data);
-+    }
-+
-+    private void writeLineBreak(String data) throws IOException {
-+        this.whitespace = true;
-+        this.indention = true;
-+        this.column = 0;
-+        if (data == null) {
-+            stream.write(this.bestLineBreak);
-+        } else {
-+            stream.write(data);
-+        }
-+    }
-+
-+    void writeVersionDirective(String versionText) throws IOException {
-+        stream.write("%YAML ");
-+        stream.write(versionText);
-+        writeLineBreak(null);
-+    }
-+
-+    void writeTagDirective(String handleText, String prefixText) throws IOException {
-+        // XXX: not sure 4 invocations better then StringBuilders created by str
-+        // + str
-+        stream.write("%TAG ");
-+        stream.write(handleText);
-+        stream.write(SPACE);
-+        stream.write(prefixText);
-+        writeLineBreak(null);
-+    }
-+
-+    // Scalar streams.
-+    private void writeSingleQuoted(String text, boolean split) throws IOException {
-+        writeIndicator("'", true, false, false);
-+        boolean spaces = false;
-+        boolean breaks = false;
-+        int start = 0, end = 0;
-+        char ch;
-+        while (end <= text.length()) {
-+            ch = 0;
-+            if (end < text.length()) {
-+                ch = text.charAt(end);
-+            }
-+            if (spaces) {
-+                if (ch == 0 || ch != ' ') {
-+                    if (start + 1 == end && this.column > this.bestWidth && split && start != 0
-+                        && end != text.length()) {
-+                        writeIndent();
-+                    } else {
-+                        int len = end - start;
-+                        this.column += len;
-+                        stream.write(text, start, len);
-+                    }
-+                    start = end;
-+                }
-+            } else if (breaks) {
-+                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
-+                    if (text.charAt(start) == '\n') {
-+                        writeLineBreak(null);
-+                    }
-+                    String data = text.substring(start, end);
-+                    for (char br : data.toCharArray()) {
-+                        if (br == '\n') {
-+                            writeLineBreak(null);
-+                        } else {
-+                            writeLineBreak(String.valueOf(br));
-+                        }
-+                    }
-+                    writeIndent();
-+                    start = end;
-+                }
-+            } else {
-+                if (Constant.LINEBR.has(ch, "\0 '")) {
-+                    if (start < end) {
-+                        int len = end - start;
-+                        this.column += len;
-+                        stream.write(text, start, len);
-+                        start = end;
-+                    }
-+                }
-+            }
-+            if (ch == '\'') {
-+                this.column += 2;
-+                stream.write("''");
-+                start = end + 1;
-+            }
-+            if (ch != 0) {
-+                spaces = ch == ' ';
-+                breaks = Constant.LINEBR.has(ch);
-+            }
-+            end++;
-+        }
-+        writeIndicator("'", false, false, false);
-+    }
-+
-+    private void writeDoubleQuoted(String text, boolean split) throws IOException {
-+        writeIndicator("\"", true, false, false);
-+        int start = 0;
-+        int end = 0;
-+        while (end <= text.length()) {
-+            Character ch = null;
-+            if (end < text.length()) {
-+                ch = text.charAt(end);
-+            }
-+            if (ch == null || "\"\\\u0085\u2028\u2029\uFEFF".indexOf(ch) != -1
-+                || !('\u0020' <= ch && ch <= '\u007E')) {
-+                if (start < end) {
-+                    int len = end - start;
-+                    this.column += len;
-+                    stream.write(text, start, len);
-+                    start = end;
-+                }
-+                if (ch != null) {
-+                    String data;
-+                    if (ESCAPE_REPLACEMENTS.containsKey(ch)) {
-+                        data = "\\" + ESCAPE_REPLACEMENTS.get(ch);
-+                    } else if (!this.allowUnicode || !StreamReader.isPrintable(ch)) {
-+                        // if !allowUnicode or the character is not printable,
-+                        // we must encode it
-+                        if (ch <= '\u00FF') {
-+                            String s = "0" + Integer.toString(ch, 16);
-+                            data = "\\x" + s.substring(s.length() - 2);
-+                        } else if (ch >= '\uD800' && ch <= '\uDBFF') {
-+                            if (end + 1 < text.length()) {
-+                                Character ch2 = text.charAt(++end);
-+                                String s = "000" + Long.toHexString(Character.toCodePoint(ch, ch2));
-+                                data = "\\U" + s.substring(s.length() - 8);
-+                            } else {
-+                                String s = "000" + Integer.toString(ch, 16);
-+                                data = "\\u" + s.substring(s.length() - 4);
-+                            }
-+                        } else {
-+                            String s = "000" + Integer.toString(ch, 16);
-+                            data = "\\u" + s.substring(s.length() - 4);
-+                        }
-+                    } else {
-+                        data = String.valueOf(ch);
-+                    }
-+                    this.column += data.length();
-+                    stream.write(data);
-+                    start = end + 1;
-+                }
-+            }
-+            if ((0 < end && end < (text.length() - 1)) && (ch == ' ' || start >= end)
-+                && (this.column + (end - start)) > this.bestWidth && split) {
-+                String data;
-+                if (start >= end) {
-+                    data = "\\";
-+                } else {
-+                    data = text.substring(start, end) + "\\";
-+                }
-+                if (start < end) {
-+                    start = end;
-+                }
-+                this.column += data.length();
-+                stream.write(data);
-+                writeIndent();
-+                this.whitespace = false;
-+                this.indention = false;
-+                if (text.charAt(start) == ' ') {
-+                    data = "\\";
-+                    this.column += data.length();
-+                    stream.write(data);
-+                }
-+            }
-+            end += 1;
-+        }
-+        writeIndicator("\"", false, false, false);
-+    }
-+
-+    private boolean writeCommentLines(List<CommentLine> commentLines) throws IOException {
-+        boolean wroteComment = false;
-+        if(emitComments) {
-+            int indentColumns = 0;
-+            boolean firstComment = true;
-+            for (CommentLine commentLine : commentLines) {
-+                if (commentLine.getCommentType() != CommentType.BLANK_LINE) {
-+                    if (firstComment) {
-+                        firstComment = false;
-+                        writeIndicator("#", commentLine.getCommentType() == CommentType.IN_LINE, false, false);
-+                        indentColumns = this.column > 0 ? this.column - 1 : 0;
-+                    } else {
-+                        writeWhitespace(indentColumns);
-+                        writeIndicator("#", false, false, false);
-+                    }
-+                    stream.write(commentLine.getValue());
-+                    writeLineBreak(null);
-+                } else {
-+                    writeLineBreak(null);
-+                    writeIndent();
-+                }
-+                wroteComment = true;
-+            }
-+        }
-+        return wroteComment;
-+    }
-+
-+    private void writeBlockComment() throws IOException {
-+        if(!blockCommentsCollector.isEmpty()) {
-+            writeIndent();
-+            writeCommentLines(blockCommentsCollector.consume());
-+        }
-+    }
-+
-+    private boolean writeInlineComments() throws IOException {
-+        return writeCommentLines(inlineCommentsCollector.consume());
-+    }
-+
-+    private String determineBlockHints(String text) {
-+        StringBuilder hints = new StringBuilder();
-+        if (Constant.LINEBR.has(text.charAt(0), " ")) {
-+            hints.append(bestIndent);
-+        }
-+        char ch1 = text.charAt(text.length() - 1);
-+        if (Constant.LINEBR.hasNo(ch1)) {
-+            hints.append("-");
-+        } else if (text.length() == 1 || Constant.LINEBR.has(text.charAt(text.length() - 2))) {
-+            hints.append("+");
-+        }
-+        return hints.toString();
-+    }
-+
-+    void writeFolded(String text, boolean split) throws IOException {
-+        String hints = determineBlockHints(text);
-+        writeIndicator(">" + hints, true, false, false);
-+        if (hints.length() > 0 && (hints.charAt(hints.length() - 1) == '+')) {
-+            openEnded = true;
-+        }
-+        if(!writeInlineComments()) {
-+            writeLineBreak(null);
-+        }
-+        boolean leadingSpace = true;
-+        boolean spaces = false;
-+        boolean breaks = true;
-+        int start = 0, end = 0;
-+        while (end <= text.length()) {
-+            char ch = 0;
-+            if (end < text.length()) {
-+                ch = text.charAt(end);
-+            }
-+            if (breaks) {
-+                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
-+                    if (!leadingSpace && ch != 0 && ch != ' ' && text.charAt(start) == '\n') {
-+                        writeLineBreak(null);
-+                    }
-+                    leadingSpace = ch == ' ';
-+                    String data = text.substring(start, end);
-+                    for (char br : data.toCharArray()) {
-+                        if (br == '\n') {
-+                            writeLineBreak(null);
-+                        } else {
-+                            writeLineBreak(String.valueOf(br));
-+                        }
-+                    }
-+                    if (ch != 0) {
-+                        writeIndent();
-+                    }
-+                    start = end;
-+                }
-+            } else if (spaces) {
-+                if (ch != ' ') {
-+                    if (start + 1 == end && this.column > this.bestWidth && split) {
-+                        writeIndent();
-+                    } else {
-+                        int len = end - start;
-+                        this.column += len;
-+                        stream.write(text, start, len);
-+                    }
-+                    start = end;
-+                }
-+            } else {
-+                if (Constant.LINEBR.has(ch, "\0 ")) {
-+                    int len = end - start;
-+                    this.column += len;
-+                    stream.write(text, start, len);
-+                    if (ch == 0) {
-+                        writeLineBreak(null);
-+                    }
-+                    start = end;
-+                }
-+            }
-+            if (ch != 0) {
-+                breaks = Constant.LINEBR.has(ch);
-+                spaces = ch == ' ';
-+            }
-+            end++;
-+        }
-+    }
-+
-+    void writeLiteral(String text) throws IOException {
-+        String hints = determineBlockHints(text);
-+        writeIndicator("|" + hints, true, false, false);
-+        if (hints.length() > 0 && (hints.charAt(hints.length() - 1)) == '+') {
-+            openEnded = true;
-+        }
-+        if(!writeInlineComments()) {
-+            writeLineBreak(null);
-+        }
-+        boolean breaks = true;
-+        int start = 0, end = 0;
-+        while (end <= text.length()) {
-+            char ch = 0;
-+            if (end < text.length()) {
-+                ch = text.charAt(end);
-+            }
-+            if (breaks) {
-+                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
-+                    String data = text.substring(start, end);
-+                    for (char br : data.toCharArray()) {
-+                        if (br == '\n') {
-+                            writeLineBreak(null);
-+                        } else {
-+                            writeLineBreak(String.valueOf(br));
-+                        }
-+                    }
-+                    if (ch != 0) {
-+                        writeIndent();
-+                    }
-+                    start = end;
-+                }
-+            } else {
-+                if (ch == 0 || Constant.LINEBR.has(ch)) {
-+                    stream.write(text, start, end - start);
-+                    if (ch == 0) {
-+                        writeLineBreak(null);
-+                    }
-+                    start = end;
-+                }
-+            }
-+            if (ch != 0) {
-+                breaks = Constant.LINEBR.has(ch);
-+            }
-+            end++;
-+        }
-+    }
-+
-+    void writePlain(String text, boolean split) throws IOException {
-+        if (rootContext) {
-+            openEnded = true;
-+        }
-+        if (text.length() == 0) {
-+            return;
-+        }
-+        if (!this.whitespace) {
-+            this.column++;
-+            stream.write(SPACE);
-+        }
-+        this.whitespace = false;
-+        this.indention = false;
-+        boolean spaces = false;
-+        boolean breaks = false;
-+        int start = 0, end = 0;
-+        while (end <= text.length()) {
-+            char ch = 0;
-+            if (end < text.length()) {
-+                ch = text.charAt(end);
-+            }
-+            if (spaces) {
-+                if (ch != ' ') {
-+                    if (start + 1 == end && this.column > this.bestWidth && split) {
-+                        writeIndent();
-+                        this.whitespace = false;
-+                        this.indention = false;
-+                    } else {
-+                        int len = end - start;
-+                        this.column += len;
-+                        stream.write(text, start, len);
-+                    }
-+                    start = end;
-+                }
-+            } else if (breaks) {
-+                if (Constant.LINEBR.hasNo(ch)) {
-+                    if (text.charAt(start) == '\n') {
-+                        writeLineBreak(null);
-+                    }
-+                    String data = text.substring(start, end);
-+                    for (char br : data.toCharArray()) {
-+                        if (br == '\n') {
-+                            writeLineBreak(null);
-+                        } else {
-+                            writeLineBreak(String.valueOf(br));
-+                        }
-+                    }
-+                    writeIndent();
-+                    this.whitespace = false;
-+                    this.indention = false;
-+                    start = end;
-+                }
-+            } else {
-+                if (Constant.LINEBR.has(ch, "\0 ")) {
-+                    int len = end - start;
-+                    this.column += len;
-+                    stream.write(text, start, len);
-+                    start = end;
-+                }
-+            }
-+            if (ch != 0) {
-+                spaces = ch == ' ';
-+                breaks = Constant.LINEBR.has(ch);
-+            }
-+            end++;
-+        }
-+    }
-+}
diff --git a/work/Bukkit b/work/Bukkit
index 0994345029..006049902d 160000
--- a/work/Bukkit
+++ b/work/Bukkit
@@ -1 +1 @@
-Subproject commit 0994345029c4d127696616de3bab3e8044b03749
+Subproject commit 006049902d41e91d01844126dd968ca92fb99c8a
diff --git a/work/Spigot b/work/Spigot
index 4aa5ead237..89741d3e9f 160000
--- a/work/Spigot
+++ b/work/Spigot
@@ -1 +1 @@
-Subproject commit 4aa5ead2377692b934a84b6853d188e2d9cbd53a
+Subproject commit 89741d3e9ff89cb2aa3b2e34a714a3a073b60d95