entry : this.entrySet()) {
+                String name = entry.getKey();
+                Object valueThis = entry.getValue();
+                Object valueOther = ((JSONObject)other).get(name);
+                if(valueThis == valueOther) {
+                	continue;
+                }
+                if(valueThis == null) {
+                	return false;
+                }
+                if (valueThis instanceof JSONObject) {
+                    if (!((JSONObject)valueThis).similar(valueOther)) {
+                        return false;
+                    }
+                } else if (valueThis instanceof JSONArray) {
+                    if (!((JSONArray)valueThis).similar(valueOther)) {
+                        return false;
+                    }
+                } else if (!valueThis.equals(valueOther)) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (Throwable exception) {
+            return false;
+        }
+    }
+    
+    /**
+     * Tests if the value should be tried as a decimal. It makes no test if there are actual digits.
+     * 
+     * @param val value to test
+     * @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise.
+     */
+    protected static boolean isDecimalNotation(final String val) {
+        return val.indexOf('.') > -1 || val.indexOf('e') > -1
+                || val.indexOf('E') > -1 || "-0".equals(val);
+    }
+    
+    /**
+     * Converts a string to a number using the narrowest possible type. Possible 
+     * returns for this function are BigDecimal, Double, BigInteger, Long, and Integer.
+     * When a Double is returned, it should always be a valid Double and not NaN or +-infinity.
+     * 
+     * @param val value to convert
+     * @return Number representation of the value.
+     * @throws NumberFormatException thrown if the value is not a valid number. A public
+     *      caller should catch this and wrap it in a {@link JSONException} if applicable.
+     */
+    protected static Number stringToNumber(final String val) throws NumberFormatException {
+        char initial = val.charAt(0);
+        if ((initial >= '0' && initial <= '9') || initial == '-') {
+            // decimal representation
+            if (isDecimalNotation(val)) {
+                // quick dirty way to see if we need a BigDecimal instead of a Double
+                // this only handles some cases of overflow or underflow
+                if (val.length()>14) {
+                    return new BigDecimal(val);
+                }
+                final Double d = Double.valueOf(val);
+                if (d.isInfinite() || d.isNaN()) {
+                    // if we can't parse it as a double, go up to BigDecimal
+                    // this is probably due to underflow like 4.32e-678
+                    // or overflow like 4.65e5324. The size of the string is small
+                    // but can't be held in a Double.
+                    return new BigDecimal(val);
+                }
+                return d;
+            }
+            // integer representation.
+            // This will narrow any values to the smallest reasonable Object representation
+            // (Integer, Long, or BigInteger)
+            
+            // string version
+            // The compare string length method reduces GC,
+            // but leads to smaller integers being placed in larger wrappers even though not
+            // needed. i.e. 1,000,000,000 -> Long even though it's an Integer
+            // 1,000,000,000,000,000,000 -> BigInteger even though it's a Long
+            //if(val.length()<=9){
+            //    return Integer.valueOf(val);
+            //}
+            //if(val.length()<=18){
+            //    return Long.valueOf(val);
+            //}
+            //return new BigInteger(val);
+            
+            // BigInteger version: We use a similar bitLength compare as
+            // BigInteger#intValueExact uses. Increases GC, but objects hold
+            // only what they need. i.e. Less runtime overhead if the value is
+            // long lived. Which is the better tradeoff? This is closer to what's
+            // in stringToValue.
+            BigInteger bi = new BigInteger(val);
+            if(bi.bitLength()<=31){
+                return Integer.valueOf(bi.intValue());
+            }
+            if(bi.bitLength()<=63){
+                return Long.valueOf(bi.longValue());
+            }
+            return bi;
+        }
+        throw new NumberFormatException("val ["+val+"] is not a valid number.");
+    }
+
+    /**
+     * Try to convert a string into a number, boolean, or null. If the string
+     * can't be converted, return the string.
+     *
+     * @param string
+     *            A String. can not be null.
+     * @return A simple JSON value.
+     * @throws NullPointerException
+     *             Thrown if the string is null.
+     */
+    // Changes to this method must be copied to the corresponding method in
+    // the XML class to keep full support for Android
+    public static Object stringToValue(String string) {
+        if ("".equals(string)) {
+            return string;
+        }
+
+        // check JSON key words true/false/null
+        if ("true".equalsIgnoreCase(string)) {
+            return Boolean.TRUE;
+        }
+        if ("false".equalsIgnoreCase(string)) {
+            return Boolean.FALSE;
+        }
+        if ("null".equalsIgnoreCase(string)) {
+            return JSONObject.NULL;
+        }
+
+        /*
+         * If it might be a number, try converting it. If a number cannot be
+         * produced, then the value will just be a string.
+         */
+
+        char initial = string.charAt(0);
+        if ((initial >= '0' && initial <= '9') || initial == '-') {
+            try {
+                // if we want full Big Number support the contents of this
+                // `try` block can be replaced with:
+                // return stringToNumber(string);
+                if (isDecimalNotation(string)) {
+                    Double d = Double.valueOf(string);
+                    if (!d.isInfinite() && !d.isNaN()) {
+                        return d;
+                    }
+                } else {
+                    Long myLong = Long.valueOf(string);
+                    if (string.equals(myLong.toString())) {
+                        if (myLong.longValue() == myLong.intValue()) {
+                            return Integer.valueOf(myLong.intValue());
+                        }
+                        return myLong;
+                    }
+                }
+            } catch (Exception ignore) {
+            }
+        }
+        return string;
+    }
+
+    /**
+     * Throw an exception if the object is a NaN or infinite number.
+     *
+     * @param o
+     *            The object to test.
+     * @throws JSONException
+     *             If o is a non-finite number.
+     */
+    public static void testValidity(Object o) throws JSONException {
+        if (o != null) {
+            if (o instanceof Double) {
+                if (((Double) o).isInfinite() || ((Double) o).isNaN()) {
+                    throw new JSONException(
+                            "JSON does not allow non-finite numbers.");
+                }
+            } else if (o instanceof Float) {
+                if (((Float) o).isInfinite() || ((Float) o).isNaN()) {
+                    throw new JSONException(
+                            "JSON does not allow non-finite numbers.");
+                }
+            }
+        }
+    }
+
+    /**
+     * Produce a JSONArray containing the values of the members of this
+     * JSONObject.
+     *
+     * @param names
+     *            A JSONArray containing a list of key strings. This determines
+     *            the sequence of the values in the result.
+     * @return A JSONArray of values.
+     * @throws JSONException
+     *             If any of the values are non-finite numbers.
+     */
+    public JSONArray toJSONArray(JSONArray names) throws JSONException {
+        if (names == null || names.isEmpty()) {
+            return null;
+        }
+        JSONArray ja = new JSONArray();
+        for (int i = 0; i < names.length(); i += 1) {
+            ja.put(this.opt(names.getString(i)));
+        }
+        return ja;
+    }
+
+    /**
+     * Make a JSON text of this JSONObject. For compactness, no whitespace is
+     * added. If this would not result in a syntactically correct JSON text,
+     * then null will be returned instead.
+     * 
+     * Warning: This method assumes that the data structure is acyclical.
+     * 
+     * 
+     * @return a printable, displayable, portable, transmittable representation
+     *         of the object, beginning with { (left
+     *         brace) and ending with } (right
+     *         brace).
+     */
+    @Override
+    public String toString() {
+        try {
+            return this.toString(0);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * Make a pretty-printed JSON text of this JSONObject.
+     * 
+     * 
If indentFactor > 0 and the {@link JSONObject}
+     * has only one key, then the object will be output on a single line:
+     * 
{@code {"key": 1}}
+     * 
+     * If an object has 2 or more keys, then it will be output across
+     * multiple lines: {
+     *  "key1": 1,
+     *  "key2": "value 2",
+     *  "key3": 3
+     * }
+     * Warning: This method assumes that the data structure is acyclical.
+     * 
+     *
+     * @param indentFactor
+     *            The number of spaces to add to each level of indentation.
+     * @return a printable, displayable, portable, transmittable representation
+     *         of the object, beginning with { (left
+     *         brace) and ending with } (right
+     *         brace).
+     * @throws JSONException
+     *             If the object contains an invalid number.
+     */
+    public String toString(int indentFactor) throws JSONException {
+        StringWriter w = new StringWriter();
+        synchronized (w.getBuffer()) {
+            return this.write(w, indentFactor, 0).toString();
+        }
+    }
+
+    /**
+     * Make a JSON text of an Object value. If the object has an
+     * value.toJSONString() method, then that method will be used to produce the
+     * JSON text. The method is required to produce a strictly conforming text.
+     * If the object does not contain a toJSONString method (which is the most
+     * common case), then a text will be produced by other means. If the value
+     * is an array or Collection, then a JSONArray will be made from it and its
+     * toJSONString method will be called. If the value is a MAP, then a
+     * JSONObject will be made from it and its toJSONString method will be
+     * called. Otherwise, the value's toString method will be called, and the
+     * result will be quoted.
+     *
+     * 
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @param value
+     *            The value to be serialized.
+     * @return a printable, displayable, transmittable representation of the
+     *         object, beginning with { (left
+     *         brace) and ending with } (right
+     *         brace).
+     * @throws JSONException
+     *             If the value is or contains an invalid number.
+     */
+    public static String valueToString(Object value) throws JSONException {
+    	// moves the implementation to JSONWriter as:
+    	// 1. It makes more sense to be part of the writer class
+    	// 2. For Android support this method is not available. By implementing it in the Writer
+    	//    Android users can use the writer with the built in Android JSONObject implementation.
+        return JSONWriter.valueToString(value);
+    }
+
+    /**
+     * Wrap an object, if necessary. If the object is null, return the NULL
+     * object. If it is an array or collection, wrap it in a JSONArray. If it is
+     * a map, wrap it in a JSONObject. If it is a standard property (Double,
+     * String, et al) then it is already wrapped. Otherwise, if it comes from
+     * one of the java packages, turn it into a string. And if it doesn't, try
+     * to wrap it in a JSONObject. If the wrapping fails, then null is returned.
+     *
+     * @param object
+     *            The object to wrap
+     * @return The wrapped value
+     */
+    public static Object wrap(Object object) {
+        try {
+            if (object == null) {
+                return NULL;
+            }
+            if (object instanceof JSONObject || object instanceof JSONArray
+                    || NULL.equals(object) || object instanceof JSONString
+                    || object instanceof Byte || object instanceof Character
+                    || object instanceof Short || object instanceof Integer
+                    || object instanceof Long || object instanceof Boolean
+                    || object instanceof Float || object instanceof Double
+                    || object instanceof String || object instanceof BigInteger
+                    || object instanceof BigDecimal || object instanceof Enum) {
+                return object;
+            }
+
+            if (object instanceof Collection) {
+                Collection> coll = (Collection>) object;
+                return new JSONArray(coll);
+            }
+            if (object.getClass().isArray()) {
+                return new JSONArray(object);
+            }
+            if (object instanceof Map) {
+                Map, ?> map = (Map, ?>) object;
+                return new JSONObject(map);
+            }
+            Package objectPackage = object.getClass().getPackage();
+            String objectPackageName = objectPackage != null ? objectPackage
+                    .getName() : "";
+            if (objectPackageName.startsWith("java.")
+                    || objectPackageName.startsWith("javax.")
+                    || object.getClass().getClassLoader() == null) {
+                return object.toString();
+            }
+            return new JSONObject(object);
+        } catch (Exception exception) {
+            return null;
+        }
+    }
+
+    /**
+     * Write the contents of the JSONObject as JSON text to a writer. For
+     * compactness, no whitespace is added.
+     * 
+     * Warning: This method assumes that the data structure is acyclical.
+     * 
+     * 
+     * @return The writer.
+     * @throws JSONException
+     */
+    public Writer write(Writer writer) throws JSONException {
+        return this.write(writer, 0, 0);
+    }
+
+    static final Writer writeValue(Writer writer, Object value,
+            int indentFactor, int indent) throws JSONException, IOException {
+        if (value == null || value.equals(null)) {
+            writer.write("null");
+        } else if (value instanceof JSONString) {
+            Object o;
+            try {
+                o = ((JSONString) value).toJSONString();
+            } catch (Exception e) {
+                throw new JSONException(e);
+            }
+            writer.write(o != null ? o.toString() : quote(value.toString()));
+        } else if (value instanceof Number) {
+            // not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
+            final String numberAsString = numberToString((Number) value);
+            if(NUMBER_PATTERN.matcher(numberAsString).matches()) {
+                writer.write(numberAsString);
+            } else {
+                // The Number value is not a valid JSON number.
+                // Instead we will quote it as a string
+                quote(numberAsString, writer);
+            }
+        } else if (value instanceof Boolean) {
+            writer.write(value.toString());
+        } else if (value instanceof Enum>) {
+            writer.write(quote(((Enum>)value).name()));
+        } else if (value instanceof JSONObject) {
+            ((JSONObject) value).write(writer, indentFactor, indent);
+        } else if (value instanceof JSONArray) {
+            ((JSONArray) value).write(writer, indentFactor, indent);
+        } else if (value instanceof Map) {
+            Map, ?> map = (Map, ?>) value;
+            new JSONObject(map).write(writer, indentFactor, indent);
+        } else if (value instanceof Collection) {
+            Collection> coll = (Collection>) value;
+            new JSONArray(coll).write(writer, indentFactor, indent);
+        } else if (value.getClass().isArray()) {
+            new JSONArray(value).write(writer, indentFactor, indent);
+        } else {
+            quote(value.toString(), writer);
+        }
+        return writer;
+    }
+
+    static final void indent(Writer writer, int indent) throws IOException {
+        for (int i = 0; i < indent; i += 1) {
+            writer.write(' ');
+        }
+    }
+
+    /**
+     * Write the contents of the JSONObject as JSON text to a writer.
+     * 
+     * 
If indentFactor > 0 and the {@link JSONObject}
+     * has only one key, then the object will be output on a single line:
+     * 
{@code {"key": 1}}
+     * 
+     * If an object has 2 or more keys, then it will be output across
+     * multiple lines: {
+     *  "key1": 1,
+     *  "key2": "value 2",
+     *  "key3": 3
+     * }
+     * Warning: This method assumes that the data structure is acyclical.
+     * 
+     *
+     * @param writer
+     *            Writes the serialized JSON
+     * @param indentFactor
+     *            The number of spaces to add to each level of indentation.
+     * @param indent
+     *            The indentation of the top level.
+     * @return The writer.
+     * @throws JSONException
+     */
+    public Writer write(Writer writer, int indentFactor, int indent)
+            throws JSONException {
+        try {
+            boolean needsComma = false;
+            final int length = this.length();
+            writer.write('{');
+
+            if (length == 1) {
+            	final Entry entry = this.entrySet().iterator().next();
+                final String key = entry.getKey();
+                writer.write(quote(key));
+                writer.write(':');
+                if (indentFactor > 0) {
+                    writer.write(' ');
+                }
+                try{
+                    writeValue(writer, entry.getValue(), indentFactor, indent);
+                } catch (Exception e) {
+                    throw new JSONException("Unable to write JSONObject value for key: " + key, e);
+                }
+            } else if (length != 0) {
+                final int newIndent = indent + indentFactor;
+                for (final Entry entry : this.entrySet()) {
+                    if (needsComma) {
+                        writer.write(',');
+                    }
+                    if (indentFactor > 0) {
+                        writer.write('\n');
+                    }
+                    indent(writer, newIndent);
+                    final String key = entry.getKey();
+                    writer.write(quote(key));
+                    writer.write(':');
+                    if (indentFactor > 0) {
+                        writer.write(' ');
+                    }
+                    try {
+                        writeValue(writer, entry.getValue(), indentFactor, newIndent);
+                    } catch (Exception e) {
+                        throw new JSONException("Unable to write JSONObject value for key: " + key, e);
+                    }
+                    needsComma = true;
+                }
+                if (indentFactor > 0) {
+                    writer.write('\n');
+                }
+                indent(writer, indent);
+            }
+            writer.write('}');
+            return writer;
+        } catch (IOException exception) {
+            throw new JSONException(exception);
+        }
+    }
+
+    /**
+     * Returns a java.util.Map containing all of the entries in this object.
+     * If an entry in the object is a JSONArray or JSONObject it will also
+     * be converted.
+     * 
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @return a java.util.Map containing the entries of this object
+     */
+    public Map toMap() {
+        Map results = new HashMap();
+        for (Entry entry : this.entrySet()) {
+            Object value;
+            if (entry.getValue() == null || NULL.equals(entry.getValue())) {
+                value = null;
+            } else if (entry.getValue() instanceof JSONObject) {
+                value = ((JSONObject) entry.getValue()).toMap();
+            } else if (entry.getValue() instanceof JSONArray) {
+                value = ((JSONArray) entry.getValue()).toList();
+            } else {
+                value = entry.getValue();
+            }
+            results.put(entry.getKey(), value);
+        }
+        return results;
+    }
+    
+    /**
+     * Create a new JSONException in a common format for incorrect conversions.
+     * @param key name of the key
+     * @param valueType the type of value being coerced to
+     * @param cause optional cause of the coercion failure
+     * @return JSONException that can be thrown.
+     */
+    private static JSONException wrongValueFormatException(
+            String key,
+            String valueType,
+            Throwable cause) {
+        return new JSONException(
+                "JSONObject[" + quote(key) + "] is not a " + valueType + "."
+                , cause);
+    }
+    
+    /**
+     * Create a new JSONException in a common format for incorrect conversions.
+     * @param key name of the key
+     * @param valueType the type of value being coerced to
+     * @param cause optional cause of the coercion failure
+     * @return JSONException that can be thrown.
+     */
+    private static JSONException wrongValueFormatException(
+            String key,
+            String valueType,
+            Object value,
+            Throwable cause) {
+        return new JSONException(
+                "JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value + ")."
+                , cause);
+    }
+}
diff --git a/src/main/java/json/JSONPointer.java b/src/main/java/json/JSONPointer.java
new file mode 100644
index 0000000..bef1ee0
--- /dev/null
+++ b/src/main/java/json/JSONPointer.java
@@ -0,0 +1,293 @@
+package json;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static java.lang.String.format;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * A JSON Pointer is a simple query language defined for JSON documents by
+ * RFC 6901.
+ * 
+ * In a nutshell, JSONPointer allows the user to navigate into a JSON document
+ * using strings, and retrieve targeted objects, like a simple form of XPATH.
+ * Path segments are separated by the '/' char, which signifies the root of
+ * the document when it appears as the first char of the string. Array 
+ * elements are navigated using ordinals, counting from 0. JSONPointer strings
+ * may be extended to any arbitrary number of segments. If the navigation
+ * is successful, the matched item is returned. A matched item may be a
+ * JSONObject, a JSONArray, or a JSON value. If the JSONPointer string building 
+ * fails, an appropriate exception is thrown. If the navigation fails to find
+ * a match, a JSONPointerException is thrown. 
+ * 
+ * @author JSON.org
+ * @version 2016-05-14
+ */
+public class JSONPointer {
+
+    // used for URL encoding and decoding
+    private static final String ENCODING = "utf-8";
+
+    /**
+     * This class allows the user to build a JSONPointer in steps, using
+     * exactly one segment in each step.
+     */
+    public static class Builder {
+
+        // Segments for the eventual JSONPointer string
+        private final List refTokens = new ArrayList();
+
+        /**
+         * Creates a {@code JSONPointer} instance using the tokens previously set using the
+         * {@link #append(String)} method calls.
+         */
+        public JSONPointer build() {
+            return new JSONPointer(this.refTokens);
+        }
+
+        /**
+         * Adds an arbitrary token to the list of reference tokens. It can be any non-null value.
+         * 
+         * Unlike in the case of JSON string or URI fragment representation of JSON pointers, the
+         * argument of this method MUST NOT be escaped. If you want to query the property called
+         * {@code "a~b"} then you should simply pass the {@code "a~b"} string as-is, there is no
+         * need to escape it as {@code "a~0b"}.
+         * 
+         * @param token the new token to be appended to the list
+         * @return {@code this}
+         * @throws NullPointerException if {@code token} is null
+         */
+        public Builder append(String token) {
+            if (token == null) {
+                throw new NullPointerException("token cannot be null");
+            }
+            this.refTokens.add(token);
+            return this;
+        }
+
+        /**
+         * Adds an integer to the reference token list. Although not necessarily, mostly this token will
+         * denote an array index. 
+         * 
+         * @param arrayIndex the array index to be added to the token list
+         * @return {@code this}
+         */
+        public Builder append(int arrayIndex) {
+            this.refTokens.add(String.valueOf(arrayIndex));
+            return this;
+        }
+    }
+
+    /**
+     * Static factory method for {@link Builder}. Example usage:
+     * 
+     * 
+     * JSONPointer pointer = JSONPointer.builder()
+     *       .append("obj")
+     *       .append("other~key").append("another/key")
+     *       .append("\"")
+     *       .append(0)
+     *       .build();
+     * 
+     * 
+     *  @return a builder instance which can be used to construct a {@code JSONPointer} instance by chained
+     *  {@link Builder#append(String)} calls.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    // Segments for the JSONPointer string
+    private final List refTokens;
+
+    /**
+     * Pre-parses and initializes a new {@code JSONPointer} instance. If you want to
+     * evaluate the same JSON Pointer on different JSON documents then it is recommended
+     * to keep the {@code JSONPointer} instances due to performance considerations.
+     * 
+     * @param pointer the JSON String or URI Fragment representation of the JSON pointer.
+     * @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer
+     */
+    public JSONPointer(final String pointer) {
+        if (pointer == null) {
+            throw new NullPointerException("pointer cannot be null");
+        }
+        if (pointer.isEmpty() || pointer.equals("#")) {
+            this.refTokens = Collections.emptyList();
+            return;
+        }
+        String refs;
+        if (pointer.startsWith("#/")) {
+            refs = pointer.substring(2);
+            try {
+                refs = URLDecoder.decode(refs, ENCODING);
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+        } else if (pointer.startsWith("/")) {
+            refs = pointer.substring(1);
+        } else {
+            throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'");
+        }
+        this.refTokens = new ArrayList();
+        int slashIdx = -1;
+        int prevSlashIdx = 0;
+        do {
+            prevSlashIdx = slashIdx + 1;
+            slashIdx = refs.indexOf('/', prevSlashIdx);
+            if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) {
+                // found 2 slashes in a row ( obj//next )
+                // or single slash at the end of a string ( obj/test/ )
+                this.refTokens.add("");
+            } else if (slashIdx >= 0) {
+                final String token = refs.substring(prevSlashIdx, slashIdx);
+                this.refTokens.add(unescape(token));
+            } else {
+                // last item after separator, or no separator at all.
+                final String token = refs.substring(prevSlashIdx);
+                this.refTokens.add(unescape(token));
+            }
+        } while (slashIdx >= 0);
+        // using split does not take into account consecutive separators or "ending nulls"
+        //for (String token : refs.split("/")) {
+        //    this.refTokens.add(unescape(token));
+        //}
+    }
+
+    public JSONPointer(List refTokens) {
+        this.refTokens = new ArrayList(refTokens);
+    }
+
+    private static String unescape(String token) {
+        return token.replace("~1", "/").replace("~0", "~")
+                .replace("\\\"", "\"")
+                .replace("\\\\", "\\");
+    }
+
+    /**
+     * Evaluates this JSON Pointer on the given {@code document}. The {@code document}
+     * is usually a {@link JSONObject} or a {@link JSONArray} instance, but the empty
+     * JSON Pointer ({@code ""}) can be evaluated on any JSON values and in such case the
+     * returned value will be {@code document} itself. 
+     * 
+     * @param document the JSON document which should be the subject of querying.
+     * @return the result of the evaluation
+     * @throws JSONPointerException if an error occurs during evaluation
+     */
+    public Object queryFrom(Object document) throws JSONPointerException {
+        if (this.refTokens.isEmpty()) {
+            return document;
+        }
+        Object current = document;
+        for (String token : this.refTokens) {
+            if (current instanceof JSONObject) {
+                current = ((JSONObject) current).opt(unescape(token));
+            } else if (current instanceof JSONArray) {
+                current = readByIndexToken(current, token);
+            } else {
+                throw new JSONPointerException(format(
+                        "value [%s] is not an array or object therefore its key %s cannot be resolved", current,
+                        token));
+            }
+        }
+        return current;
+    }
+
+    /**
+     * Matches a JSONArray element by ordinal position
+     * @param current the JSONArray to be evaluated
+     * @param indexToken the array index in string form
+     * @return the matched object. If no matching item is found a
+     * @throws JSONPointerException is thrown if the index is out of bounds
+     */
+    private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException {
+        try {
+            int index = Integer.parseInt(indexToken);
+            JSONArray currentArr = (JSONArray) current;
+            if (index >= currentArr.length()) {
+                throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken,
+                        Integer.valueOf(currentArr.length())));
+            }
+            try {
+				return currentArr.get(index);
+			} catch (JSONException e) {
+				throw new JSONPointerException("Error reading value at index position " + index, e);
+			}
+        } catch (NumberFormatException e) {
+            throw new JSONPointerException(format("%s is not an array index", indexToken), e);
+        }
+    }
+
+    /**
+     * Returns a string representing the JSONPointer path value using string
+     * representation
+     */
+    @Override
+    public String toString() {
+        StringBuilder rval = new StringBuilder("");
+        for (String token: this.refTokens) {
+            rval.append('/').append(escape(token));
+        }
+        return rval.toString();
+    }
+
+    /**
+     * Escapes path segment values to an unambiguous form.
+     * The escape char to be inserted is '~'. The chars to be escaped 
+     * are ~, which maps to ~0, and /, which maps to ~1. Backslashes
+     * and double quote chars are also escaped.
+     * @param token the JSONPointer segment value to be escaped
+     * @return the escaped value for the token
+     */
+    private static String escape(String token) {
+        return token.replace("~", "~0")
+                .replace("/", "~1")
+                .replace("\\", "\\\\")
+                .replace("\"", "\\\"");
+    }
+
+    /**
+     * Returns a string representing the JSONPointer path value using URI
+     * fragment identifier representation
+     */
+    public String toURIFragment() {
+        try {
+            StringBuilder rval = new StringBuilder("#");
+            for (String token : this.refTokens) {
+                rval.append('/').append(URLEncoder.encode(token, ENCODING));
+            }
+            return rval.toString();
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+}
diff --git a/src/main/java/json/JSONPointerException.java b/src/main/java/json/JSONPointerException.java
new file mode 100644
index 0000000..294d4e3
--- /dev/null
+++ b/src/main/java/json/JSONPointerException.java
@@ -0,0 +1,45 @@
+package json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * The JSONPointerException is thrown by {@link JSONPointer} if an error occurs
+ * during evaluating a pointer.
+ * 
+ * @author JSON.org
+ * @version 2016-05-13
+ */
+public class JSONPointerException extends JSONException {
+    private static final long serialVersionUID = 8872944667561856751L;
+
+    public JSONPointerException(String message) {
+        super(message);
+    }
+
+    public JSONPointerException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/src/main/java/json/JSONPropertyIgnore.java b/src/main/java/json/JSONPropertyIgnore.java
new file mode 100644
index 0000000..c2b18bd
--- /dev/null
+++ b/src/main/java/json/JSONPropertyIgnore.java
@@ -0,0 +1,43 @@
+package json;
+
+/*
+Copyright (c) 2018 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Documented
+@Retention(RUNTIME)
+@Target({METHOD})
+/**
+ * Use this annotation on a getter method to override the Bean name
+ * parser for Bean -> JSONObject mapping. If this annotation is
+ * present at any level in the class hierarchy, then the method will
+ * not be serialized from the bean into the JSONObject.
+ */
+public @interface JSONPropertyIgnore { }
diff --git a/src/main/java/json/JSONPropertyName.java b/src/main/java/json/JSONPropertyName.java
new file mode 100644
index 0000000..539dad2
--- /dev/null
+++ b/src/main/java/json/JSONPropertyName.java
@@ -0,0 +1,47 @@
+package json;
+
+/*
+Copyright (c) 2018 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Documented
+@Retention(RUNTIME)
+@Target({METHOD})
+/**
+ * Use this annotation on a getter method to override the Bean name
+ * parser for Bean -> JSONObject mapping. A value set to empty string ""
+ * will have the Bean parser fall back to the default field name processing.
+ */
+public @interface JSONPropertyName {
+    /**
+     * @return The name of the property as to be used in the JSON Object.
+     */
+    String value();
+}
diff --git a/src/main/java/json/JSONString.java b/src/main/java/json/JSONString.java
new file mode 100644
index 0000000..dbf7fb2
--- /dev/null
+++ b/src/main/java/json/JSONString.java
@@ -0,0 +1,18 @@
+package json;
+/**
+ * The JSONString interface allows a toJSONString()
+ * method so that a class can change the behavior of
+ * JSONObject.toString(), JSONArray.toString(),
+ * and JSONWriter.value(Object). The
+ * toJSONString method will be used instead of the default behavior
+ * of using the Object's toString() method and quoting the result.
+ */
+public interface JSONString {
+    /**
+     * The toJSONString method allows a class to produce its own JSON
+     * serialization.
+     *
+     * @return A strictly syntactically correct JSON text.
+     */
+    public String toJSONString();
+}
diff --git a/src/main/java/json/JSONStringer.java b/src/main/java/json/JSONStringer.java
new file mode 100644
index 0000000..46979bb
--- /dev/null
+++ b/src/main/java/json/JSONStringer.java
@@ -0,0 +1,79 @@
+package json;
+
+/*
+Copyright (c) 2006 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.io.StringWriter;
+
+/**
+ * JSONStringer provides a quick and convenient way of producing JSON text.
+ * The texts produced strictly conform to JSON syntax rules. No whitespace is
+ * added, so the results are ready for transmission or storage. Each instance of
+ * JSONStringer can produce one JSON text.
+ * 
+ * A JSONStringer instance provides a value method for appending
+ * values to the
+ * text, and a key
+ * method for adding keys before values in objects. There are array
+ * and endArray methods that make and bound array values, and
+ * object and endObject methods which make and bound
+ * object values. All of these methods return the JSONWriter instance,
+ * permitting cascade style. For example, 
+ * myString = new JSONStringer()
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject()
+ *     .toString(); which produces the string 
+ * {"JSON":"Hello, World!"}
+ * 
+ * The first method called must be array or object.
+ * There are no methods for adding commas or colons. JSONStringer adds them for
+ * you. Objects and arrays can be nested up to 20 levels deep.
+ * 
+ * This can sometimes be easier than using a JSONObject to build a string.
+ * @author JSON.org
+ * @version 2015-12-09
+ */
+public class JSONStringer extends JSONWriter {
+    /**
+     * Make a fresh JSONStringer. It can be used to build one JSON text.
+     */
+    public JSONStringer() {
+        super(new StringWriter());
+    }
+
+    /**
+     * Return the JSON text. This method is used to obtain the product of the
+     * JSONStringer instance. It will return null if there was a
+     * problem in the construction of the JSON text (such as the calls to
+     * array were not properly balanced with calls to
+     * endArray).
+     * @return The JSON text.
+     */
+    @Override
+    public String toString() {
+        return this.mode == 'd' ? this.writer.toString() : null;
+    }
+}
diff --git a/src/main/java/json/JSONTokener.java b/src/main/java/json/JSONTokener.java
new file mode 100644
index 0000000..f7f450e
--- /dev/null
+++ b/src/main/java/json/JSONTokener.java
@@ -0,0 +1,526 @@
+package json;
+
+import java.io.*;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ */
+
+/**
+ * A JSONTokener takes a source string and extracts characters and tokens from
+ * it. It is used by the JSONObject and JSONArray constructors to parse
+ * JSON source strings.
+ * @author JSON.org
+ * @version 2014-05-03
+ */
+public class JSONTokener {
+    /** current read character position on the current line. */
+    private long character;
+    /** flag to indicate if the end of the input has been found. */
+    private boolean eof;
+    /** current read index of the input. */
+    private long index;
+    /** current line of the input. */
+    private long line;
+    /** previous character read from the input. */
+    private char previous;
+    /** Reader for the input. */
+    private final Reader reader;
+    /** flag to indicate that a previous character was requested. */
+    private boolean usePrevious;
+    /** the number of characters read in the previous line. */
+    private long characterPreviousLine;
+
+
+    /**
+     * Construct a JSONTokener from a Reader. The caller must close the Reader.
+     *
+     * @param reader     A reader.
+     */
+    public JSONTokener(Reader reader) {
+        this.reader = reader.markSupported()
+                ? reader
+                        : new BufferedReader(reader);
+        this.eof = false;
+        this.usePrevious = false;
+        this.previous = 0;
+        this.index = 0;
+        this.character = 1;
+        this.characterPreviousLine = 0;
+        this.line = 1;
+    }
+
+
+    /**
+     * Construct a JSONTokener from an InputStream. The caller must close the input stream.
+     * @param inputStream The source.
+     */
+    public JSONTokener(InputStream inputStream) {
+        this(new InputStreamReader(inputStream));
+    }
+
+
+    /**
+     * Construct a JSONTokener from a string.
+     *
+     * @param s     A source string.
+     */
+    public JSONTokener(String s) {
+        this(new StringReader(s));
+    }
+
+
+    /**
+     * Back up one character. This provides a sort of lookahead capability,
+     * so that you can test for a digit or letter before attempting to parse
+     * the next number or identifier.
+     * @throws JSONException Thrown if trying to step back more than 1 step
+     *  or if already at the start of the string
+     */
+    public void back() throws JSONException {
+        if (this.usePrevious || this.index <= 0) {
+            throw new JSONException("Stepping back two steps is not supported");
+        }
+        this.decrementIndexes();
+        this.usePrevious = true;
+        this.eof = false;
+    }
+
+    /**
+     * Decrements the indexes for the {@link #back()} method based on the previous character read.
+     */
+    private void decrementIndexes() {
+        this.index--;
+        if(this.previous=='\r' || this.previous == '\n') {
+            this.line--;
+            this.character=this.characterPreviousLine ;
+        } else if(this.character > 0){
+            this.character--;
+        }
+    }
+
+    /**
+     * Get the hex value of a character (base16).
+     * @param c A character between '0' and '9' or between 'A' and 'F' or
+     * between 'a' and 'f'.
+     * @return  An int between 0 and 15, or -1 if c was not a hex digit.
+     */
+    public static int dehexchar(char c) {
+        if (c >= '0' && c <= '9') {
+            return c - '0';
+        }
+        if (c >= 'A' && c <= 'F') {
+            return c - ('A' - 10);
+        }
+        if (c >= 'a' && c <= 'f') {
+            return c - ('a' - 10);
+        }
+        return -1;
+    }
+
+    /**
+     * Checks if the end of the input has been reached.
+     *  
+     * @return true if at the end of the file and we didn't step back
+     */
+    public boolean end() {
+        return this.eof && !this.usePrevious;
+    }
+
+
+    /**
+     * Determine if the source string still contains characters that next()
+     * can consume.
+     * @return true if not yet at the end of the source.
+     * @throws JSONException thrown if there is an error stepping forward
+     *  or backward while checking for more data.
+     */
+    public boolean more() throws JSONException {
+        if(this.usePrevious) {
+            return true;
+        }
+        try {
+            this.reader.mark(1);
+        } catch (IOException e) {
+            throw new JSONException("Unable to preserve stream position", e);
+        }
+        try {
+            // -1 is EOF, but next() can not consume the null character '\0'
+            if(this.reader.read() <= 0) {
+                this.eof = true;
+                return false;
+            }
+            this.reader.reset();
+        } catch (IOException e) {
+            throw new JSONException("Unable to read the next character from the stream", e);
+        }
+        return true;
+    }
+
+
+    /**
+     * Get the next character in the source string.
+     *
+     * @return The next character, or 0 if past the end of the source string.
+     * @throws JSONException Thrown if there is an error reading the source string.
+     */
+    public char next() throws JSONException {
+        int c;
+        if (this.usePrevious) {
+            this.usePrevious = false;
+            c = this.previous;
+        } else {
+            try {
+                c = this.reader.read();
+            } catch (IOException exception) {
+                throw new JSONException(exception);
+            }
+        }
+        if (c <= 0) { // End of stream
+            this.eof = true;
+            return 0;
+        }
+        this.incrementIndexes(c);
+        this.previous = (char) c;
+        return this.previous;
+    }
+
+    /**
+     * Increments the internal indexes according to the previous character
+     * read and the character passed as the current character.
+     * @param c the current character read.
+     */
+    private void incrementIndexes(int c) {
+        if(c > 0) {
+            this.index++;
+            if(c=='\r') {
+                this.line++;
+                this.characterPreviousLine = this.character;
+                this.character=0;
+            }else if (c=='\n') {
+                if(this.previous != '\r') {
+                    this.line++;
+                    this.characterPreviousLine = this.character;
+                }
+                this.character=0;
+            } else {
+                this.character++;
+            }
+        }
+    }
+
+    /**
+     * Consume the next character, and check that it matches a specified
+     * character.
+     * @param c The character to match.
+     * @return The character.
+     * @throws JSONException if the character does not match.
+     */
+    public char next(char c) throws JSONException {
+        char n = this.next();
+        if (n != c) {
+            if(n > 0) {
+                throw this.syntaxError("Expected '" + c + "' and instead saw '" +
+                        n + "'");
+            }
+            throw this.syntaxError("Expected '" + c + "' and instead saw ''");
+        }
+        return n;
+    }
+
+
+    /**
+     * Get the next n characters.
+     *
+     * @param n     The number of characters to take.
+     * @return      A string of n characters.
+     * @throws JSONException
+     *   Substring bounds error if there are not
+     *   n characters remaining in the source string.
+     */
+    public String next(int n) throws JSONException {
+        if (n == 0) {
+            return "";
+        }
+
+        char[] chars = new char[n];
+        int pos = 0;
+
+        while (pos < n) {
+            chars[pos] = this.next();
+            if (this.end()) {
+                throw this.syntaxError("Substring bounds error");
+            }
+            pos += 1;
+        }
+        return new String(chars);
+    }
+
+
+    /**
+     * Get the next char in the string, skipping whitespace.
+     * @throws JSONException Thrown if there is an error reading the source string.
+     * @return  A character, or 0 if there are no more characters.
+     */
+    public char nextClean() throws JSONException {
+        for (;;) {
+            char c = this.next();
+            if (c == 0 || c > ' ') {
+                return c;
+            }
+        }
+    }
+
+
+    /**
+     * Return the characters up to the next close quote character.
+     * Backslash processing is done. The formal JSON format does not
+     * allow strings in single quotes, but an implementation is allowed to
+     * accept them.
+     * @param quote The quoting character, either
+     *      " (double quote) or
+     *      ' (single quote).
+     * @return      A String.
+     * @throws JSONException Unterminated string.
+     */
+    public String nextString(char quote) throws JSONException {
+        char c;
+        StringBuilder sb = new StringBuilder();
+        for (;;) {
+            c = this.next();
+            switch (c) {
+            case 0:
+            case '\n':
+            case '\r':
+                throw this.syntaxError("Unterminated string");
+            case '\\':
+                c = this.next();
+                switch (c) {
+                case 'b':
+                    sb.append('\b');
+                    break;
+                case 't':
+                    sb.append('\t');
+                    break;
+                case 'n':
+                    sb.append('\n');
+                    break;
+                case 'f':
+                    sb.append('\f');
+                    break;
+                case 'r':
+                    sb.append('\r');
+                    break;
+                case 'u':
+                    try {
+                        sb.append((char)Integer.parseInt(this.next(4), 16));
+                    } catch (NumberFormatException e) {
+                        throw this.syntaxError("Illegal escape.", e);
+                    }
+                    break;
+                case '"':
+                case '\'':
+                case '\\':
+                case '/':
+                    sb.append(c);
+                    break;
+                default:
+                    throw this.syntaxError("Illegal escape.");
+                }
+                break;
+            default:
+                if (c == quote) {
+                    return sb.toString();
+                }
+                sb.append(c);
+            }
+        }
+    }
+
+
+    /**
+     * Get the text up but not including the specified character or the
+     * end of line, whichever comes first.
+     * @param  delimiter A delimiter character.
+     * @return   A string.
+     * @throws JSONException Thrown if there is an error while searching
+     *  for the delimiter
+     */
+    public String nextTo(char delimiter) throws JSONException {
+        StringBuilder sb = new StringBuilder();
+        for (;;) {
+            char c = this.next();
+            if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
+                if (c != 0) {
+                    this.back();
+                }
+                return sb.toString().trim();
+            }
+            sb.append(c);
+        }
+    }
+
+
+    /**
+     * Get the text up but not including one of the specified delimiter
+     * characters or the end of line, whichever comes first.
+     * @param delimiters A set of delimiter characters.
+     * @return A string, trimmed.
+     * @throws JSONException Thrown if there is an error while searching
+     *  for the delimiter
+     */
+    public String nextTo(String delimiters) throws JSONException {
+        char c;
+        StringBuilder sb = new StringBuilder();
+        for (;;) {
+            c = this.next();
+            if (delimiters.indexOf(c) >= 0 || c == 0 ||
+                    c == '\n' || c == '\r') {
+                if (c != 0) {
+                    this.back();
+                }
+                return sb.toString().trim();
+            }
+            sb.append(c);
+        }
+    }
+
+
+    /**
+     * Get the next value. The value can be a Boolean, Double, Integer,
+     * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
+     * @throws JSONException If syntax error.
+     *
+     * @return An object.
+     */
+    public Object nextValue() throws JSONException {
+        char c = this.nextClean();
+        String string;
+
+        switch (c) {
+        case '"':
+        case '\'':
+            return this.nextString(c);
+        case '{':
+            this.back();
+            return new JSONObject(this);
+        case '[':
+            this.back();
+            return new JSONArray(this);
+        }
+
+        /*
+         * Handle unquoted text. This could be the values true, false, or
+         * null, or it can be a number. An implementation (such as this one)
+         * is allowed to also accept non-standard forms.
+         *
+         * Accumulate characters until we reach the end of the text or a
+         * formatting character.
+         */
+
+        StringBuilder sb = new StringBuilder();
+        while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
+            sb.append(c);
+            c = this.next();
+        }
+        if (!this.eof) {
+            this.back();
+        }
+
+        string = sb.toString().trim();
+        if ("".equals(string)) {
+            throw this.syntaxError("Missing value");
+        }
+        return JSONObject.stringToValue(string);
+    }
+
+
+    /**
+     * Skip characters until the next character is the requested character.
+     * If the requested character is not found, no characters are skipped.
+     * @param to A character to skip to.
+     * @return The requested character, or zero if the requested character
+     * is not found.
+     * @throws JSONException Thrown if there is an error while searching
+     *  for the to character
+     */
+    public char skipTo(char to) throws JSONException {
+        char c;
+        try {
+            long startIndex = this.index;
+            long startCharacter = this.character;
+            long startLine = this.line;
+            this.reader.mark(1000000);
+            do {
+                c = this.next();
+                if (c == 0) {
+                    // in some readers, reset() may throw an exception if
+                    // the remaining portion of the input is greater than
+                    // the mark size (1,000,000 above).
+                    this.reader.reset();
+                    this.index = startIndex;
+                    this.character = startCharacter;
+                    this.line = startLine;
+                    return 0;
+                }
+            } while (c != to);
+            this.reader.mark(1);
+        } catch (IOException exception) {
+            throw new JSONException(exception);
+        }
+        this.back();
+        return c;
+    }
+
+    /**
+     * Make a JSONException to signal a syntax error.
+     *
+     * @param message The error message.
+     * @return  A JSONException object, suitable for throwing
+     */
+    public JSONException syntaxError(String message) {
+        return new JSONException(message + this.toString());
+    }
+
+    /**
+     * Make a JSONException to signal a syntax error.
+     *
+     * @param message The error message.
+     * @param causedBy The throwable that caused the error.
+     * @return  A JSONException object, suitable for throwing
+     */
+    public JSONException syntaxError(String message, Throwable causedBy) {
+        return new JSONException(message + this.toString(), causedBy);
+    }
+
+    /**
+     * Make a printable string of this JSONTokener.
+     *
+     * @return " at {index} [character {character} line {line}]"
+     */
+    @Override
+    public String toString() {
+        return " at " + this.index + " [character " + this.character + " line " +
+                this.line + "]";
+    }
+}
diff --git a/src/main/java/json/JSONWriter.java b/src/main/java/json/JSONWriter.java
new file mode 100644
index 0000000..1576383
--- /dev/null
+++ b/src/main/java/json/JSONWriter.java
@@ -0,0 +1,413 @@
+package json;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+/*
+Copyright (c) 2006 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * JSONWriter provides a quick and convenient way of producing JSON text.
+ * The texts produced strictly conform to JSON syntax rules. No whitespace is
+ * added, so the results are ready for transmission or storage. Each instance of
+ * JSONWriter can produce one JSON text.
+ * 
+ * A JSONWriter instance provides a value method for appending
+ * values to the
+ * text, and a key
+ * method for adding keys before values in objects. There are array
+ * and endArray methods that make and bound array values, and
+ * object and endObject methods which make and bound
+ * object values. All of these methods return the JSONWriter instance,
+ * permitting a cascade style. For example, 
+ * new JSONWriter(myWriter)
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject(); which writes 
+ * {"JSON":"Hello, World!"}
+ * 
+ * The first method called must be array or object.
+ * There are no methods for adding commas or colons. JSONWriter adds them for
+ * you. Objects and arrays can be nested up to 200 levels deep.
+ * 
+ * This can sometimes be easier than using a JSONObject to build a string.
+ * @author JSON.org
+ * @version 2016-08-08
+ */
+public class JSONWriter {
+    private static final int maxdepth = 200;
+
+    /**
+     * The comma flag determines if a comma should be output before the next
+     * value.
+     */
+    private boolean comma;
+
+    /**
+     * The current mode. Values:
+     * 'a' (array),
+     * 'd' (done),
+     * 'i' (initial),
+     * 'k' (key),
+     * 'o' (object).
+     */
+    protected char mode;
+
+    /**
+     * The object/array stack.
+     */
+    private final JSONObject stack[];
+
+    /**
+     * The stack top index. A value of 0 indicates that the stack is empty.
+     */
+    private int top;
+
+    /**
+     * The writer that will receive the output.
+     */
+    protected Appendable writer;
+
+    /**
+     * Make a fresh JSONWriter. It can be used to build one JSON text.
+     */
+    public JSONWriter(Appendable w) {
+        this.comma = false;
+        this.mode = 'i';
+        this.stack = new JSONObject[maxdepth];
+        this.top = 0;
+        this.writer = w;
+    }
+
+    /**
+     * Append a value.
+     * @param string A string value.
+     * @return this
+     * @throws JSONException If the value is out of sequence.
+     */
+    private JSONWriter append(String string) throws JSONException {
+        if (string == null) {
+            throw new JSONException("Null pointer");
+        }
+        if (this.mode == 'o' || this.mode == 'a') {
+            try {
+                if (this.comma && this.mode == 'a') {
+                    this.writer.append(',');
+                }
+                this.writer.append(string);
+            } catch (IOException e) {
+            	// Android as of API 25 does not support this exception constructor
+            	// however we won't worry about it. If an exception is happening here
+            	// it will just throw a "Method not found" exception instead.
+                throw new JSONException(e);
+            }
+            if (this.mode == 'o') {
+                this.mode = 'k';
+            }
+            this.comma = true;
+            return this;
+        }
+        throw new JSONException("Value out of sequence.");
+    }
+
+    /**
+     * Begin appending a new array. All values until the balancing
+     * endArray will be appended to this array. The
+     * endArray method must be called to mark the array's end.
+     * @return this
+     * @throws JSONException If the nesting is too deep, or if the object is
+     * started in the wrong place (for example as a key or after the end of the
+     * outermost array or object).
+     */
+    public JSONWriter array() throws JSONException {
+        if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
+            this.push(null);
+            this.append("[");
+            this.comma = false;
+            return this;
+        }
+        throw new JSONException("Misplaced array.");
+    }
+
+    /**
+     * End something.
+     * @param m Mode
+     * @param c Closing character
+     * @return this
+     * @throws JSONException If unbalanced.
+     */
+    private JSONWriter end(char m, char c) throws JSONException {
+        if (this.mode != m) {
+            throw new JSONException(m == 'a'
+                ? "Misplaced endArray."
+                : "Misplaced endObject.");
+        }
+        this.pop(m);
+        try {
+            this.writer.append(c);
+        } catch (IOException e) {
+        	// Android as of API 25 does not support this exception constructor
+        	// however we won't worry about it. If an exception is happening here
+        	// it will just throw a "Method not found" exception instead.
+            throw new JSONException(e);
+        }
+        this.comma = true;
+        return this;
+    }
+
+    /**
+     * End an array. This method most be called to balance calls to
+     * array.
+     * @return this
+     * @throws JSONException If incorrectly nested.
+     */
+    public JSONWriter endArray() throws JSONException {
+        return this.end('a', ']');
+    }
+
+    /**
+     * End an object. This method most be called to balance calls to
+     * object.
+     * @return this
+     * @throws JSONException If incorrectly nested.
+     */
+    public JSONWriter endObject() throws JSONException {
+        return this.end('k', '}');
+    }
+
+    /**
+     * Append a key. The key will be associated with the next value. In an
+     * object, every value must be preceded by a key.
+     * @param string A key string.
+     * @return this
+     * @throws JSONException If the key is out of place. For example, keys
+     *  do not belong in arrays or if the key is null.
+     */
+    public JSONWriter key(String string) throws JSONException {
+        if (string == null) {
+            throw new JSONException("Null key.");
+        }
+        if (this.mode == 'k') {
+            try {
+                JSONObject topObject = this.stack[this.top - 1];
+                // don't use the built in putOnce method to maintain Android support
+				if(topObject.has(string)) {
+					throw new JSONException("Duplicate key \"" + string + "\"");
+				}
+                topObject.put(string, true);
+                if (this.comma) {
+                    this.writer.append(',');
+                }
+                this.writer.append(JSONObject.quote(string));
+                this.writer.append(':');
+                this.comma = false;
+                this.mode = 'o';
+                return this;
+            } catch (IOException e) {
+            	// Android as of API 25 does not support this exception constructor
+            	// however we won't worry about it. If an exception is happening here
+            	// it will just throw a "Method not found" exception instead.
+                throw new JSONException(e);
+            }
+        }
+        throw new JSONException("Misplaced key.");
+    }
+
+
+    /**
+     * Begin appending a new object. All keys and values until the balancing
+     * endObject will be appended to this object. The
+     * endObject method must be called to mark the object's end.
+     * @return this
+     * @throws JSONException If the nesting is too deep, or if the object is
+     * started in the wrong place (for example as a key or after the end of the
+     * outermost array or object).
+     */
+    public JSONWriter object() throws JSONException {
+        if (this.mode == 'i') {
+            this.mode = 'o';
+        }
+        if (this.mode == 'o' || this.mode == 'a') {
+            this.append("{");
+            this.push(new JSONObject());
+            this.comma = false;
+            return this;
+        }
+        throw new JSONException("Misplaced object.");
+
+    }
+
+
+    /**
+     * Pop an array or object scope.
+     * @param c The scope to close.
+     * @throws JSONException If nesting is wrong.
+     */
+    private void pop(char c) throws JSONException {
+        if (this.top <= 0) {
+            throw new JSONException("Nesting error.");
+        }
+        char m = this.stack[this.top - 1] == null ? 'a' : 'k';
+        if (m != c) {
+            throw new JSONException("Nesting error.");
+        }
+        this.top -= 1;
+        this.mode = this.top == 0
+            ? 'd'
+            : this.stack[this.top - 1] == null
+            ? 'a'
+            : 'k';
+    }
+
+    /**
+     * Push an array or object scope.
+     * @param jo The scope to open.
+     * @throws JSONException If nesting is too deep.
+     */
+    private void push(JSONObject jo) throws JSONException {
+        if (this.top >= maxdepth) {
+            throw new JSONException("Nesting too deep.");
+        }
+        this.stack[this.top] = jo;
+        this.mode = jo == null ? 'a' : 'k';
+        this.top += 1;
+    }
+
+    /**
+     * Make a JSON text of an Object value. If the object has an
+     * value.toJSONString() method, then that method will be used to produce the
+     * JSON text. The method is required to produce a strictly conforming text.
+     * If the object does not contain a toJSONString method (which is the most
+     * common case), then a text will be produced by other means. If the value
+     * is an array or Collection, then a JSONArray will be made from it and its
+     * toJSONString method will be called. If the value is a MAP, then a
+     * JSONObject will be made from it and its toJSONString method will be
+     * called. Otherwise, the value's toString method will be called, and the
+     * result will be quoted.
+     *
+     * 
+     * Warning: This method assumes that the data structure is acyclical.
+     *
+     * @param value
+     *            The value to be serialized.
+     * @return a printable, displayable, transmittable representation of the
+     *         object, beginning with { (left
+     *         brace) and ending with } (right
+     *         brace).
+     * @throws JSONException
+     *             If the value is or contains an invalid number.
+     */
+    public static String valueToString(Object value) throws JSONException {
+        if (value == null || value.equals(null)) {
+            return "null";
+        }
+        if (value instanceof JSONString) {
+            String object;
+            try {
+                object = ((JSONString) value).toJSONString();
+            } catch (Exception e) {
+                throw new JSONException(e);
+            }
+            if (object != null) {
+                return object;
+            }
+            throw new JSONException("Bad value from toJSONString: " + object);
+        }
+        if (value instanceof Number) {
+            // not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
+            final String numberAsString = JSONObject.numberToString((Number) value);
+            if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) {
+                // Close enough to a JSON number that we will return it unquoted
+                return numberAsString;
+            }
+            // The Number value is not a valid JSON number.
+            // Instead we will quote it as a string
+            return JSONObject.quote(numberAsString);
+        }
+        if (value instanceof Boolean || value instanceof JSONObject
+                || value instanceof JSONArray) {
+            return value.toString();
+        }
+        if (value instanceof Map) {
+            Map, ?> map = (Map, ?>) value;
+            return new JSONObject(map).toString();
+        }
+        if (value instanceof Collection) {
+            Collection> coll = (Collection>) value;
+            return new JSONArray(coll).toString();
+        }
+        if (value.getClass().isArray()) {
+            return new JSONArray(value).toString();
+        }
+        if(value instanceof Enum>){
+            return JSONObject.quote(((Enum>)value).name());
+        }
+        return JSONObject.quote(value.toString());
+    }
+
+    /**
+     * Append either the value true or the value
+     * false.
+     * @param b A boolean.
+     * @return this
+     * @throws JSONException
+     */
+    public JSONWriter value(boolean b) throws JSONException {
+        return this.append(b ? "true" : "false");
+    }
+
+    /**
+     * Append a double value.
+     * @param d A double.
+     * @return this
+     * @throws JSONException If the number is not finite.
+     */
+    public JSONWriter value(double d) throws JSONException {
+        return this.value(Double.valueOf(d));
+    }
+
+    /**
+     * Append a long value.
+     * @param l A long.
+     * @return this
+     * @throws JSONException
+     */
+    public JSONWriter value(long l) throws JSONException {
+        return this.append(Long.toString(l));
+    }
+
+
+    /**
+     * Append an object value.
+     * @param object The object to append. It can be null, or a Boolean, Number,
+     *   String, JSONObject, or JSONArray, or an object that implements JSONString.
+     * @return this
+     * @throws JSONException If the value is out of sequence.
+     */
+    public JSONWriter value(Object object) throws JSONException {
+        return this.append(valueToString(object));
+    }
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
new file mode 100644
index 0000000..1478a00
--- /dev/null
+++ b/src/main/resources/config.yml
@@ -0,0 +1,24 @@
+# 当前服务端玩法
+GameMode: BlockWars
+# 调试模式
+MainDebug: false
+# 请勿调动此参数
+GiftDelay: 6000
+# 直播平台
+LivePlatform: DouYin
+# 后台输出信息
+Settings:
+  # 礼物
+  debug_gifts: false
+  # 点赞
+  debug_dianzan: false
+  # 关注
+  debug_guanzhu: false
+  # 进入
+  debug_join: true
+  # 聊天消息
+  debug_message: true
+  # 进入提示信息
+  join_show: true
+AutoLink: false
+LiveId: {}
\ No newline at end of file
diff --git a/src/main/resources/keyconfig.yml b/src/main/resources/keyconfig.yml
new file mode 100644
index 0000000..d1e48f8
--- /dev/null
+++ b/src/main/resources/keyconfig.yml
@@ -0,0 +1,22 @@
+# 1 = 左Shift 16 = 右Shift
+# 2 = 左Ctrl 32 = 右Ctrl
+# 8 = 左Alt 128 = 右Alt
+32+21: 加油鸭 #右Ctrl+Y
+32+22: 小心心 #右Ctrl+U
+32+23: 玫瑰 #右Ctrl+I
+32+24: 你最好看 #右Ctrl+O
+32+25: 抖音 #右Ctrl+P
+32+26: 爱你哟 #右Ctrl+{
+32+27: 为你闪耀 #右Ctrl+}
+32+43: 送你花花 #右Ctrl+|
+32+34: 亲吻 #右Ctrl+G
+32+35: 真的爱你 #右Ctrl+H
+32+36: 闪耀星辰 #右Ctrl+J
+32+37: 爱的纸鹤 #右Ctrl+K
+32+38: 比心兔兔 #右Ctrl+L
+32+39: 私人飞机 #右Ctrl+:
+32+40: 直升机 #右Ctrl+"
+32+47: 大啤酒 #右Ctrl+V
+32+48: 人气票 #右Ctrl+B
+32+49: 荧光棒 #右Ctrl+N
+32+50: Thuglife #右Ctrl+M
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
new file mode 100644
index 0000000..a3ee14d
--- /dev/null
+++ b/src/main/resources/plugin.yml
@@ -0,0 +1,8 @@
+name: McLiveAPI
+version: 1.3
+main: com.io.yutian.mclive.Main
+api-version: 1.18
+author: yutian
+
+commands:
+  mclive:
\ No newline at end of file
diff --git a/target/classes/com/io/yutian/livemutually/liveroom/Chat.class b/target/classes/com/io/yutian/livemutually/liveroom/Chat.class
new file mode 100644
index 0000000..4ea9b4d
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/liveroom/Chat.class differ
diff --git a/target/classes/com/io/yutian/livemutually/liveroom/Follow.class b/target/classes/com/io/yutian/livemutually/liveroom/Follow.class
new file mode 100644
index 0000000..c1def0c
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/liveroom/Follow.class differ
diff --git a/target/classes/com/io/yutian/livemutually/liveroom/Gift.class b/target/classes/com/io/yutian/livemutually/liveroom/Gift.class
new file mode 100644
index 0000000..a34aec3
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/liveroom/Gift.class differ
diff --git a/target/classes/com/io/yutian/livemutually/liveroom/KSLiveRoomClient.class b/target/classes/com/io/yutian/livemutually/liveroom/KSLiveRoomClient.class
new file mode 100644
index 0000000..18c0418
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/liveroom/KSLiveRoomClient.class differ
diff --git a/target/classes/com/io/yutian/livemutually/liveroom/Like.class b/target/classes/com/io/yutian/livemutually/liveroom/Like.class
new file mode 100644
index 0000000..9bad31e
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/liveroom/Like.class differ
diff --git a/target/classes/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.class b/target/classes/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.class
new file mode 100644
index 0000000..f1abefc
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.class differ
diff --git a/target/classes/com/io/yutian/livemutually/liveroom/User.class b/target/classes/com/io/yutian/livemutually/liveroom/User.class
new file mode 100644
index 0000000..40bccba
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/liveroom/User.class differ
diff --git a/target/classes/com/io/yutian/livemutually/manager/KSLiveRoomManager.class b/target/classes/com/io/yutian/livemutually/manager/KSLiveRoomManager.class
new file mode 100644
index 0000000..495c486
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/manager/KSLiveRoomManager.class differ
diff --git a/target/classes/com/io/yutian/livemutually/wss/KSAPILiveRoomWatcher.class b/target/classes/com/io/yutian/livemutually/wss/KSAPILiveRoomWatcher.class
new file mode 100644
index 0000000..1f4dc70
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/wss/KSAPILiveRoomWatcher.class differ
diff --git a/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient$1.class b/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient$1.class
new file mode 100644
index 0000000..9178d31
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient$1.class differ
diff --git a/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient$2.class b/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient$2.class
new file mode 100644
index 0000000..f1b7163
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient$2.class differ
diff --git a/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient.class b/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient.class
new file mode 100644
index 0000000..8bbc45a
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/wss/KSWebSocketClient.class differ
diff --git a/target/classes/com/io/yutian/livemutually/wss/KuaiShouChat.class b/target/classes/com/io/yutian/livemutually/wss/KuaiShouChat.class
new file mode 100644
index 0000000..afb66a6
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/wss/KuaiShouChat.class differ
diff --git a/target/classes/com/io/yutian/livemutually/wss/KuaiShouGift.class b/target/classes/com/io/yutian/livemutually/wss/KuaiShouGift.class
new file mode 100644
index 0000000..12b28bb
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/wss/KuaiShouGift.class differ
diff --git a/target/classes/com/io/yutian/livemutually/wss/KuaiShouLike.class b/target/classes/com/io/yutian/livemutually/wss/KuaiShouLike.class
new file mode 100644
index 0000000..aca5146
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/wss/KuaiShouLike.class differ
diff --git a/target/classes/com/io/yutian/livemutually/wss/KuaiShouUser.class b/target/classes/com/io/yutian/livemutually/wss/KuaiShouUser.class
new file mode 100644
index 0000000..2fcee99
Binary files /dev/null and b/target/classes/com/io/yutian/livemutually/wss/KuaiShouUser.class differ
diff --git a/target/classes/com/io/yutian/mclive/ConfigYml.class b/target/classes/com/io/yutian/mclive/ConfigYml.class
new file mode 100644
index 0000000..4498583
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/ConfigYml.class differ
diff --git a/target/classes/com/io/yutian/mclive/LinkRoom.class b/target/classes/com/io/yutian/mclive/LinkRoom.class
new file mode 100644
index 0000000..a08a6ed
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/LinkRoom.class differ
diff --git a/target/classes/com/io/yutian/mclive/LiveAdminGui.class b/target/classes/com/io/yutian/mclive/LiveAdminGui.class
new file mode 100644
index 0000000..96e71d3
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/LiveAdminGui.class differ
diff --git a/target/classes/com/io/yutian/mclive/LiveEvent.class b/target/classes/com/io/yutian/mclive/LiveEvent.class
new file mode 100644
index 0000000..05f781d
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/LiveEvent.class differ
diff --git a/target/classes/com/io/yutian/mclive/Main.class b/target/classes/com/io/yutian/mclive/Main.class
new file mode 100644
index 0000000..c1bc3fa
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/Main.class differ
diff --git a/target/classes/com/io/yutian/mclive/event/LiveChatEvents.class b/target/classes/com/io/yutian/mclive/event/LiveChatEvents.class
new file mode 100644
index 0000000..8f9fa30
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/event/LiveChatEvents.class differ
diff --git a/target/classes/com/io/yutian/mclive/event/LiveEnterEvents.class b/target/classes/com/io/yutian/mclive/event/LiveEnterEvents.class
new file mode 100644
index 0000000..8577bd3
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/event/LiveEnterEvents.class differ
diff --git a/target/classes/com/io/yutian/mclive/event/LiveFollowEvents.class b/target/classes/com/io/yutian/mclive/event/LiveFollowEvents.class
new file mode 100644
index 0000000..4421290
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/event/LiveFollowEvents.class differ
diff --git a/target/classes/com/io/yutian/mclive/event/LiveGiftEvents.class b/target/classes/com/io/yutian/mclive/event/LiveGiftEvents.class
new file mode 100644
index 0000000..fb71780
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/event/LiveGiftEvents.class differ
diff --git a/target/classes/com/io/yutian/mclive/event/LiveLikeEvents.class b/target/classes/com/io/yutian/mclive/event/LiveLikeEvents.class
new file mode 100644
index 0000000..6e5bfdc
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/event/LiveLikeEvents.class differ
diff --git a/target/classes/com/io/yutian/mclive/event/ZhuboAPI.class b/target/classes/com/io/yutian/mclive/event/ZhuboAPI.class
new file mode 100644
index 0000000..92f8f66
Binary files /dev/null and b/target/classes/com/io/yutian/mclive/event/ZhuboAPI.class differ
diff --git a/target/classes/com/io/yutian/verify/AESUtil.class b/target/classes/com/io/yutian/verify/AESUtil.class
new file mode 100644
index 0000000..66e6c27
Binary files /dev/null and b/target/classes/com/io/yutian/verify/AESUtil.class differ
diff --git a/target/classes/com/io/yutian/verify/PluginVerifyResult.class b/target/classes/com/io/yutian/verify/PluginVerifyResult.class
new file mode 100644
index 0000000..d483c3d
Binary files /dev/null and b/target/classes/com/io/yutian/verify/PluginVerifyResult.class differ
diff --git a/target/classes/com/io/yutian/verify/VerifyHandler.class b/target/classes/com/io/yutian/verify/VerifyHandler.class
new file mode 100644
index 0000000..a20f5e7
Binary files /dev/null and b/target/classes/com/io/yutian/verify/VerifyHandler.class differ
diff --git a/target/classes/config.yml b/target/classes/config.yml
new file mode 100644
index 0000000..b50f40e
--- /dev/null
+++ b/target/classes/config.yml
@@ -0,0 +1,24 @@
+# ǰ淨
+GameMode: BlockWars
+# ģʽ
+MainDebug: false
+# ˲
+GiftDelay: 6000
+# ֱƽ̨
+LivePlatform: DouYin
+# ̨Ϣ
+Settings:
+  # 
+  debug_gifts: false
+  # 
+  debug_dianzan: false
+  # ע
+  debug_guanzhu: false
+  # 
+  debug_join: true
+  # Ϣ
+  debug_message: true
+  # ʾϢ
+  join_show: true
+AutoLink: false
+LiveId: {}
\ No newline at end of file
diff --git a/target/classes/json/JSONArray.class b/target/classes/json/JSONArray.class
new file mode 100644
index 0000000..1564b0f
Binary files /dev/null and b/target/classes/json/JSONArray.class differ
diff --git a/target/classes/json/JSONException.class b/target/classes/json/JSONException.class
new file mode 100644
index 0000000..a12d327
Binary files /dev/null and b/target/classes/json/JSONException.class differ
diff --git a/target/classes/json/JSONObject$Null.class b/target/classes/json/JSONObject$Null.class
new file mode 100644
index 0000000..2a408e7
Binary files /dev/null and b/target/classes/json/JSONObject$Null.class differ
diff --git a/target/classes/json/JSONObject.class b/target/classes/json/JSONObject.class
new file mode 100644
index 0000000..246e34d
Binary files /dev/null and b/target/classes/json/JSONObject.class differ
diff --git a/target/classes/json/JSONPointer$Builder.class b/target/classes/json/JSONPointer$Builder.class
new file mode 100644
index 0000000..8c00583
Binary files /dev/null and b/target/classes/json/JSONPointer$Builder.class differ
diff --git a/target/classes/json/JSONPointer.class b/target/classes/json/JSONPointer.class
new file mode 100644
index 0000000..6852361
Binary files /dev/null and b/target/classes/json/JSONPointer.class differ
diff --git a/target/classes/json/JSONPointerException.class b/target/classes/json/JSONPointerException.class
new file mode 100644
index 0000000..f7d9c66
Binary files /dev/null and b/target/classes/json/JSONPointerException.class differ
diff --git a/target/classes/json/JSONPropertyIgnore.class b/target/classes/json/JSONPropertyIgnore.class
new file mode 100644
index 0000000..5e092d5
Binary files /dev/null and b/target/classes/json/JSONPropertyIgnore.class differ
diff --git a/target/classes/json/JSONPropertyName.class b/target/classes/json/JSONPropertyName.class
new file mode 100644
index 0000000..6fae440
Binary files /dev/null and b/target/classes/json/JSONPropertyName.class differ
diff --git a/target/classes/json/JSONString.class b/target/classes/json/JSONString.class
new file mode 100644
index 0000000..2ca4132
Binary files /dev/null and b/target/classes/json/JSONString.class differ
diff --git a/target/classes/json/JSONStringer.class b/target/classes/json/JSONStringer.class
new file mode 100644
index 0000000..ab5b7d5
Binary files /dev/null and b/target/classes/json/JSONStringer.class differ
diff --git a/target/classes/json/JSONTokener.class b/target/classes/json/JSONTokener.class
new file mode 100644
index 0000000..5ca4c55
Binary files /dev/null and b/target/classes/json/JSONTokener.class differ
diff --git a/target/classes/json/JSONWriter.class b/target/classes/json/JSONWriter.class
new file mode 100644
index 0000000..ffb752b
Binary files /dev/null and b/target/classes/json/JSONWriter.class differ
diff --git a/target/classes/keyconfig.yml b/target/classes/keyconfig.yml
new file mode 100644
index 0000000..fe1398e
--- /dev/null
+++ b/target/classes/keyconfig.yml
@@ -0,0 +1,22 @@
+# 1 = Shift 16 = Shift
+# 2 = Ctrl 32 = Ctrl
+# 8 = Alt 128 = Alt
+32+21: Ѽ #Ctrl+Y
+32+22: С #Ctrl+U
+32+23: õ #Ctrl+I
+32+24: ÿ #Ctrl+O
+32+25:  #Ctrl+P
+32+26: Ӵ #Ctrl+{
+32+27: Ϊҫ #Ctrl+}
+32+43: 㻨 #Ctrl+|
+32+34:  #Ctrl+G
+32+35: İ #Ctrl+H
+32+36: ҫdz #Ctrl+J
+32+37: ֽ #Ctrl+K
+32+38:  #Ctrl+L
+32+39: ˽˷ɻ #Ctrl+:
+32+40: ֱ #Ctrl+"
+32+47: ơ #Ctrl+V
+32+48: Ʊ #Ctrl+B
+32+49: ӫ #Ctrl+N
+32+50: Thuglife #Ctrl+M
diff --git a/target/classes/plugin.yml b/target/classes/plugin.yml
new file mode 100644
index 0000000..a3ee14d
--- /dev/null
+++ b/target/classes/plugin.yml
@@ -0,0 +1,8 @@
+name: McLiveAPI
+version: 1.3
+main: com.io.yutian.mclive.Main
+api-version: 1.18
+author: yutian
+
+commands:
+  mclive:
\ No newline at end of file