diff -u1 -rN MCP-Reborn-vanilla/build.gradle MCP-Reborn/build.gradle
--- MCP-Reborn-vanilla/build.gradle	2024-03-10 15:43:28.553996503 +0800
+++ MCP-Reborn/build.gradle	2024-03-10 18:29:57.541071038 +0800
@@ -3,8 +3,31 @@
         mavenLocal()
-        maven { url = 'http://files.minecraftforge.net/maven' }
-        jcenter()
+
+        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+        // Configure a custom proxy
+        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
         mavenCentral()
+        jcenter()
+        maven { url = "https://repo.spongepowered.org/maven/"}
     }
     dependencies {
-        classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.+'
+        classpath 'net.minecraftforge.gradle:ForgeGradle:4.1.10'
+        // classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
+        // 22-05-2023 (Miffyli) Removed as not needed and failed the build // classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT'
+        // classpath group: 'com.googlecode.mavennatives', name: 'maven-nativedependencies-plugin', version: '0.0.5'
+        classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.4'
+
+        // Add BINPATCHER dependency here so that it is downloaded through our proxy, otherwise the build fails when
+        // we remove the rogue repositories that get added by the MCForge plugin.
+        // public static final String BINPATCHER =  "net.minecraftforge:binarypatcher:1.+:fatjar";
+        classpath 'net.minecraftforge:binarypatcher:1.+:fatjar' // match version with what ForgeGradle specifies
+
+        configurations {
+        compileClasspath {
+            resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+                if (details.requested.group == 'org.apache.logging.log4j') {
+                    details.useVersion '2.17.1'
+                }
+            }
+        }
+    }
     }
@@ -12,4 +35,145 @@
 
-apply plugin: 'eclipse'
+project.repositories.clear()
+repositories {
+    mavenCentral()
+    jcenter()
+    maven { url = "https://repo.spongepowered.org/maven/"}
+}
+
+// apply plugin: 'net.minecraftforge.gradle'
 apply plugin: 'net.minecraftforge.gradle.forgedev.patcher'
+// 22-05-2023 (Miffyli) Removed as not needed and failed the build // apply plugin: 'org.spongepowered.mixin'
+// apply plugin: 'unpacknatives'
+
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+apply plugin: 'java'
+apply plugin: 'com.github.johnrengelman.shadow'
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// Remove repositories that we proxy that have been added by plugins
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ext.print_project_repositories = { ->
+    project.repositories.each {
+        if (it.name.startsWith("BUNDELED")) {
+            println " | - " + it.name + " -> downloads to: " + it.root
+            // extra info:
+//            println "   | - " + it.repository
+//            println "   | - " + it.repository.provider
+//            println "   | - " + it.repository.provider.mapper
+//            println "   | - " + it.repository.provider.providers
+//            println "   | - " + it.root
+//            println "   | - " + it.cache
+        } else if (it instanceof MavenArtifactRepository) {
+            println " | - " + it.name + " -> " + it.url
+        } else {
+            println " | - " + it.name + " - " + it.class
+        }
+    }
+}
+
+println "* * * * * * * * * * * * * * * * * * * * * * * * "
+println "These are the repositories after plugins have been applied:"
+print_project_repositories()
+
+
+println "* * * * * * * * * * * * * * * * * * * * * * * * "
+println "Repositories after removing proxied repos:"
+print_project_repositories()
+println "* * * * * * * * * * * * * * * * * * * * * * * * "
+
+// this is an additional task that can be used for final debugging of repositories
+task listrepos {
+    doLast {
+        println "Repositories:"
+        print_project_repositories()
+    }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// verifyRepos: Task that auto-runs before each other task and checks whether there are rogue repositories added
+// somewhere else in the code or by plugins.
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+task verifyRepos {
+    doLast {
+        boolean allReposOk = true;
+
+        // -- CHECK BUILD SCRIPT REPOS
+        /*
+        Note it is actually too late here to check the build script repos, since buildScript executes and applies
+        plugins immediately without having a hook in between to check for the repositories.
+            >> only buildscript {} and other plugins {} script blocks are allowed before plugins {} blocks, no
+            >> other statements are allowed.
+        However, we can still fail the build so that the user changes the configuration after 1 build that
+        leaked repositories during buildScript phase.
+         */
+        println "Checking BuildScript Repositories:"
+        project.buildscript.repositories.each {
+            if (it.name.contains("Gradle Central Plugin Repository")) {
+                // gradle plugins is ok
+                println " | - [ OK ]: " + it.name + " -> (Plugins)";
+                return
+            } else if (it instanceof MavenArtifactRepository) {
+                if (it.url.toString().startsWith("file:")) { // maybe there's a better way to check for file URI
+                    // maven repo pointing to local file
+                    println " | - [ OK ]: " + it.name + " -> " + it.url;
+                    return
+                }
+            }
+
+            // if we get here, this is not an accepted repository
+            if (it instanceof MavenArtifactRepository) {
+                println " | - [FAIL]: " + it.name + " -> " + it.url;
+            } else if (it instanceof FlatDirectoryArtifactRepository)
+                println " | - [FAIL]: " + it.name + " -> " + it.dirs;
+            else {
+                println " | - [FAIL]: " + it.name + "?" + it.class
+            }
+            // allReposOk = false
+        }
+
+        // -- CHECK PROJECT REPOS
+        println "Checking Project Repositories:"
+        project.repositories.each {
+            if (it instanceof MavenArtifactRepository) {
+                if (it.url.toString().startsWith("file:")) { // maybe there's a better way to check for file URI
+                    // maven repo pointing to local file
+                    println " | - [ OK ]: " + it.name + " -> " + it.url;
+                    return
+                }
+            } else if (it instanceof FlatDirectoryArtifactRepository) {
+                // directories are ok
+                println " | - [ OK ]: " + it.name + " -> " + it.dirs;
+                return
+            } else if (it.name.startsWith("BUNDELED_")) {
+                // we whitelist this repository because it is not easily proxyable
+                println " | - [ OK ]: " + it.name + " (accepted custom MC repo) ";
+                return
+            }
+
+            // if we get here, this is not an accepted repository
+            if (it instanceof MavenArtifactRepository) {
+                println " | - [FAIL]: " + it.name + " -> " + it.url;
+            } else {
+                println " | - [FAIL]: " + it.name + " ? " + it.class;
+            }
+            // allReposOk = false
+        }
+
+        if (!allReposOk) {
+            throw new GradleException("Some repositories were not accepted. This typically means you bypassed the proxy.")
+        }
+    }
+}
+
+// add `verifyRepos` as a dependency for all other tasks, so that it always executes first
+project.tasks.each {
+    if (it != verifyRepos) {
+        it.dependsOn(verifyRepos)
+    }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
@@ -21,3 +185,7 @@
 group = 'me.yourname'
-version = '1.0.0'
+version = '6.13'
+
+/* // 22-05-2023 (Miffyli) Removed as not needed and failed the build // mixin {
+    add sourceSets.main, "modid.refmap.json"
+}*/
 
@@ -30,6 +198,2 @@
 
-repositories {
-    mavenCentral()
-
-}
 
@@ -42,4 +206,39 @@
 //    compile 'package-here'
+    compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
+    // compile 'org.spongepowered:mixin:0.7.5-SNAPSHOT'
+    compile group: 'org.openpnp', name: 'opencv', version: '3.4.2-0'
+    compile group: 'com.microsoft.azure', name: 'azure-storage', version: '8.6.5'
+
+    // specify lwjgl deps explicitly to force the download of natives for all 
+    // platforms
+    compile group: 'org.lwjgl', name: 'lwjgl', version: '3.2.1', classifier: "natives-windows"
+    // compile group: 'org.lwjgl', name: 'lwjgl', version: '3.2.1', classifier: "natives-osx"
+    compile group: 'org.lwjgl', name: 'lwjgl', version: '3.2.1', classifier: "natives-linux"
+
+    compile group: 'org.lwjgl', name: 'lwjgl-glfw', version: '3.2.1', classifier: "natives-windows"
+    // compile group: 'org.lwjgl', name: 'lwjgl-glfw', version: '3.2.1', classifier: "natives-osx"
+    compile group: 'org.lwjgl', name: 'lwjgl-glfw', version: '3.2.1', classifier: "natives-linux"
+
+    compile group: 'org.lwjgl', name: 'lwjgl-jemalloc', version: '3.2.1', classifier: "natives-windows"
+    // compile group: 'org.lwjgl', name: 'lwjgl-jemalloc', version: '3.2.1', classifier: "natives-osx"
+    compile group: 'org.lwjgl', name: 'lwjgl-jemalloc', version: '3.2.1', classifier: "natives-linux"
+
+    compile group: 'org.lwjgl', name: 'lwjgl-openal', version: '3.2.1', classifier: "natives-windows"
+    // compile group: 'org.lwjgl', name: 'lwjgl-openal', version: '3.2.1', classifier: "natives-osx"
+    compile group: 'org.lwjgl', name: 'lwjgl-openal', version: '3.2.1', classifier: "natives-linux"
+
+    compile group: 'org.lwjgl', name: 'lwjgl-opengl', version: '3.2.1', classifier: "natives-windows"
+    // compile group: 'org.lwjgl', name: 'lwjgl-opengl', version: '3.2.1', classifier: "natives-osx"
+    compile group: 'org.lwjgl', name: 'lwjgl-opengl', version: '3.2.1', classifier: "natives-linux"
+
+    compile group: 'org.lwjgl', name: 'lwjgl-stb', version: '3.2.1', classifier: "natives-windows"
+    // compile group: 'org.lwjgl', name: 'lwjgl-stb', version: '3.2.1', classifier: "natives-osx"
+    compile group: 'org.lwjgl', name: 'lwjgl-stb', version: '3.2.1', classifier: "natives-linux"
+
+    compile group: 'org.lwjgl', name: 'lwjgl-tinyfd', version: '3.2.1', classifier: "natives-windows"
+    // compile group: 'org.lwjgl', name: 'lwjgl-tinyfd', version: '3.2.1', classifier: "natives-osx"
+    compile group: 'org.lwjgl', name: 'lwjgl-tinyfd', version: '3.2.1', classifier: "natives-linux"
 }
 
+
 project(':mcp') {
@@ -62,9 +261,18 @@
 jar {
-    configurations.shade.each { dep ->
-        from(project.zipTree(dep)) {
-            exclude 'META-INF', 'META-INF/**'
-        }
+    manifest {
+        attributes(
+                'Main-Class': 'net.minecraft.client.main.Main',
+                'Multi-Release': 'true'
+        )
     }
+    zip64 = true
 }
 
+
+
+if (project.hasProperty("args")) {
+    ext.cmdargs = project.getProperty("args")
+} else { 
+    ext.cmdargs = ""
+}
 task runclient(type: JavaExec) {
@@ -76,7 +284,11 @@
     }
+    if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+        jvmArgs '-Xmx6G'
+    }
     args '--gameDir', '.'
     args '--version', minecraft_version
-    args '--assetsDir', downloadAssets.output
-    args '--assetIndex', "1.16"
+    // args '--assetsDir', downloadAssets.output
+    // args '--assetIndex', "1.16"
     args '--accessToken', '0'
+    args cmdargs.split()
     main 'net.minecraft.client.main.Main'
@@ -100,3 +312,3 @@
     dependsOn ':downloadAssets'
-    mkdir 'run/assets'
+   mkdir 'run/assets'
     copy {
@@ -105,2 +317,76 @@
     }
+}
+
+task createProperties(dependsOn: processResources) {
+  doLast {
+    new File("$buildDir/resources/main/version.properties").withWriter { w ->
+        Properties p = new Properties()
+        p['version'] = project.version.toString()
+        p.store w, null
+    }
+  }
+}
+
+classes {
+    dependsOn createProperties
+}
+
+shadowJar {
+    exclude "**/Log4j2Plugins.dat"
+}
+
+gradle.projectsEvaluated {
+    // compileJava target isn't available until after forge has initialized things
+    compileJava.dependsOn jaxb
+    jaxb.dependsOn copySchemas
+    copySchemas.dependsOn deleteSchemas
+}
+
+configurations {
+    jaxb
+}
+
+dependencies {
+    jaxb group: 'com.sun.xml.bind', name: 'jaxb-xjc', version: '2.2.4-1'
+}
+
+task copySchemas(type: Copy) {
+    from '../Malmo/Schemas/'
+    into 'src/main/resources/'
+    include ('*.xsd')
+}
+
+task jaxb() {
+    description 'Generate source files for our XML schemas using JAXB'
+
+    // Create an index file listing all the schemas:
+    def schemaIndexFile = new File('src/main/resources/schemas.index')
+    def contents = ""
+    def tree = copySchemas.source
+    tree.visit { fileDetails ->
+        contents += "${fileDetails.relativePath}" + "\n"
+    }
+    schemaIndexFile.write contents
+
+    inputs.files fileTree( dir: 'src/main/resources', include:'*.xsd' )
+    outputs.dir 'src/main/java/com/microsoft/Malmo/Schemas'
+    
+    doLast {
+        // first clear the old .java files out of the folder in case some are no longer current
+        delete fileTree(dir: 'src/main/java/com/microsoft/Malmo/Schemas', include: '*.java')
+
+        // use xjc to generate java files from the XML schema
+        ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.jaxb.asPath)
+        ant.xjc( destdir: 'src/main/java', package: 'com.microsoft.Malmo.Schemas' )
+        {
+            schema( dir: 'src/main/resources', includes: '*.xsd' )
+        }
+    }
+}
+
+task deleteSchemas() {
+    doLast {
+        // first clear the old .xsd files out of the folder in case some are no longer current
+        delete fileTree(dir: 'src/main/resources', include: '*.xsd')
+    }
 }
diff -u1 -rN MCP-Reborn-vanilla/gradle/wrapper/gradle-wrapper.properties MCP-Reborn/gradle/wrapper/gradle-wrapper.properties
--- MCP-Reborn-vanilla/gradle/wrapper/gradle-wrapper.properties	2024-03-10 15:43:28.553996503 +0800
+++ MCP-Reborn/gradle/wrapper/gradle-wrapper.properties	2024-03-10 18:29:57.541071038 +0800
@@ -5,2 +5,2 @@
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip
diff -u1 -rN MCP-Reborn-vanilla/gradle.properties MCP-Reborn/gradle.properties
--- MCP-Reborn-vanilla/gradle.properties	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/gradle.properties	2024-03-10 18:29:57.541071038 +0800
@@ -0,0 +1,2 @@
+# add more heap mem for compiling  (should fix 'Expiring Daemon because JVM heap space is exhausted' error)
+org.gradle.jvmargs=-Xmx2g
diff -u1 -rN MCP-Reborn-vanilla/launchClient.bat MCP-Reborn/launchClient.bat
--- MCP-Reborn-vanilla/launchClient.bat	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/launchClient.bat	2024-03-10 18:29:57.541071038 +0800
@@ -0,0 +1,62 @@
+@echo off
+:: Test attempt to run Minecraft with commandline arguments.
+:: Eg "launchClient -channel 2"
+:: Currently must be run from within the mod dev folder.
+:: Works by generating a Minecraft config file from the commandline arguments,
+:: which the Mod then loads at initialisation time.
+
+REM Command line parser due to dbenham - see here: http://stackoverflow.com/a/8162578
+
+setlocal enableDelayedExpansion
+
+:: Define the option names along with default values, using a <space>
+:: delimiter between options.
+:: Each option has the format -name:[default]
+:: The option names are NOT case sensitive.
+::
+:: Options that have a default value expect the subsequent command line
+:: argument to contain the value. If the option is not provided then the
+:: option is set to the default. If the default contains spaces, contains
+:: special characters, or starts with a colon, then it should be enclosed
+:: within double quotes. The default can be undefined by specifying the
+:: default as empty quotes "".
+:: NOTE - defaults cannot contain * or ? with this solution.
+::
+:: Options that are specified without any default value are simply flags
+:: that are either defined or undefined. All flags start out undefined by
+:: default and become defined if the option is supplied.
+::
+:: The order of the definitions is not important.
+::
+set "options=-port:0 -replaceable: -scorepolicy:0 -env: -runDir:run  -performanceDir:NONE -seed:NONE -maxMem:4G"
+
+:: Set the default option values
+for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
+
+:loop
+:: Validate and store the options, one at a time, using a loop.
+:: Each SHIFT is done starting at the first option so required args are preserved.
+::
+if not "%~1"=="" (
+  set "test=!options:*%~1:=! "
+  if "!test!"=="!options! " (
+    rem No substitution was made so this is an invalid option.
+    rem Error handling goes here.
+    rem I will simply echo an error message.
+    echo Error: Invalid option %~1
+  ) else if "!test:~0,1!"==" " (
+    rem Set the flag option using the option name.
+    rem The value doesn't matter, it just needs to be defined.
+    set "%~1=true"
+  ) else (
+    rem Set the option value using the option as the name.
+    rem and the next arg as the value
+    set "%~1=%~2"
+    shift /1
+  )
+  shift /1
+  goto :loop
+)
+
+REM finally run Minecraft:
+call java -Xmx"!-maxMem!" -jar build/libs/mcprec-6.13.jar --envPort="!-port!"
diff -u1 -rN MCP-Reborn-vanilla/launchClient.sh MCP-Reborn/launchClient.sh
--- MCP-Reborn-vanilla/launchClient.sh	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/launchClient.sh	2024-03-10 18:29:57.541071038 +0800
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# run from the script directory
+cd "$(dirname "$0")"
+
+echo "$(dirname "$0")"
+
+replaceable=0
+port=0
+scorepolicy=0
+env=0
+seed="NONE"
+performanceDir="NONE"
+runDir="run"
+maxMem="4G"
+
+while [ $# -gt 0 ]
+do
+    case "$1" in
+        -replaceable) replaceable=1;;
+        -port) port="$2"; shift;;
+        -seed) seed="$2"; shift;;
+        -scorepolicy) scorepolicy="$2"; shift;;
+        -env) env=1;;
+        -runDir) runDir="$2"; shift;;
+        -performanceDir) performanceDir="$2"; shift;;
+        -maxMem) maxMem="$2"; shift;;
+        *) echo >&2 \
+            "usage: $0 [-replaceable] [-port 10000] [-seed 123123] [-scorepolicy 0123] [-env] [-runDir /home/asdasd] [-performanceDir /home/asdasd]"
+            exit 1;;
+    esac
+    shift
+done
+  
+if ! [[ $port =~ ^-?[0-9]+$ ]]; then
+    echo "Port value should be numeric"
+    exit 1
+fi
+
+
+if [ \( $port -lt 0 \) -o \( $port -gt 65535 \) ]; then
+    echo "Port value out of range 0-65535"
+    exit 1
+fi
+
+if ! [[ $scorepolicy =~ ^-?[0-9]+$ ]]; then
+    echo "Score policy should be numeric"
+    exit 1
+fi
+
+fatjar=build/libs/mcprec-6.13.jar
+java -Xmx$maxMem -jar $fatjar --envPort=$port
+[ $replaceable -gt 0 ]
+
diff -u1 -rN MCP-Reborn-vanilla/settings.gradle MCP-Reborn/settings.gradle
--- MCP-Reborn-vanilla/settings.gradle	2024-03-10 15:43:28.549996501 +0800
+++ MCP-Reborn/settings.gradle	2024-03-10 18:29:57.541071038 +0800
@@ -1,2 +1,2 @@
-rootProject.name = 'mcp-reborn'
+rootProject.name = 'mcprec'
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java MCP-Reborn/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java
--- MCP-Reborn-vanilla/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/microsoft/Malmo/Utils/CraftingHelper.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,807 @@
+// --------------------------------------------------------------------------------------------------
+//  Copyright (c) 2016 Microsoft Corporation
+//  
+//  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 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.
+// --------------------------------------------------------------------------------------------------
+
+package com.microsoft.Malmo.Utils;
+
+import com.google.common.base.CaseFormat;
+import com.google.gson.*;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.material.Material;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.player.ClientPlayerEntity;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.MobEntity;
+import net.minecraft.item.BlockItem;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.crafting.IRecipe;
+import net.minecraft.item.crafting.Ingredient;
+import net.minecraft.item.crafting.ShapedRecipe;
+import net.minecraft.state.DirectionProperty;
+import net.minecraft.state.EnumProperty;
+import net.minecraft.state.Property;
+import net.minecraft.stats.Stat;
+import net.minecraft.stats.StatType;
+import net.minecraft.stats.Stats;
+import net.minecraft.tileentity.FurnaceTileEntity;
+import net.minecraft.util.NonNullList;
+import net.minecraft.util.RegistryKey;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.datafix.fixes.FurnaceRecipes;
+import net.minecraft.util.registry.Registry;
+import org.lwjgl.system.CallbackI;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CraftingHelper {
+    private static Map<ClientPlayerEntity, Integer> fuelCaches = new HashMap<ClientPlayerEntity, Integer>();
+    // private static final int smeltingCookingTime = new FurnaceTileEntity().getCookTime();
+    /**
+     * Reset caches<br>
+     * Needed to make sure the player starts with a fresh fuel stash.
+     */
+    public static void reset() {
+        fuelCaches = new HashMap<ClientPlayerEntity, Integer>();
+    }
+
+    /**
+     * Attempt to return the raw ingredients required for this recipe.<br>
+     * Ignores all shaping.
+     *
+     * @param recipe the IRecipe to dissect.
+     * @return a list of ItemStacks, amalgamated so that all items of the same type are placed in the same stack.
+     */
+
+    /**
+     * Take a list of ItemStacks and amalgamate where possible.<br>
+     *
+     * @param inputStacks a list of ItemStacks
+     * @return a list of ItemStacks, where all items of the same type are grouped into one stack.
+     */
+    public static NonNullList<ItemStack> consolidateItemStacks(NonNullList<ItemStack> inputStacks) {
+        // Horrible n^2 method - we should do something nicer if this ever becomes a bottleneck.
+        NonNullList<ItemStack> outputStacks = NonNullList.create();
+        for (ItemStack sourceIS : inputStacks) {
+            boolean bFound = false;
+            for (ItemStack destIS : outputStacks) {
+                if (destIS != null && sourceIS != null && itemStackIngredientsMatch(destIS, sourceIS)) {
+                    bFound = true;
+                    destIS.setCount(destIS.getCount() + sourceIS.getCount());
+                }
+            }
+            if (!bFound) {
+                assert sourceIS != null;
+                outputStacks.add(sourceIS.copy());
+            }
+        }
+        return outputStacks;
+    }
+
+    /**
+     * Inspect a player's inventory to see whether they have enough items to form the supplied list of ItemStacks.<br>
+     * The ingredients list MUST be amalgamated such that no two ItemStacks contain the same type of item.
+     *
+     * @param player
+     * @param ingredients an amalgamated list of ingredients
+     * @return true if the player's inventory contains sufficient quantities of all the required items.
+     */
+    public static boolean playerHasIngredients(ClientPlayerEntity player, List<ItemStack> ingredients) {
+        NonNullList<ItemStack> main = player.inventory.mainInventory;
+        NonNullList<ItemStack> arm = player.inventory.armorInventory;
+
+        for (ItemStack isIngredient : ingredients) {
+            int target = isIngredient.getCount();
+            for (int i = 0; i < main.size() + arm.size() && target > 0; i++) {
+                ItemStack isPlayer = (i >= main.size()) ? arm.get(i - main.size()) : main.get(i);
+                if (isPlayer != null && isIngredient != null && itemStackIngredientsMatch(isPlayer, isIngredient))
+                    target -= isPlayer.getCount();
+            }
+            if (target > 0)
+                return false;   // Don't have enough of this.
+        }
+        return true;
+    }
+
+    /**
+     * Compare two ItemStacks and see if their items match - take wildcards into account, don't take stacksize into account.
+     *
+     * @param A ItemStack A
+     * @param B ItemStack B
+     * @return true if the stacks contain matching items.
+     */
+    private static boolean itemStackIngredientsMatch(ItemStack A, ItemStack B) {
+        if (A == null && B == null)
+            return true;
+        if (A == null || B == null)
+            return false;
+        // if (A.getMetadata() == OreDictionary.WILDCARD_VALUE || B.getMetadata() == OreDictionary.WILDCARD_VALUE)
+        //     return A.getItem() == B.getItem();
+        return ItemStack.areItemsEqual(A, B);
+    }
+
+    /**
+     * Go through player's inventory and see how much fuel they have.
+     *
+     * @param player
+     * @return the amount of fuel available in ticks
+     */
+    public static int totalBurnTimeInInventory(ClientPlayerEntity player) {
+        Integer fromCache = fuelCaches.get(player);
+        int total = (fromCache != null) ? fromCache : 0;
+        for (int i = 0; i < player.inventory.mainInventory.size(); i++) {
+            ItemStack is = player.inventory.mainInventory.get(i);
+            total += is.getCount() * FurnaceTileEntity.getBurnTimes().get(is);
+        }
+        return total;
+    }
+
+//    /**
+//     * Consume fuel from the player's inventory.<br>
+//     * Take it first from their cache, if present, and then from their inventory, starting
+//     * at the first slot and working upwards.
+//     *
+//     * @param player
+//     * @param burnAmount amount of fuel to burn, in ticks.
+//     */
+//    public static void burnInventory(ClientPlayerEntity player, int burnAmount, ItemStack input) {
+//        if (!fuelCaches.containsKey(player))
+//            fuelCaches.put(player, -burnAmount);
+//        else
+//            fuelCaches.put(player, fuelCaches.get(player) - burnAmount);
+//        int index = 0;
+//        while (fuelCaches.get(player) < 0 && index < player.inventory.mainInventory.size()) {
+//            ItemStack is = player.inventory.mainInventory.get(index);
+//            if (is != null) {
+//                int burnTime = FurnaceTileEntity.getBurnTimes().get(is);
+//                if (burnTime != 0) {
+//                    // Consume item:
+//                    if (is.getCount() > 1)
+//                        is.setCount(is.getCount() - 1);
+//                    else {
+//                        // If this is a bucket of lava, we need to consume the lava but leave the bucket.
+//                        if (is.getItem() == Items.LAVA_BUCKET) {
+//                            // And if we're cooking wet sponge, we need to leave the bucket filled with water.
+//                            if (input.getItem() == Item.getItemFromBlock(Blocks.SPONGE) && input.getMetadata() == 1)
+//                                player.inventory.mainInventory.set(index, new ItemStack(Items.WATER_BUCKET));
+//                            else
+//                                player.inventory.mainInventory.set(index, new ItemStack(Items.BUCKET));
+//                        } else
+//                            player.inventory.mainInventory.get(index).setCount(0);
+//                        index++;
+//                    }
+//                    fuelCaches.put(player, fuelCaches.get(player) + burnTime);
+//                } else
+//                    index++;
+//            } else
+//                index++;
+//        }
+//    }
+
+    /**
+     * Manually attempt to remove ingredients from the player's inventory.<br>
+     *
+     * @param player
+     * @param ingredients
+     */
+    public static void removeIngredientsFromPlayer(ClientPlayerEntity player, List<ItemStack> ingredients) {
+        NonNullList<ItemStack> main = player.inventory.mainInventory;
+        NonNullList<ItemStack> arm = player.inventory.armorInventory;
+
+        for (ItemStack isIngredient : ingredients) {
+            int target = isIngredient.getCount();
+            for (int i = 0; i < main.size() + arm.size() && target > 0; i++) {
+                ItemStack isPlayer = (i >= main.size()) ? arm.get(i - main.size()) : main.get(i);
+                if (itemStackIngredientsMatch(isPlayer, isIngredient)) {
+                    if (target >= isPlayer.getCount()) {
+                        // Consume this stack:
+                        target -= isPlayer.getCount();
+                        if (i >= main.size())
+                            arm.get(i - main.size()).setCount(0);
+                        else
+                            main.get(i).setCount(0);
+                    } else {
+                        isPlayer.setCount(isPlayer.getCount() - target);
+                        target = 0;
+                    }
+                }
+            }
+            ItemStack resultForReward = isIngredient.copy();
+        }
+    }
+
+//    /**
+//     * Attempt to find all recipes that result in an item of the requested output.
+//     *
+//     * @param output the desired item, eg from Types.xsd - "diamond_pickaxe" etc - or as a Minecraft name - eg "tile.woolCarpet.blue"
+//     * @param variant if variants should be obeyed in constructing the recipes, i.e. if false, variant blind
+//     * @return a list of IRecipe objects that result in this item.
+//     */
+//    public static List<IRecipe> getRecipesForRequestedOutput(String output, boolean variant) {
+//        List<IRecipe> matchingRecipes = new ArrayList<IRecipe>();
+//        ItemStack target = MinecraftTypeHelper.getItemStackFromParameterString(output);
+//        List<?> recipes = CraftingManager.getInstance().getRecipeList();
+//        for (Object obj : recipes) {
+//            if (obj == null)
+//                continue;
+//            if (obj instanceof IRecipe) {
+//                ItemStack is = ((IRecipe) obj).getRecipeOutput();
+//                if (target == null)
+//                    continue;
+//                if (variant && ItemStack.areItemsEqual(is, target))
+//                    matchingRecipes.add((IRecipe) obj);
+//                else if (!variant && is.getItem() == target.getItem())
+//                    matchingRecipes.add((IRecipe) obj);
+//            }
+//        }
+//        return matchingRecipes;
+//    }
+
+//    /**
+//     * Attempt to find all recipes that result in an item of the requested output.
+//     *
+//     * @param output the desired item, eg from Types.xsd - "diamond_pickaxe" etc - or as a Minecraft name - eg "tile.woolCarpet.blue"
+//     * @param variant if variants should be obeyed in constructing the recipes, i.e. if false, variant blind
+//     * @return a list of IRecipe objects that result in this item.
+//     */
+//    public static List<IRecipe> getRecipesForRequestedOutput(ItemStack output, boolean variant) {
+//        List<IRecipe> matchingRecipes = new ArrayList<IRecipe>();
+//        List<?> recipes = CraftingManager.getInstance().getRecipeList();
+//        for (Object obj : recipes) {
+//            if (obj == null)
+//                continue;
+//            if (obj instanceof IRecipe) {
+//                ItemStack is = ((IRecipe) obj).getRecipeOutput();
+//                if (output == null)
+//                    continue;
+//                if (variant && ItemStack.areItemsEqual(is, output))
+//                    matchingRecipes.add((IRecipe) obj);
+//                else if (!variant && is.getItem() == output.getItem())
+//                    matchingRecipes.add((IRecipe) obj);
+//            }
+//        }
+//        return matchingRecipes;
+//    }
+
+//    /**
+//     * Attempt to find a smelting recipe that results in the requested output.
+//     *
+//     * @param output The output of the furnace burn
+//     * @return an ItemStack representing the required input.
+//     */
+//    public static ItemStack getSmeltingRecipeForRequestedOutput(String output, EntityPlayerMP player) {
+//        ItemStack target = MinecraftTypeHelper.getItemStackFromParameterString(output);
+//        if (target == null)
+//            return null;
+//        for (Map.Entry<ItemStack, ItemStack> e : FurnaceRecipes.instance().getSmeltingList().entrySet()) {
+//            if (itemStackIngredientsMatch(target, e.getValue())
+//                    && playerHasIngredients(player, Collections.singletonList(e.getKey()))
+//                    && totalBurnTimeInInventory(player) >= smeltingCookingTime
+//                    ) {
+//                return e.getKey();
+//            }
+//        }
+//        return null;
+//    }
+
+//    /**
+//     * This code is copied from SlotCrafting.onCrafting
+//     * TODO - convert this into a mixin to avoid duplicating code
+//     * @param player - player crafting the items
+//     * @param stack - item and quantity that was crafted
+//     * @param craftMatrix - the InventoryCrafting representing the item recipe
+//     */
+//    protected static void onCrafting(EntityPlayer player, ItemStack stack, InventoryCrafting craftMatrix)
+//    {
+//        // Unclear why you would get achievements without crafting a non-zero amount of an item but this is behavior
+//        // directly from MC
+//        if (stack.getCount() > 0)
+//        {
+//            stack.onCrafting(player.world, player, stack.getCount());
+//            net.minecraftforge.fml.common.FMLCommonHandler.instance().firePlayerCraftingEvent(player, stack, craftMatrix);
+//        }
+//
+//        if (stack.getItem() == Item.getItemFromBlock(Blocks.CRAFTING_TABLE))
+//        {
+//            player.addStat(AchievementList.BUILD_WORK_BENCH);
+//        }
+//
+//        if (stack.getItem() instanceof ItemPickaxe)
+//        {
+//            player.addStat(AchievementList.BUILD_PICKAXE);
+//        }
+//
+//        if (stack.getItem() == Item.getItemFromBlock(Blocks.FURNACE))
+//        {
+//            player.addStat(AchievementList.BUILD_FURNACE);
+//        }
+//
+//        if (stack.getItem() instanceof ItemHoe)
+//        {
+//            player.addStat(AchievementList.BUILD_HOE);
+//        }
+//
+//        if (stack.getItem() == Items.BREAD)
+//        {
+//            player.addStat(AchievementList.MAKE_BREAD);
+//        }
+//
+//        if (stack.getItem() == Items.CAKE)
+//        {
+//            player.addStat(AchievementList.BAKE_CAKE);
+//        }
+//
+//        if (stack.getItem() instanceof ItemPickaxe && ((ItemPickaxe)stack.getItem()).getToolMaterial() != Item.ToolMaterial.WOOD)
+//        {
+//            player.addStat(AchievementList.BUILD_BETTER_PICKAXE);
+//        }
+//
+//        if (stack.getItem() instanceof ItemSword)
+//        {
+//            player.addStat(AchievementList.BUILD_SWORD);
+//        }
+//
+//        if (stack.getItem() == Item.getItemFromBlock(Blocks.ENCHANTING_TABLE))
+//        {
+//            player.addStat(AchievementList.ENCHANTMENTS);
+//        }
+//
+//        if (stack.getItem() == Item.getItemFromBlock(Blocks.BOOKSHELF))
+//        {
+//            player.addStat(AchievementList.BOOKCASE);
+//        }
+//    }
+//
+//    /**
+//     * Attempt to craft the given recipe.<br>
+//     * This pays no attention to tedious things like using the right crafting table
+//     * / brewing stand etc, or getting the right shape.<br>
+//     * It simply takes the raw ingredients out of the player's inventory, and
+//     * inserts the output of the recipe, if possible.
+//     *
+//     * @param player the SERVER SIDE player that will do the crafting.
+//     * @param recipe the IRecipe we wish to craft.
+//     * @return true if the recipe had an output, and the player had the required
+//     *         ingred:xients to create it; false otherwise.
+//     */
+//    public static boolean attemptCrafting(EntityPlayerMP player, IRecipe recipe) {
+//        if (player == null || recipe == null)
+//            return false;
+//
+//        ItemStack is = recipe.getRecipeOutput();
+//
+//        List<ItemStack> ingredients = getIngredients(recipe);
+//        if (playerHasIngredients(player, ingredients)) {
+//            // We have the ingredients we need, so directly manipulate the inventory.
+//            // First, remove the ingredients:
+//            removeIngredientsFromPlayer(player, ingredients);
+//            // Now add the output of the recipe:
+//            ItemStack resultForInventory = is.copy();
+//            ItemStack resultForReward = is.copy();
+//            player.inventory.addItemStackToInventory(resultForInventory);
+//            RewardForCollectingItemImplementation.GainItemEvent event = new RewardForCollectingItemImplementation.GainItemEvent(
+//                    player, resultForReward);
+//            event.setCause(1);
+//            MinecraftForge.EVENT_BUS.post(event);
+//
+//            // Now trigger a craft event
+//            List<IRecipe> recipes = getRecipesForRequestedOutput(resultForReward, true);
+//            for (IRecipe iRecipe : recipes) {
+//                if (iRecipe instanceof ShapedRecipes) {
+//                    ShapedRecipes shapedRecipe = (ShapedRecipes) iRecipe;
+//                    InventoryCrafting craftMatrix;
+//                    if (shapedRecipe.recipeItems.length <= 4)
+//                        craftMatrix = new InventoryCrafting(player.inventoryContainer, 2, 2);
+//                    else
+//                        craftMatrix = new InventoryCrafting(player.inventoryContainer, 3, 3);
+//                    for (int i = 0; i < shapedRecipe.recipeItems.length; i++)
+//                        craftMatrix.setInventorySlotContents(i, shapedRecipe.recipeItems[i]);
+//
+//                    onCrafting(player, resultForReward, craftMatrix);
+//                    break;
+//                } else if (iRecipe instanceof ShapelessRecipes) {
+//                    ShapelessRecipes shapelessRecipe = (ShapelessRecipes) iRecipe;
+//                    InventoryCrafting craftMatrix;
+//                    if (shapelessRecipe.recipeItems.size() <= 4) {
+//                        craftMatrix = new InventoryCrafting(player.inventoryContainer, 2, 2);
+//                        for (int i = 0; i < shapelessRecipe.recipeItems.size(); i++)
+//                            craftMatrix.setInventorySlotContents(i, shapelessRecipe.recipeItems.get(i));
+//                    } else {
+//                        craftMatrix = new InventoryCrafting(player.inventoryContainer, 3, 3);
+//                        for (int i = 0; i < shapelessRecipe.recipeItems.size(); i++)
+//                            craftMatrix.setInventorySlotContents(i, shapelessRecipe.recipeItems.get(i));
+//                    }
+//                    onCrafting(player, resultForReward, craftMatrix);
+//                    break;
+//                } else if (iRecipe instanceof ShapedOreRecipe) {
+//                    ShapedOreRecipe oreRecipe = (ShapedOreRecipe) iRecipe;
+//                    Object[] input = oreRecipe.getInput();
+//                    InventoryCrafting craftMatrix = new InventoryCrafting(player.inventoryContainer, 3, 3);
+//                    for (int i = 0; i < input.length; i++) {
+//                        if (input[i] instanceof ItemStack)
+//                            craftMatrix.setInventorySlotContents(i, (ItemStack) input[i]);
+//                        else if (input[i] instanceof NonNullList)
+//                            if (((NonNullList) input[i]).size() != 0)
+//                                craftMatrix.setInventorySlotContents(i, (ItemStack) ((NonNullList) input[i]).get(0));
+//                    }
+//                    onCrafting(player, resultForReward, craftMatrix);
+//                }
+//            }
+//            return true;
+//        }
+//        return false;
+//    }
+//
+//    /**
+//     * TODO Copied from SlotFurncaeOutput.onCrafting - change to mixin to remove redundant code
+//     * @param stack - item stack that was crafted
+//     */
+//    protected static void onSmelting(EntityPlayer player, ItemStack stack)
+//    {
+//        stack.onCrafting(player.world, player, stack.getCount());
+//
+//        if (!player.world.isRemote)
+//        {
+//            int i = stack.getCount();
+//            float f = FurnaceRecipes.instance().getSmeltingExperience(stack);
+//
+//            if (f == 0.0F)
+//            {
+//                i = 0;
+//            }
+//            else if (f < 1.0F)
+//            {
+//                int j = MathHelper.floor((float)i * f);
+//
+//                if (j < MathHelper.ceil((float)i * f) && RandomHelper.randomDouble() < (double)((float)i * f - (float)j))
+//                {
+//                    ++j;
+//                }
+//
+//                i = j;
+//            }
+//
+//            while (i > 0)
+//            {
+//                int k = EntityXPOrb.getXPSplit(i);
+//                i -= k;
+//                player.world.spawnEntity(new EntityXPOrb(player.world, player.posX, player.posY + 0.5D, player.posZ + 0.5D, k));
+//            }
+//        }
+//
+//        net.minecraftforge.fml.common.FMLCommonHandler.instance().firePlayerSmeltedEvent(player, stack);
+//
+//        if (stack.getItem() == Items.IRON_INGOT)
+//        {
+//            player.addStat(AchievementList.ACQUIRE_IRON);
+//        }
+//
+//        if (stack.getItem() == Items.COOKED_FISH)
+//        {
+//            player.addStat(AchievementList.COOK_FISH);
+//        }
+//    }
+//
+//    /**
+//     * Attempt to smelt the given item.<br>
+//     * This returns instantly, callously disregarding such frivolous niceties as cooking times or the presence of a furnace.<br>
+//     * It will, however, consume fuel from the player's inventory.
+//     *
+//     * @param player
+//     * @param input  the raw ingredients we want to cook.
+//     * @return true if cooking was successful.
+//     */
+//    public static boolean attemptSmelting(EntityPlayerMP player, ItemStack input) {
+//        if (player == null || input == null)
+//            return false;
+//        List<ItemStack> ingredients = new ArrayList<ItemStack>();
+//        ingredients.add(input);
+//        ItemStack isOutput = FurnaceRecipes.instance().getSmeltingList().get(input);
+//        if (isOutput == null)
+//            return false;
+//        if (playerHasIngredients(player, ingredients) && totalBurnTimeInInventory(player) >= smeltingCookingTime) {
+//            removeIngredientsFromPlayer(player, ingredients);
+//            burnInventory(player, smeltingCookingTime, input);
+//
+//            ItemStack resultForInventory = isOutput.copy();
+//            ItemStack resultForReward = isOutput.copy();
+//            player.inventory.addItemStackToInventory(resultForInventory);
+//            RewardForCollectingItemImplementation.GainItemEvent event = new RewardForCollectingItemImplementation.GainItemEvent(
+//                    player, resultForReward);
+//            event.setCause(2);
+//            MinecraftForge.EVENT_BUS.post(event);
+//
+//            // Trigger the furnace output removed item events
+//            onSmelting(player, isOutput);
+//            return true;
+//        }
+//        return false;
+//    }
+
+    private static JsonObject listIngredients(NonNullList<ItemStack> ingredients){
+        JsonObject jsonObject = new JsonObject();
+        for (ItemStack ingredient: ingredients){
+            if (!ingredient.isEmpty() && ingredient.getItem().getName() != null)
+                jsonObject.addProperty(ingredient.getItem().getName().toString(), ingredient.getCount());
+        }
+        return jsonObject;
+    }
+
+    /**
+     * Little utility method for dumping out a json array of all the Minecraft items,  plus as many useful
+     * attributes as we can find for them. This is primarily used by decision_tree_test.py but might be useful for
+     * real-world applications too.
+     */
+    public static JsonArray generateItemJson(){
+        JsonArray items = new JsonArray();
+        for (Map.Entry<RegistryKey<Item>, Item> entry : Registry.ITEM.getEntries()) {
+            Item item = entry.getValue();
+            if (item != null && item.getName() != null) {
+                JsonObject json = new JsonObject();
+                json.addProperty("type", item.toString());
+                json.addProperty("damageable", item.isDamageable());
+                // json.addProperty("rendersIn3D", item.isFull3D());
+                // json.addProperty("repairable", item item.isRepairable());
+                // CreativeTabs tab = item.getCreativeTab();
+                // json.addProperty("tab", ((tab != null) ? item.getCreativeTab().getTabLabel() : "none"));
+                ItemStack is = item.getDefaultInstance();
+                json.addProperty("stackable", is.isStackable());
+                json.addProperty("stackSize", is.getMaxStackSize());
+                json.addProperty("useAction", item.getUseAction(is).toString());
+                json.addProperty("enchantable", item.isEnchantable(is));
+                json.addProperty("rarity", is.getRarity().toString());
+                // json.addProperty("hasSubtypes", item);
+                json.addProperty("maxDamage", is.getMaxDamage());
+                json.addProperty("maxUseDuration", item.getUseDuration(is));
+                json.addProperty("block", item instanceof BlockItem);
+                json.addProperty("hasContainerItem", item.hasContainerItem());
+                json.addProperty("bestEquipmentSlot", MobEntity.getSlotForItemStack(is).getName());
+                if (item instanceof BlockItem) {
+                    BlockItem ib = (BlockItem) item;
+                    Block b = ib.getBlock();
+                    BlockState bs = b.getDefaultState();
+                    json.addProperty("slipperiness", b.getSlipperiness());
+                    json.addProperty("hardness", bs.getBlockHardness(null, null));
+                    // json.addProperty("causesSuffocation", bs.causesSuffocation());
+                    json.addProperty("canProvidePower", bs.canProvidePower());
+                    json.addProperty("transparent", bs.isTransparent());
+                    Material mat = bs.getMaterial();
+                    json.addProperty("canBurn", mat.isFlammable());
+                    json.addProperty("isLiquid", mat.isLiquid());
+                    json.addProperty("blocksMovement", mat.blocksMovement());
+                    // json.addProperty("needsNoTool", mat.isToolNotRequired());
+                    json.addProperty("isReplaceable", mat.isReplaceable());
+                    // json.addProperty("pistonPushable", mat.getMobilityFlag() == EnumPushReaction.NORMAL);
+                    json.addProperty("woodenMaterial", mat == Material.WOOD);
+                    json.addProperty("ironMaterial", mat == Material.IRON);
+                    json.addProperty("glassyMaterial", mat == Material.GLASS);
+                    // TODO add more materials?
+                    // json.addProperty("clothMaterial", mat == Material.CLOTH);
+
+                    boolean hasDirection = false;
+                    boolean hasColour = false;
+                    boolean hasVariant = false;
+                    for (Property prop : bs.getProperties()) {
+                        System.out.println(item.getName().toString() + " -- " + prop);
+                        if (prop instanceof DirectionProperty)
+                            hasDirection = true;
+                        if (prop instanceof EnumProperty && prop.getName().equals("color"))
+                            hasColour = true;
+                        if (prop instanceof EnumProperty && prop.getName().equals("variant")) {
+                            hasVariant = true;
+                            json.addProperty("variant", bs.get(prop).toString());
+                        }
+                    }
+                    json.addProperty("hasDirection", hasDirection);
+                    json.addProperty("hasColour", hasColour);
+                    json.addProperty("hasVariant", hasVariant);
+                }
+                items.add(json);
+            }
+        }
+        return items;
+    }
+
+    /**
+     * Little utility method for generating a json array of all of the Minecraft blocks
+     */
+//    public static JsonArray generateBlockJson(){
+//        JsonArray blocks = new JsonArray();
+//        for (ResourceLocation i : Block.REGISTRY.getKeys()) {
+//            Block block = Block.REGISTRY.getObject(i);
+//            JsonObject json = new JsonObject();
+//            json.addProperty("name", Block.REGISTRY.getNameForObject(block).toString().replace("minecraft:", ""));
+//            json.addProperty("particleGravity", block.blockParticleGravity);
+//            json.addProperty("slipperiness", block.slipperiness);
+//            json.addProperty("spawnInBlock", block.canSpawnInBlock());
+//            json.addProperty("isCollidable", block.isCollidable());
+//            try{
+//                json.addProperty("quantityDropped", block.quantityDropped(null));
+//            } catch (NullPointerException ignored){}
+//            blocks.add(json);
+//        }
+//        return blocks;
+//    }
+//
+//    /**
+//     * Little utility method for generating a json array of all of the Minecraft achievements
+//     */
+//    public static JsonArray generateAchievements(){
+//        JsonArray achievements = new JsonArray();
+//        for (Achievement achievement : AchievementList.ACHIEVEMENTS) {
+//            JsonObject json = new JsonObject();
+//            json.addProperty("statID", achievement.statId);
+//            if (achievement.parentAchievement != null && achievement.parentAchievement.statId != null)
+//                json.addProperty("parentStatID", achievement.parentAchievement.statId);
+//            json.addProperty("isIndependent", achievement.isIndependent);
+//            json.addProperty("displayColumn", achievement.displayColumn);
+//            json.addProperty("displayRow", achievement.displayRow);
+//            json.addProperty("isSpecial", achievement.getSpecial());
+//            json.addProperty("description", achievement.getDescription());
+//
+//            achievements.add(json);
+//        }
+//        return achievements;
+//    }
+//
+//
+    /**
+     * Little utility method for generating a json array of all of the Minecraft base stats
+     */
+    public static JsonArray generateStats(){
+        JsonArray stats = new JsonArray();
+        for (Map.Entry<RegistryKey<StatType<?>>, StatType<?>> entry : Registry.STATS.getEntries()) {
+            String statType = entry.getKey().getLocation().getPath();
+            if (statType.equals("custom")) {
+                entry.getValue().forEach(stat -> {
+                    stats.add(statToJson(stat.getName()));
+                });
+            } else if (statType.equals(Stats.BLOCK_MINED.getTranslationKey())) {
+                for (Block block : Registry.BLOCK) {
+                    stats.add(statToJson(statType + "." + block.toString()));
+                }
+            } else {
+                for (Item item : Registry.ITEM) {
+                    stats.add(statToJson(statType + "." + item.toString()));
+                }
+            }
+
+        }
+        return stats;
+    }
+
+    private static JsonObject statToJson(String statId) {
+        JsonObject json = new JsonObject();
+        json.addProperty("statID",statId);
+        JsonArray tokens = new JsonArray();
+        for (String token : statId.split("\\.")){
+            if (token.contains("minecraft")) {
+                continue;
+            }
+//            // BAH map drop stat to items_dropped to prevent hash collision in dict keys
+//            // MUST change this in JSONWorldDataHelper.java as well!!!! (search above comment)
+//            if (token.equals(statId.split("\\.")[statId.split("\\.").length - 1]))
+//                if (token.equals("drop"))
+//                    token = "items_dropped";
+            tokens.add(new JsonPrimitive(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, token)));
+        }
+        json.add("minerl_keys", tokens);
+        // json.addProperty("isIndependent", stat.isIndependent);
+        // json.addProperty("isAchievement",stat.isAchievement());
+        return json;
+    }
+
+
+    /**
+     * Little utility method for generating a json array of all of the Minecraft crafting recipes
+     */
+    public static JsonArray generateCraftingRecipeJson(){
+        JsonArray craftingRecipes = new JsonArray();
+        Minecraft mc = Minecraft.getInstance();
+        for (IRecipe recipe : mc.world.getRecipeManager().getRecipes()) {
+            if (recipe == null || recipe.getRecipeOutput().getItem().getName().toString() == null)
+                continue;
+            JsonObject jsonObject = new JsonObject();
+            jsonObject.addProperty("outputItemName", recipe.getRecipeOutput().getItem().getName().toString());
+            jsonObject.addProperty("outputCount", recipe.getRecipeOutput().getCount());
+            jsonObject.addProperty("recipeSize", getRecipeSize(recipe));
+            jsonObject.addProperty("type", recipe.getClass().getSimpleName());
+            jsonObject.add( "ingredients", listIngredients(recipe.getIngredients()));
+            craftingRecipes.add(jsonObject);
+        }
+        return craftingRecipes;
+    }
+
+    private static int getRecipeSize(IRecipe recipe) {
+        if (recipe.canFit(1, 1)) {
+            return 1;
+        } else if (recipe.canFit(2, 2)) {
+            return 4;
+        } else {
+            return 9;
+        }
+    }
+
+//    /**
+//     * Little utility method for generating a json array of all of the Minecraft smelting recipes
+//     */
+//    public static JsonArray generateSmeltingRecipeJson(){
+//        JsonArray smeltingRecipes = new JsonArray();
+//        for (ItemStack isInput : FurnaceRecipes.getInstance().getSmeltingList().keySet()) {
+//            ItemStack isOutput = FurnaceRecipes.instance().getSmeltingList().get(isInput);
+//            if (isOutput.getItem().getName() == null)
+//                continue;
+//            JsonObject jsonObject = new JsonObject();
+//            jsonObject.addProperty("outputItemName", isOutput.getItem().getName().toString());
+//            jsonObject.addProperty("out", isOutput.getCount());
+//            jsonObject.add("ingredients", listIngredients(NonNullList.withSize(1, isInput)));
+//            smeltingRecipes.add(jsonObject);
+//        }
+//        return smeltingRecipes;
+//    }
+
+    /**
+     * Little utility method for dumping out a list of all the Minecraft items, plus as many useful attributes as
+     * we can find for them. This is primarily used by decision_tree_test.py but might be useful for real-world applications too.
+     *
+     * @param filename location to save the dumped list.
+     * @throws IOException
+     */
+    public static void dumpItemProperties(String filename) throws IOException {
+        FileOutputStream fos = new FileOutputStream("..//..//build//install//Python_Examples//item_database.json");
+        OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
+        BufferedWriter writer = new BufferedWriter(osw);
+        JsonArray itemTypes = generateItemJson();
+        writer.write(itemTypes.toString());
+        writer.close();
+    }
+
+    /**
+     * Utility method to auto-generate item, block, and recipe lists as individual json arrays
+     *
+     * @param filename location to save the dumped json file.
+     */
+    public static void dumpMinecraftObjectRules(String filename) {
+        JsonObject allRecipes = new JsonObject();
+        allRecipes.addProperty("docstring", "THIS IS AN AUTO GENERATED FILE! This file was generated by " +
+                "com.microsoft.Malmo.Utils.CraftingHelper.dumpMinecraftObjectRules()");
+        // allRecipes.add("craftingRecipes", generateCraftingRecipeJson());
+        //allRecipes.add("smeltingRecipes", generateSmeltingRecipeJson());
+        allRecipes.add("items", generateItemJson());
+//        allRecipes.add("blocks", generateBlockJson());
+//        allRecipes.add("achievements", generateAchievements());
+        allRecipes.add("stats", generateStats());
+        try {
+            Writer writer = new FileWriter(filename);
+            Gson gson = new GsonBuilder().setPrettyPrinting().create();
+            gson.toJson(allRecipes, writer);
+            System.out.println("Wrote json to " + System.getProperty("user.dir") + filename);
+            writer.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+    public static void main(String[] args) {
+        dumpMinecraftObjectRules("../herobraine/hero/mc_constants.1.16.json");
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/microsoft/Malmo/Utils/JSONWorldDataHelper.java MCP-Reborn/src/main/java/com/microsoft/Malmo/Utils/JSONWorldDataHelper.java
--- MCP-Reborn-vanilla/src/main/java/com/microsoft/Malmo/Utils/JSONWorldDataHelper.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/microsoft/Malmo/Utils/JSONWorldDataHelper.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,295 @@
+// --------------------------------------------------------------------------------------------------
+//  Copyright (c) 2016 Microsoft Corporation
+//  
+//  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 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.
+// --------------------------------------------------------------------------------------------------
+
+package com.microsoft.Malmo.Utils;
+
+import com.google.common.base.CaseFormat;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.player.ClientPlayerEntity;
+import net.minecraft.network.play.client.CClientStatusPacket;
+import net.minecraft.stats.Stat;
+import net.minecraft.stats.StatType;
+import net.minecraft.stats.StatisticsManager;
+import net.minecraft.stats.Stats;
+import net.minecraft.util.RegistryKey;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.registry.Registry;
+import net.minecraft.world.biome.Biome;
+
+import java.util.Arrays;
+import java.util.Map;
+
+
+/**
+ * Helper class for building the "World data" to be passed from Minecraft back to the agent.<br>
+ * This class contains helper methods to build up a JSON tree of useful information, such as health, XP, food levels, distance travelled, etc.etc.<br>
+ * It can also build up a grid of the block types around the player or somewhere else in the world.
+ * Call this on the Server side only.
+ */
+public class JSONWorldDataHelper
+{
+    /**
+     * Simple class to hold the dimensions of the environment
+     * that we want to return in the World Data.<br>
+     * Min and max define an inclusive range, where the player's feet are situated at (0,0,0) if absoluteCoords=false.
+     */
+    static public class GridDimensions {
+        public int xMin;
+        public int xMax;
+        public int yMin;
+        public int yMax;
+        public int zMin;
+        public int zMax;
+        public boolean absoluteCoords;
+        public boolean projectDown;
+
+        /**
+         * Default constructor asks for an environment just big enough to contain
+         * the player and one block all around him.
+         */
+        public GridDimensions() {
+            this.xMin = -1; this.xMax = 1;
+            this.zMin = -1; this.zMax = 1;
+            this.yMin = -1; this.yMax = 2;
+            this.absoluteCoords = false;
+            this.projectDown = false;
+        }
+
+        /**
+         * Convenient constructor - effectively specifies the margin around the player<br>
+         * Passing (1,1,1) will have the same effect as the default constructor.
+         * @param xMargin number of blocks to the left and right of the player
+         * @param yMargin number of blocks above and below player
+         * @param zMargin number of blocks in front of and behind player
+         */
+        public GridDimensions(int xMargin, int yMargin, int zMargin) {
+            this.xMin = -xMargin; this.xMax = xMargin;
+            this.yMin = -yMargin; this.yMax = yMargin + 1;  // +1 because the player is two blocks tall.
+            this.zMin = -zMargin; this.zMax = zMargin;
+            this.absoluteCoords = false;
+            this.projectDown = false;
+        }
+
+        /**
+         * Convenient constructor for the case where all that is required is the flat patch of ground<br>
+         * around the player's feet.
+         * @param xMargin number of blocks around the player in the x-axis
+         * @param zMargin number of blocks around the player in the z-axis
+         */
+        public GridDimensions(int xMargin, int zMargin) {
+            this.xMin = -xMargin; this.xMax = xMargin;
+            this.yMin = -1; this.yMax = -1;  // Flat patch of ground at the player's feet.
+            this.zMin = -zMargin; this.zMax = zMargin;
+            this.absoluteCoords = false;
+            this.projectDown = false;
+        }
+    };
+
+    public static void buildAllStats(JsonObject json, ClientPlayerEntity player){
+        buildBaseMinecraftStats(json, player);
+        buildLifeStats(json, player);
+        buildPositionStats(json, player);
+        buildBiomeStats(json, player);
+        buildWeatherStats(json, player);
+    }
+
+
+    /** Matches the mc_constants.json format for statistics ensuring all will be added to the provided json object.
+     * @param json a JSON object into which the stats will be added (at the root, be careful of conflicts). Stats that are 0 will be omitted, loading a save from a different username will break most stats
+     */
+    public static void buildBaseMinecraftStats(JsonObject json, ClientPlayerEntity player)
+    {
+        if (Minecraft.getInstance().getConnection() == null)
+            return;
+        Minecraft.getInstance().getConnection().sendPacket(new CClientStatusPacket(CClientStatusPacket.State.REQUEST_STATS));
+        StatisticsManager statisticsManager = player.getStats();
+
+        for(Stat<?> stat : statisticsManager.getKeys()) {
+            // Skip the "minecraft" namespace.
+            String statTypeName = stat.getName().split(":")[0].substring("minecraft.".length());
+            String statName = stat.getName().split(":")[1].substring("minecraft.".length());
+
+            // First check if statTypeName is already in the statsJson object
+            if(!json.has(statTypeName)) {
+                json.add(statTypeName, new JsonObject());
+            }
+            // Then add the stat to the statTypeName object
+            json.getAsJsonObject(statTypeName).addProperty(statName, statisticsManager.getValue(stat));
+
+            // Debugging
+            System.out.println( statTypeName + "." + statName);
+        }
+    }
+
+    /** Builds the basic achievement world data to be used as observation signals by the listener.
+     * @param json a JSON object into which the achievement stats will be added.
+     */
+    public static void oldBuildBaseMinecraftStats(JsonObject json, ClientPlayerEntity player)
+    {
+        Minecraft.getInstance().getConnection().sendPacket(new CClientStatusPacket(CClientStatusPacket.State.REQUEST_STATS));
+        StatisticsManager statisticsManager = player.getStats();
+
+//        json.addProperty("distance_travelled_cm",
+//            statisticsManager.getValue(Stat(Stats.WALK_ONE_CM)
+//            + statisticsManager.getValue(Stats.CROUCH_ONE_CM.getPath())
+//            + statisticsManager.getValue(StatList.SPRINT_ONE_CM)
+//            + statisticsManager.readStat(StatList.SWIM_ONE_CM)
+//            + statisticsManager.readStat(StatList.FALL_ONE_CM)
+//            + statisticsManager.readStat(StatList.CLIMB_ONE_CM)
+//            + statisticsManager.readStat(StatList.FLY_ONE_CM)
+//            + statisticsManager.readStat(StatList.DIVE_ONE_CM)
+//            + statisticsManager.readStat(StatList.MINECART_ONE_CM)
+//            + statisticsManager.readStat(StatList.BOAT_ONE_CM)
+//            + statisticsManager.readStat(StatList.PIG_ONE_CM)
+//            + statisticsManager.readStat(StatList.HORSE_ONE_CM)
+//            + statisticsManager.readStat(StatList.AVIATE_ONE_CM)
+//            );
+
+        // TODO the logic below is for mineRL compat sake, may need to
+        // revisit at some point
+        for(Stat stat : statisticsManager.getKeys()) {
+            // For MineRL, split over . and convert all camelCase to snake_case
+            String[] stat_fields = stat.getName().split("\\.");
+            JsonObject head = json;
+            for (String unformatted_token : stat_fields) {
+                if (unformatted_token.equals("minecraft")) {
+                    continue;
+                }
+                String token = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, unformatted_token);
+                if (token.endsWith(":minecraft")) {
+                    token = token.replace(":minecraft", "");
+                }
+                // Last element is a leaf
+                if (unformatted_token.equals(stat_fields[stat_fields.length - 1])) {
+                    // BAH map drop stat to items_dropped to prevent hash collision in dict keys
+                    // MUST change this in CraftingHelper.java as well!!!! (search above comment)
+                    if (token.equals("drop"))
+                        token = "items_dropped";
+                    head.addProperty(token, statisticsManager.getValue(stat));
+                } else {
+                    if (head.has(token))
+                        if (head.get(token) instanceof JsonObject)
+                            head = head.getAsJsonObject(token);
+                        else {
+                            System.out.println("Duplicate token! " + Arrays.toString(stat_fields));
+                            head.remove(token);
+                            JsonObject newRoot = new JsonObject();
+                            head.add(token, newRoot);
+                            head = newRoot;
+                        }
+                    else {
+                        JsonObject newRoot = new JsonObject();
+                        head.add(token, newRoot);
+                        head = newRoot;
+                    }
+                }
+            }
+        }
+    }
+
+    /** Builds the basic life world data to be used as observation signals by the listener.
+     * @param json a JSON object into which the life stats will be added.
+     */
+    public static void buildLifeStats(JsonObject json, ClientPlayerEntity player)
+    {
+        JsonObject lifeStats = new JsonObject();
+        lifeStats.addProperty("life", player.getHealth());
+        lifeStats.addProperty("score", player.getScore());    // Might always be the same as XP?
+        lifeStats.addProperty("food", player.getFoodStats().getFoodLevel());
+        lifeStats.addProperty("saturation", player.getFoodStats().getSaturationLevel());
+        lifeStats.addProperty("xp", player.experienceTotal);
+        lifeStats.addProperty("is_alive", player.isAlive());
+        lifeStats.addProperty("air", player.getAir());
+        lifeStats.addProperty("name", player.getName().toString());
+        json.add("life_stats", lifeStats);
+    }
+    /** Builds the player position data to be used as observation signals by the listener.
+     * @param json a JSON object into which the positional information will be added.
+     */
+    public static void buildPositionStats(JsonObject json, ClientPlayerEntity player)
+    {
+        json.addProperty("xpos",  player.getPosX());
+        json.addProperty("ypos",  player.getPosY());
+        json.addProperty("zpos", player.getPosZ());
+        json.addProperty("pitch",  player.rotationPitch);
+        json.addProperty("yaw", player.rotationYaw);
+    }
+
+    /** Builds the player's biome data.
+     * @param json a JSON object into which the biome information will be added.
+     * @param player - Non-null, must have player.world
+     */
+    public static void buildBiomeStats(JsonObject json, ClientPlayerEntity player)
+    {
+        BlockPos playerPos = player.getPosition();
+        Biome playerBiome = player.world.getBiome(playerPos);
+        // Name of the current biome
+        json.addProperty("biome_name", playerBiome.toString());
+        // ID of the current biome
+        // json.addProperty("biome_id", RegistryKey.getOrCreateKey(Registry.BIOME_KEY, playerBiome.toString()).);
+        // The average temperature of the current biome
+        json.addProperty("biome_temperature", playerBiome.getTemperature());
+        // The average rainfall chance of the current biome
+        json.addProperty("biome_downfall", playerBiome.getDownfall());
+        // The water level for oceans and rivers
+        json.addProperty("sea_level", player.world.getSeaLevel());
+    }
+
+    /** Builds the player's weather information
+     * @param json a JSON object into which the weather information will be added.
+     * @param player - Non-null, must have player.world
+     */
+    public static void buildWeatherStats(JsonObject json, ClientPlayerEntity player)
+    {
+        BlockPos playerPos = player.getPosition();
+        json.addProperty("light_level", player.world.getLight(playerPos));
+        // If it is currently precipitating here
+        json.addProperty("is_raining", player.world.isRaining());
+        // If the playerPos has LOS to the sky
+        json.addProperty("can_see_sky", player.world.canSeeSky(playerPos));
+        // [0, 1] Brightness factor of the sun
+        // json.addProperty("sun_brightness", player.world.getSunBrightnessFactor(0));
+        // [0, 1] Light level provided by the sky
+        // json.addProperty("sky_light_level", player.world.getSunBrightness(0));
+        // TODO add other statuses such as is_raining or other current weather
+    }
+
+//    public static void buildEnvironmentStats(JsonObject json, ClientPlayerEntity player)
+//    {
+//        // json.addProperty("world_time", player.world.getWorldTime());  // Current time in ticks
+//        // json.addProperty("total_time", player.world.getTotalWorldTime());  // Total time world has been running
+//    }
+//    /**
+// * Build a signal for the cubic block grid centred on the player.<br>
+// * Default is 3x3x4. (One cube all around the player.)<br>
+// * Blocks are returned as a 1D array, in order
+// * along the x, then z, then y axes.<br>
+// * Data will be returned in an array called "Cells"
+// * @param json a JSON object into which the info for the object under the mouse will be added.
+// * @param environmentDimensions object which specifies the required dimensions of the grid to be returned.
+// * @param jsonName name to use for identifying the returned JSON array.
+// */
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/env/EnvServer.java MCP-Reborn/src/main/java/com/minerl/multiagent/env/EnvServer.java
--- MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/env/EnvServer.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/minerl/multiagent/env/EnvServer.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,743 @@
+package com.minerl.multiagent.env;
+
+import com.google.common.base.Charsets;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.microsoft.Malmo.Schemas.*;
+import com.microsoft.Malmo.Utils.JSONWorldDataHelper;
+import com.minerl.multiagent.RandomHelper;
+import com.minerl.multiagent.recorder.AzureUpload;
+import com.minerl.multiagent.recorder.PlayRecorder;
+import net.minecraft.client.*;
+import net.minecraft.client.entity.player.ClientPlayerEntity;
+import net.minecraft.client.gui.screen.ConnectingScreen;
+import net.minecraft.client.gui.screen.MainMenuScreen;
+import net.minecraft.client.multiplayer.ServerAddress;
+import net.minecraft.client.multiplayer.ServerData;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.inventory.EquipmentSlotType;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.profiler.IResultableProfiler;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.datafix.codec.DatapackCodec;
+import net.minecraft.util.registry.DynamicRegistries;
+import net.minecraft.util.registry.Registry;
+import net.minecraft.util.SoundCategory;
+import net.minecraft.world.Difficulty;
+import net.minecraft.world.GameRules;
+import net.minecraft.world.GameType;
+import net.minecraft.world.WorldSettings;
+import net.minecraft.world.gen.settings.DimensionGeneratorSettings;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.jmx.Server;
+
+import java.io.*;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.List;
+
+public class EnvServer {
+    private static Logger LOGGER = LogManager.getLogger();
+    private static String hello = "<MalmoEnv" ;
+    private static final int stepClientTagLength = "<StepClient_>".length();
+    private static final int stepServerTagLength = "<StepServer_>".length();
+    private boolean iwanttoquit = false;
+    private boolean doneOnDeath = false;
+
+    static final int BYTES_INT = 4;
+    static final int BYTES_DOUBLE = 8;
+    // this many steps with noop action will be taken at the beginning of
+    // the episode. Helps to render scene more fully and avoid unrendered chunks
+    // TODO peterz validate this is actually still necessary, given the sync chunk loading
+    private static final int DEFAULT_SKIP_FIRST_FRAMES = 20;
+
+    private int envTickCounter = -1;
+    private MissionInit missionInit;
+
+    private int port;
+    private String version;
+    public EnvServer(int port, String version) {
+        this.port = port;
+        this.version = version;
+    }
+
+    public void serve() {
+        ServerSocket serverSocket;
+        try {
+            serverSocket = new ServerSocket(port);
+            serverSocket.setPerformancePreferences(0, 2, 1);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        // expected malmo text
+        System.out.println("***** Start MalmoEnvServer on port " + port);
+        System.out.println("CLIENT enter state: DORMANT");
+        System.out.println("SERVER enter state: DORMANT");
+
+        while (!iwanttoquit) {
+            try {
+                final Socket socket = serverSocket.accept();
+                socket.setTcpNoDelay(true);
+                Thread thread = new Thread("EnvServerSocketHandler") {
+                    public void run() {
+                        boolean running = false;
+                        try {
+                            checkHello(socket);
+
+                            while (true) {
+
+                                DataInputStream din = new DataInputStream(socket.getInputStream());
+                                int hdr = 0;
+                                try {
+                                    hdr = din.readInt();
+                                } catch (EOFException e) {
+                                    LOGGER.debug("Incoming socket connection closed, likely by peer (without Exit message): " + e);
+                                    socket.close();
+                                    break;
+                                }
+                                byte[] data = new byte[hdr];
+
+                                din.readFully(data);
+                                String command = new String(data, Charset.forName("UTF-8"));
+
+                                // TODO this comms schema is seriously an atrocity
+                                // Needs to be rewritten such that schema is explicit
+                                // maybe use grpc or something like that?
+                                if (command.startsWith("<StepClient")) {
+
+                                    stepClient(command, socket, din);
+
+                                } else if (command.startsWith("<StepServer")) {
+
+                                    stepServer(command, socket);
+
+                                } else if (command.startsWith("<Peek")) {
+
+                                    peek(command, socket, din);
+
+                                } else if (command.startsWith("<MissionInit")) {
+
+                                    if (initMission(din, command, socket)) {
+                                        running = true;
+                                    }
+
+                                } else if (command.startsWith("<Quit")) {
+
+                                    quit(command, socket);
+
+                                    // profiler.profilingEnabled = false;
+
+                                } else if (command.startsWith("<Exit")) {
+
+                                    quit(command, socket);
+                                    AzureUpload.finish();
+                                    Minecraft.getInstance().shutdown();
+
+                                    // profiler.profilingEnabled = false;
+
+                                    return; // exit
+
+                                } else if (command.startsWith("<Close")) {
+
+                                    // close(command, socket);
+                                    // profiler.profilingEnabled = false;
+
+                                }  else if (command.startsWith("<Echo")) {
+                                    command = "<Echo>" + command + "</Echo>";
+                                    data = command.getBytes(Charset.forName("UTF-8"));
+                                    hdr = data.length;
+
+                                    DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
+                                    dout.writeInt(hdr);
+                                    dout.write(data, 0, hdr);
+                                    dout.flush();
+                                } else if (command.startsWith("<Disconnect")) {
+                                    socket.close();
+                                    break;
+                                } else {
+                                    throw new IOException("Unknown env service command: " + command);
+                                }
+                            }
+                        } catch (IOException ioe) {
+                            ioe.printStackTrace();
+                            LOGGER.fatal("MalmoEnv socket error: " + ioe + " (can be on disconnect)");
+
+                            // TimeHelper.SyncManager.debugLog("[MALMO_ENV_SERVER] MalmoEnv socket error");
+                            try {
+                                if (running) {
+                                    LOGGER.info("Want to quit on disconnect.");
+                                    System.out.println( "[LOGTOPY] " + "Want to quit on disconnect.");
+                                    setWantToQuit();
+                                }
+                                socket.close();
+                            } catch (IOException ioe2) {
+                            }
+                        } catch (Exception e) {
+                            LOGGER.error("Error while processing commands", e);
+                            try {
+                                socket.close();
+                            } catch (IOException ioe2) {
+                            }
+                        }
+                    }
+                };
+                thread.start();
+            } catch (IOException ioe) {
+                LOGGER.log(Level.FATAL, "MalmoEnv service exits on " + ioe);
+                LOGGER.error("IO Error while processing commands", ioe);
+            } catch (Exception e) {
+                LOGGER.error("Error while processing commands", e);
+            }
+        }
+    }
+
+    private void checkHello(Socket socket) throws IOException {
+
+        DataInputStream din = new DataInputStream(socket.getInputStream());
+        int hdr = din.readInt();
+        if (hdr <= 0 || hdr > hello.length() + 8) // Version number may be somewhat longer in future.
+            throw new IOException("Invalid MalmoEnv hello header length");
+        byte[] data = new byte[hdr];
+        din.readFully(data);
+        if (!new String(data).startsWith(hello + version))
+            throw new IOException("MalmoEnv invalid protocol or version - expected " + hello + version);
+
+    }
+
+    private void setWantToQuit() {
+        // todo make sure this is really neccessary
+        iwanttoquit = true;
+    }
+
+
+    boolean initMission(DataInputStream din, String command, Socket socket) throws IOException, InterruptedException {
+        int hdr;
+        byte[] data;
+        hdr = din.readInt();
+        data = new byte[hdr];
+        din.readFully(data);
+        String id = new String(data, Charsets.UTF_8);
+        LOGGER.info("Received Mission token " + id);
+        LOGGER.info("Received mission init command  " + command);
+
+        // todo world settings and dimension generator settings from mission xml
+        Minecraft mc = Minecraft.getInstance();
+        missionInit = MissionSpec.decodeMissionInit(command);
+
+        // Manual parsing seed from the token, as done in older code
+        // id is string of ":" separated values. The sixth is seed if it exists.
+        // This is done to support the `env.seed` command of Gym-like environments, which
+        // would change/modify the mission XML constantly.
+        // WorldSeed handler is also supported below, but this overrides WorldSeed.
+
+        Long seed = null;
+        String[] parts = id.split(":");
+        if (parts.length >= 6) {
+            try {
+                seed = Long.parseLong(parts[5]);
+            } catch (NumberFormatException e) {
+                LOGGER.error("Received invalid seed: " + parts[5]);
+            }
+        }
+        if (seed == null) {
+            // If seed was not set in mission token, see if XML file has it
+            seed = getSeed(missionInit);
+        }
+
+        final Long final_seed = seed;
+
+        setGameSetttings(missionInit);
+        mc.getSession().setUsername(missionInit.getMission().getAgentSection().get(0).getName());
+        setUsername(missionInit);
+        mc.execute(() -> loadOrCreateWorld(missionInit, final_seed));
+        while (!PlayRecorder.getInstance().isRecording()) {
+            Thread.sleep(10);
+        }
+
+        mc.execute(() -> setAgentInventory(mc.player, missionInit));
+        mc.execute(() -> setAgentPosition(mc.player, missionInit));
+        envTickCounter = PlayRecorder.getInstance().getTickCounter();
+        // TODO possibly remove
+        // if necessary, can set this from missionInit ?
+        int skipFrames = DEFAULT_SKIP_FIRST_FRAMES;
+        for (int i = 0; i < skipFrames; i++) {
+            execActions("camera 0 0.0", 0);
+            waitForNextObservation();
+        }
+
+        mc.execute( () -> {
+                    Pos startV = getAgentStart(missionInit).getVelocity();
+                    if (startV != null) {
+                        mc.player.setMotion(startV.getX(), startV.getY(), startV.getZ());
+                    }
+                });
+
+
+        DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
+        dout.writeInt(4);
+        dout.writeInt(1);
+        dout.flush();
+        return true;
+    }
+
+    private void setAgentInventory(ClientPlayerEntity player, MissionInit missionInit) {
+        // using forEach and lambda instead of for loop to avoid atrociously long
+        // type name
+        AgentStart.Inventory inventory = getAgentStart(missionInit).getInventory();
+        if (inventory == null) {
+            return;
+        }
+        inventory.getInventoryObject().forEach( e -> {
+            String type = e.getValue().getType();
+            int quantity = e.getValue().getQuantity();
+            int slot = e.getValue().getSlot();
+            Item item = Registry.ITEM.getOrDefault(new ResourceLocation(type));
+            player.inventory.setInventorySlotContents(slot, new ItemStack(item, quantity));
+            Minecraft.getInstance().getIntegratedServer().getPlayerList().getPlayers().forEach( p -> {
+                    p.inventory.setInventorySlotContents(slot, new ItemStack(item, quantity));
+                });
+        });
+    }
+
+    private void setAgentPosition(ClientPlayerEntity player, MissionInit missionInit) {
+        PosAndDirection startPos = getAgentStart(missionInit).getPlacement();
+        if (startPos == null) {
+            return;
+        }
+        player.setPosition(startPos.getX(), startPos.getY(), startPos.getZ());
+        player.rotationYaw = startPos.getYaw();
+        player.rotationPitch = startPos.getPitch();
+    }
+
+    private String getSaveFile(MissionInit missionInit) {
+        return missionInit.getMission().getAgentSection().get(0).getAgentStart().getLoadWorldFile();
+    }
+
+    private void setUsername(MissionInit missionInit) {
+        String username = getAgentStart(missionInit).getMultiplayerUsername();
+        if (username != null) {
+            Minecraft.getInstance().getSession().setUsername(username);
+        }
+    }
+    
+    private void setGameSetttings(MissionInit missionInit) {
+        Minecraft mc = Minecraft.getInstance();
+        GameSettings settings = mc.gameSettings;
+        AgentStart agentStart = getAgentStart(missionInit);
+        settings.gamma = agentStart.getGammaSetting();
+        settings.fov = agentStart.getFOVSetting();
+        settings.disableRecorder = agentStart.isEnableRecorder() == null || !agentStart.isEnableRecorder();
+        settings.fakeCursorSize = agentStart.getFakeCursorSize();
+        float guiScale = agentStart.getGuiScale();
+        settings.setSoundLevel(SoundCategory.MASTER, 0.0f);
+
+        MainWindow window = mc.getMainWindow();
+        getAgentHandlers().filter(h -> h instanceof VideoProducer).forEach(h -> {
+            VideoProducer vp = (VideoProducer)h;
+            System.out.println("Setting width, height to " + vp.getWidth() + ", " + vp.getHeight());
+            double fbToWindowRatio = (double) window.getFramebufferWidth() / window.getWidth();
+            mc.execute(() -> {
+                window.resize((int) (vp.getWidth() / fbToWindowRatio), (int) (vp.getHeight() / fbToWindowRatio));
+                mc.updateWindowSize();
+                window.setGuiScale(guiScale);
+            });
+        });
+
+        System.out.println("Gamma: " + settings.gamma);
+        System.out.println("FOV: " + settings.fov);
+        System.out.println("GuiScale: " + guiScale);
+    }
+
+    private AgentStart getAgentStart(MissionInit missionInit) {
+        return missionInit.getMission().getAgentSection().get(0).getAgentStart();
+    }
+
+    private void loadOrCreateWorld(MissionInit missionInit, Long seed) {
+        String saveZipFile = getSaveFile(missionInit);
+        if (saveZipFile == null) {
+            String serverAddress = getServerAddress(missionInit);
+            if (serverAddress == null) {
+                createNewWorld(missionInit, seed);
+            } else {
+                connectToServer(serverAddress);
+            }
+        } else {
+            ReplaySender.getInstance().loadWorldFromZip(saveZipFile);
+        }
+    }
+
+    private String getServerAddress(MissionInit missionInit) {
+        return getServerInit(missionInit).getRemoteServer();
+    }
+
+    private void connectToServer(String serverAddress) {
+        Minecraft mc = Minecraft.getInstance();
+        ServerData serverData = new ServerData("social", serverAddress, true);
+        ServerAddress serveraddress = ServerAddress.fromString(serverData.serverIP);
+        mc.displayGuiScreen(new ConnectingScreen(new MainMenuScreen(false), mc, serverData));
+    }
+
+    private void createNewWorld(MissionInit missionInit) {
+        createNewWorld(missionInit, null);
+    }
+
+    private void createNewWorld(MissionInit missionInit, Long seed) {
+        Minecraft mc = Minecraft.getInstance();
+        boolean bonusChest = isBonusChest(missionInit);
+        boolean generateFeatures = isGenerateFeatures(missionInit);
+        boolean spawnInVillage = isSpawnInVillage(missionInit);
+        this.doneOnDeath = isDoneOnDeath(missionInit);
+        if (this.doneOnDeath) {
+            // If we are resetting environment, this ensures
+            // the flag is reset to false
+            mc.setHasPlayerRespawned(false);
+        }
+        if (seed == null) {
+            seed = new Random().nextLong();
+            System.out.println("Seed not provided, generating random one: " + String.valueOf(seed));
+        }
+        String worldName = "mcpworld" + RandomHelper.getRandomHexString();
+        String spawnBiome = getAgentStart(missionInit).getPreferredSpawnBiome();
+        if (spawnBiome != null) {
+            checkValidBiome(spawnBiome);
+            MinecraftServer.setSpawnBiomePredicate( b -> b.getCategory().getName().equals(spawnBiome) );
+        }
+
+        if (spawnInVillage) {
+            MinecraftServer.setSpawnInVillage(true);
+        }
+
+
+        WorldSettings worldSettings = new WorldSettings(worldName, GameType.SURVIVAL, false, Difficulty.HARD, true, new GameRules(), DatapackCodec.VANILLA_CODEC);
+        DimensionGeneratorSettings dms = DimensionGeneratorSettings.fromDynamicRegistries(DynamicRegistries.getImpl(), seed, generateFeatures, bonusChest);
+        mc.createWorld(worldName, worldSettings, DynamicRegistries.getImpl(), dms);
+    }
+
+    private void checkValidBiome(String spawnBiome) {
+        Set<String> biomeCategories = DynamicRegistries.getImpl().getRegistry(Registry.BIOME_KEY).getEntries().stream()
+                .map(e -> e.getValue().getCategory().getName())
+                .collect(Collectors.toSet());
+        if (!biomeCategories.contains(spawnBiome)) {
+            LOGGER.error("Bad starting biome " + spawnBiome);
+            LOGGER.error("Biome should be one of the following: ");
+            for (String b : biomeCategories) {
+                LOGGER.error("- " + b);
+            }
+            throw new RuntimeException("Bad starting biome " + spawnBiome);
+        }
+    }
+
+    private Long getSeed(MissionInit missionInit){
+        // return null;
+        return getAgentStart(missionInit).getWorldSeed();
+    }
+
+    private boolean isBonusChest(MissionInit missionInit) {
+        Boolean bonusChest = getAgentStart(missionInit).isBonusChest();
+        return bonusChest != null && bonusChest;
+    }
+
+    private boolean isGenerateFeatures(MissionInit missionInit) {
+        Boolean genFeatures = getAgentStart(missionInit).isGenerateFeatures();
+        return genFeatures == null || genFeatures;
+    }
+
+    private boolean isSpawnInVillage(MissionInit missionInit) {
+        Boolean spawnInVillage = getAgentStart(missionInit).isSpawnInVillage();
+        return spawnInVillage != null && spawnInVillage;
+    }
+
+    private boolean isDoneOnDeath(MissionInit missionInit) {
+        Boolean doneOnDeath = getAgentStart(missionInit).isDoneOnDeath();
+        return doneOnDeath != null && doneOnDeath;
+    }
+
+
+    void peek(String command, Socket socket, DataInputStream din) throws IOException, ExecutionException, InterruptedException {
+        Minecraft mc = Minecraft.getInstance();
+        DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
+        byte[] obs = getPOVObservation();
+        boolean done = false;
+        String info = getInfo();
+        dout.writeInt(obs.length);
+        dout.write(obs);
+        byte[] infoBytes = info.getBytes(Charset.forName("UTF-8"));
+        dout.writeInt(infoBytes.length);
+        dout.write(infoBytes);
+        dout.writeInt(1);
+        dout.writeByte(done ? 1 : 0);
+        dout.flush();
+    }
+
+    private byte[] getPOVObservation() {
+        return PlayRecorder.getInstance().getLastImageBytes();
+    }
+
+    private void waitForNextObservation() {
+        // this dependency on tick counter seems a little spaghetti
+        // ideally, instead addAction returns a future on next observation (gym-style)
+        // these futures are then resolved by ReplaySender or similar entity
+        PlayRecorder pr = PlayRecorder.getInstance();
+
+        try {
+            synchronized (pr) {
+                while (envTickCounter == pr.getTickCounter()) {
+                    pr.wait();
+                }
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        envTickCounter = PlayRecorder.getInstance().getTickCounter();
+    }
+
+
+    private void stepClient(String command, Socket socket, DataInputStream din) throws IOException {
+        Minecraft mc = Minecraft.getInstance();
+        String actions = command.substring(stepClientTagLength, command.length() - (stepClientTagLength + 2));
+        int options =  Character.getNumericValue(command.charAt(stepServerTagLength - 2));
+        boolean withInfo = options == 0 || options == 2;
+        envTickCounter = PlayRecorder.getInstance().getTickCounter();
+        execActions(actions, options);
+        waitForNextObservation();
+        byte[] obs = getPOVObservation();
+        boolean done = mc.player == null || !mc.player.isAlive() || (this.doneOnDeath && mc.isHasPlayerRespawned());
+        boolean sent = true;
+        DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
+        dout.writeInt(obs.length);
+        dout.write(obs);
+        dout.writeInt(BYTES_DOUBLE + 2);
+        dout.writeDouble(0.0);
+        dout.writeByte(done ? 1 : 0);
+        dout.writeByte(sent ? 1 : 0);
+
+        if (withInfo) {
+            String info = getInfo();
+            byte[] infoBytes = info.getBytes(Charsets.UTF_8);
+            dout.writeInt(infoBytes.length);
+            dout.write(infoBytes);
+        }
+        dout.flush();
+    }
+
+    private Stream<Object> getAgentHandlers() {
+        return missionInit.getMission().getAgentSection().get(0).getAgentHandlers().getAgentMissionHandlers().stream();
+    }
+    
+    private static ServerInitialConditions getServerInit(MissionInit missionInit) {
+        return missionInit.getMission().getServerSection().getServerInitialConditions();
+    }
+
+    private String getInfo() {
+        Minecraft mc = Minecraft.getInstance();
+        JsonObject infoJson = new JsonObject();
+        List<Object> handlers = missionInit.getMission().getAgentSection().get(0).getAgentHandlers().getAgentMissionHandlers();
+        if (mc.player != null) {
+            getAgentHandlers().filter(h -> h instanceof ObservationFromFullInventory).limit(1)
+                    .forEach(h -> infoJson.add("inventory", getInventoryJson()));
+            getAgentHandlers().filter(h -> h instanceof ObservationFromFullStats).limit(1)
+                    .forEach(h -> {
+                        JSONWorldDataHelper.buildAllStats(infoJson, mc.player);
+                    });
+            getAgentHandlers().filter(h -> h instanceof ObservationFromEquippedItem).limit(1)
+                    .forEach(h -> {
+                        infoJson.add("equipped_items", getEquippedItemJson());
+                    });
+        }
+
+        JsonObject pos = new JsonObject();
+        pos.addProperty("pitch", mc.player.getRotationPitch());
+        pos.addProperty("yaw", mc.player.getRotationYaw());
+        pos.addProperty("x", mc.player.getPlayerX());
+        pos.addProperty("y", mc.player.getPlayerY());
+        pos.addProperty("z", mc.player.getPlayerZ());
+        infoJson.addProperty("player_pos", pos.toString());
+
+        infoJson.addProperty("isGuiOpen", mc.currentScreen != null);
+        return infoJson.toString();
+    }
+
+    public static JsonArray getInventoryJson() {
+        JsonArray result = new JsonArray();
+        PlayerInventory inventory = Minecraft.getInstance().player.inventory;
+        List<ItemStack> items = inventory.mainInventory.getList();
+        for (int i = 0; i < items.size(); i++) {
+            ItemStack item = items.get(i);
+            JsonObject stack = new JsonObject();
+            stack.addProperty("slot_id", i);
+            if (item.getCount() == 0) {
+                stack.addProperty("type", "none");
+                stack.addProperty("quantity", 0);
+            } else {
+                stack.addProperty("type", item.getItem().toString());
+                stack.addProperty("quantity", item.getCount());
+            }
+            
+            result.add(stack);
+        }
+        return result;
+    }
+
+    public static JsonObject getEquippedItemJson() {
+        JsonObject result = new JsonObject();
+        PlayerEntity player = Minecraft.getInstance().player;
+        assert player != null;
+        result.addProperty("mainhand", getEquipmentJsonObjectFromPlayer(player, EquipmentSlotType.MAINHAND));
+        result.addProperty("offhand", getEquipmentJsonObjectFromPlayer(player, EquipmentSlotType.OFFHAND));
+        result.addProperty("head", getEquipmentJsonObjectFromPlayer(player, EquipmentSlotType.HEAD));
+        result.addProperty("chest", getEquipmentJsonObjectFromPlayer(player, EquipmentSlotType.CHEST));
+        result.addProperty("legs", getEquipmentJsonObjectFromPlayer(player, EquipmentSlotType.LEGS));
+        result.addProperty("feet", getEquipmentJsonObjectFromPlayer(player, EquipmentSlotType.FEET));
+        return result;
+    }
+
+    private static String getEquipmentJsonObjectFromPlayer(PlayerEntity player, EquipmentSlotType type) {
+        JsonObject result = new JsonObject();
+        ItemStack item = player.getItemStackFromSlot(type);
+        result.addProperty("type", item.getItem().toString());
+        result.addProperty("maxDamage", item.getMaxDamage());
+        result.addProperty("damage", item.getDamage());
+        return result.toString();
+    }
+
+    public static void execActions(String actions, int options) {
+        doChatActions(actions);
+        KeyboardListener.State keysState = constructKeyboardState(actions);
+        MouseHelper.State mouseState = constructMouseState(actions);
+        PlayRecorder.getInstance().setMouseKeyboardState(mouseState, keysState);
+        ReplaySender.getInstance().addAction(mouseState, keysState);
+    }
+    
+    
+    public static void doChatActions(String actions) {
+        for (String action: actions.split("\n")) {
+           String[] splitAction = action.trim().split(" ");
+           if (splitAction[0].equals("chat") && splitAction.length > 1) {
+              String chatMessage = action.trim().substring(splitAction[0].length()).trim();
+              Minecraft.getInstance().player.sendChatMessage(chatMessage);
+           }
+        }
+    }
+
+    private static KeyboardListener.State constructKeyboardState(String actions) {
+        List<String> keysPressed = new ArrayList<>();
+        for (String action: actions.split("\n")) {
+           String[] splitAction = action.trim().split(" ");
+           if (!splitAction[0].equals("camera") && !splitAction[0].equals("dwheel") && !splitAction[0].equals("chat") && !splitAction[0].equals("equip")) {
+               if (splitAction.length > 1 && Integer.parseInt(splitAction[1]) == 1) {
+                   String key = actionToKey(splitAction[0]);
+                   if (key != null) {
+                       keysPressed.add(key);
+                   }
+               }
+           }
+        }
+        return new KeyboardListener.State(keysPressed, Collections.emptyList(), "");
+    }
+
+    private static MouseHelper.State constructMouseState(String actions) {
+        List<Integer> buttonsPressed = new ArrayList<>();
+        double dx = 0;
+        double dy = 0;
+        double dwheel = 0;
+        // 2400 is mouse dx that corresponds to a full (360 degree) turn, hence the
+        // formula below to compute mouse -> camera sensitivity
+        // the value is screen resolution independent, as it turns out
+        // there is a manual test in monorepo (minecraft/tests/test_turn.py) that can be used to
+        // validate that, indeed, with this value of sensitivity, 360 degrees in camera pitch make
+        // a full turn, and 90 degrees in yaw make agent look fully up or fully down
+        double sensitivity = 2400.0 / 360;
+        for (String action: actions.split("\n")) {
+            String[] splitAction = action.trim().split(" ");
+            if (splitAction[0].equals("camera")) {
+                dx = Double.parseDouble(splitAction[2]) * sensitivity;
+                dy = Double.parseDouble(splitAction[1]) * sensitivity;
+            } else if (splitAction[0].equals("dwheel")) {
+                dwheel = Double.parseDouble(splitAction[1]);
+            } else if (!splitAction[0].equals("chat")){
+                if (splitAction.length > 1 && Integer.parseInt(splitAction[1]) == 1) {
+                    Integer key = actionToMouseButton(splitAction[0]);
+                    if (key != null) {
+                        buttonsPressed.add(key);
+                    }
+                }
+            }
+        }
+        return new MouseHelper.State(0, 0, dx, dy, dwheel, buttonsPressed, Collections.emptyList());
+    }
+
+    private static Integer actionToMouseButton(String action) {
+        if (action.equals("attack")) {
+            return 0;
+        } else if (action.equals("use")) {
+            return 1;
+        } else if (action.equals("pickItem")) {
+            return 2;
+        }
+        return null;
+    }
+
+    private static String actionToKey(String action) {
+        if (action.equals("forward")) {
+            return "key.keyboard.w";
+        } else if (action.equals("back")) {
+            return "key.keyboard.s";
+        } else if (action.equals("left")) {
+            return "key.keyboard.a";
+        } else if (action.equals("right")) {
+            return "key.keyboard.d";
+        } else if (action.equals("jump")) {
+            return "key.keyboard.space";
+        } else if (action.equals("sprint")) {
+            return "key.keyboard.left.control";
+        } else if (action.equals("sneak")) {
+            return "key.keyboard.left.shift";
+        } else if (action.startsWith("hotbar")) {
+            return "key.keyboard." + action.split("\\.")[1];
+        } else if (action.equals("inventory")) {
+            return "key.keyboard.e";
+        } else if (action.equals("drop")) {
+            return "key.keyboard.q";
+        } else if (action.equals("swapHands")) {
+            return "key.keyboard.f";
+        } else if (action.equals("ESC")) {
+            return "key.keyboard.escape";
+        }
+        return null;
+    };
+
+    void stepServer(String command, Socket socket) {
+        // step server
+    }
+
+    // Handler for <Quit> (quit mission) messages.
+    private void quit(String command, Socket socket) throws IOException, InterruptedException {
+        Minecraft mc = Minecraft.getInstance();
+        if (mc.getProfiler() instanceof IResultableProfiler) {
+            File profileDump = new File("profile-results-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + ".txt");
+            ((IResultableProfiler)mc.getProfiler()).getResults().writeToFile(profileDump.getAbsoluteFile());
+        }
+        PlayRecorder.getInstance().finishAndResetEpisode();
+        ReplaySender.getInstance().stop();
+
+        while (!(mc.currentScreen instanceof MainMenuScreen)) {
+            Thread.sleep(10);
+        }
+        DataOutputStream dout = new DataOutputStream(socket.getOutputStream());
+        dout.writeInt(4);
+        dout.writeInt(1);
+        dout.flush();
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/env/FakeMouseCursor.java MCP-Reborn/src/main/java/com/minerl/multiagent/env/FakeMouseCursor.java
--- MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/env/FakeMouseCursor.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/minerl/multiagent/env/FakeMouseCursor.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,87 @@
+package com.minerl.multiagent.env;
+
+import com.mojang.blaze3d.matrix.MatrixStack;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+import net.minecraft.client.GameSettings;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.renderer.texture.SimpleTexture;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.resources.*;
+import net.minecraft.util.ResourceLocation;
+
+import java.io.File;
+
+public class FakeMouseCursor {
+    private static FakeMouseCursor instance = new FakeMouseCursor();
+    private FakeMouseCursor() {
+        System.out.println("*** creating fake mouse cursor ***");
+    }
+
+    public static FakeMouseCursor getInstance() {
+        return instance;
+    }
+
+    public void render(MatrixStack matrixStack, Screen screen, int x, int y) {
+        GameSettings gameSettings = Minecraft.getInstance().gameSettings;
+        int size= gameSettings.fakeCursorSize;
+        if (size == 0) {
+            return;
+        }
+        GlStateManager.enableTexture();
+        GlStateManager.disableLighting();
+        GlStateManager.disableDepthTest();
+        if (screen == null) {
+            return;
+        }
+        GlStateManager.pushMatrix();
+        bindTexture(size);
+        GlStateManager.enableRescaleNormal();
+        GlStateManager.enableAlphaTest();
+
+        GlStateManager.alphaFunc(516, 0.1F);
+        GlStateManager.enableBlend();
+        GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA.param, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA.param);
+        RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+        screen.blit(matrixStack, x, y, 0, 0, size, size);
+
+        GlStateManager.disableAlphaTest();
+        GlStateManager.disableRescaleNormal();
+        GlStateManager.disableLighting();
+        GlStateManager.popMatrix();
+
+    }
+
+
+    private static void bindTexture (int size) {
+        if (size < 1 || size > 16) {
+            throw new RuntimeException("Cursor size should be between 1 and 16 (requested " + size + ")");
+        }
+        int rounded_size = 4;
+        // We select nearest power of 2 larger than the size (because the cursor sprites are available for sizes that
+        // are powers of two). The sprite is then cropped around top left corner is size != power of 2.
+        // Also, the smallest available sprite is 4x4, so if requested size is 1 or 2, we are still cropping 4x4 sprite.
+        while (rounded_size < size) {
+            rounded_size <<= 1;
+        }
+        String textureName = new StringBuilder()
+                    .append("cursors:mouse_cursor_white_")
+                    .append(rounded_size)
+                    .append("x")
+                    .append(rounded_size)
+                    .append(".png").toString();
+
+        TextureManager tm = Minecraft.getInstance().getTextureManager();
+        IResourceManager rm = Minecraft.getInstance().getResourceManager();
+        ResourceLocation texLocation = new ResourceLocation(textureName);
+        if (tm.getTexture(texLocation) == null) {
+            IResourcePack resourcePack = new JavaResourcePack();
+            ((SimpleReloadableResourceManager)rm).addResourcePack(resourcePack);
+            SimpleTexture texture = new SimpleTexture(texLocation);
+            tm.loadTexture(texLocation, texture);
+        }
+        tm.bindTexture(texLocation);
+        tm.getTexture(texLocation).setBlurMipmapDirect(false, false);
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/env/MissionSpec.java MCP-Reborn/src/main/java/com/minerl/multiagent/env/MissionSpec.java
--- MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/env/MissionSpec.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/minerl/multiagent/env/MissionSpec.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,21 @@
+package com.minerl.multiagent.env;
+
+import com.microsoft.Malmo.Schemas.MissionInit;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import java.io.StringReader;
+
+public class MissionSpec {
+    public static MissionInit decodeMissionInit(String command)
+    {
+        try {
+            JAXBContext context = JAXBContext.newInstance(MissionInit.class);
+            return (MissionInit) context.createUnmarshaller()
+                    .unmarshal(new StringReader(command));
+        } catch (JAXBException e) {
+            System.out.println("JAXB exception: " + e);
+            throw new RuntimeException(e);
+        }
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/RandomHelper.java MCP-Reborn/src/main/java/com/minerl/multiagent/RandomHelper.java
--- MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/RandomHelper.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/minerl/multiagent/RandomHelper.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,96 @@
+package com.minerl.multiagent;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.LogManager;
+
+import org.apache.commons.codec.binary.Hex;
+
+public class RandomHelper {
+    private static Random seedsRand = new Random(0);
+    private static Map<String, Random> rands = new HashMap<>();
+    private static int DEFAULT_LENGTH = 6;
+
+    public static Random getRandom() {
+        // TODO maybe instead generate a new Random with a seed from this rand for better thread safety or to prevent
+        // accidental seeding elsewhere
+        return getRandom("default");
+    }
+
+    public static Random getRandom(String key) {
+        // TODO maybe instead generate a new Random with a seed from this rand for better thread safety or to prevent
+        // accidental seeding elsewhere
+        if (!rands.containsKey(key)) {
+            rands.put(key, new Random(seedsRand.nextLong()));
+        }
+        return rands.get(key);
+    }
+
+    public static Random getRandom(String key, long defaultSeed) {
+        // TODO maybe instead generate a new Random with a seed from this rand for better thread safety or to prevent
+        // accidental seeding elsewhere
+        if (!rands.containsKey(key)) {
+            rands.put(key, new Random(defaultSeed));
+        }
+        return rands.get(key);
+    }
+
+    public static synchronized String getRandomHexString() {
+        return getRandomHexString(DEFAULT_LENGTH);
+    }
+
+    public static synchronized String getRandomHexString(int nbytes) {
+        byte[] buf = new byte[nbytes];
+        getRandom().nextBytes(buf);
+        return new String(Hex.encodeHexString(buf));
+    }
+
+    public static synchronized double randomDouble() {
+        return getRandom().nextDouble();
+    }
+
+
+    public static long getSeed(Random rand) {
+        try {
+            Field seedField = Random.class.
+                    getDeclaredField("seed");
+            seedField.setAccessible(true);
+            return ((AtomicLong) seedField.get(rand)).get();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static double nextDouble() {
+        return nextDouble("default");
+    }
+
+    public static double nextDouble(String key) {
+        Random rand = getRandom(key);
+        // System.out.println("nextDouble(" + key + "), seed = " + getSeed(rand));
+        return getRandom(key).nextDouble();
+    }
+
+    public static float nextFloat() {
+        return nextFloat("default");
+    }
+
+    public static float nextFloat(String key) {
+        Random rand = getRandom(key);
+        // System.out.println("nextDouble(" + key + "), seed = " + getSeed(rand));
+        return getRandom(key).nextFloat();
+    }
+
+    public static long nextLong() {
+        return nextLong("default");
+    }
+
+    public static long nextLong(String key) {
+        Random rand = getRandom(key);
+        // System.out.println("nextDouble(" + key + "), seed = " + getSeed(rand));
+        return getRandom(key).nextLong();
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/recorder/AzureUpload.java MCP-Reborn/src/main/java/com/minerl/multiagent/recorder/AzureUpload.java
--- MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/recorder/AzureUpload.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/minerl/multiagent/recorder/AzureUpload.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,119 @@
+package com.minerl.multiagent.recorder;
+
+import com.microsoft.azure.storage.CloudStorageAccount;
+import com.microsoft.azure.storage.blob.CloudBlobClient;
+import com.microsoft.azure.storage.blob.CloudBlobContainer;
+import com.microsoft.azure.storage.blob.CloudBlockBlob;
+import net.minecraft.util.LoggingPrintStream;
+
+import java.io.File;
+import java.rmi.server.ExportException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.stream.Collectors;
+
+public class AzureUpload {
+    private static final int maxRetries = 3;
+    private static ExecutorService executor = Executors.newFixedThreadPool(10);
+
+    public static boolean upload(String localPath, String azurePath) {
+        String azureSas = System.getenv("AZURE_SAS");
+        if (azureSas == null) {
+            System.out.println("No shared access signature (SAS) found, skipping upload of " + localPath);
+            return false;
+        }
+        String azureFullPath = azurePath + new File(localPath).getName();
+        CloudBlockBlob bcc = getBlobContainerClient(azureSas, azureFullPath);
+
+        int attempt = 0;
+        while (true) {
+            try {
+                System.out.println("uploading " + localPath);
+                bcc.uploadFromFile(localPath);
+                System.out.println(localPath + " -> " + azureFullPath + " upload successful!");
+                return true;
+            } catch (Exception e) {
+                if (attempt == maxRetries) {
+                    throw new RuntimeException(e);
+                }
+                System.out.println(localPath + " -> " + azureFullPath + " upload failed with " + e.toString() + ", sleeping and retrying (attempt " + attempt + " out of " + maxRetries + ")");
+                try {
+                    Thread.sleep(10000);
+                } catch (InterruptedException e1) {
+                    System.out.println("Upload thread sleep interrupted!");
+                }
+                attempt++;
+            }
+        }
+    }
+
+    public static Future<String> uploadAsync(String localPath, String azurePath) {
+        return executor.submit(() -> {
+            upload(localPath, azurePath);
+            return azurePath;
+        });
+    }
+
+    public static void finish() {
+        try {
+            executor.shutdown();
+            executor.awaitTermination(10, TimeUnit.MINUTES);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static CloudBlockBlob getBlobContainerClient(String sas, String path) {
+        // Parse the connection string and create a blob client to interact with Blob storage
+        String connectStr = buildConnectionString(sas, path);
+        try {
+            CloudStorageAccount storageAccount = CloudStorageAccount.parse(connectStr);
+            CloudBlobClient blobClient = storageAccount.createCloudBlobClient();
+            CloudBlobContainer container = blobClient.getContainerReference(getContainerFromPath(path));
+            return container.getBlockBlobReference(getBlobFromPath(path));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        /*
+        BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(connectStr).buildClient();
+
+        return blobServiceClient.getBlobContainerClient(getContainerFromPath(path));
+
+         */
+    }
+
+    private static String buildConnectionString(String sas, String azurePath) {
+        String storageAcc = getStorageAccFromPath(azurePath);
+        return String.format("BlobEndpoint=https://%s.blob.core.windows.net;SharedAccessSignature=%s", storageAcc, sas);
+    }
+
+    private static String getBlobFromPath(String azurePath) {
+        testAzurePath(azurePath);
+        return Arrays.stream(azurePath.split("/")).skip(4).collect(Collectors.joining("/"));
+    }
+
+    private static void testAzurePath(String azurePath) {
+        if (!azurePath.startsWith("az://")) {
+            throw new RuntimeException("Azure path must start with az://");
+        }
+    }
+
+    private static String getContainerFromPath(String azurePath) {
+        testAzurePath(azurePath);
+        return Arrays.stream(azurePath.split("/")).skip(3).limit(1).collect(Collectors.joining("/"));
+    }
+
+    private static String getStorageAccFromPath(String azurePath) {
+        testAzurePath(azurePath);
+        return Arrays.stream(azurePath.split("/")).skip(2).limit(1).collect(Collectors.joining("/"));
+
+    }
+
+    public static ExecutorService getExecutor() {
+        return executor;
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/recorder/PlayRecorder.java MCP-Reborn/src/main/java/com/minerl/multiagent/recorder/PlayRecorder.java
--- MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/recorder/PlayRecorder.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/minerl/multiagent/recorder/PlayRecorder.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,334 @@
+package com.minerl.multiagent.recorder;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.microsoft.Malmo.Schemas.UnnamedGridDefinition;
+import com.microsoft.Malmo.Utils.JSONWorldDataHelper;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.minerl.multiagent.RandomHelper;
+import com.minerl.multiagent.env.EnvServer;
+import net.minecraft.client.KeyboardListener;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.MouseHelper;
+import net.minecraft.client.ReplaySender;
+import net.minecraft.client.entity.player.ClientPlayerEntity;
+import net.minecraft.client.gui.screen.inventory.HorseInventoryScreen;
+import net.minecraft.client.gui.screen.inventory.InventoryScreen;
+import net.minecraft.client.shader.Framebuffer;
+import net.minecraft.server.integrated.IntegratedServer;
+import nu.pattern.OpenCV;
+import org.apache.commons.io.FilenameUtils;
+import org.opencv.core.Core;
+import org.opencv.core.CvType;
+import org.opencv.core.Mat;
+import org.opencv.core.Size;
+import org.opencv.imgproc.Imgproc;
+import org.opencv.videoio.VideoWriter;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+import static com.microsoft.Malmo.Utils.CraftingHelper.dumpMinecraftObjectRules;
+import static org.lwjgl.opengl.GL11.*;
+
+public class PlayRecorder {
+    private static PlayRecorder instance = null;
+    private String prefix;
+    private String filename;
+    private String azurePath;
+    private FileWriter actionsWriter;
+    private int tickCounter = -1;
+    private VideoWriter videoWriter;
+    private Framebuffer fbo;
+    private int width;
+    private int height;
+    private int fps = 20;
+    private boolean recording = false;
+    private boolean pausedLastTick = false;
+    private String userid;
+    private String version;
+    private Mat lastFrame;
+    private byte[] lastImageBytes;
+    private ByteBuffer imageByteBuffer;
+    // each video will be no longer than this (currently set to five minutes)
+    private static final int maxDuration = 6000;
+    // disable autosaves - the world is would be saved anyways every maxDuration steps
+    private static final int saveStatePeriod = -1;
+    private String episodeid = RandomHelper.getRandomHexString();
+    Minecraft mc = Minecraft.getInstance();
+    JsonElement mouseState;
+    JsonElement keyboardState;
+
+
+    public static PlayRecorder getInstance() {
+        if (instance == null) {
+            instance = new PlayRecorder(Paths.get(System.getProperty("user.dir"), "recordings").toString());
+        }
+        return instance;
+    }
+
+    public byte[] getLastImageBytes() {
+        return lastImageBytes;
+    }
+
+    public boolean isRecording() {
+        return recording;
+    }
+
+    public PlayRecorder(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public void start() {
+        tickCounter = 0;
+
+        try {
+            // These hoops are necessary because openpnp does not include ffmpeg bindings on linux due
+            // to licensing concerns. So, on linux, an external library must be installed (apt install libopencv3.2-jni)
+            // and loaded
+            width = mc.getFramebuffer().framebufferWidth;
+            height = mc.getFramebuffer().framebufferHeight;
+            if (!mc.gameSettings.disableRecorder) {
+                width = 640;
+                height = 360;
+            }
+            // this.fbo = new Framebuffer(width, height, true, Minecraft.IS_RUNNING_ON_MAC);
+            imageByteBuffer = ByteBuffer.allocateDirect(width * height * 3);
+            lastImageBytes = new byte[imageByteBuffer.capacity()];
+            resolveImageRequests();
+            if (!mc.gameSettings.disableRecorder) {
+                if (System.getProperty("os.name").equals("Linux")) {
+                    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+                } else {
+                    OpenCV.loadLocally();
+                }
+                Properties versionProp = new Properties();
+                versionProp.load(this.getClass().getClassLoader().getResourceAsStream("version.properties"));
+                version = versionProp.getProperty("version");
+                userid = System.getenv().getOrDefault("MINEREC_UID", "unnamed");
+
+                if (lastFrame == null) {
+                    lastFrame = new Mat(height, width, CvType.CV_8UC3);
+                }
+                String playerName = mc.player.getName().getString();
+                filename = Paths.get(this.prefix, version, playerName + "-" + episodeid + "-" + DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss").format(LocalDateTime.now())).toString();
+                azurePath = "";
+                Files.createDirectories(Paths.get(FilenameUtils.getFullPath(filename)));
+                videoWriter = new VideoWriter(filename + ".mp4", VideoWriter.fourcc('x', '2', '6', '4'),
+                        fps, new Size(width, height), true);
+                if (!videoWriter.isOpened()) {
+                    System.out.println("Cannot open VideoWriter! Here's opencv build info");
+                    System.out.println(Core.getBuildInformation());
+                    videoWriter.release();
+                    throw new IllegalArgumentException("VideoWriter Exception: VideoWriter not opened,"
+                            + "check parameters.");
+                }
+                actionsWriter = new FileWriter(filename + ".jsonl", true);
+                FileWriter optionsWriter = new FileWriter(filename + "-options.json", true);
+                optionsWriter.write(getOptionsJson().toString());
+                optionsWriter.close();
+
+                IntegratedServer is = mc.getIntegratedServer();
+                if (is != null) {
+                    is.saveAndUploadWorld(filename + ".zip");
+                    is.setUploadPath(azurePath);
+                    is.setAutosavePeriod(saveStatePeriod);
+                    is.setWorldZipPrefix(filename);
+                }
+
+            }
+            if (ReplaySender.getInstance().getMode() == ReplaySender.Mode.OFF) {
+                ReplaySender.getInstance().sendFromEnv();
+            }
+            System.out.println("Starting new video " + filename);
+            recording = true;
+            pausedLastTick = false;
+            // clearMouseKeyboardState();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private JsonObject getOptionsJson() {
+        Minecraft mc = Minecraft.getInstance();
+        JsonObject retVal = mc.gameSettings.optionsJson();
+        retVal.addProperty("framebufferToWindowRatio", mc.getMainWindow().getFramebufferWidth() / mc.getMainWindow().getWidth());
+        retVal.addProperty("windowWidth", mc.getMainWindow().getWidth());
+        retVal.addProperty("windowHeight", mc.getMainWindow().getHeight());
+        retVal.addProperty("framebufferWidth", mc.getMainWindow().getFramebufferWidth());
+        retVal.addProperty("framebufferHeight", mc.getMainWindow().getFramebufferHeight());
+        return retVal;
+    }
+
+    public void tick() {
+        Minecraft mc = Minecraft.getInstance();
+        ClientPlayerEntity player = mc.player;
+        if (player == null || !player.isAlive()) {
+            if (recording) {
+                finishAndResetEpisode();
+            }
+            return;
+        }
+        if (!recording) {
+            start();
+        }
+        if (pausedLastTick || mc.isGamePaused()) {
+            // Pause recording for one tick after the game
+            // is unpaused to prevent capture of menu frames
+            // When paused, clear mouse and keyboard state
+            mc.mouseHelper.setHumanInput(true);
+            clearMouseKeyboardState();
+        } else {
+            mc.mouseHelper.setHumanInput(false);
+            recordTickImpl();
+        }
+        pausedLastTick = mc.isGamePaused();
+        if (tickCounter == maxDuration) {
+            finish();
+        }
+    }
+
+    private void clearMouseKeyboardState() {
+        Minecraft mc = Minecraft.getInstance();
+        mc.mouseHelper.clearState();
+        mc.keyboardListener.clearState();
+    }
+
+    private void recordTickImpl() {
+        Gson gson = new Gson();
+        int capacity = width * height * 3;
+        resolveImageRequests();
+        if (!mc.gameSettings.disableRecorder) {
+            ByteBuffer imgBuffer = ByteBuffer.allocateDirect(capacity);
+            getRGBFrame(imgBuffer);
+            byte[] imgBytes = new byte[capacity];
+            imgBuffer.get(imgBytes);
+            lastFrame.put(0, 0, imgBytes);
+            Core.flip(lastFrame, lastFrame, 0);
+            Imgproc.cvtColor(lastFrame, lastFrame, Imgproc.COLOR_BGR2RGB);
+            videoWriter.write(lastFrame);
+            if (mc.gameSettings.envPort == 0) {
+                mouseState = gson.toJsonTree(mc.mouseHelper.getState());
+                keyboardState = gson.toJsonTree(mc.keyboardListener.getState());
+            }
+            JsonObject actions = new JsonObject();
+            actions.add("mouse", mouseState);
+            actions.add("keyboard", keyboardState);
+            actions.addProperty("isGuiOpen", mc.currentScreen != null);
+            actions.addProperty("isGuiInventory",
+                    mc.currentScreen != null && (
+                            mc.currentScreen instanceof InventoryScreen ||
+                            mc.currentScreen instanceof HorseInventoryScreen
+                    ));
+            actions.addProperty("hotbar", mc.player.inventory.currentItem );
+            actions.addProperty("yaw", mc.player.rotationYaw);
+            actions.addProperty("pitch", mc.player.rotationPitch);
+            actions.addProperty("xpos", mc.player.getPosX());
+            actions.addProperty("ypos", mc.player.getPosY());
+            actions.addProperty("zpos", mc.player.getPosZ());
+            actions.addProperty("tick", tickCounter);
+            actions.addProperty("milli", System.currentTimeMillis());
+            actions.add("inventory", EnvServer.getInventoryJson());
+            if (mc.getIntegratedServer() != null) {
+                actions.addProperty("serverTick", mc.getIntegratedServer().getTickCounter());
+                actions.addProperty("serverTickDurationMs", mc.getIntegratedServer().getTickTimeRaw());
+            }
+            if (ReplaySender.getInstance().getMode() == ReplaySender.Mode.EXEC_CMD && mc.gameSettings.envPort == 0) {
+                ReplaySender.getInstance().addAction(actions);
+            }
+            // System.out.println("tick " + tickCounter + ", " + getStats());
+            actions.add("stats", getStats());
+
+            try {
+                actionsWriter.write(actions.toString());
+                actionsWriter.write("\n");
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        mouseState = null;
+        keyboardState = null;
+        tickCounter++;
+        synchronized (this) {
+            this.notifyAll();
+        }
+    }
+
+    private JsonObject getStats() {
+        ClientPlayerEntity player = Minecraft.getInstance().player;
+        JsonObject infoJson = new JsonObject();
+        if (player != null) {
+            JSONWorldDataHelper.buildAllStats(infoJson, player);
+        }
+        return infoJson;
+    }
+
+
+    private void upload() {
+        AzureUpload.uploadAsync(filename + ".jsonl", azurePath);
+        AzureUpload.uploadAsync(filename + "-options.json", azurePath);
+        AzureUpload.uploadAsync(filename + ".mp4", azurePath);
+    }
+
+    public void finish() {
+        tickCounter = -1;
+        if (!recording) {
+            return;
+        }
+        try {
+            recording = false;
+            mc.mouseHelper.setHumanInput(true);
+            // lastImageBytes = null;
+            if (!Minecraft.getInstance().gameSettings.disableRecorder) {
+                System.out.println("Finalizing the video");
+                actionsWriter.close();
+                videoWriter.write(lastFrame);
+                videoWriter.release();
+                upload();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void finishAndResetEpisode() {
+        finish();
+        lastFrame = null;
+        episodeid = RandomHelper.getRandomHexString();
+    }
+
+    private void getRGBFrame(ByteBuffer buffer) {
+        RenderSystem.pushMatrix();
+        mc.getFramebuffer().framebufferRender(width, height);
+        RenderSystem.popMatrix();
+        glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
+    }
+
+    public int getTickCounter() {
+        return tickCounter;
+    }
+
+    public void setMouseKeyboardState(MouseHelper.State mouseState, KeyboardListener.State keyboardState) {
+        Gson gson = new Gson();
+        this.mouseState = gson.toJsonTree(mouseState);
+        this.keyboardState = gson.toJsonTree(keyboardState);
+    }
+
+    private void resolveImageRequests() {
+        mc.getProfiler().startSection("resolveImageRequests");
+        mc.getProfiler().endStartSection("getRBGFrame");
+        imageByteBuffer.rewind();
+        getRGBFrame(imageByteBuffer);
+        mc.getProfiler().endStartSection("getBytes");
+        imageByteBuffer.get(lastImageBytes);
+        mc.getProfiler().endSection();
+        mc.getProfiler().endSection();
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/recorder/ZipUtil.java MCP-Reborn/src/main/java/com/minerl/multiagent/recorder/ZipUtil.java
--- MCP-Reborn-vanilla/src/main/java/com/minerl/multiagent/recorder/ZipUtil.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/com/minerl/multiagent/recorder/ZipUtil.java	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,146 @@
+package com.minerl.multiagent.recorder;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+public class ZipUtil {
+
+    public static void zip(String srcPath, String zipFile) {
+        try {
+            // todo currently this zips paths relative to current path. There must be a way to do this more generally.
+            // todo can the line below be rewritten as a single statement?
+            try (FileOutputStream fos = new FileOutputStream(zipFile); ZipOutputStream zos = new ZipOutputStream(fos)) {
+                zipFiles(new File(srcPath), srcPath, zos);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void unzip(String zipFile, String dstPath) {
+        try {
+            unzipFiles(zipFile, dstPath);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static List<String> listZip(String zipFile) {
+        try {
+            return listFiles(zipFile);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void zipFiles(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
+        // todo low-pri rewrite without recursion?
+//        if (fileToZip.isHidden()) {
+//            return;
+//        }
+        if (fileToZip.isDirectory()) {
+            if (fileName.endsWith("/")) {
+                zipOut.putNextEntry(new ZipEntry(fileName));
+                zipOut.closeEntry();
+            } else {
+                zipOut.putNextEntry(new ZipEntry(fileName + "/"));
+                zipOut.closeEntry();
+            }
+            File[] children = fileToZip.listFiles();
+            for (File childFile : children) {
+                zipFiles(childFile, fileName + "/" + childFile.getName(), zipOut);
+            }
+            return;
+        }
+//        try (FileInputStream fis = new FileInputStream(fileToZip)) {
+//            ZipEntry zipEntry = new ZipEntry(fileName);
+//            zipOut.putNextEntry(zipEntry);
+//            byte[] bytes = new byte[1024];
+//            int length;
+//            while ((length = fis.read(bytes)) >= 0) {
+//                zipOut.write(bytes, 0, length);
+//            }
+//        }
+
+        try (FileChannel fc = FileChannel.open(fileToZip.toPath(), StandardOpenOption.READ); InputStream fis = Channels.newInputStream(fc)) {
+            ZipEntry zipEntry = new ZipEntry(fileName);
+            zipOut.putNextEntry(zipEntry);
+            byte[] bytes = new byte[10240];
+            // ByteBuffer bb = ByteBuffer.wrap(bytes);
+            int length = 0;
+            while ((length = fis.read(bytes)) >= 0) {
+                zipOut.write(bytes, 0, length);
+            }
+        }
+    }
+
+
+    private static void unzipFiles(String zipFile, String dstDir) throws IOException {
+        File destDir = new File(dstDir);
+        byte[] buffer = new byte[1024];
+        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
+            ZipEntry zipEntry = zis.getNextEntry();
+            while (zipEntry != null) {
+                File newFile = newFile(destDir, zipEntry);
+                if (zipEntry.isDirectory()) {
+                    if (!newFile.isDirectory() && !newFile.mkdirs()) {
+                        throw new IOException("Failed to create directory " + newFile);
+                    }
+                } else {
+                    // fix for Windows-created archives
+                    File parent = newFile.getParentFile();
+                    if (!parent.isDirectory() && !parent.mkdirs()) {
+                        throw new IOException("Failed to create directory " + parent);
+                    }
+                    // write file content
+                    FileOutputStream fos = new FileOutputStream(newFile);
+                    int len;
+                    while ((len = zis.read(buffer)) > 0) {
+                        fos.write(buffer, 0, len);
+                    }
+                    fos.close();
+                }
+                zipEntry = zis.getNextEntry();
+            }
+            zis.closeEntry();
+        }
+    }
+
+    private static List<String> listFiles(String zipFile) throws IOException {
+        List<String> retVal = new LinkedList<>();
+        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
+            while (true) {
+                ZipEntry zipEntry = zis.getNextEntry();
+                if (zipEntry == null) {
+                    break;
+                }
+                retVal.add(zipEntry.getName().replace('\\', '/'));
+            }
+        }
+        return retVal;
+    }
+
+    private static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
+        String zipEntryName = zipEntry.getName().replace('\\', '/');
+        File destFile = new File(destinationDir, zipEntryName);
+
+        String destDirPath = destinationDir.getCanonicalPath();
+        String destFilePath = destFile.getCanonicalPath();
+
+        if (!destFilePath.startsWith(destDirPath + File.separator)) {
+            throw new IOException("Entry is outside of the target dir: " + zipEntryName);
+        }
+        return destFile;
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/block/Block.java MCP-Reborn/src/main/java/net/minecraft/block/Block.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/block/Block.java	2024-03-10 15:44:47.602123294 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/block/Block.java	2024-03-10 18:29:57.617072423 +0800
@@ -5,2 +5,3 @@
 import com.google.common.cache.LoadingCache;
+import com.minerl.multiagent.RandomHelper;
 import it.unimi.dsi.fastutil.objects.Object2ByteLinkedOpenHashMap;
@@ -228,3 +229,4 @@
    public static List<ItemStack> getDrops(BlockState state, ServerWorld worldIn, BlockPos pos, @Nullable TileEntity tileEntityIn, @Nullable Entity entityIn, ItemStack stack) {
-      LootContext.Builder lootcontext$builder = (new LootContext.Builder(worldIn)).withRandom(worldIn.rand).withParameter(LootParameters.field_237457_g_, Vector3d.copyCentered(pos)).withParameter(LootParameters.TOOL, stack).withNullableParameter(LootParameters.THIS_ENTITY, entityIn).withNullableParameter(LootParameters.BLOCK_ENTITY, tileEntityIn);
+      Random dropRandom = new Random(pos.toLong());
+      LootContext.Builder lootcontext$builder = (new LootContext.Builder(worldIn)).withRandom(dropRandom).withParameter(LootParameters.field_237457_g_, Vector3d.copyCentered(pos)).withParameter(LootParameters.TOOL, stack).withNullableParameter(LootParameters.THIS_ENTITY, entityIn).withNullableParameter(LootParameters.BLOCK_ENTITY, tileEntityIn);
       return state.getDrops(lootcontext$builder);
@@ -265,5 +267,7 @@
          float f = 0.5F;
-         double d0 = (double)(worldIn.rand.nextFloat() * 0.5F) + 0.25D;
-         double d1 = (double)(worldIn.rand.nextFloat() * 0.5F) + 0.25D;
-         double d2 = (double)(worldIn.rand.nextFloat() * 0.5F) + 0.25D;
+         Random rand = new Random(pos.toLong());
+         double d0 = (double)(rand.nextDouble() * 0.5F) + 0.25D;
+         double d1 = (double)(rand.nextDouble() * 0.5F) + 0.25D;
+         double d2 = (double)(rand.nextDouble() * 0.5F) + 0.25D;
+         // System.out.println("Itemstack: " + stack + " spawned with random offset " + d0 + "," + d1 + "," + d2);
          ItemEntity itementity = new ItemEntity(worldIn, (double)pos.getX() + d0, (double)pos.getY() + d1, (double)pos.getZ() + d2, stack);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/audio/MusicTicker.java MCP-Reborn/src/main/java/net/minecraft/client/audio/MusicTicker.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/audio/MusicTicker.java	2024-03-10 15:44:47.618123336 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/audio/MusicTicker.java	2024-03-10 18:29:57.561071402 +0800
@@ -11,3 +11,3 @@
 public class MusicTicker {
-   private final Random random = new Random();
+   private final Random random = new Random(0);
    private final Minecraft client;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/audio/SoundEventAccessor.java MCP-Reborn/src/main/java/net/minecraft/client/audio/SoundEventAccessor.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/audio/SoundEventAccessor.java	2024-03-10 15:44:47.618123336 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/audio/SoundEventAccessor.java	2024-03-10 18:29:57.561071402 +0800
@@ -15,3 +15,3 @@
    private final List<ISoundEventAccessor<Sound>> accessorList = Lists.newArrayList();
-   private final Random rnd = new Random();
+   private final Random rnd = new Random(0);
    private final ResourceLocation location;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/entity/player/ClientPlayerEntity.java MCP-Reborn/src/main/java/net/minecraft/client/entity/player/ClientPlayerEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/entity/player/ClientPlayerEntity.java	2024-03-10 15:44:47.618123336 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/entity/player/ClientPlayerEntity.java	2024-03-10 18:29:57.581071767 +0800
@@ -142,2 +142,14 @@
 
+   public double getPlayerX() {
+      return this.getPosX();
+   }
+
+   public double getPlayerY() {
+      return this.getPosY();
+   }
+
+   public double getPlayerZ() {
+      return this.getPosZ();
+   }
+
    public boolean startRiding(Entity entityIn, boolean force) {
@@ -165,2 +177,10 @@
 
+   public float getRotationPitch() {
+      return this.rotationPitch;
+   }
+
+   public float getRotationYaw() {
+      return this.rotationYaw;
+   }
+
    public float getPitch(float partialTicks) {
@@ -977,2 +997,5 @@
    }
+
+
+
 }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/GameSettings.java MCP-Reborn/src/main/java/net/minecraft/client/GameSettings.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/GameSettings.java	2024-03-10 15:44:47.614123326 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/GameSettings.java	2024-03-10 18:29:57.557071330 +0800
@@ -10,2 +10,3 @@
 import com.google.gson.Gson;
+import com.google.gson.JsonObject;
 import com.google.gson.reflect.TypeToken;
@@ -77,3 +78,3 @@
    public boolean advancedItemTooltips;
-   public boolean pauseOnLostFocus = true;
+   public boolean pauseOnLostFocus = false;
    private final Set<PlayerModelPart> setModelParts = Sets.newHashSet(PlayerModelPart.values());
@@ -98,3 +99,3 @@
    public int glDebugVerbosity = 1;
-   public boolean autoJump = true;
+   public boolean autoJump = false;
    public boolean autoSuggestCommands = true;
@@ -165,3 +166,3 @@
    public double gamma;
-   public int guiScale;
+   public int guiScale = 2;
    public ParticleStatus particles = ParticleStatus.ALL;
@@ -170,2 +171,8 @@
    public boolean syncChunkWrites;
+   public boolean syncIntegratedServer = false;
+   public int envPort = 0;
+   public boolean disableRecorder = false;
+   public int fakeCursorSize = 0;
+   public String loadWorldFile = null;
+   public String replayFile = null;
 
@@ -182,3 +189,8 @@
       this.syncChunkWrites = Util.getOSType() == Util.OS.WINDOWS;
-      this.loadOptions();
+
+      for (SoundCategory sc : SoundCategory.values()) {
+         this.soundLevels.put(sc, 0.2f);
+      }
+      // disable loading option
+      // this.loadOptions();
    }
@@ -680,2 +692,104 @@
 
+   public JsonObject optionsJson() {
+      JsonObject retVal = new JsonObject();
+
+      retVal.addProperty("version", SharedConstants.getVersion().getWorldVersion());
+      retVal.addProperty("autoJump", AbstractOption.AUTO_JUMP.get(this));
+      retVal.addProperty("autoSuggestions", AbstractOption.AUTO_SUGGEST_COMMANDS.get(this));
+      retVal.addProperty("chatColors", AbstractOption.CHAT_COLOR.get(this));
+      retVal.addProperty("chatLinks", AbstractOption.CHAT_LINKS.get(this));
+      retVal.addProperty("chatLinksPrompt", AbstractOption.CHAT_LINKS_PROMPT.get(this));
+      retVal.addProperty("enableVsync", AbstractOption.VSYNC.get(this));
+      retVal.addProperty("entityShadows", AbstractOption.ENTITY_SHADOWS.get(this));
+      retVal.addProperty("forceUnicodeFont", AbstractOption.FORCE_UNICODE_FONT.get(this));
+      retVal.addProperty("discrete_mouse_scroll", AbstractOption.DISCRETE_MOUSE_SCROLL.get(this));
+      retVal.addProperty("invertYMouse", AbstractOption.INVERT_MOUSE.get(this));
+      retVal.addProperty("realmsNotifications", AbstractOption.REALMS_NOTIFICATIONS.get(this));
+      retVal.addProperty("reducedDebugInfo", AbstractOption.REDUCED_DEBUG_INFO.get(this));
+      retVal.addProperty("snooperEnabled", AbstractOption.SNOOPER.get(this));
+      retVal.addProperty("showSubtitles", AbstractOption.SHOW_SUBTITLES.get(this));
+      retVal.addProperty("touchscreen", AbstractOption.TOUCHSCREEN.get(this));
+      retVal.addProperty("fullscreen", AbstractOption.FULLSCREEN.get(this));
+      retVal.addProperty("bobView", AbstractOption.VIEW_BOBBING.get(this));
+      retVal.addProperty("toggleCrouch", this.toggleCrouch);
+      retVal.addProperty("toggleSprint", this.toggleSprint);
+      retVal.addProperty("mouseSensitivity", this.mouseSensitivity);
+      retVal.addProperty("fov", (this.fov - 70.0D) / 40.0D);
+      retVal.addProperty("screenEffectScale", this.screenEffectScale);
+      retVal.addProperty("fovEffectScale", this.fovScaleEffect);
+      retVal.addProperty("gamma", this.gamma);
+      retVal.addProperty("renderDistance", this.renderDistanceChunks);
+      retVal.addProperty("entityDistanceScaling", this.entityDistanceScaling);
+      retVal.addProperty("guiScale", this.guiScale);
+      retVal.addProperty("particles", this.particles.getId());
+      retVal.addProperty("maxFps", this.framerateLimit);
+      retVal.addProperty("difficulty", this.difficulty.getId());
+      retVal.addProperty("graphicsMode", this.graphicFanciness.func_238162_a_());
+      retVal.addProperty("ao", this.ambientOcclusionStatus.getId());
+      retVal.addProperty("biomeBlendRadius", this.biomeBlendRadius);
+      switch (this.cloudOption) {
+         case FANCY:
+            retVal.addProperty("renderClouds", "true");
+            break;
+         case FAST:
+            retVal.addProperty("renderClouds", "fast");
+            break;
+         case OFF:
+            retVal.addProperty("renderClouds", "false");
+      }
+
+      retVal.addProperty("resourcePacks", GSON.toJson(this.resourcePacks));
+      retVal.addProperty("incompatibleResourcePacks", GSON.toJson(this.incompatibleResourcePacks));
+      retVal.addProperty("lastServer", this.lastServer);
+      retVal.addProperty("lang", this.language);
+      retVal.addProperty("chatVisibility", this.chatVisibility.getId());
+      retVal.addProperty("chatOpacity", this.chatOpacity);
+      retVal.addProperty("chatLineSpacing", this.chatLineSpacing);
+      retVal.addProperty("textBackgroundOpacity", this.accessibilityTextBackgroundOpacity);
+      retVal.addProperty("backgroundForChatOnly", this.accessibilityTextBackground);
+      if (this.mc.getMainWindow().getVideoMode().isPresent()) {
+         retVal.addProperty("fullscreenResolution", this.mc.getMainWindow().getVideoMode().get().getSettingsString());
+      }
+
+      retVal.addProperty("hideServerAddress", this.hideServerAddress);
+      retVal.addProperty("advancedItemTooltips", this.advancedItemTooltips);
+      retVal.addProperty("pauseOnLostFocus", this.pauseOnLostFocus);
+      retVal.addProperty("overrideWidth", this.overrideWidth);
+      retVal.addProperty("overrideHeight", this.overrideHeight);
+      retVal.addProperty("heldItemTooltips", this.heldItemTooltips);
+      retVal.addProperty("chatHeightFocused", this.chatHeightFocused);
+      retVal.addProperty("chatDelay", this.chatDelay);
+      retVal.addProperty("chatHeightUnfocused", this.chatHeightUnfocused);
+      retVal.addProperty("chatScale", this.chatScale);
+      retVal.addProperty("chatWidth", this.chatWidth);
+      retVal.addProperty("mipmapLevels", this.mipmapLevels);
+      retVal.addProperty("useNativeTransport", this.useNativeTransport);
+      retVal.addProperty("mainHand", (this.mainHand == HandSide.LEFT ? "left" : "right"));
+      retVal.addProperty("attackIndicator", this.attackIndicator.getId());
+      retVal.addProperty("narrator", this.narrator.getId());
+      retVal.addProperty("tutorialStep", this.tutorialStep.getName());
+      retVal.addProperty("mouseWheelSensitivity", this.mouseWheelSensitivity);
+      retVal.addProperty("rawMouseInput", AbstractOption.RAW_MOUSE_INPUT.get(this));
+      retVal.addProperty("glDebugVerbosity", this.glDebugVerbosity);
+      retVal.addProperty("skipMultiplayerWarning", this.skipMultiplayerWarning);
+      retVal.addProperty("hideMatchedNames", this.field_244794_ae);
+      retVal.addProperty("joinedFirstServer", this.field_244601_E);
+      retVal.addProperty("syncChunkWrites", this.syncChunkWrites);
+
+      for (KeyBinding keybinding : this.keyBindings) {
+         retVal.addProperty("key_" + keybinding.getKeyDescription() + "", keybinding.getTranslationKey());
+      }
+
+      for (SoundCategory soundcategory : SoundCategory.values()) {
+         retVal.addProperty("soundCategory_" + soundcategory.getName() + "", this.getSoundLevel(soundcategory));
+      }
+
+      for (PlayerModelPart playermodelpart : PlayerModelPart.values()) {
+         retVal.addProperty("modelPart_" + playermodelpart.getPartName() + "", this.setModelParts.contains(playermodelpart));
+      }
+      return retVal;
+   }
+
+
+
    public float getSoundLevel(SoundCategory category) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/FontRenderer.java MCP-Reborn/src/main/java/net/minecraft/client/gui/FontRenderer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/FontRenderer.java	2024-03-10 15:44:47.618123336 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/FontRenderer.java	2024-03-10 18:29:57.573071621 +0800
@@ -40,3 +40,3 @@
    public final int FONT_HEIGHT = 9;
-   public final Random random = new Random();
+   public final Random random = new Random(0);
    private final Function<ResourceLocation, Font> font;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/fonts/Font.java MCP-Reborn/src/main/java/net/minecraft/client/gui/fonts/Font.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/fonts/Font.java	2024-03-10 15:44:47.622123347 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/fonts/Font.java	2024-03-10 18:29:57.577071694 +0800
@@ -26,3 +26,3 @@
    };
-   private static final Random RANDOM = new Random();
+   private static final Random RANDOM = new Random(0);
    private final TextureManager textureManager;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/IngameGui.java MCP-Reborn/src/main/java/net/minecraft/client/gui/IngameGui.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/IngameGui.java	2024-03-10 15:44:47.622123347 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/IngameGui.java	2024-03-10 18:29:57.573071621 +0800
@@ -82,3 +82,3 @@
    private static final ITextComponent field_243249_e = new TranslationTextComponent("demo.demoExpired");
-   private final Random rand = new Random();
+   private final Random rand = new Random(0);
    private final Minecraft mc;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/recipebook/AbstractRecipeBookGui.java MCP-Reborn/src/main/java/net/minecraft/client/gui/recipebook/AbstractRecipeBookGui.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/recipebook/AbstractRecipeBookGui.java	2024-03-10 15:44:47.622123347 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/recipebook/AbstractRecipeBookGui.java	2024-03-10 18:29:57.573071621 +0800
@@ -20,3 +20,3 @@
 @OnlyIn(Dist.CLIENT)
-public abstract class AbstractRecipeBookGui extends RecipeBookGui {
+public abstract class   AbstractRecipeBookGui extends RecipeBookGui {
    private Iterator<Item> field_212964_i;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/BiomeGeneratorTypeScreens.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/BiomeGeneratorTypeScreens.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/BiomeGeneratorTypeScreens.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/BiomeGeneratorTypeScreens.java	2024-03-10 18:29:57.577071694 +0800
@@ -93,18 +93,18 @@
    protected static final Map<Optional<BiomeGeneratorTypeScreens>, BiomeGeneratorTypeScreens.IFactory> field_239069_d_ = ImmutableMap.of(Optional.of(field_239070_e_), (p_239089_0_, p_239089_1_) -> {
-      ChunkGenerator chunkgenerator = p_239089_1_.func_236225_f_();
+      ChunkGenerator chunkgenerator = p_239089_1_.getChunkGenerator();
       return new CreateFlatWorldScreen(p_239089_0_, (p_239083_2_) -> {
-         p_239089_0_.field_238934_c_.func_239043_a_(new DimensionGeneratorSettings(p_239089_1_.getSeed(), p_239089_1_.doesGenerateFeatures(), p_239089_1_.hasBonusChest(), DimensionGeneratorSettings.func_242749_a(p_239089_0_.field_238934_c_.func_239055_b_().getRegistry(Registry.DIMENSION_TYPE_KEY), p_239089_1_.func_236224_e_(), new FlatChunkGenerator(p_239083_2_))));
-      }, chunkgenerator instanceof FlatChunkGenerator ? ((FlatChunkGenerator)chunkgenerator).func_236073_g_() : FlatGenerationSettings.func_242869_a(p_239089_0_.field_238934_c_.func_239055_b_().getRegistry(Registry.BIOME_KEY)));
+         p_239089_0_.worldOptionsScreen.func_239043_a_(new DimensionGeneratorSettings(p_239089_1_.getSeed(), p_239089_1_.doesGenerateFeatures(), p_239089_1_.hasBonusChest(), DimensionGeneratorSettings.func_242749_a(p_239089_0_.worldOptionsScreen.getDynamicRegistries().getRegistry(Registry.DIMENSION_TYPE_KEY), p_239089_1_.func_236224_e_(), new FlatChunkGenerator(p_239083_2_))));
+      }, chunkgenerator instanceof FlatChunkGenerator ? ((FlatChunkGenerator)chunkgenerator).func_236073_g_() : FlatGenerationSettings.func_242869_a(p_239089_0_.worldOptionsScreen.getDynamicRegistries().getRegistry(Registry.BIOME_KEY)));
    }, Optional.of(field_239072_g_), (p_239087_0_, p_239087_1_) -> {
-      return new CreateBuffetWorldScreen(p_239087_0_, p_239087_0_.field_238934_c_.func_239055_b_(), (p_239088_2_) -> {
-         p_239087_0_.field_238934_c_.func_239043_a_(func_243452_a(p_239087_0_.field_238934_c_.func_239055_b_(), p_239087_1_, field_239072_g_, p_239088_2_));
-      }, func_243451_a(p_239087_0_.field_238934_c_.func_239055_b_(), p_239087_1_));
+      return new CreateBuffetWorldScreen(p_239087_0_, p_239087_0_.worldOptionsScreen.getDynamicRegistries(), (p_239088_2_) -> {
+         p_239087_0_.worldOptionsScreen.func_239043_a_(func_243452_a(p_239087_0_.worldOptionsScreen.getDynamicRegistries(), p_239087_1_, field_239072_g_, p_239088_2_));
+      }, func_243451_a(p_239087_0_.worldOptionsScreen.getDynamicRegistries(), p_239087_1_));
    }, Optional.of(field_239073_h_), (p_239085_0_, p_239085_1_) -> {
-      return new CreateBuffetWorldScreen(p_239085_0_, p_239085_0_.field_238934_c_.func_239055_b_(), (p_239086_2_) -> {
-         p_239085_0_.field_238934_c_.func_239043_a_(func_243452_a(p_239085_0_.field_238934_c_.func_239055_b_(), p_239085_1_, field_239073_h_, p_239086_2_));
-      }, func_243451_a(p_239085_0_.field_238934_c_.func_239055_b_(), p_239085_1_));
+      return new CreateBuffetWorldScreen(p_239085_0_, p_239085_0_.worldOptionsScreen.getDynamicRegistries(), (p_239086_2_) -> {
+         p_239085_0_.worldOptionsScreen.func_239043_a_(func_243452_a(p_239085_0_.worldOptionsScreen.getDynamicRegistries(), p_239085_1_, field_239073_h_, p_239086_2_));
+      }, func_243451_a(p_239085_0_.worldOptionsScreen.getDynamicRegistries(), p_239085_1_));
    }, Optional.of(field_239074_i_), (p_239081_0_, p_239081_1_) -> {
-      return new CreateBuffetWorldScreen(p_239081_0_, p_239081_0_.field_238934_c_.func_239055_b_(), (p_239082_2_) -> {
-         p_239081_0_.field_238934_c_.func_239043_a_(func_243452_a(p_239081_0_.field_238934_c_.func_239055_b_(), p_239081_1_, field_239074_i_, p_239082_2_));
-      }, func_243451_a(p_239081_0_.field_238934_c_.func_239055_b_(), p_239081_1_));
+      return new CreateBuffetWorldScreen(p_239081_0_, p_239081_0_.worldOptionsScreen.getDynamicRegistries(), (p_239082_2_) -> {
+         p_239081_0_.worldOptionsScreen.func_239043_a_(func_243452_a(p_239081_0_.worldOptionsScreen.getDynamicRegistries(), p_239081_1_, field_239074_i_, p_239082_2_));
+      }, func_243451_a(p_239081_0_.worldOptionsScreen.getDynamicRegistries(), p_239081_1_));
    });
@@ -139,3 +139,3 @@
    private static Biome func_243451_a(DynamicRegistries p_243451_0_, DimensionGeneratorSettings p_243451_1_) {
-      return p_243451_1_.func_236225_f_().getBiomeProvider().getBiomes().stream().findFirst().orElse(p_243451_0_.getRegistry(Registry.BIOME_KEY).getOrThrow(Biomes.PLAINS));
+      return p_243451_1_.getChunkGenerator().getBiomeProvider().getBiomes().stream().findFirst().orElse(p_243451_0_.getRegistry(Registry.BIOME_KEY).getOrThrow(Biomes.PLAINS));
    }
@@ -143,3 +143,3 @@
    public static Optional<BiomeGeneratorTypeScreens> func_239079_a_(DimensionGeneratorSettings p_239079_0_) {
-      ChunkGenerator chunkgenerator = p_239079_0_.func_236225_f_();
+      ChunkGenerator chunkgenerator = p_239079_0_.getChunkGenerator();
       if (chunkgenerator instanceof FlatChunkGenerator) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/CreateWorldScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/CreateWorldScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/CreateWorldScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/CreateWorldScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -64,3 +64,3 @@
    private String saveDirName;
-   private CreateWorldScreen.GameMode field_228197_f_ = CreateWorldScreen.GameMode.SURVIVAL;
+   private CreateWorldScreen.GameMode gameMode = CreateWorldScreen.GameMode.SURVIVAL;
    @Nullable
@@ -68,3 +68,3 @@
    private Difficulty field_238936_v_ = Difficulty.NORMAL;
-   private Difficulty field_238937_w_ = Difficulty.NORMAL;
+   private Difficulty difficulty = Difficulty.NORMAL;
    private boolean allowCheats;
@@ -72,3 +72,3 @@
    public boolean hardCoreMode;
-   protected DatapackCodec field_238933_b_;
+   protected DatapackCodec datapackCodec;
    @Nullable
@@ -88,7 +88,7 @@
    private String worldName;
-   private GameRules field_238932_M_ = new GameRules();
-   public final WorldOptionsScreen field_238934_c_;
+   private GameRules gameRules = new GameRules();
+   public final WorldOptionsScreen worldOptionsScreen;
 
-   public CreateWorldScreen(@Nullable Screen p_i242064_1_, WorldSettings p_i242064_2_, DimensionGeneratorSettings p_i242064_3_, @Nullable Path p_i242064_4_, DatapackCodec p_i242064_5_, DynamicRegistries.Impl p_i242064_6_) {
-      this(p_i242064_1_, p_i242064_5_, new WorldOptionsScreen(p_i242064_6_, p_i242064_3_, BiomeGeneratorTypeScreens.func_239079_a_(p_i242064_3_), OptionalLong.of(p_i242064_3_.getSeed())));
+   public CreateWorldScreen(@Nullable Screen p_i242064_1_, WorldSettings p_i242064_2_, DimensionGeneratorSettings dimensionGeneratorSettings, @Nullable Path p_i242064_4_, DatapackCodec p_i242064_5_, DynamicRegistries.Impl dynamicRegistries) {
+      this(p_i242064_1_, p_i242064_5_, new WorldOptionsScreen(dynamicRegistries, dimensionGeneratorSettings, BiomeGeneratorTypeScreens.func_239079_a_(dimensionGeneratorSettings), OptionalLong.of(dimensionGeneratorSettings.getSeed())));
       this.worldName = p_i242064_2_.getWorldName();
@@ -97,10 +97,10 @@
       this.field_238936_v_ = p_i242064_2_.getDifficulty();
-      this.field_238937_w_ = this.field_238936_v_;
-      this.field_238932_M_.func_234899_a_(p_i242064_2_.getGameRules(), (MinecraftServer)null);
+      this.difficulty = this.field_238936_v_;
+      this.gameRules.func_234899_a_(p_i242064_2_.getGameRules(), (MinecraftServer)null);
       if (p_i242064_2_.isHardcoreEnabled()) {
-         this.field_228197_f_ = CreateWorldScreen.GameMode.HARDCORE;
+         this.gameMode = CreateWorldScreen.GameMode.HARDCORE;
       } else if (p_i242064_2_.getGameType().isSurvivalOrAdventure()) {
-         this.field_228197_f_ = CreateWorldScreen.GameMode.SURVIVAL;
+         this.gameMode = CreateWorldScreen.GameMode.SURVIVAL;
       } else if (p_i242064_2_.getGameType().isCreative()) {
-         this.field_228197_f_ = CreateWorldScreen.GameMode.CREATIVE;
+         this.gameMode = CreateWorldScreen.GameMode.CREATIVE;
       }
@@ -111,3 +111,3 @@
    public static CreateWorldScreen func_243425_a(@Nullable Screen p_243425_0_) {
-      DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
+      DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
       return new CreateWorldScreen(p_243425_0_, DatapackCodec.VANILLA_CODEC, new WorldOptionsScreen(dynamicregistries$impl, DimensionGeneratorSettings.func_242751_a(dynamicregistries$impl.getRegistry(Registry.DIMENSION_TYPE_KEY), dynamicregistries$impl.getRegistry(Registry.BIOME_KEY), dynamicregistries$impl.getRegistry(Registry.NOISE_SETTINGS_KEY)), Optional.of(BiomeGeneratorTypeScreens.field_239066_a_), OptionalLong.empty()));
@@ -119,4 +119,4 @@
       this.worldName = I18n.format("selectWorld.newWorld");
-      this.field_238933_b_ = p_i242063_2_;
-      this.field_238934_c_ = p_i242063_3_;
+      this.datapackCodec = p_i242063_2_;
+      this.worldOptionsScreen = p_i242063_3_;
    }
@@ -125,3 +125,3 @@
       this.worldNameField.tick();
-      this.field_238934_c_.tick();
+      this.worldOptionsScreen.tick();
    }
@@ -144,2 +144,4 @@
       int j = this.width / 2 + 5;
+
+      /*
       this.btnGameMode = this.addButton(new Button(i, 100, 150, 20, StringTextComponent.EMPTY, (p_214316_1_) -> {
@@ -198,4 +200,5 @@
          }));
-      }));
-      this.field_238934_c_.func_239048_a_(this, this.minecraft, this.font);
+      })); */
+      this.worldOptionsScreen.func_239048_a_(this, this.minecraft, this.font);
+      /*
       this.btnMoreOptions = this.addButton(new Button(j, 185, 150, 20, new TranslationTextComponent("selectWorld.moreWorldOptions"), (p_214321_1_) -> {
@@ -203,2 +206,4 @@
       }));
+      */
+
       this.btnCreateWorld = this.addButton(new Button(i, this.height - 28, 150, 20, new TranslationTextComponent("selectWorld.create"), (p_214318_1_) -> {
@@ -210,6 +215,7 @@
       }));
-      this.func_238955_g_();
+      // this.func_238955_g_();
       this.setFocusedDefault(this.worldNameField);
-      this.func_228200_a_(this.field_228197_f_);
+      // this.func_228200_a_(this.field_228197_f_);
       this.calcSaveDirName();
+      // this.createWorld();
    }
@@ -217,4 +223,4 @@
    private void func_228199_a_() {
-      this.gameModeDesc1 = new TranslationTextComponent("selectWorld.gameMode." + this.field_228197_f_.field_228217_e_ + ".line1");
-      this.gameModeDesc2 = new TranslationTextComponent("selectWorld.gameMode." + this.field_228197_f_.field_228217_e_ + ".line2");
+      this.gameModeDesc1 = new TranslationTextComponent("selectWorld.gameMode." + this.gameMode.field_228217_e_ + ".line1");
+      this.gameModeDesc2 = new TranslationTextComponent("selectWorld.gameMode." + this.gameMode.field_228217_e_ + ".line2");
    }
@@ -249,5 +255,5 @@
          this.func_243432_s();
-         DimensionGeneratorSettings dimensiongeneratorsettings = this.field_238934_c_.func_239054_a_(this.hardCoreMode);
+         DimensionGeneratorSettings dimensiongeneratorsettings = this.worldOptionsScreen.getDimensionGeneratorSettings(this.hardCoreMode);
          WorldSettings worldsettings;
-         if (dimensiongeneratorsettings.func_236227_h_()) {
+         if (dimensiongeneratorsettings.isDebug()) {
             GameRules gamerules = new GameRules();
@@ -256,6 +262,6 @@
          } else {
-            worldsettings = new WorldSettings(this.worldNameField.getText().trim(), this.field_228197_f_.field_228218_f_, this.hardCoreMode, this.field_238937_w_, this.allowCheats && !this.hardCoreMode, this.field_238932_M_, this.field_238933_b_);
+            worldsettings = new WorldSettings(this.worldNameField.getText().trim(), this.gameMode.gameType, this.hardCoreMode, this.difficulty, this.allowCheats && !this.hardCoreMode, this.gameRules, this.datapackCodec);
          }
 
-         this.minecraft.createWorld(this.saveDirName, worldsettings, this.field_238934_c_.func_239055_b_(), dimensiongeneratorsettings);
+         this.minecraft.createWorld(this.saveDirName, worldsettings, this.worldOptionsScreen.getDynamicRegistries(), dimensiongeneratorsettings);
       }
@@ -275,4 +281,4 @@
          this.btnAllowCommands.active = false;
-         this.field_238934_c_.field_239027_a_.active = false;
-         this.field_238937_w_ = Difficulty.HARD;
+         this.worldOptionsScreen.field_239027_a_.active = false;
+         this.difficulty = Difficulty.HARD;
          this.field_238929_E_.active = false;
@@ -281,4 +287,4 @@
          this.btnAllowCommands.active = true;
-         this.field_238934_c_.field_239027_a_.active = true;
-         this.field_238937_w_ = this.field_238936_v_;
+         this.worldOptionsScreen.field_239027_a_.active = true;
+         this.difficulty = this.field_238936_v_;
          this.field_238929_E_.active = true;
@@ -286,3 +292,3 @@
 
-      this.field_228197_f_ = p_228200_1_;
+      this.gameMode = p_228200_1_;
       this.func_228199_a_();
@@ -298,3 +304,3 @@
       this.field_238929_E_.visible = !this.inMoreWorldOptionsDisplay;
-      if (this.field_238934_c_.func_239042_a_()) {
+      if (this.worldOptionsScreen.func_239042_a_()) {
          this.field_238931_H_.visible = false;
@@ -302,3 +308,3 @@
          if (this.field_228198_g_ == null) {
-            this.field_228198_g_ = this.field_228197_f_;
+            this.field_228198_g_ = this.gameMode;
          }
@@ -317,3 +323,3 @@
 
-      this.field_238934_c_.func_239059_b_(this.inMoreWorldOptionsDisplay);
+      this.worldOptionsScreen.func_239059_b_(this.inMoreWorldOptionsDisplay);
       this.worldNameField.setVisible(!this.inMoreWorldOptionsDisplay);
@@ -364,2 +370,3 @@
       drawCenteredString(matrixStack, this.font, this.title, this.width / 2, 20, -1);
+
       if (this.inMoreWorldOptionsDisplay) {
@@ -367,12 +374,12 @@
          drawString(matrixStack, this.font, field_243419_s, this.width / 2 - 100, 85, -6250336);
-         this.field_238934_c_.render(matrixStack, mouseX, mouseY, partialTicks);
+         this.worldOptionsScreen.render(matrixStack, mouseX, mouseY, partialTicks);
       } else {
-         drawString(matrixStack, this.font, field_243420_t, this.width / 2 - 100, 47, -6250336);
-         drawString(matrixStack, this.font, (new StringTextComponent("")).append(field_243421_u).appendString(" ").appendString(this.saveDirName), this.width / 2 - 100, 85, -6250336);
+         // drawString(matrixStack, this.font, field_243420_t, this.width / 2 - 100, 47, -6250336);
+         // drawString(matrixStack, this.font, (new StringTextComponent("")).append(field_243421_u).appendString(" ").appendString(this.saveDirName), this.width / 2 - 100, 85, -6250336);
          this.worldNameField.render(matrixStack, mouseX, mouseY, partialTicks);
-         drawString(matrixStack, this.font, this.gameModeDesc1, this.width / 2 - 150, 122, -6250336);
-         drawString(matrixStack, this.font, this.gameModeDesc2, this.width / 2 - 150, 134, -6250336);
-         if (this.btnAllowCommands.visible) {
-            drawString(matrixStack, this.font, field_243422_v, this.width / 2 - 150, 172, -6250336);
-         }
+         // drawString(matrixStack, this.font, this.gameModeDesc1, this.width / 2 - 150, 122, -6250336);
+         // drawString(matrixStack, this.font, this.gameModeDesc2, this.width / 2 - 150, 134, -6250336);
+         // if (this.btnAllowCommands.visible) {
+            // drawString(matrixStack, this.font, field_243422_v, this.width / 2 - 150, 172, -6250336);
+         // }
       }
@@ -419,4 +426,4 @@
       DatapackCodec datapackcodec = new DatapackCodec(list, list1);
-      if (list.equals(this.field_238933_b_.getEnabled())) {
-         this.field_238933_b_ = datapackcodec;
+      if (list.equals(this.datapackCodec.getEnabled())) {
+         this.datapackCodec = datapackcodec;
       } else {
@@ -433,3 +440,3 @@
                      } else {
-                        this.field_238933_b_ = DatapackCodec.VANILLA_CODEC;
+                        this.datapackCodec = DatapackCodec.VANILLA_CODEC;
                         this.minecraft.displayGuiScreen(this);
@@ -441,4 +448,4 @@
                this.minecraft.enqueue(() -> {
-                  this.field_238933_b_ = datapackcodec;
-                  this.field_238934_c_.func_243447_a(p_241623_2_);
+                  this.datapackCodec = datapackcodec;
+                  this.worldOptionsScreen.func_243447_a(p_241623_2_);
                   p_241623_2_.close();
@@ -547,3 +554,3 @@
 
-         this.field_243416_G.setEnabledPacks(this.field_238933_b_.getEnabled());
+         this.field_243416_G.setEnabledPacks(this.datapackCodec.getEnabled());
          return Pair.of(file1, this.field_243416_G);
@@ -569,3 +576,3 @@
       private final String field_228217_e_;
-      private final GameType field_228218_f_;
+      private final GameType gameType;
 
@@ -573,3 +580,3 @@
          this.field_228217_e_ = p_i225940_3_;
-         this.field_228218_f_ = p_i225940_4_;
+         this.gameType = p_i225940_4_;
       }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/DeathScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/DeathScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/DeathScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/DeathScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -95,2 +95,8 @@
       super.render(matrixStack, mouseX, mouseY, partialTicks);
+
+      // We have to respawn the player because otherwise code hangs
+      // in obscure ways
+      this.minecraft.player.respawnPlayer();
+      this.minecraft.displayGuiScreen((Screen)null);
+      this.minecraft.setHasPlayerRespawned(true);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/EditWorldScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/EditWorldScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/EditWorldScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/EditWorldScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -96,3 +96,3 @@
       this.addButton(new Button(this.width / 2 - 100, this.height / 4 + 120 + 5, 200, 20, new TranslationTextComponent("selectWorld.edit.export_worldgen_settings"), (p_239023_1_) -> {
-         DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
+         DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/EnchantmentScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/EnchantmentScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/EnchantmentScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/EnchantmentScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -37,3 +37,3 @@
    private static final BookModel MODEL_BOOK = new BookModel();
-   private final Random random = new Random();
+   private final Random random = new Random(0);
    public int ticks;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/FlatPresetsScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/FlatPresetsScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/FlatPresetsScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/FlatPresetsScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -171,3 +171,3 @@
       this.export.setMaxStringLength(1230);
-      Registry<Biome> registry = this.parentScreen.createWorldGui.field_238934_c_.func_239055_b_().getRegistry(Registry.BIOME_KEY);
+      Registry<Biome> registry = this.parentScreen.createWorldGui.worldOptionsScreen.getDynamicRegistries().getRegistry(Registry.BIOME_KEY);
       this.export.setText(func_243303_b(registry, this.parentScreen.func_238603_g_()));
@@ -342,3 +342,3 @@
             FlatPresetsScreen.LayerItem flatpresetsscreen$layeritem = FlatPresetsScreen.FLAT_WORLD_PRESETS.get(SlotList.this.getEventListeners().indexOf(this));
-            Registry<Biome> registry = FlatPresetsScreen.this.parentScreen.createWorldGui.field_238934_c_.func_239055_b_().getRegistry(Registry.BIOME_KEY);
+            Registry<Biome> registry = FlatPresetsScreen.this.parentScreen.createWorldGui.worldOptionsScreen.getDynamicRegistries().getRegistry(Registry.BIOME_KEY);
             FlatPresetsScreen.this.field_241594_u_ = flatpresetsscreen$layeritem.field_238643_c_.apply(registry);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/IngameMenuScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/IngameMenuScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/IngameMenuScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/IngameMenuScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -3,2 +3,3 @@
 import com.mojang.blaze3d.matrix.MatrixStack;
+import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.advancements.AdvancementsScreen;
@@ -25,3 +26,2 @@
       }
-
    }
@@ -107,2 +107,3 @@
       super.render(matrixStack, mouseX, mouseY, partialTicks);
+
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/inventory/ContainerScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/inventory/ContainerScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/inventory/ContainerScreen.java	2024-03-10 15:44:47.630123368 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/inventory/ContainerScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -319,3 +319,3 @@
                      } else {
-                        boolean flag2 = l != -999 && (InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 340) || InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 344));
+                        boolean flag2 = l != -999 && hasShiftDown();
                         ClickType clicktype = ClickType.PICKUP;
@@ -493,3 +493,3 @@
             } else {
-               boolean flag1 = k != -999 && (InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 340) || InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 344));
+               boolean flag1 = k != -999 && hasShiftDown();
                if (flag1) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/MainMenuScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/MainMenuScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/MainMenuScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/MainMenuScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -6,3 +6,7 @@
 import com.mojang.blaze3d.systems.RenderSystem;
+
+import java.io.File;
 import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.Random;
@@ -11,2 +15,8 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.env.EnvServer;
+import com.minerl.multiagent.recorder.AzureUpload;
+import net.minecraft.client.GameSettings;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.ReplaySender;
 import net.minecraft.client.gui.AccessibilityScreen;
@@ -21,2 +31,3 @@
 import net.minecraft.client.resources.I18n;
+import net.minecraft.profiler.IResultableProfiler;
 import net.minecraft.realms.RealmsBridgeScreen;
@@ -37,2 +48,3 @@
 
+
 @OnlyIn(Dist.CLIENT)
@@ -64,3 +76,3 @@
       this.showFadeInAnimation = fadeIn;
-      this.showTitleWronglySpelled = (double)(new Random()).nextFloat() < 1.0E-4D;
+      this.showTitleWronglySpelled = (double)(new Random(0)).nextFloat() < 1.0E-4D;
    }
@@ -112,2 +124,7 @@
       this.addButton(new Button(this.width / 2 + 2, j + 72 + 12, 98, 20, new TranslationTextComponent("menu.quit"), (p_213094_1_) -> {
+         AzureUpload.finish();
+         if (this.minecraft.getProfiler() instanceof IResultableProfiler) {
+            File profileDump = new File("profile-results-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + ".txt");
+            ((IResultableProfiler)this.minecraft.getProfiler()).getResults().writeToFile(profileDump.getAbsoluteFile());
+         }
          this.minecraft.shutdown();
@@ -127,3 +144,2 @@
       }
-
    }
@@ -156,4 +172,4 @@
          } else {
-            DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
-            this.minecraft.createWorld("Demo_World", MinecraftServer.DEMO_WORLD_SETTINGS, dynamicregistries$impl, DimensionGeneratorSettings.func_242752_a(dynamicregistries$impl));
+            DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
+            this.minecraft.createWorld("Demo_World", MinecraftServer.DEMO_WORLD_SETTINGS, dynamicregistries$impl, DimensionGeneratorSettings.fromDynamicRegistries(dynamicregistries$impl));
          }
@@ -196,2 +212,3 @@
          this.firstRenderTime = Util.milliTime();
+         onLoad();
       }
@@ -289,3 +306,2 @@
       }
-
    }
@@ -304,2 +320,19 @@
    }
+
+   private void onLoad() {
+      GameSettings settings = Minecraft.getInstance().gameSettings;
+      int envPort = settings.envPort;
+      settings.syncIntegratedServer = true;
+      if (envPort != 0) {
+         new Thread(() -> new EnvServer(Minecraft.getInstance().gameSettings.envPort, "0.37.0").serve()).start();
+         return;
+      }
+      ReplaySender replaySender = ReplaySender.getInstance();
+      if (settings.loadWorldFile != null) {
+         replaySender.loadWorldFromZip(settings.loadWorldFile);
+      }
+      if (settings.replayFile != null ) {
+         replaySender.loadWorldAndReplay(settings.replayFile);
+      }
+  }
 }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/OptimizeWorldScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/OptimizeWorldScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/OptimizeWorldScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/OptimizeWorldScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -41,3 +41,3 @@
    public static OptimizeWorldScreen func_239025_a_(Minecraft p_239025_0_, BooleanConsumer p_239025_1_, DataFixer p_239025_2_, SaveFormat.LevelSave p_239025_3_, boolean p_239025_4_) {
-      DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
+      DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/OptionsScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/OptionsScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/OptionsScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/OptionsScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -39,3 +39,5 @@
       int i = 0;
-
+      if (true) {
+         return;
+      }
       for(AbstractOption abstractoption : SCREEN_OPTIONS) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/Screen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/Screen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/Screen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/Screen.java	2024-03-10 18:29:57.577071694 +0800
@@ -15,2 +15,4 @@
 import javax.annotation.Nullable;
+
+import net.minecraft.client.KeyboardListener;
 import net.minecraft.client.Minecraft;
@@ -376,3 +378,4 @@
    public static boolean hasShiftDown() {
-      return InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 340) || InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 344);
+      KeyboardListener kl = Minecraft.getInstance().keyboardListener;
+      return kl.isKeyPressed(340) || kl.isKeyPressed(344);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/WorldOptionsScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/WorldOptionsScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/WorldOptionsScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/WorldOptionsScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -10,2 +10,3 @@
 import com.mojang.serialization.Lifecycle;
+import com.minerl.multiagent.RandomHelper;
 import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
@@ -18,2 +19,3 @@
 import java.util.OptionalLong;
+import java.util.Random;
 import java.util.concurrent.CompletableFuture;
@@ -67,4 +69,4 @@
    private Button field_239037_k_;
-   private DynamicRegistries.Impl field_239038_l_;
-   private DimensionGeneratorSettings field_239039_m_;
+   private DynamicRegistries.Impl dynamicRegistries;
+   private DimensionGeneratorSettings dimensionGeneratorSettings;
    private Optional<BiomeGeneratorTypeScreens> field_239040_n_;
@@ -72,5 +74,5 @@
 
-   public WorldOptionsScreen(DynamicRegistries.Impl p_i242065_1_, DimensionGeneratorSettings p_i242065_2_, Optional<BiomeGeneratorTypeScreens> p_i242065_3_, OptionalLong p_i242065_4_) {
-      this.field_239038_l_ = p_i242065_1_;
-      this.field_239039_m_ = p_i242065_2_;
+   public WorldOptionsScreen(DynamicRegistries.Impl dynamicRegistries, DimensionGeneratorSettings p_i242065_2_, Optional<BiomeGeneratorTypeScreens> p_i242065_3_, OptionalLong p_i242065_4_) {
+      this.dynamicRegistries = dynamicRegistries;
+      this.dimensionGeneratorSettings = p_i242065_2_;
       this.field_239040_n_ = p_i242065_3_;
@@ -85,3 +87,3 @@
       this.field_239033_g_.setResponder((p_239058_1_) -> {
-         this.field_243444_q = this.func_243449_f();
+         this.field_243444_q = this.getWorldSeed();
       });
@@ -91,3 +93,3 @@
       this.field_239034_h_ = p_239048_1_.addButton(new Button(i, 100, 150, 20, new TranslationTextComponent("selectWorld.mapFeatures"), (p_239056_1_) -> {
-         this.field_239039_m_ = this.field_239039_m_.func_236231_l_();
+         this.dimensionGeneratorSettings = this.dimensionGeneratorSettings.func_236231_l_();
          p_239056_1_.queueNarration(250);
@@ -95,3 +97,3 @@
          public ITextComponent getMessage() {
-            return DialogTexts.getComposedOptionMessage(super.getMessage(), WorldOptionsScreen.this.field_239039_m_.doesGenerateFeatures());
+            return DialogTexts.getComposedOptionMessage(super.getMessage(), WorldOptionsScreen.this.dimensionGeneratorSettings.doesGenerateFeatures());
          }
@@ -113,4 +115,4 @@
                this.field_239040_n_ = Optional.of(biomegeneratortypescreens);
-               this.field_239039_m_ = biomegeneratortypescreens.func_241220_a_(this.field_239038_l_, this.field_239039_m_.getSeed(), this.field_239039_m_.doesGenerateFeatures(), this.field_239039_m_.hasBonusChest());
-               if (this.field_239039_m_.func_236227_h_() && !Screen.hasShiftDown()) {
+               this.dimensionGeneratorSettings = biomegeneratortypescreens.func_241220_a_(this.dynamicRegistries, this.dimensionGeneratorSettings.getSeed(), this.dimensionGeneratorSettings.doesGenerateFeatures(), this.dimensionGeneratorSettings.hasBonusChest());
+               if (this.dimensionGeneratorSettings.isDebug() && !Screen.hasShiftDown()) {
                   continue;
@@ -137,3 +139,3 @@
          if (biomegeneratortypescreens$ifactory != null) {
-            p_239048_2_.displayGuiScreen(biomegeneratortypescreens$ifactory.createEditScreen(p_239048_1_, this.field_239039_m_));
+            p_239048_2_.displayGuiScreen(biomegeneratortypescreens$ifactory.createEditScreen(p_239048_1_, this.dimensionGeneratorSettings));
          }
@@ -143,3 +145,3 @@
       this.field_239027_a_ = p_239048_1_.addButton(new Button(i, 151, 150, 20, new TranslationTextComponent("selectWorld.bonusItems"), (p_239047_1_) -> {
-         this.field_239039_m_ = this.field_239039_m_.func_236232_m_();
+         this.dimensionGeneratorSettings = this.dimensionGeneratorSettings.func_236232_m_();
          p_239047_1_.queueNarration(250);
@@ -147,3 +149,3 @@
          public ITextComponent getMessage() {
-            return DialogTexts.getComposedOptionMessage(super.getMessage(), WorldOptionsScreen.this.field_239039_m_.hasBonusChest() && !p_239048_1_.hardCoreMode);
+            return DialogTexts.getComposedOptionMessage(super.getMessage(), WorldOptionsScreen.this.dimensionGeneratorSettings.hasBonusChest() && !p_239048_1_.hardCoreMode);
          }
@@ -155,3 +157,3 @@
          if (s != null) {
-            DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
+            DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
             ResourcePackList resourcepacklist = new ResourcePackList(new ServerPackFinder(), new FolderPackFinder(p_239048_1_.func_238957_j_().toFile(), IPackNameDecorator.WORLD));
@@ -160,3 +162,3 @@
             try {
-               MinecraftServer.func_240772_a_(resourcepacklist, p_239048_1_.field_238933_b_, false);
+               MinecraftServer.func_240772_a_(resourcepacklist, p_239048_1_.datapackCodec, false);
                CompletableFuture<DataPackRegistries> completablefuture = DataPackRegistries.func_240961_a_(resourcepacklist.func_232623_f_(), Commands.EnvironmentType.INTEGRATED, 2, Util.getServerExecutor(), p_239048_2_);
@@ -218,4 +220,4 @@
    private void func_239052_a_(DynamicRegistries.Impl p_239052_1_, DimensionGeneratorSettings p_239052_2_) {
-      this.field_239038_l_ = p_239052_1_;
-      this.field_239039_m_ = p_239052_2_;
+      this.dynamicRegistries = p_239052_1_;
+      this.dimensionGeneratorSettings = p_239052_2_;
       this.field_239040_n_ = BiomeGeneratorTypeScreens.func_239079_a_(p_239052_2_);
@@ -243,3 +245,3 @@
    protected void func_239043_a_(DimensionGeneratorSettings p_239043_1_) {
-      this.field_239039_m_ = p_239043_1_;
+      this.dimensionGeneratorSettings = p_239043_1_;
    }
@@ -258,8 +260,11 @@
 
-   public DimensionGeneratorSettings func_239054_a_(boolean p_239054_1_) {
-      OptionalLong optionallong = this.func_243449_f();
-      return this.field_239039_m_.create(p_239054_1_, optionallong);
+   public DimensionGeneratorSettings getDimensionGeneratorSettings(boolean isHardcore) {
+      OptionalLong optionalSeed = this.getWorldSeed();
+      if (!optionalSeed.isPresent()) {
+         optionalSeed = OptionalLong.of(new Random().nextLong());
+      }
+      return this.dimensionGeneratorSettings.create(isHardcore, optionalSeed);
    }
 
-   private OptionalLong func_243449_f() {
+   private OptionalLong getWorldSeed() {
       String s = this.field_239033_g_.getText();
@@ -281,3 +286,3 @@
    public boolean func_239042_a_() {
-      return this.field_239039_m_.func_236227_h_();
+      return this.dimensionGeneratorSettings.isDebug();
    }
@@ -286,3 +291,3 @@
       this.field_239035_i_.visible = p_239059_1_;
-      if (this.field_239039_m_.func_236227_h_()) {
+      if (this.dimensionGeneratorSettings.isDebug()) {
          this.field_239034_h_.visible = false;
@@ -301,4 +306,4 @@
 
-   public DynamicRegistries.Impl func_239055_b_() {
-      return this.field_239038_l_;
+   public DynamicRegistries.Impl getDynamicRegistries() {
+      return this.dynamicRegistries;
    }
@@ -306,6 +311,6 @@
    void func_243447_a(DataPackRegistries p_243447_1_) {
-      DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
-      WorldGenSettingsExport<JsonElement> worldgensettingsexport = WorldGenSettingsExport.create(JsonOps.INSTANCE, this.field_239038_l_);
+      DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
+      WorldGenSettingsExport<JsonElement> worldgensettingsexport = WorldGenSettingsExport.create(JsonOps.INSTANCE, this.dynamicRegistries);
       WorldSettingsImport<JsonElement> worldsettingsimport = WorldSettingsImport.create(JsonOps.INSTANCE, p_243447_1_.getResourceManager(), dynamicregistries$impl);
-      DataResult<DimensionGeneratorSettings> dataresult = DimensionGeneratorSettings.field_236201_a_.encodeStart(worldgensettingsexport, this.field_239039_m_).flatMap((p_243446_1_) -> {
+      DataResult<DimensionGeneratorSettings> dataresult = DimensionGeneratorSettings.field_236201_a_.encodeStart(worldgensettingsexport, this.dimensionGeneratorSettings).flatMap((p_243446_1_) -> {
          return DimensionGeneratorSettings.field_236201_a_.parse(worldsettingsimport, p_243446_1_);
@@ -313,4 +318,4 @@
       dataresult.resultOrPartial(Util.func_240982_a_("Error parsing worldgen settings after loading data packs: ", field_239028_b_::error)).ifPresent((p_243448_2_) -> {
-         this.field_239039_m_ = p_243448_2_;
-         this.field_239038_l_ = dynamicregistries$impl;
+         this.dimensionGeneratorSettings = p_243448_2_;
+         this.dynamicRegistries = dynamicregistries$impl;
       });
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/WorldSelectionList.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/WorldSelectionList.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/WorldSelectionList.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/WorldSelectionList.java	2024-03-10 18:29:57.577071694 +0800
@@ -123,3 +123,3 @@
       if (entry != null) {
-         WorldSummary worldsummary = entry.field_214451_d;
+         WorldSummary worldsummary = entry.worldSelector;
          NarratorChatListener.INSTANCE.say((new TranslationTextComponent("narrator.select", new TranslationTextComponent("narrator.select.world", worldsummary.getDisplayName(), new Date(worldsummary.getLastTimePlayed()), worldsummary.isHardcoreModeEnabled() ? new TranslationTextComponent("gameMode.hardcore") : new TranslationTextComponent("gameMode." + worldsummary.getEnumGameType().getName()), worldsummary.getCheatsEnabled() ? new TranslationTextComponent("selectWorld.cheats") : StringTextComponent.EMPTY, worldsummary.getVersionName()))).getString());
@@ -127,3 +127,3 @@
 
-      this.worldSelection.func_214324_a(entry != null && !entry.field_214451_d.isLocked());
+      this.worldSelection.setActive(entry != null && !entry.worldSelector.isLocked());
    }
@@ -132,3 +132,3 @@
       this.func_241572_a_(p_241219_1_, (p_241652_0_) -> {
-         return !p_241652_0_.field_214451_d.isLocked();
+         return !p_241652_0_.worldSelector.isLocked();
       });
@@ -146,5 +146,5 @@
    public final class Entry extends ExtendedList.AbstractListEntry<WorldSelectionList.Entry> implements AutoCloseable {
-      private final Minecraft field_214449_b;
+      private final Minecraft minecraft;
       private final WorldSelectionScreen field_214450_c;
-      private final WorldSummary field_214451_d;
+      private final WorldSummary worldSelector;
       private final ResourceLocation field_214452_e;
@@ -157,4 +157,4 @@
          this.field_214450_c = p_i242066_2_.getGuiWorldSelection();
-         this.field_214451_d = p_i242066_3_;
-         this.field_214449_b = Minecraft.getInstance();
+         this.worldSelector = p_i242066_3_;
+         this.minecraft = Minecraft.getInstance();
          String s = p_i242066_3_.getFileName();
@@ -170,4 +170,4 @@
       public void render(MatrixStack p_230432_1_, int p_230432_2_, int p_230432_3_, int p_230432_4_, int p_230432_5_, int p_230432_6_, int p_230432_7_, int p_230432_8_, boolean p_230432_9_, float p_230432_10_) {
-         String s = this.field_214451_d.getDisplayName();
-         String s1 = this.field_214451_d.getFileName() + " (" + WorldSelectionList.field_214377_b.format(new Date(this.field_214451_d.getLastTimePlayed())) + ")";
+         String s = this.worldSelector.getDisplayName();
+         String s1 = this.worldSelector.getFileName() + " (" + WorldSelectionList.field_214377_b.format(new Date(this.worldSelector.getLastTimePlayed())) + ")";
          if (StringUtils.isEmpty(s)) {
@@ -176,8 +176,8 @@
 
-         ITextComponent itextcomponent = this.field_214451_d.getDescription();
-         this.field_214449_b.fontRenderer.drawString(p_230432_1_, s, (float)(p_230432_4_ + 32 + 3), (float)(p_230432_3_ + 1), 16777215);
-         this.field_214449_b.fontRenderer.drawString(p_230432_1_, s1, (float)(p_230432_4_ + 32 + 3), (float)(p_230432_3_ + 9 + 3), 8421504);
-         this.field_214449_b.fontRenderer.func_243248_b(p_230432_1_, itextcomponent, (float)(p_230432_4_ + 32 + 3), (float)(p_230432_3_ + 9 + 9 + 3), 8421504);
+         ITextComponent itextcomponent = this.worldSelector.getDescription();
+         this.minecraft.fontRenderer.drawString(p_230432_1_, s, (float)(p_230432_4_ + 32 + 3), (float)(p_230432_3_ + 1), 16777215);
+         this.minecraft.fontRenderer.drawString(p_230432_1_, s1, (float)(p_230432_4_ + 32 + 3), (float)(p_230432_3_ + 9 + 3), 8421504);
+         this.minecraft.fontRenderer.func_243248_b(p_230432_1_, itextcomponent, (float)(p_230432_4_ + 32 + 3), (float)(p_230432_3_ + 9 + 9 + 3), 8421504);
          RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
-         this.field_214449_b.getTextureManager().bindTexture(this.field_214454_g != null ? this.field_214452_e : WorldSelectionList.field_214378_c);
+         this.minecraft.getTextureManager().bindTexture(this.field_214454_g != null ? this.field_214452_e : WorldSelectionList.field_214378_c);
          RenderSystem.enableBlend();
@@ -185,4 +185,4 @@
          RenderSystem.disableBlend();
-         if (this.field_214449_b.gameSettings.touchscreen || p_230432_9_) {
-            this.field_214449_b.getTextureManager().bindTexture(WorldSelectionList.field_214379_d);
+         if (this.minecraft.gameSettings.touchscreen || p_230432_9_) {
+            this.minecraft.getTextureManager().bindTexture(WorldSelectionList.field_214379_d);
             AbstractGui.fill(p_230432_1_, p_230432_4_, p_230432_3_, p_230432_4_ + 32, p_230432_3_ + 32, -1601138544);
@@ -192,10 +192,10 @@
             int j = flag ? 32 : 0;
-            if (this.field_214451_d.isLocked()) {
+            if (this.worldSelector.isLocked()) {
                AbstractGui.blit(p_230432_1_, p_230432_4_, p_230432_3_, 96.0F, (float)j, 32, 32, 256, 256);
                if (flag) {
-                  this.field_214450_c.func_239026_b_(this.field_214449_b.fontRenderer.trimStringToWidth(WorldSelectionList.field_243466_v, 175));
+                  this.field_214450_c.func_239026_b_(this.minecraft.fontRenderer.trimStringToWidth(WorldSelectionList.field_243466_v, 175));
                }
-            } else if (this.field_214451_d.markVersionInList()) {
+            } else if (this.worldSelector.markVersionInList()) {
                AbstractGui.blit(p_230432_1_, p_230432_4_, p_230432_3_, 32.0F, (float)j, 32, 32, 256, 256);
-               if (this.field_214451_d.askToOpenWorld()) {
+               if (this.worldSelector.askToOpenWorld()) {
                   AbstractGui.blit(p_230432_1_, p_230432_4_, p_230432_3_, 96.0F, (float)j, 32, 32, 256, 256);
@@ -218,3 +218,3 @@
       public boolean mouseClicked(double mouseX, double mouseY, int button) {
-         if (this.field_214451_d.isLocked()) {
+         if (this.worldSelector.isLocked()) {
             return true;
@@ -222,8 +222,8 @@
             WorldSelectionList.this.setSelected(this);
-            this.field_214450_c.func_214324_a(WorldSelectionList.this.func_214376_a().isPresent());
+            this.field_214450_c.setActive(WorldSelectionList.this.func_214376_a().isPresent());
             if (mouseX - (double)WorldSelectionList.this.getRowLeft() <= 32.0D) {
-               this.func_214438_a();
+               this.loadWorld();
                return true;
             } else if (Util.milliTime() - this.field_214455_h < 250L) {
-               this.func_214438_a();
+               this.loadWorld();
                return true;
@@ -236,15 +236,15 @@
 
-      public void func_214438_a() {
-         if (!this.field_214451_d.isLocked()) {
-            if (this.field_214451_d.askToCreateBackup()) {
+      public void loadWorld() {
+         if (!this.worldSelector.isLocked()) {
+            if (this.worldSelector.askToCreateBackup()) {
                ITextComponent itextcomponent = new TranslationTextComponent("selectWorld.backupQuestion");
-               ITextComponent itextcomponent1 = new TranslationTextComponent("selectWorld.backupWarning", this.field_214451_d.getVersionName(), SharedConstants.getVersion().getName());
-               this.field_214449_b.displayGuiScreen(new ConfirmBackupScreen(this.field_214450_c, (p_214436_1_, p_214436_2_) -> {
+               ITextComponent itextcomponent1 = new TranslationTextComponent("selectWorld.backupWarning", this.worldSelector.getVersionName(), SharedConstants.getVersion().getName());
+               this.minecraft.displayGuiScreen(new ConfirmBackupScreen(this.field_214450_c, (p_214436_1_, p_214436_2_) -> {
                   if (p_214436_1_) {
-                     String s = this.field_214451_d.getFileName();
+                     String s = this.worldSelector.getFileName();
 
-                     try (SaveFormat.LevelSave saveformat$levelsave = this.field_214449_b.getSaveLoader().getLevelSave(s)) {
+                     try (SaveFormat.LevelSave saveformat$levelsave = this.minecraft.getSaveLoader().getLevelSave(s)) {
                         EditWorldScreen.func_239019_a_(saveformat$levelsave);
                      } catch (IOException ioexception) {
-                        SystemToast.func_238535_a_(this.field_214449_b, s);
+                        SystemToast.func_238535_a_(this.minecraft, s);
                         WorldSelectionList.LOGGER.error("Failed to backup level {}", s, ioexception);
@@ -253,13 +253,13 @@
 
-                  this.func_214443_e();
+                  this.loadWorldImpl();
                }, itextcomponent, itextcomponent1, false));
-            } else if (this.field_214451_d.askToOpenWorld()) {
-               this.field_214449_b.displayGuiScreen(new ConfirmScreen((p_214434_1_) -> {
+            } else if (this.worldSelector.askToOpenWorld()) {
+               this.minecraft.displayGuiScreen(new ConfirmScreen((p_214434_1_) -> {
                   if (p_214434_1_) {
                      try {
-                        this.func_214443_e();
+                        this.loadWorldImpl();
                      } catch (Exception exception) {
                         WorldSelectionList.LOGGER.error("Failure to open 'future world'", (Throwable)exception);
-                        this.field_214449_b.displayGuiScreen(new AlertScreen(() -> {
-                           this.field_214449_b.displayGuiScreen(this.field_214450_c);
+                        this.minecraft.displayGuiScreen(new AlertScreen(() -> {
+                           this.minecraft.displayGuiScreen(this.field_214450_c);
                         }, new TranslationTextComponent("selectWorld.futureworld.error.title"), new TranslationTextComponent("selectWorld.futureworld.error.text")));
@@ -267,8 +267,8 @@
                   } else {
-                     this.field_214449_b.displayGuiScreen(this.field_214450_c);
+                     this.minecraft.displayGuiScreen(this.field_214450_c);
                   }
 
-               }, new TranslationTextComponent("selectWorld.versionQuestion"), new TranslationTextComponent("selectWorld.versionWarning", this.field_214451_d.getVersionName(), new TranslationTextComponent("selectWorld.versionJoinButton"), DialogTexts.GUI_CANCEL)));
+               }, new TranslationTextComponent("selectWorld.versionQuestion"), new TranslationTextComponent("selectWorld.versionWarning", this.worldSelector.getVersionName(), new TranslationTextComponent("selectWorld.versionJoinButton"), DialogTexts.GUI_CANCEL)));
             } else {
-               this.func_214443_e();
+               this.loadWorldImpl();
             }
@@ -279,7 +279,7 @@
       public void func_214442_b() {
-         this.field_214449_b.displayGuiScreen(new ConfirmScreen((p_214440_1_) -> {
+         this.minecraft.displayGuiScreen(new ConfirmScreen((p_214440_1_) -> {
             if (p_214440_1_) {
-               this.field_214449_b.displayGuiScreen(new WorkingScreen());
-               SaveFormat saveformat = this.field_214449_b.getSaveLoader();
-               String s = this.field_214451_d.getFileName();
+               this.minecraft.displayGuiScreen(new WorkingScreen());
+               SaveFormat saveformat = this.minecraft.getSaveLoader();
+               String s = this.worldSelector.getFileName();
 
@@ -288,3 +288,3 @@
                } catch (IOException ioexception) {
-                  SystemToast.func_238538_b_(this.field_214449_b, s);
+                  SystemToast.func_238538_b_(this.minecraft, s);
                   WorldSelectionList.LOGGER.error("Failed to delete world {}", s, ioexception);
@@ -297,4 +297,4 @@
 
-            this.field_214449_b.displayGuiScreen(this.field_214450_c);
-         }, new TranslationTextComponent("selectWorld.deleteQuestion"), new TranslationTextComponent("selectWorld.deleteWarning", this.field_214451_d.getDisplayName()), new TranslationTextComponent("selectWorld.deleteButton"), DialogTexts.GUI_CANCEL));
+            this.minecraft.displayGuiScreen(this.field_214450_c);
+         }, new TranslationTextComponent("selectWorld.deleteQuestion"), new TranslationTextComponent("selectWorld.deleteWarning", this.worldSelector.getDisplayName()), new TranslationTextComponent("selectWorld.deleteButton"), DialogTexts.GUI_CANCEL));
       }
@@ -302,7 +302,7 @@
       public void func_214444_c() {
-         String s = this.field_214451_d.getFileName();
+         String s = this.worldSelector.getFileName();
 
          try {
-            SaveFormat.LevelSave saveformat$levelsave = this.field_214449_b.getSaveLoader().getLevelSave(s);
-            this.field_214449_b.displayGuiScreen(new EditWorldScreen((p_239096_3_) -> {
+            SaveFormat.LevelSave saveformat$levelsave = this.minecraft.getSaveLoader().getLevelSave(s);
+            this.minecraft.displayGuiScreen(new EditWorldScreen((p_239096_3_) -> {
                try {
@@ -319,6 +319,6 @@
 
-               this.field_214449_b.displayGuiScreen(this.field_214450_c);
+               this.minecraft.displayGuiScreen(this.field_214450_c);
             }, saveformat$levelsave));
          } catch (IOException ioexception) {
-            SystemToast.func_238535_a_(this.field_214449_b, s);
+            SystemToast.func_238535_a_(this.minecraft, s);
             WorldSelectionList.LOGGER.error("Failed to access level {}", s, ioexception);
@@ -332,8 +332,8 @@
       public void func_214445_d() {
-         this.func_241653_f_();
-         DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
+         this.showLoadWorldScreen();
+         DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
 
          try (
-            SaveFormat.LevelSave saveformat$levelsave = this.field_214449_b.getSaveLoader().getLevelSave(this.field_214451_d.getFileName());
-            Minecraft.PackManager minecraft$packmanager = this.field_214449_b.reloadDatapacks(dynamicregistries$impl, Minecraft::loadDataPackCodec, Minecraft::loadWorld, false, saveformat$levelsave);
+                 SaveFormat.LevelSave saveformat$levelsave = this.minecraft.getSaveLoader().getLevelSave(this.worldSelector.getFileName());
+                 Minecraft.PackManager minecraft$packmanager = this.minecraft.reloadDatapacks(dynamicregistries$impl, Minecraft::loadDataPackCodec, Minecraft::loadWorld, false, saveformat$levelsave);
          ) {
@@ -341,10 +341,10 @@
             DatapackCodec datapackcodec = worldsettings.getDatapackCodec();
-            DimensionGeneratorSettings dimensiongeneratorsettings = minecraft$packmanager.getServerConfiguration().getDimensionGeneratorSettings();
-            Path path = CreateWorldScreen.func_238943_a_(saveformat$levelsave.resolveFilePath(FolderName.DATAPACKS), this.field_214449_b);
-            if (dimensiongeneratorsettings.func_236229_j_()) {
-               this.field_214449_b.displayGuiScreen(new ConfirmScreen((p_239095_6_) -> {
-                  this.field_214449_b.displayGuiScreen((Screen)(p_239095_6_ ? new CreateWorldScreen(this.field_214450_c, worldsettings, dimensiongeneratorsettings, path, datapackcodec, dynamicregistries$impl) : this.field_214450_c));
+            DimensionGeneratorSettings dimensionGeneratorSettings = minecraft$packmanager.getServerConfiguration().getDimensionGeneratorSettings();
+            Path path = CreateWorldScreen.func_238943_a_(saveformat$levelsave.resolveFilePath(FolderName.DATAPACKS), this.minecraft);
+            if (dimensionGeneratorSettings.func_236229_j_()) {
+               this.minecraft.displayGuiScreen(new ConfirmScreen((p_239095_6_) -> {
+                  this.minecraft.displayGuiScreen((Screen)(p_239095_6_ ? new CreateWorldScreen(this.field_214450_c, worldsettings, dimensionGeneratorSettings, path, datapackcodec, dynamicregistries$impl) : this.field_214450_c));
                }, new TranslationTextComponent("selectWorld.recreate.customized.title"), new TranslationTextComponent("selectWorld.recreate.customized.text"), DialogTexts.GUI_PROCEED, DialogTexts.GUI_CANCEL));
             } else {
-               this.field_214449_b.displayGuiScreen(new CreateWorldScreen(this.field_214450_c, worldsettings, dimensiongeneratorsettings, path, datapackcodec, dynamicregistries$impl));
+               this.minecraft.displayGuiScreen(new CreateWorldScreen(this.field_214450_c, worldsettings, dimensionGeneratorSettings, path, datapackcodec, dynamicregistries$impl));
             }
@@ -352,4 +352,4 @@
             WorldSelectionList.LOGGER.error("Unable to recreate world", (Throwable)exception);
-            this.field_214449_b.displayGuiScreen(new AlertScreen(() -> {
-               this.field_214449_b.displayGuiScreen(this.field_214450_c);
+            this.minecraft.displayGuiScreen(new AlertScreen(() -> {
+               this.minecraft.displayGuiScreen(this.field_214450_c);
             }, new TranslationTextComponent("selectWorld.recreate.error.title"), new TranslationTextComponent("selectWorld.recreate.error.text")));
@@ -359,7 +359,7 @@
 
-      private void func_214443_e() {
-         this.field_214449_b.getSoundHandler().play(SimpleSound.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
-         if (this.field_214449_b.getSaveLoader().canLoadWorld(this.field_214451_d.getFileName())) {
-            this.func_241653_f_();
-            this.field_214449_b.loadWorld(this.field_214451_d.getFileName());
+      private void loadWorldImpl() {
+         this.minecraft.getSoundHandler().play(SimpleSound.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
+         if (this.minecraft.getSaveLoader().canLoadWorld(this.worldSelector.getFileName())) {
+            this.showLoadWorldScreen();
+            this.minecraft.loadWorld(this.worldSelector.getFileName());
          }
@@ -368,4 +368,4 @@
 
-      private void func_241653_f_() {
-         this.field_214449_b.forcedScreenTick(new DirtMessageScreen(new TranslationTextComponent("selectWorld.data_read")));
+      private void showLoadWorldScreen() {
+         this.minecraft.forcedScreenTick(new DirtMessageScreen(new TranslationTextComponent("selectWorld.data_read")));
       }
@@ -381,6 +381,6 @@
                DynamicTexture dynamictexture = new DynamicTexture(nativeimage);
-               this.field_214449_b.getTextureManager().loadTexture(this.field_214452_e, dynamictexture);
+               this.minecraft.getTextureManager().loadTexture(this.field_214452_e, dynamictexture);
                return dynamictexture;
             } catch (Throwable throwable) {
-               WorldSelectionList.LOGGER.error("Invalid icon for world {}", this.field_214451_d.getFileName(), throwable);
+               WorldSelectionList.LOGGER.error("Invalid icon for world {}", this.worldSelector.getFileName(), throwable);
                this.field_214453_f = null;
@@ -389,3 +389,3 @@
          } else {
-            this.field_214449_b.getTextureManager().deleteTexture(this.field_214452_e);
+            this.minecraft.getTextureManager().deleteTexture(this.field_214452_e);
             return null;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/WorldSelectionScreen.java MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/WorldSelectionScreen.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/screen/WorldSelectionScreen.java	2024-03-10 15:44:47.626123358 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/screen/WorldSelectionScreen.java	2024-03-10 18:29:57.577071694 +0800
@@ -50,3 +50,3 @@
       this.selectButton = this.addButton(new Button(this.width / 2 - 154, this.height - 52, 150, 20, new TranslationTextComponent("selectWorld.select"), (p_214325_1_) -> {
-         this.selectionList.func_214376_a().ifPresent(WorldSelectionList.Entry::func_214438_a);
+         this.selectionList.func_214376_a().ifPresent(WorldSelectionList.Entry::loadWorld);
       }));
@@ -67,3 +67,3 @@
       }));
-      this.func_214324_a(false);
+      this.setActive(false);
       this.setFocusedDefault(this.searchField);
@@ -99,7 +99,7 @@
 
-   public void func_214324_a(boolean p_214324_1_) {
-      this.selectButton.active = p_214324_1_;
-      this.deleteButton.active = p_214324_1_;
-      this.renameButton.active = p_214324_1_;
-      this.copyButton.active = p_214324_1_;
+   public void setActive(boolean active) {
+      this.selectButton.active = active;
+      this.deleteButton.active = active;
+      this.renameButton.active = active;
+      this.copyButton.active = active;
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/spectator/categories/TeleportToTeam.java MCP-Reborn/src/main/java/net/minecraft/client/gui/spectator/categories/TeleportToTeam.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/gui/spectator/categories/TeleportToTeam.java	2024-03-10 15:44:47.630123368 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/gui/spectator/categories/TeleportToTeam.java	2024-03-10 18:29:57.577071694 +0800
@@ -90,3 +90,3 @@
          } else {
-            String s1 = this.players.get((new Random()).nextInt(this.players.size())).getGameProfile().getName();
+            String s1 = this.players.get((new Random(0)).nextInt(this.players.size())).getGameProfile().getName();
             this.location = AbstractClientPlayerEntity.getLocationSkin(s1);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/KeyboardListener.java MCP-Reborn/src/main/java/net/minecraft/client/KeyboardListener.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/KeyboardListener.java	2024-03-10 15:44:47.614123326 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/KeyboardListener.java	2024-03-10 18:29:57.557071330 +0800
@@ -2,4 +2,11 @@
 
-import java.util.Locale;
+import java.util.*;
+import java.util.stream.Collectors;
 import javax.annotation.Nullable;
+
+import com.google.common.base.Joiner;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
 import net.minecraft.block.BlockState;
@@ -48,2 +55,6 @@
    private boolean actionKeyF3;
+   private Set<Integer> keysPressed = new HashSet<>();
+   private Set<Integer> accumKeysPressed = new HashSet<>();
+   private Set<Integer> keysNewlyPressed = new HashSet<>();
+   private List<Integer> charsPressed = new ArrayList<>();
 
@@ -207,3 +218,2 @@
          }
-
       }
@@ -245,105 +255,122 @@
       if (windowPointer == this.mc.getMainWindow().getHandle()) {
-         if (this.debugCrashKeyPressTime > 0L) {
-            if (!InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 67) || !InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292)) {
-               this.debugCrashKeyPressTime = -1L;
-            }
-         } else if (InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 67) && InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292)) {
-            this.actionKeyF3 = true;
-            this.debugCrashKeyPressTime = Util.milliTime();
-            this.lastDebugCrashWarning = Util.milliTime();
-            this.debugCrashWarningsSent = 0L;
-         }
-
-         INestedGuiEventHandler inestedguieventhandler = this.mc.currentScreen;
-         if (action == 1 && (!(this.mc.currentScreen instanceof ControlsScreen) || ((ControlsScreen)inestedguieventhandler).time <= Util.milliTime() - 20L)) {
-            if (this.mc.gameSettings.keyBindFullscreen.matchesKey(key, scanCode)) {
-               this.mc.getMainWindow().toggleFullscreen();
-               this.mc.gameSettings.fullscreen = this.mc.getMainWindow().isFullscreen();
-               this.mc.gameSettings.saveOptions();
-               return;
-            }
+         if (action == 0) {
+            keysPressed.remove(key);
+         } else {
+            keysPressed.add(key);
+            accumKeysPressed.add(key);
+            keysNewlyPressed.add(key);
+         }
+         if (mc.mouseHelper.getHumanInput()) {
+            onKeyEventImpl(key, scanCode, action, modifiers);
+         }
+      }
+   }
 
-            if (this.mc.gameSettings.keyBindScreenshot.matchesKey(key, scanCode)) {
-               if (Screen.hasControlDown()) {
-               }
+   public void onKeyEventImpl(int key, int scanCode, int action, int modifiers) {
+      if (action == 0) {
+         keysPressed.remove(key);
+      } else {
+         keysPressed.add(key);
+      }
+      if (this.debugCrashKeyPressTime > 0L) {
+         if (!InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 67) || !InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292)) {
+            this.debugCrashKeyPressTime = -1L;
+         }
+      } else if (InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 67) && InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292)) {
+         this.actionKeyF3 = true;
+         this.debugCrashKeyPressTime = Util.milliTime();
+         this.lastDebugCrashWarning = Util.milliTime();
+         this.debugCrashWarningsSent = 0L;
+      }
 
-               ScreenShotHelper.saveScreenshot(this.mc.gameDir, this.mc.getMainWindow().getFramebufferWidth(), this.mc.getMainWindow().getFramebufferHeight(), this.mc.getFramebuffer(), (p_212449_1_) -> {
-                  this.mc.execute(() -> {
-                     this.mc.ingameGUI.getChatGUI().printChatMessage(p_212449_1_);
-                  });
-               });
-               return;
-            }
+      INestedGuiEventHandler inestedguieventhandler = this.mc.currentScreen;
+      if (action == 1 && (!(this.mc.currentScreen instanceof ControlsScreen) || ((ControlsScreen)inestedguieventhandler).time <= Util.milliTime() - 20L)) {
+         if (this.mc.gameSettings.keyBindFullscreen.matchesKey(key, scanCode)) {
+            this.mc.getMainWindow().toggleFullscreen();
+            this.mc.gameSettings.fullscreen = this.mc.getMainWindow().isFullscreen();
+            this.mc.gameSettings.saveOptions();
+            return;
          }
 
-         boolean flag = inestedguieventhandler == null || !(inestedguieventhandler.getListener() instanceof TextFieldWidget) || !((TextFieldWidget)inestedguieventhandler.getListener()).canWrite();
-         if (action != 0 && key == 66 && Screen.hasControlDown() && flag) {
-            AbstractOption.NARRATOR.setValueIndex(this.mc.gameSettings, 1);
-            if (inestedguieventhandler instanceof WithNarratorSettingsScreen) {
-               ((WithNarratorSettingsScreen)inestedguieventhandler).func_243317_i();
+         if (this.mc.gameSettings.keyBindScreenshot.matchesKey(key, scanCode)) {
+            if (Screen.hasControlDown()) {
             }
-         }
 
-         if (inestedguieventhandler != null) {
-            boolean[] aboolean = new boolean[]{false};
-            Screen.wrapScreenError(() -> {
-               if (action != 1 && (action != 2 || !this.repeatEventsEnabled)) {
-                  if (action == 0) {
-                     aboolean[0] = inestedguieventhandler.keyReleased(key, scanCode, modifiers);
-                  }
-               } else {
-                  aboolean[0] = inestedguieventhandler.keyPressed(key, scanCode, modifiers);
-               }
+            ScreenShotHelper.saveScreenshot(this.mc.gameDir, this.mc.getMainWindow().getFramebufferWidth(), this.mc.getMainWindow().getFramebufferHeight(), this.mc.getFramebuffer(), (p_212449_1_) -> {
+               this.mc.execute(() -> {
+                  this.mc.ingameGUI.getChatGUI().printChatMessage(p_212449_1_);
+               });
+            });
+            return;
+         }
+      }
 
-            }, "keyPressed event handler", inestedguieventhandler.getClass().getCanonicalName());
-            if (aboolean[0]) {
-               return;
-            }
+      boolean flag = inestedguieventhandler == null || !(inestedguieventhandler.getListener() instanceof TextFieldWidget) || !((TextFieldWidget)inestedguieventhandler.getListener()).canWrite();
+      if (action != 0 && key == 66 && Screen.hasControlDown() && flag) {
+         AbstractOption.NARRATOR.setValueIndex(this.mc.gameSettings, 1);
+         if (inestedguieventhandler instanceof WithNarratorSettingsScreen) {
+            ((WithNarratorSettingsScreen)inestedguieventhandler).func_243317_i();
          }
+      }
 
-         if (this.mc.currentScreen == null || this.mc.currentScreen.passEvents) {
-            InputMappings.Input inputmappings$input = InputMappings.getInputByCode(key, scanCode);
-            if (action == 0) {
-               KeyBinding.setKeyBindState(inputmappings$input, false);
-               if (key == 292) {
-                  if (this.actionKeyF3) {
-                     this.actionKeyF3 = false;
-                  } else {
-                     this.mc.gameSettings.showDebugInfo = !this.mc.gameSettings.showDebugInfo;
-                     this.mc.gameSettings.showDebugProfilerChart = this.mc.gameSettings.showDebugInfo && Screen.hasShiftDown();
-                     this.mc.gameSettings.showLagometer = this.mc.gameSettings.showDebugInfo && Screen.hasAltDown();
-                  }
+      if (inestedguieventhandler != null) {
+         boolean[] aboolean = new boolean[]{false};
+         Screen.wrapScreenError(() -> {
+            if (action != 1 && (action != 2 || !this.repeatEventsEnabled)) {
+               if (action == 0) {
+                  aboolean[0] = inestedguieventhandler.keyReleased(key, scanCode, modifiers);
                }
             } else {
-               if (key == 293 && this.mc.gameRenderer != null) {
-                  this.mc.gameRenderer.switchUseShader();
-               }
+               aboolean[0] = inestedguieventhandler.keyPressed(key, scanCode, modifiers);
+            }
 
-               boolean flag1 = false;
-               if (this.mc.currentScreen == null) {
-                  if (key == 256) {
-                     boolean flag2 = InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292);
-                     this.mc.displayInGameMenu(flag2);
-                  }
-
-                  flag1 = InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292) && this.processKeyF3(key);
-                  this.actionKeyF3 |= flag1;
-                  if (key == 290) {
-                     this.mc.gameSettings.hideGUI = !this.mc.gameSettings.hideGUI;
-                  }
-               }
+         }, "keyPressed event handler", inestedguieventhandler.getClass().getCanonicalName());
+         if (aboolean[0]) {
+            return;
+         }
+      }
 
-               if (flag1) {
-                  KeyBinding.setKeyBindState(inputmappings$input, false);
+      if (this.mc.currentScreen == null || this.mc.currentScreen.passEvents) {
+         InputMappings.Input inputmappings$input = InputMappings.getInputByCode(key, scanCode);
+         if (action == 0) {
+            KeyBinding.setKeyBindState(inputmappings$input, false);
+            if (key == 292) {
+               if (this.actionKeyF3) {
+                  this.actionKeyF3 = false;
                } else {
-                  KeyBinding.setKeyBindState(inputmappings$input, true);
-                  KeyBinding.onTick(inputmappings$input);
+                  this.mc.gameSettings.showDebugInfo = !this.mc.gameSettings.showDebugInfo;
+                  this.mc.gameSettings.showDebugProfilerChart = this.mc.gameSettings.showDebugInfo && Screen.hasShiftDown();
+                  this.mc.gameSettings.showLagometer = this.mc.gameSettings.showDebugInfo && Screen.hasAltDown();
+               }
+            }
+         } else {
+            if (key == 293 && this.mc.gameRenderer != null) {
+               this.mc.gameRenderer.switchUseShader();
+            }
+
+            boolean flag1 = false;
+            if (this.mc.currentScreen == null) {
+               if (key == 256) {
+                  boolean flag2 = InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292);
+                  this.mc.displayInGameMenu(flag2);
                }
 
-               if (this.mc.gameSettings.showDebugProfilerChart && key >= 48 && key <= 57) {
-                  this.mc.updateDebugProfilerName(key - 48);
+               flag1 = InputMappings.isKeyDown(Minecraft.getInstance().getMainWindow().getHandle(), 292) && this.processKeyF3(key);
+               this.actionKeyF3 |= flag1;
+               if (key == 290) {
+                  this.mc.gameSettings.hideGUI = !this.mc.gameSettings.hideGUI;
                }
             }
-         }
 
+            if (flag1) {
+               KeyBinding.setKeyBindState(inputmappings$input, false);
+            } else {
+               KeyBinding.setKeyBindState(inputmappings$input, true);
+               KeyBinding.onTick(inputmappings$input);
+            }
+
+            if (this.mc.gameSettings.showDebugProfilerChart && key >= 48 && key <= 57) {
+               this.mc.updateDebugProfilerName(key - 48);
+            }
+         }
       }
@@ -351,20 +378,27 @@
 
-   private void onCharEvent(long windowPointer, int codePoint, int modifiers) {
+   public void onCharEvent(long windowPointer, int codePoint, int modifiers) {
       if (windowPointer == this.mc.getMainWindow().getHandle()) {
-         IGuiEventListener iguieventlistener = this.mc.currentScreen;
-         if (iguieventlistener != null && this.mc.getLoadingGui() == null) {
-            if (Character.charCount(codePoint) == 1) {
+         charsPressed.add(codePoint);
+         if (mc.mouseHelper.getHumanInput()) {
+            onCharEventImpl(codePoint, modifiers);
+         }
+      }
+   }
+
+   public void onCharEventImpl(int codePoint, int modifiers) {
+      IGuiEventListener iguieventlistener = this.mc.currentScreen;
+      if (iguieventlistener != null && this.mc.getLoadingGui() == null) {
+         if (Character.charCount(codePoint) == 1) {
+            Screen.wrapScreenError(() -> {
+               iguieventlistener.charTyped((char)codePoint, modifiers);
+            }, "charTyped event handler", iguieventlistener.getClass().getCanonicalName());
+         } else {
+            for(char c0 : Character.toChars(codePoint)) {
                Screen.wrapScreenError(() -> {
-                  iguieventlistener.charTyped((char)codePoint, modifiers);
+                  iguieventlistener.charTyped(c0, modifiers);
                }, "charTyped event handler", iguieventlistener.getClass().getCanonicalName());
-            } else {
-               for(char c0 : Character.toChars(codePoint)) {
-                  Screen.wrapScreenError(() -> {
-                     iguieventlistener.charTyped(c0, modifiers);
-                  }, "charTyped event handler", iguieventlistener.getClass().getCanonicalName());
-               }
             }
+         }
 
          }
-      }
    }
@@ -387,2 +421,3 @@
 
+
    public String getClipboardString() {
@@ -426,2 +461,44 @@
    }
+
+   public boolean isKeyPressed(int key) {
+      return keysPressed.contains(key);
+   }
+
+   private static List<String> processKeys(Collection<Integer> keys) {
+      return keys.stream().map(key -> InputMappings.getInputByCode(key, 0).getTranslationKey()).collect(Collectors.toList());
+   }
+
+   private static String processChars(Collection<Integer> chars) {
+      return chars.stream().map(code -> Character.valueOf((char) code.intValue()).toString()).collect(Collectors.joining());
+   }
+
+   public State getState() {
+      List<String> keyCodes = processKeys(accumKeysPressed);
+      List<String> newKeyCodes = processKeys(keysNewlyPressed);
+      String chars = processChars(charsPressed);
+      charsPressed.clear();
+      accumKeysPressed.retainAll(keysPressed);
+      keysNewlyPressed.clear();
+      return new State(keyCodes, newKeyCodes, chars);
+   }
+
+   public static class State {
+      public final Set<String> keys;
+      public final Set<String> newKeys;
+      public final String chars;
+      public State(Collection<String> keys, Collection<String> newKeys, String chars) {
+         this.keys = new HashSet<>();
+         this.keys.addAll(keys);
+         this.newKeys = new HashSet<>();
+         this.newKeys.addAll(newKeys);
+         this.chars = chars;
+      }
+   }
+
+   public void clearState() {
+      accumKeysPressed.clear();
+      keysNewlyPressed.clear();
+      charsPressed.clear();
+   }
 }
+
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/main/Main.java MCP-Reborn/src/main/java/net/minecraft/client/main/Main.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/main/Main.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/main/Main.java	2024-03-10 18:29:57.573071621 +0800
@@ -17,2 +17,3 @@
 import javax.annotation.Nullable;
+
 import joptsimple.ArgumentAcceptingOptionSpec;
@@ -64,6 +65,6 @@
       OptionSpec<String> optionspec11 = optionparser.accepts("uuid").withRequiredArg();
-      OptionSpec<String> optionspec12 = optionparser.accepts("accessToken").withRequiredArg().required();
-      OptionSpec<String> optionspec13 = optionparser.accepts("version").withRequiredArg().required();
-      OptionSpec<Integer> optionspec14 = optionparser.accepts("width").withRequiredArg().ofType(Integer.class).defaultsTo(854);
-      OptionSpec<Integer> optionspec15 = optionparser.accepts("height").withRequiredArg().ofType(Integer.class).defaultsTo(480);
+      OptionSpec<String> optionspec12 = optionparser.accepts("accessToken").withRequiredArg();
+      OptionSpec<String> optionspec13 = optionparser.accepts("version").withRequiredArg();
+      OptionSpec<Integer> optionspec14 = optionparser.accepts("width").withRequiredArg().ofType(Integer.class).defaultsTo(1280);
+      OptionSpec<Integer> optionspec15 = optionparser.accepts("height").withRequiredArg().ofType(Integer.class).defaultsTo(720);
       OptionSpec<Integer> optionspec16 = optionparser.accepts("fullscreenWidth").withRequiredArg().ofType(Integer.class);
@@ -75,2 +76,6 @@
       OptionSpec<String> optionspec22 = optionparser.accepts("versionType").withRequiredArg().defaultsTo("release");
+      OptionSpec<Integer> envPortOpspec = optionparser.accepts("envPort").withRequiredArg().ofType(Integer.class).defaultsTo(0);
+      OptionSpec<Void> disableRecOpspec = optionparser.accepts("disableRecorder");
+      OptionSpec<String> loadWorldOpspec = optionparser.accepts("loadWorldFile").withRequiredArg().ofType(String.class);
+      OptionSpec<String> replayOpspec = optionparser.accepts("replayFile").withRequiredArg().ofType(String.class);
       OptionSpec<String> optionspec23 = optionparser.nonOptions();
@@ -142,3 +147,3 @@
       new Empty3i();
-
+      int envServerPort = getValue(optionset, envPortOpspec);
       final Minecraft minecraft;
@@ -149,2 +154,6 @@
          minecraft = new Minecraft(gameconfiguration);
+         minecraft.gameSettings.envPort = envServerPort;
+         minecraft.gameSettings.disableRecorder = optionset.has(disableRecOpspec);
+         minecraft.gameSettings.replayFile = getValue(optionset, replayOpspec);
+         minecraft.gameSettings.loadWorldFile = getValue(optionset, loadWorldOpspec);
          RenderSystem.finishInitialization();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/MainWindow.java MCP-Reborn/src/main/java/net/minecraft/client/MainWindow.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/MainWindow.java	2024-03-10 15:44:47.614123326 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/MainWindow.java	2024-03-10 18:29:57.557071330 +0800
@@ -88,2 +88,4 @@
       GLFW.glfwWindowHint(139272, 0);
+      GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_FALSE);
+      GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
       this.handle = GLFW.glfwCreateWindow(this.width, this.height, titleIn, this.fullscreen && monitor != null ? monitor.getMonitorPointer() : 0L, 0L);
@@ -280,5 +282,25 @@
 
+   public void resize(int newWidth, int newHeight) {
+      if (width == newWidth && height == newHeight) {
+         return;
+      }
+      int fbWidth = framebufferWidth;
+      int fbHeight = framebufferHeight;
+      GLFW.glfwSetWindowSize(handle, newWidth, newHeight);
+      // sometimes GL does not update the framebuffer size instantaneously, need to wait for it before proceeding
+      // however, we cannot rely on new framebuffer size being equal to window size, because on some displays
+      // (e.g. retina) framebuffer width(height) is an integer factor of a window width(height). So, we do a bit of
+      // a dance to check that the width or height has changed, and that
+      // frameBuffer / width == framebufferHeight / height
+      while ((fbWidth == framebufferWidth && fbWidth == framebufferHeight) ||
+              (framebufferHeight * newWidth != framebufferWidth * newHeight)) {
+         updateFramebufferSize();
+      }
+      onWindowSizeUpdate(handle, newWidth, newHeight);
+      onFramebufferSizeUpdate(handle, framebufferWidth, framebufferHeight);
+   }
    private void onWindowFocusUpdate(long windowPointer, boolean hasFocus) {
       if (windowPointer == this.handle) {
-         this.mc.setGameFocused(hasFocus);
+         // this.mc.setGameFocused(hasFocus);
+         this.mc.setGameFocused(true);
       }
@@ -393,3 +415,7 @@
 
-      return i;
+      double frameBufferToSizeRatio = framebufferWidth / width;
+      if (framebufferHeight / height != frameBufferToSizeRatio) {
+         throw new RuntimeException("frameBufferWidth to width ratio is not the same as framebufferHeight to height");
+      }
+      return (int) (i * frameBufferToSizeRatio);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/Minecraft.java MCP-Reborn/src/main/java/net/minecraft/client/Minecraft.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/Minecraft.java	2024-03-10 15:44:47.618123336 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/Minecraft.java	2024-03-10 18:29:57.557071330 +0800
@@ -28,2 +28,3 @@
 import java.nio.ByteOrder;
+import java.nio.file.Path;
 import java.text.DecimalFormat;
@@ -44,2 +45,5 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
+import com.minerl.multiagent.recorder.PlayRecorder;
 import net.minecraft.block.Block;
@@ -172,21 +176,4 @@
 import net.minecraft.network.play.client.CPlayerDiggingPacket;
-import net.minecraft.profiler.DataPoint;
-import net.minecraft.profiler.EmptyProfiler;
-import net.minecraft.profiler.IProfileResult;
-import net.minecraft.profiler.IProfiler;
-import net.minecraft.profiler.ISnooperInfo;
-import net.minecraft.profiler.LongTickDetector;
-import net.minecraft.profiler.Snooper;
-import net.minecraft.profiler.TimeTracker;
-import net.minecraft.resources.DataPackRegistries;
-import net.minecraft.resources.FolderPackFinder;
-import net.minecraft.resources.IPackNameDecorator;
-import net.minecraft.resources.IReloadableResourceManager;
-import net.minecraft.resources.IResourceManager;
-import net.minecraft.resources.IResourcePack;
-import net.minecraft.resources.ResourcePackInfo;
-import net.minecraft.resources.ResourcePackList;
-import net.minecraft.resources.ResourcePackType;
-import net.minecraft.resources.ServerPackFinder;
-import net.minecraft.resources.SimpleReloadableResourceManager;
+import net.minecraft.profiler.*;
+import net.minecraft.resources.*;
 import net.minecraft.resources.data.PackMetadataSection;
@@ -242,2 +229,4 @@
 
+import static java.lang.Thread.currentThread;
+
 @OnlyIn(Dist.CLIENT)
@@ -310,2 +299,3 @@
    private final FilterManager field_244597_aC;
+   private boolean hasPlayerRespawned = false;
    public static byte[] memoryReserve = new byte[10485760];
@@ -404,3 +394,3 @@
       this.tutorial = new Tutorial(this);
-      this.thread = Thread.currentThread();
+      this.thread = currentThread();
       this.gameSettings = new GameSettings(this, this.gameDir);
@@ -421,4 +411,4 @@
       try {
-         InputStream inputstream = this.getPackFinder().getVanillaPack().getResourceStream(ResourcePackType.CLIENT_RESOURCES, new ResourceLocation("icons/icon_16x16.png"));
-         InputStream inputstream1 = this.getPackFinder().getVanillaPack().getResourceStream(ResourcePackType.CLIENT_RESOURCES, new ResourceLocation("icons/icon_32x32.png"));
+         InputStream inputstream = new JavaResourcePack().getResourceStream(ResourcePackType.CLIENT_RESOURCES, new ResourceLocation("icons", "icon_16x16.png"));
+         InputStream inputstream1 = new JavaResourcePack().getResourceStream(ResourcePackType.CLIENT_RESOURCES, new ResourceLocation("icons", "icon_32x32.png"));
          this.mainWindow.setWindowIcon(inputstream, inputstream1);
@@ -548,3 +538,3 @@
       } catch (AuthenticationException authenticationexception) {
-         LOGGER.error("Failed to verify authentication", (Throwable)authenticationexception);
+         LOGGER.error("Failed to verify authentication!");
          return new OfflineSocialInteractions();
@@ -586,3 +576,3 @@
    public void run() {
-      this.thread = Thread.currentThread();
+      this.thread = currentThread();
 
@@ -934,10 +924,26 @@
 
+      int j = this.timer.getPartialTicks(Util.milliTime());
       if (renderWorldIn) {
-         int j = this.timer.getPartialTicks(Util.milliTime());
+         if (gameSettings.syncIntegratedServer) {
+            this.timer.renderPartialTicks = gameSettings.disableRecorder ?  0 : RandomHelper.nextFloat();
+            j = 1;
+         }
          this.profiler.startSection("scheduledExecutables");
          this.drainTasks();
-         this.profiler.endSection();
-         this.profiler.startSection("tick");
+         this.profiler.endStartSection("tick");
+
 
          for(int k = 0; k < Math.min(10, j); ++k) {
+            if (k == 0) {
+               this.profiler.startSection("PlayRecorderTick");
+               PlayRecorder.getInstance().tick();
+               this.profiler.endStartSection("ReplaySenderTick");
+               ReplaySender.getInstance().tick();
+               this.profiler.endSection();
+            }
+            if (gameSettings.syncIntegratedServer && integratedServer != null) {
+               profiler.startSection("serverTick");
+               integratedServer.runSyncTick(profiler);
+               profiler.endSection();
+            }
             this.profiler.func_230035_c_("clientTick");
@@ -945,3 +951,2 @@
          }
-
          this.profiler.endSection();
@@ -949,3 +954,4 @@
 
-      this.mouseHelper.updatePlayerLook();
+
+      // this.mouseHelper.updatePlayerLook();
       this.mainWindow.setRenderPhase("Render");
@@ -983,13 +989,15 @@
       RenderSystem.popMatrix();
+
       this.profiler.endStartSection("updateDisplay");
       this.mainWindow.flipFrame();
-      int i1 = this.getFramerateLimit();
-      if ((double)i1 < AbstractOption.FRAMERATE_LIMIT.getMaxValue()) {
-         RenderSystem.limitDisplayFPS(i1);
-      }
+//      int i1 = this.getFramerateLimit();
+//      if ((double)i1 < AbstractOption.FRAMERATE_LIMIT.getMaxValue()) {
+//         RenderSystem.limitDisplayFPS(i1);
+//      }
 
       this.profiler.endStartSection("yield");
-      Thread.yield();
+      // Thread.yield();
       this.profiler.endSection();
       this.mainWindow.setRenderPhase("Post render");
+
       ++this.fpsCounter;
@@ -1005,6 +1013,25 @@
       }
-
       long l = Util.nanoTime();
-      this.frameTimer.addFrame(l - this.startNanoTime);
-      this.startNanoTime = l;
+      long tickNano = l - this.startNanoTime;
+
+      long nanosInMilli = 1000000;
+      // this is super weird - 20 fps should mean 50 ms / step. However, at 50 ms
+      // desired tick duration the game actually runs faster than normal.
+      // Roughly calibrated the value below using wood breaking speed by hand (that should be 3 s)
+      long desiredTickNano = 50 * nanosInMilli;
+      // if the env server is not active, i.e. it is human playing the game,
+      // limit fps so that the game does not run too fast
+      if (gameSettings.envPort == 0 && desiredTickNano > tickNano && tickNano > 0) {
+         try {
+            long sleepTimeMilli = (desiredTickNano - tickNano) / nanosInMilli;
+            Thread.sleep((desiredTickNano - tickNano) / nanosInMilli);
+            long actualTickMill = (Util.nanoTime() - this.startNanoTime) / nanosInMilli;
+            LOGGER.debug("Slept for " + sleepTimeMilli  + " ms for fps limiting, actual tick time " + actualTickMill);
+
+         } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+         }
+      }
+      this.startNanoTime = Util.nanoTime();
+      this.frameTimer.addFrame(tickNano);
       this.profiler.startSection("fpsUpdate");
@@ -1026,2 +1053,4 @@
    private boolean isDebugMode() {
+      // uncomment line below to enable in-game profiling
+      // return this.integratedServer != null && this.integratedServer.isServerRunning();
       return this.gameSettings.showDebugInfo && this.gameSettings.showDebugProfilerChart && !this.gameSettings.hideGUI;
@@ -1031,5 +1060,5 @@
       if (isDebug) {
-         if (!this.gameTimeTracker.func_233505_a_()) {
+         if (!this.gameTimeTracker.isTracking()) {
             this.gameTime = 0;
-            this.gameTimeTracker.func_233507_c_();
+            this.gameTimeTracker.startTracking();
          }
@@ -1038,6 +1067,6 @@
       } else {
-         this.gameTimeTracker.func_233506_b_();
+         this.gameTimeTracker.stopTracking();
       }
 
-      this.profiler = LongTickDetector.func_233523_a_(this.gameTimeTracker.func_233508_d_(), detector);
+      this.profiler = LongTickDetector.getProfiler(this.gameTimeTracker.getProfiler(), detector);
    }
@@ -1050,3 +1079,3 @@
       if (isDebug) {
-         this.profilerResult = this.gameTimeTracker.func_233509_e_();
+         this.profilerResult = this.gameTimeTracker.getResults();
       } else {
@@ -1055,3 +1084,3 @@
 
-      this.profiler = this.gameTimeTracker.func_233508_d_();
+      this.profiler = this.gameTimeTracker.getProfiler();
    }
@@ -1068,3 +1097,3 @@
       this.gameRenderer.updateShaderGroupSize(this.mainWindow.getFramebufferWidth(), this.mainWindow.getFramebufferHeight());
-      this.mouseHelper.setIgnoreFirstMove();
+      this.mouseHelper.ignoreFirstMove();
    }
@@ -1640,3 +1669,7 @@
    public void loadWorld(String worldName) {
-      this.loadWorld(worldName, DynamicRegistries.func_239770_b_(), Minecraft::loadDataPackCodec, Minecraft::loadWorld, false, Minecraft.WorldSelectionType.BACKUP);
+      loadWorld(saveFormat.getSavesDir(), worldName);
+   }
+
+   public void loadWorld(Path savePath, String worldName) {
+      loadWorld(savePath, worldName, DynamicRegistries.getImpl(),Minecraft::loadDataPackCodec,Minecraft::loadWorld,false,Minecraft.WorldSelectionType.BACKUP);
    }
@@ -1644,3 +1677,3 @@
    public void createWorld(String worldName, WorldSettings worldSettings, DynamicRegistries.Impl dynamicRegistriesIn, DimensionGeneratorSettings dimensionGeneratorSettings) {
-      this.loadWorld(worldName, dynamicRegistriesIn, (p_238179_1_) -> {
+      this.loadWorld(saveFormat.getSavesDir(), worldName, dynamicRegistriesIn, (p_238179_1_) -> {
          return worldSettings.getDatapackCodec();
@@ -1657,6 +1690,6 @@
 
-   private void loadWorld(String worldName, DynamicRegistries.Impl dynamicRegistries, Function<SaveFormat.LevelSave, DatapackCodec> levelSaveToDatapackFunction, Function4<SaveFormat.LevelSave, DynamicRegistries.Impl, IResourceManager, DatapackCodec, IServerConfiguration> quadFunction, boolean vanillaOnly, Minecraft.WorldSelectionType selectionType) {
+   private void loadWorld(Path savePath, String worldName, DynamicRegistries.Impl dynamicRegistries, Function<SaveFormat.LevelSave, DatapackCodec> levelSaveToDatapackFunction, Function4<SaveFormat.LevelSave, DynamicRegistries.Impl, IResourceManager, DatapackCodec, IServerConfiguration> quadFunction, boolean vanillaOnly, Minecraft.WorldSelectionType selectionType) {
       SaveFormat.LevelSave saveformat$levelsave;
       try {
-         saveformat$levelsave = this.saveFormat.getLevelSave(worldName);
+         saveformat$levelsave = this.saveFormat.getLevelSave(savePath, worldName);
       } catch (IOException ioexception2) {
@@ -1674,3 +1707,3 @@
          this.displayGuiScreen(new DatapackFailureScreen(() -> {
-            this.loadWorld(worldName, dynamicRegistries, levelSaveToDatapackFunction, quadFunction, true, selectionType);
+            this.loadWorld(savePath, worldName, dynamicRegistries, levelSaveToDatapackFunction, quadFunction, true, selectionType);
          }));
@@ -1703,4 +1736,4 @@
             PlayerProfileCache.setOnlineMode(false);
-            this.integratedServer = MinecraftServer.startServer((p_238188_8_) -> {
-               return new IntegratedServer(p_238188_8_, this, dynamicRegistries, saveformat$levelsave, minecraft$packmanager.getResourcePacks(), minecraft$packmanager.getDataPackRegistries(), iserverconfiguration, minecraftsessionservice, gameprofilerepository, playerprofilecache, (p_238211_1_) -> {
+            if (gameSettings.syncIntegratedServer) {
+               integratedServer = new IntegratedServer(currentThread(), this, dynamicRegistries, saveformat$levelsave, minecraft$packmanager.getResourcePacks(), minecraft$packmanager.getDataPackRegistries(), iserverconfiguration, minecraftsessionservice, gameprofilerepository, playerprofilecache, (p_238211_1_) -> {
                   TrackingChunkStatusListener trackingchunkstatuslistener = new TrackingChunkStatusListener(p_238211_1_ + 0);
@@ -1710,4 +1743,16 @@
                });
-            });
-            this.integratedServerIsRunning = true;
+               integratedServer.init();
+               integratedServerIsRunning = true;
+               integratedServer.runTickAndTasks();
+            } else {
+               this.integratedServer = MinecraftServer.startServer((p_238188_8_) -> {
+                  return new IntegratedServer(p_238188_8_, this, dynamicRegistries, saveformat$levelsave, minecraft$packmanager.getResourcePacks(), minecraft$packmanager.getDataPackRegistries(), iserverconfiguration, minecraftsessionservice, gameprofilerepository, playerprofilecache, (p_238211_1_) -> {
+                     TrackingChunkStatusListener trackingchunkstatuslistener = new TrackingChunkStatusListener(p_238211_1_ + 0);
+                     trackingchunkstatuslistener.startTracking();
+                     this.refChunkStatusListener.set(trackingchunkstatuslistener);
+                     return new ChainedChunkStatusListener(trackingchunkstatuslistener, this.queueChunkTracking::add);
+                  });
+               });
+               this.integratedServerIsRunning = true;
+            }
          } catch (Throwable throwable) {
@@ -1753,3 +1798,3 @@
          this.deleteWorld(selectionType, worldName, flag, () -> {
-            this.loadWorld(worldName, dynamicRegistries, levelSaveToDatapackFunction, quadFunction, vanillaOnly, Minecraft.WorldSelectionType.NONE);
+            this.loadWorld(savePath, worldName, dynamicRegistries, levelSaveToDatapackFunction, quadFunction, vanillaOnly, Minecraft.WorldSelectionType.NONE);
          });
@@ -1861,3 +1906,3 @@
 
-            while(!integratedserver.isThreadAlive()) {
+            while(!integratedserver.isThreadAlive() && !gameSettings.syncIntegratedServer) {
                this.runGameLoop(false);
@@ -2441,2 +2486,10 @@
    }
+
+   public boolean isHasPlayerRespawned() {
+      return this.hasPlayerRespawned;
+   }
+
+   public void setHasPlayerRespawned(boolean hasRespawned) {
+      this.hasPlayerRespawned = hasRespawned;
+   }
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/MouseHelper.java MCP-Reborn/src/main/java/net/minecraft/client/MouseHelper.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/MouseHelper.java	2024-03-10 15:44:47.618123336 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/MouseHelper.java	2024-03-10 18:29:57.557071330 +0800
@@ -4,4 +4,4 @@
 import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
+
 import net.minecraft.client.gui.IGuiEventListener;
@@ -37,2 +37,10 @@
    private boolean mouseGrabbed;
+   private double dx = 0;
+   private double dy = 0;
+   private double accumDwheel = 0;
+   private boolean humanInput = true;
+
+   private Set<Integer> buttonsPressed = new HashSet<>();
+   private Set<Integer> accumButtonsPressed = new HashSet<>();
+   private Set<Integer> buttonsNewlyPressed = new HashSet<>();
 
@@ -42,106 +50,126 @@
 
-   private void mouseButtonCallback(long handle, int button, int action, int mods) {
-      if (handle == this.minecraft.getMainWindow().getHandle()) {
-         boolean flag = action == 1;
-         if (Minecraft.IS_RUNNING_ON_MAC && button == 0) {
-            if (flag) {
-               if ((mods & 2) == 2) {
-                  button = 1;
-                  ++this.simulatedRightClicks;
-               }
-            } else if (this.simulatedRightClicks > 0) {
-               button = 1;
-               --this.simulatedRightClicks;
-            }
+   void mouseButtonCallback(long handle, int button, int action, int mods) {
+      if (handle == minecraft.getMainWindow().getHandle()) {
+         if (humanInput) {
+            mouseButtonCallbackImpl(handle, button, action, mods);
          }
-
-         int i = button;
+      }
+      if (action == 1) {
+         this.buttonsPressed.add(button);
+         this.accumButtonsPressed.add(button);
+         this.buttonsNewlyPressed.add(button);
+      } else if (action == 0) {
+         this.buttonsPressed.remove(button);
+      } else {
+         throw new RuntimeException("Unknown action! " + action);
+      }
+   }
+   void mouseButtonCallbackImpl(long handle, int button, int action, int mods) {
+      boolean flag = action == 1;
+      if (Minecraft.IS_RUNNING_ON_MAC && button == 0) {
          if (flag) {
-            if (this.minecraft.gameSettings.touchscreen && this.touchScreenCounter++ > 0) {
-               return;
+            if ((mods & 2) == 2) {
+               button = 1;
+               ++this.simulatedRightClicks;
             }
+         } else if (this.simulatedRightClicks > 0) {
+            button = 1;
+            --this.simulatedRightClicks;
+         }
+      }
 
-            this.activeButton = i;
-            this.eventTime = NativeUtil.getTime();
-         } else if (this.activeButton != -1) {
-            if (this.minecraft.gameSettings.touchscreen && --this.touchScreenCounter > 0) {
-               return;
-            }
+      int i = button;
+      if (flag) {
+         if (this.minecraft.gameSettings.touchscreen && this.touchScreenCounter++ > 0) {
+            return;
+         }
 
-            this.activeButton = -1;
+         this.activeButton = i;
+         this.eventTime = NativeUtil.getTime();
+      } else if (this.activeButton != -1) {
+         if (this.minecraft.gameSettings.touchscreen && --this.touchScreenCounter > 0) {
+            return;
          }
 
-         boolean[] aboolean = new boolean[]{false};
-         if (this.minecraft.loadingGui == null) {
-            if (this.minecraft.currentScreen == null) {
-               if (!this.mouseGrabbed && flag) {
-                  this.grabMouse();
-               }
+         this.activeButton = -1;
+      }
+
+      boolean[] aboolean = new boolean[]{false};
+      if (this.minecraft.loadingGui == null) {
+         if (this.minecraft.currentScreen == null) {
+            if (!this.mouseGrabbed && flag) {
+               this.grabMouse();
+            }
+         } else {
+            double d0 = this.mouseX * (double)this.minecraft.getMainWindow().getScaledWidth() / (double)this.minecraft.getMainWindow().getWidth();
+            double d1 = this.mouseY * (double)this.minecraft.getMainWindow().getScaledHeight() / (double)this.minecraft.getMainWindow().getHeight();
+            if (flag) {
+               Screen.wrapScreenError(() -> {
+                  aboolean[0] = this.minecraft.currentScreen.mouseClicked(d0, d1, i);
+               }, "mouseClicked event handler", this.minecraft.currentScreen.getClass().getCanonicalName());
             } else {
-               double d0 = this.mouseX * (double)this.minecraft.getMainWindow().getScaledWidth() / (double)this.minecraft.getMainWindow().getWidth();
-               double d1 = this.mouseY * (double)this.minecraft.getMainWindow().getScaledHeight() / (double)this.minecraft.getMainWindow().getHeight();
-               if (flag) {
-                  Screen.wrapScreenError(() -> {
-                     aboolean[0] = this.minecraft.currentScreen.mouseClicked(d0, d1, i);
-                  }, "mouseClicked event handler", this.minecraft.currentScreen.getClass().getCanonicalName());
-               } else {
-                  Screen.wrapScreenError(() -> {
-                     aboolean[0] = this.minecraft.currentScreen.mouseReleased(d0, d1, i);
-                  }, "mouseReleased event handler", this.minecraft.currentScreen.getClass().getCanonicalName());
-               }
+               Screen.wrapScreenError(() -> {
+                  aboolean[0] = this.minecraft.currentScreen.mouseReleased(d0, d1, i);
+               }, "mouseReleased event handler", this.minecraft.currentScreen.getClass().getCanonicalName());
             }
          }
+      }
 
-         if (!aboolean[0] && (this.minecraft.currentScreen == null || this.minecraft.currentScreen.passEvents) && this.minecraft.loadingGui == null) {
-            if (i == 0) {
-               this.leftDown = flag;
-            } else if (i == 2) {
-               this.middleDown = flag;
-            } else if (i == 1) {
-               this.rightDown = flag;
-            }
+      if (!aboolean[0] && (this.minecraft.currentScreen == null || this.minecraft.currentScreen.passEvents) && this.minecraft.loadingGui == null) {
+         if (i == 0) {
+            this.leftDown = flag;
+         } else if (i == 2) {
+            this.middleDown = flag;
+         } else if (i == 1) {
+            this.rightDown = flag;
+         }
 
-            KeyBinding.setKeyBindState(InputMappings.Type.MOUSE.getOrMakeInput(i), flag);
-            if (flag) {
-               if (this.minecraft.player.isSpectator() && i == 2) {
-                  this.minecraft.ingameGUI.getSpectatorGui().onMiddleClick();
-               } else {
-                  KeyBinding.onTick(InputMappings.Type.MOUSE.getOrMakeInput(i));
-               }
+         KeyBinding.setKeyBindState(InputMappings.Type.MOUSE.getOrMakeInput(i), flag);
+         if (flag) {
+            if (this.minecraft.player.isSpectator() && i == 2) {
+               this.minecraft.ingameGUI.getSpectatorGui().onMiddleClick();
+            } else {
+               KeyBinding.onTick(InputMappings.Type.MOUSE.getOrMakeInput(i));
             }
          }
+      }
+   }
 
+   void scrollCallback(long handle, double xoffset, double yoffset) {
+      if (handle == minecraft.getMainWindow().getHandle()) {
+         if (humanInput) {
+            scrollCallbackImpl(handle, xoffset, yoffset);
+         }
       }
+      this.accumDwheel += yoffset;
    }
 
-   private void scrollCallback(long handle, double xoffset, double yoffset) {
-      if (handle == Minecraft.getInstance().getMainWindow().getHandle()) {
-         double d0 = (this.minecraft.gameSettings.discreteMouseScroll ? Math.signum(yoffset) : yoffset) * this.minecraft.gameSettings.mouseWheelSensitivity;
-         if (this.minecraft.loadingGui == null) {
-            if (this.minecraft.currentScreen != null) {
-               double d1 = this.mouseX * (double)this.minecraft.getMainWindow().getScaledWidth() / (double)this.minecraft.getMainWindow().getWidth();
-               double d2 = this.mouseY * (double)this.minecraft.getMainWindow().getScaledHeight() / (double)this.minecraft.getMainWindow().getHeight();
-               this.minecraft.currentScreen.mouseScrolled(d1, d2, d0);
-            } else if (this.minecraft.player != null) {
-               if (this.accumulatedScrollDelta != 0.0D && Math.signum(d0) != Math.signum(this.accumulatedScrollDelta)) {
-                  this.accumulatedScrollDelta = 0.0D;
-               }
+   void scrollCallbackImpl(long handle, double xoffset, double yoffset) {
+      double d0 = (this.minecraft.gameSettings.discreteMouseScroll ? Math.signum(yoffset) : yoffset) * this.minecraft.gameSettings.mouseWheelSensitivity;
+      if (this.minecraft.loadingGui == null) {
+         if (this.minecraft.currentScreen != null) {
+            double d1 = this.mouseX * (double) this.minecraft.getMainWindow().getScaledWidth() / (double) this.minecraft.getMainWindow().getWidth();
+            double d2 = this.mouseY * (double) this.minecraft.getMainWindow().getScaledHeight() / (double) this.minecraft.getMainWindow().getHeight();
+            this.minecraft.currentScreen.mouseScrolled(d1, d2, d0);
+         } else if (this.minecraft.player != null) {
+            if (this.accumulatedScrollDelta != 0.0D && Math.signum(d0) != Math.signum(this.accumulatedScrollDelta)) {
+               this.accumulatedScrollDelta = 0.0D;
+            }
 
-               this.accumulatedScrollDelta += d0;
-               float f1 = (float)((int)this.accumulatedScrollDelta);
-               if (f1 == 0.0F) {
-                  return;
-               }
+            this.accumulatedScrollDelta += d0;
+            float f1 = (float) ((int) this.accumulatedScrollDelta);
+            if (f1 == 0.0F) {
+               return;
+            }
 
-               this.accumulatedScrollDelta -= (double)f1;
-               if (this.minecraft.player.isSpectator()) {
-                  if (this.minecraft.ingameGUI.getSpectatorGui().isMenuActive()) {
-                     this.minecraft.ingameGUI.getSpectatorGui().onMouseScroll((double)(-f1));
-                  } else {
-                     float f = MathHelper.clamp(this.minecraft.player.abilities.getFlySpeed() + f1 * 0.005F, 0.0F, 0.2F);
-                     this.minecraft.player.abilities.setFlySpeed(f);
-                  }
+            this.accumulatedScrollDelta -= (double) f1;
+            if (this.minecraft.player.isSpectator()) {
+               if (this.minecraft.ingameGUI.getSpectatorGui().isMenuActive()) {
+                  this.minecraft.ingameGUI.getSpectatorGui().onMouseScroll((double) (-f1));
                } else {
-                  this.minecraft.player.inventory.changeCurrentItem((double)f1);
+                  float f = MathHelper.clamp(this.minecraft.player.abilities.getFlySpeed() + f1 * 0.005F, 0.0F, 0.2F);
+                  this.minecraft.player.abilities.setFlySpeed(f);
                }
+            } else {
+               this.minecraft.player.inventory.changeCurrentItem((double) f1);
             }
@@ -149,3 +177,2 @@
       }
-
    }
@@ -160,15 +187,20 @@
    public void registerCallbacks(long handle) {
-      InputMappings.setMouseCallbacks(handle, (p_228032_1_, p_228032_3_, p_228032_5_) -> {
-         this.minecraft.execute(() -> {
-            this.cursorPosCallback(p_228032_1_, p_228032_3_, p_228032_5_);
+      if (minecraft.gameSettings.envPort != 0) {
+         // if minecraft operates in the env mode (envPort != 0), we should not
+         // interact with the mouse; hence, the callbacks are not registered.
+         return;
+      }
+      InputMappings.setMouseCallbacks(handle, (handle_, xpos, ypos) -> {
+         minecraft.execute(() -> {
+            this.cursorPosCallback(handle_, xpos, ypos);
          });
-      }, (p_228028_1_, p_228028_3_, p_228028_4_, p_228028_5_) -> {
-         this.minecraft.execute(() -> {
-            this.mouseButtonCallback(p_228028_1_, p_228028_3_, p_228028_4_, p_228028_5_);
+      }, (handle_, button, action, mods) -> {
+         minecraft.execute(() -> {
+            this.mouseButtonCallback(handle_, button, action, mods);
          });
-      }, (p_228029_1_, p_228029_3_, p_228029_5_) -> {
-         this.minecraft.execute(() -> {
-            this.scrollCallback(p_228029_1_, p_228029_3_, p_228029_5_);
+      }, (handle_, xoffset, yoffset) -> {
+         minecraft.execute(() -> {
+            this.scrollCallback(handle_, xoffset, yoffset);
          });
-      }, (p_238227_1_, p_238227_3_, p_238227_4_) -> {
+      }, (window, p_238227_3_, p_238227_4_) -> {
          Path[] apath = new Path[p_238227_3_];
@@ -180,3 +212,3 @@
          this.minecraft.execute(() -> {
-            this.addPacksToScreen(p_238227_1_, Arrays.asList(apath));
+            this.addPacksToScreen(window, Arrays.asList(apath));
          });
@@ -185,4 +217,10 @@
 
-   private void cursorPosCallback(long handle, double xpos, double ypos) {
-      if (handle == Minecraft.getInstance().getMainWindow().getHandle()) {
+   public void executeIfHuman(Runnable runnable) {
+      if (this.humanInput) {
+         this.minecraft.execute(runnable);
+      }
+   }
+
+   void cursorPosCallback(long handle, double xpos, double ypos) {
+      if (handle == minecraft.getMainWindow().getHandle()) {
          if (this.ignoreFirstMove) {
@@ -193,29 +231,38 @@
 
-         IGuiEventListener iguieventlistener = this.minecraft.currentScreen;
-         if (iguieventlistener != null && this.minecraft.loadingGui == null) {
-            double d0 = xpos * (double)this.minecraft.getMainWindow().getScaledWidth() / (double)this.minecraft.getMainWindow().getWidth();
-            double d1 = ypos * (double)this.minecraft.getMainWindow().getScaledHeight() / (double)this.minecraft.getMainWindow().getHeight();
-            Screen.wrapScreenError(() -> {
-               iguieventlistener.mouseMoved(d0, d1);
-            }, "mouseMoved event handler", iguieventlistener.getClass().getCanonicalName());
-            if (this.activeButton != -1 && this.eventTime > 0.0D) {
-               double d2 = (xpos - this.mouseX) * (double)this.minecraft.getMainWindow().getScaledWidth() / (double)this.minecraft.getMainWindow().getWidth();
-               double d3 = (ypos - this.mouseY) * (double)this.minecraft.getMainWindow().getScaledHeight() / (double)this.minecraft.getMainWindow().getHeight();
-               Screen.wrapScreenError(() -> {
-                  iguieventlistener.mouseDragged(d0, d1, this.activeButton, d2, d3);
-               }, "mouseDragged event handler", iguieventlistener.getClass().getCanonicalName());
-            }
+         if (humanInput) {
+            cursorPosCallbackImpl(xpos, ypos);
+         } else {
+            this.dx = xpos - this.mouseX;
+            this.dy = ypos - this.mouseY;
          }
+      }
+   }
 
-         this.minecraft.getProfiler().startSection("mouse");
-         if (this.isMouseGrabbed() && this.minecraft.isGameFocused()) {
-            this.xVelocity += xpos - this.mouseX;
-            this.yVelocity += ypos - this.mouseY;
+   void cursorPosCallbackImpl(double xpos, double ypos) {
+      IGuiEventListener iguieventlistener = this.minecraft.currentScreen;
+      if (iguieventlistener != null && this.minecraft.loadingGui == null) {
+         double d0 = xpos * (double)this.minecraft.getMainWindow().getScaledWidth() / (double)this.minecraft.getMainWindow().getWidth();
+         double d1 = ypos * (double)this.minecraft.getMainWindow().getScaledHeight() / (double)this.minecraft.getMainWindow().getHeight();
+         Screen.wrapScreenError(() -> {
+            iguieventlistener.mouseMoved(d0, d1);
+         }, "mouseMoved event handler", iguieventlistener.getClass().getCanonicalName());
+         if (this.activeButton != -1 && this.eventTime > 0.0D) {
+            double d2 = (xpos - this.mouseX) * (double)this.minecraft.getMainWindow().getScaledWidth() / (double)this.minecraft.getMainWindow().getWidth();
+            double d3 = (ypos - this.mouseY) * (double)this.minecraft.getMainWindow().getScaledHeight() / (double)this.minecraft.getMainWindow().getHeight();
+            Screen.wrapScreenError(() -> {
+               iguieventlistener.mouseDragged(d0, d1, this.activeButton, d2, d3);
+            }, "mouseDragged event handler", iguieventlistener.getClass().getCanonicalName());
          }
+      }
 
-         this.updatePlayerLook();
-         this.mouseX = xpos;
-         this.mouseY = ypos;
-         this.minecraft.getProfiler().endSection();
+      this.minecraft.getProfiler().startSection("mouse");
+      if (this.isMouseGrabbed() && this.minecraft.isGameFocused()) {
+         this.xVelocity += xpos - this.mouseX;
+         this.yVelocity += ypos - this.mouseY;
       }
+      this.updatePlayerLook();
+      this.minecraft.getProfiler().endSection();
+      this.mouseX = xpos;
+      this.mouseY = ypos;
+
    }
@@ -223,15 +270,15 @@
    public void updatePlayerLook() {
-      double d0 = NativeUtil.getTime();
-      double d1 = d0 - this.lastLookTime;
-      this.lastLookTime = d0;
-      if (this.isMouseGrabbed() && this.minecraft.isGameFocused()) {
+      double time = NativeUtil.getTime();
+      double dt = time - this.lastLookTime;
+      this.lastLookTime = time;
+      if (true || this.isMouseGrabbed() && this.minecraft.isGameFocused()) {
          double d4 = this.minecraft.gameSettings.mouseSensitivity * (double)0.6F + (double)0.2F;
-         double d5 = d4 * d4 * d4 * 8.0D;
-         double d2;
-         double d3;
+         double sensitivity = d4 * d4 * d4 * 8.0D;
+         double yaw;
+         double pitch;
          if (this.minecraft.gameSettings.smoothCamera) {
-            double d6 = this.xSmoother.smooth(this.xVelocity * d5, d1 * d5);
-            double d7 = this.ySmoother.smooth(this.yVelocity * d5, d1 * d5);
-            d2 = d6;
-            d3 = d7;
+            double d6 = this.xSmoother.smooth(this.xVelocity * sensitivity, dt * sensitivity);
+            double d7 = this.ySmoother.smooth(this.yVelocity * sensitivity, dt * sensitivity);
+            yaw = d6;
+            pitch = d7;
          } else {
@@ -239,6 +286,6 @@
             this.ySmoother.reset();
-            d2 = this.xVelocity * d5;
-            d3 = this.yVelocity * d5;
+            yaw = this.xVelocity * sensitivity;
+            pitch = this.yVelocity * sensitivity;
          }
-
+         double xvel = this.xVelocity;
          this.xVelocity = 0.0D;
@@ -250,5 +297,5 @@
 
-         this.minecraft.getTutorial().onMouseMove(d2, d3);
+         this.minecraft.getTutorial().onMouseMove(yaw, pitch);
          if (this.minecraft.player != null) {
-            this.minecraft.player.rotateTowards(d2, d3 * (double)i);
+            this.minecraft.player.rotateTowards(yaw, pitch * (double)i);
          }
@@ -277,6 +324,2 @@
 
-   public void setIgnoreFirstMove() {
-      this.ignoreFirstMove = true;
-   }
-
    public boolean isMouseGrabbed() {
@@ -295,3 +338,5 @@
             this.mouseY = (double)(this.minecraft.getMainWindow().getHeight() / 2);
-            InputMappings.setCursorPosAndMode(this.minecraft.getMainWindow().getHandle(), 212995, this.mouseX, this.mouseY);
+            if (minecraft.gameSettings.envPort == 0) {
+               InputMappings.setCursorPosAndMode(this.minecraft.getMainWindow().getHandle(), 212995, this.mouseX, this.mouseY);
+            }
             this.minecraft.displayGuiScreen((Screen)null);
@@ -308,3 +353,5 @@
          this.mouseY = (double)(this.minecraft.getMainWindow().getHeight() / 2);
-         InputMappings.setCursorPosAndMode(this.minecraft.getMainWindow().getHandle(), 212993, this.mouseX, this.mouseY);
+         if (minecraft.gameSettings.envPort == 0) {
+            InputMappings.setCursorPosAndMode(this.minecraft.getMainWindow().getHandle(), 212993, this.mouseX, this.mouseY);
+         }
       }
@@ -315,2 +362,76 @@
    }
+
+   public State getState() {
+      double scaleFactor = getScaleFactor();
+      State state = new State(mouseX, mouseY, dx / scaleFactor, dy / scaleFactor, accumDwheel, accumButtonsPressed, buttonsNewlyPressed);
+      dx = 0;
+      dy = 0;
+      accumDwheel = 0;
+      accumButtonsPressed.retainAll(buttonsPressed);
+      buttonsNewlyPressed.clear();
+      return state;
+   }
+
+   private boolean isGuiOpen() {
+      return Minecraft.getInstance().currentScreen != null;
+   }
+
+   public double getScaleFactor() {
+      double scaleFactor = 1.0;
+      if (isGuiOpen()) {
+         Minecraft mc = Minecraft.getInstance();
+         // this factor (ratio of framebuffer size to window size) would be 1 on normal screens,
+         // but is 2 on retina screens. Such correction is needed, because
+         // guiScale factor is defined wrt framebuffer size, mouse movements are calculated wrt window size
+         double retinaFactor = mc.getFramebuffer().framebufferWidth / mc.getMainWindow().getWidth();
+         scaleFactor = mc.getMainWindow().getGuiScaleFactor() / retinaFactor;
+      }
+      return scaleFactor;
+   }
+
+   public void clearState() {
+      dx = 0;
+      dy = 0;
+      accumDwheel = 0;
+      accumButtonsPressed.clear();
+      buttonsPressed.clear();
+      buttonsNewlyPressed.clear();
+   }
+
+   public boolean getHumanInput() {
+      return humanInput;
+   }
+
+   public void setHumanInput(boolean humanInput) {
+      this.humanInput = humanInput;
+   }
+
+   public static class State {
+      public final double x;
+      public final double y;
+      public final double dx;
+      public final double dy;
+      public final double scaledX;
+      public final double scaledY;
+      public final double dwheel;
+      public final Set<Integer> buttons;
+      public final Set<Integer> newButtons;
+
+      public State(double x, double y, double dx, double dy, double dwheel, Collection<Integer> buttons, Collection<Integer> newButtons) {
+         MainWindow mw = Minecraft.getInstance().getMainWindow();
+         MouseHelper mh = Minecraft.getInstance().mouseHelper;
+         this.x = x;
+         this.y = y;
+         this.dx = dx;
+         this.dy = dy;
+         double scaleFactor = mh.getScaleFactor();
+         this.scaledX = (x - mw.getWidth() / 2) / scaleFactor;
+         this.scaledY = (y - mw.getHeight() / 2) / scaleFactor;
+         this.dwheel = dwheel;
+         this.buttons = new HashSet<>();
+         this.buttons.addAll(buttons);
+         this.newButtons = new HashSet<>();
+         this.newButtons.addAll(newButtons);
+      }
+   }
 }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/network/play/ClientPlayNetHandler.java MCP-Reborn/src/main/java/net/minecraft/client/network/play/ClientPlayNetHandler.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/network/play/ClientPlayNetHandler.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/network/play/ClientPlayNetHandler.java	2024-03-10 18:29:57.581071767 +0800
@@ -328,3 +328,3 @@
    private int viewDistance = 3;
-   private final Random avRandomizer = new Random();
+   private final Random avRandomizer = new Random(0);
    private CommandDispatcher<ISuggestionProvider> commandDispatcher = new CommandDispatcher<>();
@@ -333,3 +333,3 @@
    private Set<RegistryKey<World>> field_239162_s_;
-   private DynamicRegistries field_239163_t_ = DynamicRegistries.func_239770_b_();
+   private DynamicRegistries field_239163_t_ = DynamicRegistries.getImpl();
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/BubbleColumnUpParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/BubbleColumnUpParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/BubbleColumnUpParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/BubbleColumnUpParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -15,6 +16,6 @@
       this.particleScale *= this.rand.nextFloat() * 0.6F + 0.2F;
-      this.motionX = motionX * (double)0.2F + (Math.random() * 2.0D - 1.0D) * (double)0.02F;
-      this.motionY = motionY * (double)0.2F + (Math.random() * 2.0D - 1.0D) * (double)0.02F;
-      this.motionZ = motionZ * (double)0.2F + (Math.random() * 2.0D - 1.0D) * (double)0.02F;
-      this.maxAge = (int)(40.0D / (Math.random() * 0.8D + 0.2D));
+      this.motionX = motionX * (double)0.2F + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.02F;
+      this.motionY = motionY * (double)0.2F + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.02F;
+      this.motionZ = motionZ * (double)0.2F + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.02F;
+      this.maxAge = (int)(40.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/BubbleParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/BubbleParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/BubbleParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/BubbleParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -15,6 +16,6 @@
       this.particleScale *= this.rand.nextFloat() * 0.6F + 0.2F;
-      this.motionX = motionX * (double)0.2F + (Math.random() * 2.0D - 1.0D) * (double)0.02F;
-      this.motionY = motionY * (double)0.2F + (Math.random() * 2.0D - 1.0D) * (double)0.02F;
-      this.motionZ = motionZ * (double)0.2F + (Math.random() * 2.0D - 1.0D) * (double)0.02F;
-      this.maxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
+      this.motionX = motionX * (double)0.2F + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.02F;
+      this.motionY = motionY * (double)0.2F + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.02F;
+      this.motionZ = motionZ * (double)0.2F + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.02F;
+      this.maxAge = (int)(8.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/CloudParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/CloudParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/CloudParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/CloudParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -23,3 +24,3 @@
       this.motionZ += motionZ;
-      float f1 = 1.0F - (float)(Math.random() * (double)0.3F);
+      float f1 = 1.0F - (float)(RandomHelper.randomDouble() * (double)0.3F);
       this.particleRed = f1;
@@ -28,3 +29,3 @@
       this.particleScale *= 1.875F;
-      int i = (int)(8.0D / (Math.random() * 0.8D + 0.3D));
+      int i = (int)(8.0D / (RandomHelper.randomDouble() * 0.8D + 0.3D));
       this.maxAge = (int)Math.max((float)i * 2.5F, 1.0F);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/CritParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/CritParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/CritParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/CritParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -18,3 +19,3 @@
       this.motionZ += motionZ * 0.4D;
-      float f = (float)(Math.random() * (double)0.3F + (double)0.6F);
+      float f = (float)(RandomHelper.randomDouble() * (double)0.3F + (double)0.6F);
       this.particleRed = f;
@@ -23,3 +24,3 @@
       this.particleScale *= 0.75F;
-      this.maxAge = Math.max((int)(6.0D / (Math.random() * 0.8D + 0.6D)), 1);
+      this.maxAge = Math.max((int)(6.0D / (RandomHelper.randomDouble() * 0.8D + 0.6D)), 1);
       this.canCollide = false;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/CurrentDownParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/CurrentDownParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/CurrentDownParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/CurrentDownParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -16,3 +17,3 @@
       super(world, x, y, z);
-      this.maxAge = (int)(Math.random() * 60.0D) + 30;
+      this.maxAge = (int)(RandomHelper.randomDouble() * 60.0D) + 30;
       this.canCollide = false;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/DeceleratingParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/DeceleratingParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/DeceleratingParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/DeceleratingParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -16,3 +17,3 @@
       this.posZ += (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.05F);
-      this.maxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D)) + 4;
+      this.maxAge = (int)(8.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D)) + 4;
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/DripParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/DripParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/DripParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/DripParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -253,3 +254,3 @@
          DripParticle dripparticle = new DripParticle.FallingNectarParticle(worldIn, x, y, z, Fluids.EMPTY);
-         dripparticle.maxAge = (int)(16.0D / (Math.random() * 0.8D + 0.2D));
+         dripparticle.maxAge = (int)(16.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
          dripparticle.particleGravity = 0.007F;
@@ -265,3 +266,3 @@
          super(world, x, y, z, fluid);
-         this.maxAge = (int)(64.0D / (Math.random() * 0.8D + 0.2D));
+         this.maxAge = (int)(64.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
       }
@@ -314,3 +315,3 @@
          super(world, x, y, z, fluid);
-         this.maxAge = (int)(16.0D / (Math.random() * 0.8D + 0.2D));
+         this.maxAge = (int)(16.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
       }
@@ -328,3 +329,3 @@
          DripParticle dripparticle = new DripParticle.Landing(worldIn, x, y, z, Fluids.EMPTY);
-         dripparticle.maxAge = (int)(128.0D / (Math.random() * 0.8D + 0.2D));
+         dripparticle.maxAge = (int)(128.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
          dripparticle.setColor(0.522F, 0.408F, 0.082F);
@@ -362,3 +363,3 @@
          dripparticle.fullbright = true;
-         dripparticle.maxAge = (int)(28.0D / (Math.random() * 0.8D + 0.2D));
+         dripparticle.maxAge = (int)(28.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
          dripparticle.setColor(0.51171875F, 0.03125F, 0.890625F);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/EnchantmentTableParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/EnchantmentTableParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/EnchantmentTableParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/EnchantmentTableParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -33,3 +34,3 @@
       this.canCollide = false;
-      this.maxAge = (int)(Math.random() * 10.0D) + 30;
+      this.maxAge = (int)(RandomHelper.randomDouble() * 10.0D) + 30;
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/FallingDustParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/FallingDustParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/FallingDustParticle.java	2024-03-10 15:44:47.634123379 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/FallingDustParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -3,2 +3,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.block.BlockRenderType;
@@ -27,7 +29,7 @@
       this.particleScale *= 0.67499995F;
-      int i = (int)(32.0D / (Math.random() * 0.8D + 0.2D));
+      int i = (int)(32.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
       this.maxAge = (int)Math.max((float)i * 0.9F, 1.0F);
       this.selectSpriteWithAge(spriteWithAge);
-      this.rotSpeed = ((float)Math.random() - 0.5F) * 0.1F;
-      this.particleAngle = (float)Math.random() * ((float)Math.PI * 2F);
+      this.rotSpeed = ((float) RandomHelper.randomDouble() - 0.5F) * 0.1F;
+      this.particleAngle = (float)RandomHelper.randomDouble() * ((float)Math.PI * 2F);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/LavaParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/LavaParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/LavaParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/LavaParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -17,3 +18,3 @@
       this.particleScale *= this.rand.nextFloat() * 2.0F + 0.2F;
-      this.maxAge = (int)(16.0D / (Math.random() * 0.8D + 0.2D));
+      this.maxAge = (int)(16.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/Particle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/Particle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/Particle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/Particle.java	2024-03-10 18:29:57.573071621 +0800
@@ -5,2 +5,4 @@
 import java.util.stream.Stream;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.renderer.ActiveRenderInfo;
@@ -38,3 +40,3 @@
    protected float height = 1.8F;
-   protected final Random rand = new Random();
+   protected final Random rand = new Random(0);
    protected int age;
@@ -61,6 +63,6 @@
       this(world, x, y, z);
-      this.motionX = motionX + (Math.random() * 2.0D - 1.0D) * (double)0.4F;
-      this.motionY = motionY + (Math.random() * 2.0D - 1.0D) * (double)0.4F;
-      this.motionZ = motionZ + (Math.random() * 2.0D - 1.0D) * (double)0.4F;
-      float f = (float)(Math.random() + Math.random() + 1.0D) * 0.15F;
+      this.motionX = motionX + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.4F;
+      this.motionY = motionY + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.4F;
+      this.motionZ = motionZ + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.4F;
+      float f = (float)(RandomHelper.randomDouble() + RandomHelper.randomDouble() + 1.0D) * 0.15F;
       float f1 = MathHelper.sqrt(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/ParticleManager.java MCP-Reborn/src/main/java/net/minecraft/client/particle/ParticleManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/ParticleManager.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/ParticleManager.java	2024-03-10 18:29:57.573071621 +0800
@@ -66,3 +66,3 @@
    private final TextureManager renderer;
-   private final Random rand = new Random();
+   private final Random rand = new Random(0);
    private final Int2ObjectMap<IParticleFactory<?>> factories = new Int2ObjectOpenHashMap<>();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/PoofParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/PoofParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/PoofParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/PoofParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -14,5 +15,5 @@
       this.spriteWithAge = spriteWithAge;
-      this.motionX = motionX + (Math.random() * 2.0D - 1.0D) * (double)0.05F;
-      this.motionY = motionY + (Math.random() * 2.0D - 1.0D) * (double)0.05F;
-      this.motionZ = motionZ + (Math.random() * 2.0D - 1.0D) * (double)0.05F;
+      this.motionX = motionX + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.05F;
+      this.motionY = motionY + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.05F;
+      this.motionZ = motionZ + (RandomHelper.randomDouble() * 2.0D - 1.0D) * (double)0.05F;
       float f = this.rand.nextFloat() * 0.3F + 0.7F;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/PortalParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/PortalParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/PortalParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/PortalParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -29,3 +30,3 @@
       this.particleBlue = f;
-      this.maxAge = (int)(Math.random() * 10.0D) + 40;
+      this.maxAge = (int)(RandomHelper.randomDouble() * 10.0D) + 40;
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/RainParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/RainParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/RainParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/RainParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -14,3 +15,3 @@
       this.motionX *= (double)0.3F;
-      this.motionY = Math.random() * (double)0.2F + (double)0.1F;
+      this.motionY = RandomHelper.randomDouble() * (double)0.2F + (double)0.1F;
       this.motionZ *= (double)0.3F;
@@ -18,3 +19,3 @@
       this.particleGravity = 0.06F;
-      this.maxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
+      this.maxAge = (int)(8.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
    }
@@ -38,3 +39,3 @@
          if (this.onGround) {
-            if (Math.random() < 0.5D) {
+            if (RandomHelper.randomDouble() < 0.5D) {
                this.setExpired();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/RedstoneParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/RedstoneParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/RedstoneParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/RedstoneParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -18,8 +19,8 @@
       this.motionZ *= (double)0.1F;
-      float f = (float)Math.random() * 0.4F + 0.6F;
-      this.particleRed = ((float)(Math.random() * (double)0.2F) + 0.8F) * particleData.getRed() * f;
-      this.particleGreen = ((float)(Math.random() * (double)0.2F) + 0.8F) * particleData.getGreen() * f;
-      this.particleBlue = ((float)(Math.random() * (double)0.2F) + 0.8F) * particleData.getBlue() * f;
+      float f = (float)RandomHelper.randomDouble() * 0.4F + 0.6F;
+      this.particleRed = ((float)(RandomHelper.randomDouble() * (double)0.2F) + 0.8F) * particleData.getRed() * f;
+      this.particleGreen = ((float)(RandomHelper.randomDouble() * (double)0.2F) + 0.8F) * particleData.getGreen() * f;
+      this.particleBlue = ((float)(RandomHelper.randomDouble() * (double)0.2F) + 0.8F) * particleData.getBlue() * f;
       this.particleScale *= 0.75F * particleData.getAlpha();
-      int i = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
+      int i = (int)(8.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
       this.maxAge = (int)Math.max((float)i * particleData.getAlpha(), 1.0F);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/ReversePortalParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/ReversePortalParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/ReversePortalParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/ReversePortalParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -12,3 +13,3 @@
       this.particleScale = (float)((double)this.particleScale * 1.5D);
-      this.maxAge = (int)(Math.random() * 2.0D) + 60;
+      this.maxAge = (int)(RandomHelper.randomDouble() * 2.0D) + 60;
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/SpellParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/SpellParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/SpellParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/SpellParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -3,2 +3,4 @@
 import java.util.Random;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -10,3 +12,3 @@
 public class SpellParticle extends SpriteTexturedParticle {
-   private static final Random RANDOM = new Random();
+   private static final Random RANDOM = new Random(0);
    private final IAnimatedSprite spriteWithAge;
@@ -23,3 +25,3 @@
       this.particleScale *= 0.75F;
-      this.maxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
+      this.maxAge = (int)(8.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
       this.canCollide = false;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/SquidInkParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/SquidInkParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/SquidInkParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/SquidInkParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -15,3 +16,3 @@
       this.setColor(0.0F, 0.0F, 0.0F);
-      this.maxAge = (int)((double)(this.particleScale * 12.0F) / (Math.random() * (double)0.8F + (double)0.2F));
+      this.maxAge = (int)((double)(this.particleScale * 12.0F) / (RandomHelper.randomDouble() * (double)0.8F + (double)0.2F));
       this.selectSpriteWithAge(spriteWithAge);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/SuspendedTownParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/SuspendedTownParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/SuspendedTownParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/SuspendedTownParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -20,3 +21,3 @@
       this.motionZ *= (double)0.02F;
-      this.maxAge = (int)(20.0D / (Math.random() * 0.8D + 0.2D));
+      this.maxAge = (int)(20.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/UnderwaterParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/UnderwaterParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/UnderwaterParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/UnderwaterParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -3,2 +3,4 @@
 import java.util.Random;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -17,3 +19,3 @@
       this.particleScale *= this.rand.nextFloat() * 0.6F + 0.2F;
-      this.maxAge = (int)(16.0D / (Math.random() * 0.8D + 0.2D));
+      this.maxAge = (int)(16.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
       this.canCollide = false;
@@ -25,3 +27,3 @@
       this.particleScale *= this.rand.nextFloat() * 0.6F + 0.6F;
-      this.maxAge = (int)(16.0D / (Math.random() * 0.8D + 0.2D));
+      this.maxAge = (int)(16.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
       this.canCollide = false;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/WaterWakeParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/WaterWakeParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/WaterWakeParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/WaterWakeParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -15,6 +16,6 @@
       this.motionX *= (double)0.3F;
-      this.motionY = Math.random() * (double)0.2F + (double)0.1F;
+      this.motionY = RandomHelper.randomDouble() * (double)0.2F + (double)0.1F;
       this.motionZ *= (double)0.3F;
       this.setSize(0.01F, 0.01F);
-      this.maxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
+      this.maxAge = (int)(8.0D / (RandomHelper.randomDouble() * 0.8D + 0.2D));
       this.selectSpriteWithAge(spriteWithAge);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/WhiteAshParticle.java MCP-Reborn/src/main/java/net/minecraft/client/particle/WhiteAshParticle.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/particle/WhiteAshParticle.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/particle/WhiteAshParticle.java	2024-03-10 18:29:57.573071621 +0800
@@ -3,2 +3,3 @@
 import java.util.Random;
+
 import net.minecraft.client.world.ClientWorld;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/BlockModelRenderer.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/BlockModelRenderer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/BlockModelRenderer.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/BlockModelRenderer.java	2024-03-10 18:29:57.561071402 +0800
@@ -212,3 +212,3 @@
    public void renderModelBrightnessColor(MatrixStack.Entry matrixEntry, IVertexBuilder buffer, @Nullable BlockState state, IBakedModel modelIn, float red, float green, float blue, int combinedLightIn, int combinedOverlayIn) {
-      Random random = new Random();
+      Random random = new Random(0);
       long i = 42L;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/BlockRendererDispatcher.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/BlockRendererDispatcher.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/BlockRendererDispatcher.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/BlockRendererDispatcher.java	2024-03-10 18:29:57.561071402 +0800
@@ -29,3 +29,3 @@
    private final FluidBlockRenderer fluidRenderer;
-   private final Random random = new Random();
+   private final Random random = new Random(0);
    private final BlockColors blockColors;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/chunk/ChunkRenderDispatcher.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/chunk/ChunkRenderDispatcher.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/chunk/ChunkRenderDispatcher.java	2024-03-10 15:44:47.642123400 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/chunk/ChunkRenderDispatcher.java	2024-03-10 18:29:57.561071402 +0800
@@ -462,3 +462,3 @@
                BlockModelRenderer.enableCache();
-               Random random = new Random();
+               Random random = new Random(0);
                BlockRendererDispatcher blockrendererdispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/entity/EndermanRenderer.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/entity/EndermanRenderer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/entity/EndermanRenderer.java	2024-03-10 15:44:47.646123411 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/entity/EndermanRenderer.java	2024-03-10 18:29:57.565071476 +0800
@@ -18,3 +18,3 @@
    private static final ResourceLocation ENDERMAN_TEXTURES = new ResourceLocation("textures/entity/enderman/enderman.png");
-   private final Random rnd = new Random();
+   private final Random rnd = new Random(0);
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/entity/FallingBlockRenderer.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/entity/FallingBlockRenderer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/entity/FallingBlockRenderer.java	2024-03-10 15:44:47.646123411 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/entity/FallingBlockRenderer.java	2024-03-10 18:29:57.565071476 +0800
@@ -35,3 +35,3 @@
             BlockRendererDispatcher blockrendererdispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
-            blockrendererdispatcher.getBlockModelRenderer().renderModel(world, blockrendererdispatcher.getModelForState(blockstate), blockstate, blockpos, matrixStackIn, bufferIn.getBuffer(RenderTypeLookup.func_239221_b_(blockstate)), false, new Random(), blockstate.getPositionRandom(entityIn.getOrigin()), OverlayTexture.NO_OVERLAY);
+            blockrendererdispatcher.getBlockModelRenderer().renderModel(world, blockrendererdispatcher.getModelForState(blockstate), blockstate, blockpos, matrixStackIn, bufferIn.getBuffer(RenderTypeLookup.func_239221_b_(blockstate)), false, new Random(0), blockstate.getPositionRandom(entityIn.getOrigin()), OverlayTexture.NO_OVERLAY);
             matrixStackIn.pop();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/entity/ItemRenderer.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/entity/ItemRenderer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/entity/ItemRenderer.java	2024-03-10 15:44:47.646123411 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/entity/ItemRenderer.java	2024-03-10 18:29:57.565071476 +0800
@@ -22,3 +22,3 @@
    private final net.minecraft.client.renderer.ItemRenderer itemRenderer;
-   private final Random random = new Random();
+   private final Random random = new Random(0);
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/GameRenderer.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/GameRenderer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/GameRenderer.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/GameRenderer.java	2024-03-10 18:29:57.561071402 +0800
@@ -10,2 +10,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.env.FakeMouseCursor;
 import net.minecraft.block.BlockState;
@@ -62,3 +64,3 @@
    private final IResourceManager resourceManager;
-   private final Random random = new Random();
+   private final Random random = new Random(0);
    private float farPlaneDistance;
@@ -489,2 +491,3 @@
                this.mc.currentScreen.render(matrixstack, i, j, this.mc.getTickLength());
+               FakeMouseCursor.getInstance().render(matrixstack, this.mc.currentScreen, i, j);
             } catch (Throwable throwable) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/ItemRenderer.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/ItemRenderer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/ItemRenderer.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/ItemRenderer.java	2024-03-10 18:29:57.561071402 +0800
@@ -77,3 +77,3 @@
    private void renderModel(IBakedModel modelIn, ItemStack stack, int combinedLightIn, int combinedOverlayIn, MatrixStack matrixStackIn, IVertexBuilder bufferIn) {
-      Random random = new Random();
+      Random random = new Random(0);
       long i = 42L;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/LightTexture.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/LightTexture.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/LightTexture.java	2024-03-10 15:44:47.638123390 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/LightTexture.java	2024-03-10 18:29:57.561071402 +0800
@@ -3,2 +3,3 @@
 import com.mojang.blaze3d.systems.RenderSystem;
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.Minecraft;
@@ -45,3 +46,3 @@
    public void tick() {
-      this.torchFlicker = (float)((double)this.torchFlicker + (Math.random() - Math.random()) * Math.random() * Math.random() * 0.1D);
+      this.torchFlicker = (float)((double)this.torchFlicker + (RandomHelper.randomDouble() - RandomHelper.randomDouble()) * RandomHelper.randomDouble() * RandomHelper.randomDouble() * 0.1D);
       this.torchFlicker = (float)((double)this.torchFlicker * 0.9D);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/tileentity/PistonTileEntityRenderer.java MCP-Reborn/src/main/java/net/minecraft/client/renderer/tileentity/PistonTileEntityRenderer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/renderer/tileentity/PistonTileEntityRenderer.java	2024-03-10 15:44:47.658123443 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/renderer/tileentity/PistonTileEntityRenderer.java	2024-03-10 18:29:57.569071549 +0800
@@ -65,3 +65,3 @@
       IVertexBuilder ivertexbuilder = p_228876_4_.getBuffer(rendertype);
-      this.blockRenderer.getBlockModelRenderer().renderModel(p_228876_5_, this.blockRenderer.getModelForState(p_228876_2_), p_228876_2_, p_228876_1_, p_228876_3_, ivertexbuilder, p_228876_6_, new Random(), p_228876_2_.getPositionRandom(p_228876_1_), p_228876_7_);
+      this.blockRenderer.getBlockModelRenderer().renderModel(p_228876_5_, this.blockRenderer.getModelForState(p_228876_2_), p_228876_2_, p_228876_1_, p_228876_3_, ivertexbuilder, p_228876_6_, new Random(0), p_228876_2_.getPositionRandom(p_228876_1_), p_228876_7_);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/ReplaySender.java MCP-Reborn/src/main/java/net/minecraft/client/ReplaySender.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/ReplaySender.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/ReplaySender.java	2024-03-10 18:29:57.557071330 +0800
@@ -0,0 +1,330 @@
+package net.minecraft.client;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.minerl.multiagent.RandomHelper;
+import com.minerl.multiagent.recorder.ZipUtil;
+import net.minecraft.client.gui.screen.DirtMessageScreen;
+import net.minecraft.client.gui.screen.MainMenuScreen;
+import net.minecraft.client.util.InputMappings;
+import net.minecraft.util.text.TranslationTextComponent;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class ReplaySender {
+    private static ReplaySender instance = new ReplaySender();
+    private List<JsonObject> actions;
+    private boolean firstTick = false;
+    private int serverStartTick;
+    private JsonObject lastAction = null;
+
+    public enum Mode {
+        REPLAY_FILE, OFF, EXEC_CMD
+    }
+
+    private Mode mode = Mode.OFF;
+
+    public static ReplaySender getInstance() {
+        return instance;
+    }
+
+    public synchronized void sendFromFile(String recordingFile, int serverStartTick) {
+        // load game settings?
+        // open and load jsonl file
+        if (mode != Mode.OFF) {
+            throw new RuntimeException("ReplaySender is already sending the replay, need to stop it first");
+        }
+        Gson gson = new Gson();
+        try {
+            this.serverStartTick = serverStartTick;
+            actions = Files.readAllLines(new File(recordingFile).toPath(), Charset.defaultCharset())
+                    .stream()
+                    .map(s -> gson.fromJson(s, JsonObject.class))
+                    .filter(j -> j.get("serverTick").getAsInt() >= (serverStartTick - 1))
+                    .collect(Collectors.toList());
+
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        Minecraft mc = Minecraft.getInstance();
+        mc.mouseHelper.setHumanInput(false);
+        mode = Mode.REPLAY_FILE;
+        firstTick = true;
+        // EnvServer.execActions("camera 0 0", 0);
+    }
+
+    public synchronized void sendFromEnv() {
+        if (mode != Mode.OFF) {
+            throw new RuntimeException("ReplaySender is already sending the replay, need to stop it first");
+        }
+        Minecraft mc = Minecraft.getInstance();
+        mc.mouseHelper.setHumanInput(false);
+        actions = new LinkedList<>();
+        mode = Mode.EXEC_CMD;
+        firstTick = true;
+        // EnvServer.execActions("camera 0 0", 0);
+    }
+
+    public void tick() {
+        if (mode == Mode.OFF) {
+            return;
+        }
+        Minecraft mc = Minecraft.getInstance();
+        // PlayRecorder.getInstance().reseedEntities();
+
+        if (mode == Mode.REPLAY_FILE && actions.size() == 0) {
+            stop();
+        }
+        if (!mc.mouseHelper.getHumanInput()) {
+            mc.getProfiler().startSection("waitForAction");
+
+
+            try {
+                synchronized (this) {
+                    while (mode == Mode.EXEC_CMD && actions.size() == 0) {
+                        this.wait();
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+
+            mc.getProfiler().endSection();
+
+            if (firstTick) {
+                onFirstTick();
+                firstTick = false;
+            }
+            mc.getProfiler().startSection("execAction");
+            if (actions.size() > 0) {
+                JsonObject action = actions.remove(0);
+                execAction(action);
+            }
+            mc.getProfiler().endSection();
+        } else {
+            actions.clear();
+        }
+    }
+
+    private void onFirstTick() {
+    }
+
+    public void addAction(JsonObject e) {
+        actions.add(e);
+        synchronized (this) {
+            this.notifyAll();
+        }
+    }
+
+    public void addAction(MouseHelper.State mouseState, KeyboardListener.State keyboardState) {
+        Gson gson = new Gson();
+        JsonObject jo = new JsonObject();
+        jo.add("mouse", gson.toJsonTree(mouseState));
+        jo.add("keyboard", gson.toJsonTree(keyboardState));
+        jo.addProperty("isGuiOpen", false);
+        addAction(jo);
+    }
+
+    public void stop() {
+        if (mode != Mode.OFF) {
+            System.out.println("*** Stopping the replay, returning control to the inputs");
+            Minecraft mc = Minecraft.getInstance();
+            mc.execute( () -> {
+                        mc.mouseHelper.setHumanInput(true);
+                        mc.world.sendQuittingDisconnectingPacket();
+                        mc.unloadWorld(new DirtMessageScreen(new TranslationTextComponent("menu.savingLevel")));
+                        mc.displayGuiScreen(new MainMenuScreen());
+                    });
+            mode = Mode.OFF;
+            synchronized (this) {
+                this.notifyAll();
+            }
+        }
+    }
+
+    private static KeyboardListener.State getKeyboardState(JsonObject action) {
+        Gson gson = new Gson();
+        return gson.fromJson(action.getAsJsonObject("keyboard"), KeyboardListener.State.class);
+    }
+
+    private static MouseHelper.State getMouseState(JsonObject action) {
+        Gson gson = new Gson();
+        return gson.fromJson(action.getAsJsonObject("mouse"), MouseHelper.State.class );
+    }
+
+    private void execAction(JsonObject action) {
+        Minecraft mc = Minecraft.getInstance();
+        MouseHelper.State mouseState = getMouseState(action);
+        KeyboardListener.State keyboardState = getKeyboardState(action);
+        // boolean newIsGuiOpen = action.get("isGuiOpen").getAsBoolean();
+        int mods = getModifiers(keyboardState.keys);
+
+        if (action.get("serverTick") == null || action.get("serverTick").getAsInt() >= serverStartTick) {
+            if (firstTick) {
+                onFirstTick();
+                firstTick = false;
+            }
+            if (mode == Mode.REPLAY_FILE) {
+                checkPlayerPosition();
+                checkPlayerInventory();
+            }
+            execKeyboard(keyboardState);
+            execMouseMove(mouseState);
+            execMouseButtons(mouseState.buttons, mods);
+            execMouseScroll(mouseState.dwheel);
+        }
+        lastAction = action;
+    }
+
+    private static void setPlayerPosition(JsonObject action) {
+        if (action == null) {
+            return;
+        }
+        Minecraft mc = Minecraft.getInstance();
+        mc.player.rotationYaw = action.get("yaw").getAsFloat();
+        mc.player.rotationPitch = action.get("pitch").getAsFloat();
+        mc.player.setPosition(action.get("xpos").getAsDouble(), action.get("ypos").getAsDouble(), action.get("zpos").getAsDouble());
+    }
+
+    private void checkPlayerPosition() {
+        JsonObject action = lastAction;
+        if (action == null) {
+            return;
+        }
+        double maxCoordDiff = 1;
+        double maxAngleDiff = 1;
+        Minecraft mc = Minecraft.getInstance();
+
+        if (Math.abs(mc.player.getPosX() - lastAction.get("xpos").getAsDouble()) > maxCoordDiff ||
+            Math.abs(mc.player.getPosY() - lastAction.get("ypos").getAsDouble()) > maxCoordDiff ||
+            Math.abs(mc.player.getPosZ() - lastAction.get("zpos").getAsDouble()) > maxCoordDiff ||
+            Math.abs(mc.player.rotationYaw - lastAction.get("yaw").getAsDouble()) > maxAngleDiff ||
+            Math.abs(mc.player.rotationPitch - lastAction.get("pitch").getAsDouble()) > maxAngleDiff
+        ) {
+            // stop();
+            System.out.println("position or angle have drifted, should stop the replay");
+        }
+    }
+
+    private void checkPlayerInventory() {
+        return;
+    }
+
+    private int getModifiers(Set<String> keys) {
+        int retval = 0;
+        if (keys.contains("key.keyboard.left.shift") || keys.contains("key.keyboard.right.shift")) {
+            retval |= 1;
+        }
+        return retval;
+    }
+
+    private double clip(double v, double lower, double upper) {
+        return Math.min(Math.max(v, lower), upper);
+    }
+
+    private void execMouseMove(MouseHelper.State mouseState) {
+        Minecraft mc = Minecraft.getInstance();
+        MouseHelper mh = mc.mouseHelper;
+        MainWindow mw = mc.getMainWindow();
+        if (mouseState.dx != 0 || mouseState.dy != 0) {
+            double scaleFactor = mh.getScaleFactor();
+            double newMouseX = mh.getMouseX() + mouseState.dx * scaleFactor;
+            double newMouseY = mh.getMouseY() + mouseState.dy * scaleFactor;
+            if (mc.currentScreen != null) {
+                newMouseX = clip(newMouseX, 0, mw.getWidth());
+                newMouseY = clip(newMouseY, 0, mw.getHeight());
+            }
+            mh.cursorPosCallbackImpl(newMouseX, newMouseY);
+        }
+    }
+
+    private void execMouseButtons(Set<Integer> buttons, int mods) {
+        Minecraft mc = Minecraft.getInstance();
+        Set<Integer> pressedButtons = new HashSet<>();
+        Set<Integer> releasedButtons = new HashSet<>();
+        pressedButtons.addAll(buttons);
+        if (lastAction != null) {
+            pressedButtons.removeAll(getMouseState(lastAction).buttons);
+            releasedButtons.addAll(getMouseState(lastAction).buttons);
+            releasedButtons.removeAll(buttons);
+        }
+        for (int button : pressedButtons) {
+            mc.mouseHelper.mouseButtonCallbackImpl(mc.getMainWindow().getHandle(), button, 1, 0);
+        }
+        for (int button : releasedButtons) {
+            mc.mouseHelper.mouseButtonCallbackImpl(mc.getMainWindow().getHandle(), button, 0, 0);
+        }
+        mc.mouseHelper.getState();
+    }
+
+    private void execKeyboard(KeyboardListener.State state) {
+        Minecraft mc = Minecraft.getInstance();
+        Set<String> newlyHitKeys = new HashSet<>();
+        Set<String> releasedKeys = new HashSet<>();
+        int mods = 0;
+        newlyHitKeys.addAll(state.keys);
+        if (lastAction != null) {
+            newlyHitKeys.removeAll(getKeyboardState(lastAction).keys);
+            releasedKeys.addAll(getKeyboardState(lastAction).keys);
+            releasedKeys.removeAll(state.keys);
+        }
+        for (String key : newlyHitKeys) {
+            InputMappings.Input input = InputMappings.getInputByName(key);
+            if (lastAction != null &&
+                    mc.currentScreen == null &&
+                    key.equals("key.keyboard.escape") && mc.gameSettings.envPort != 0 ) {
+                // prevent from sending "pause" menu command when in the environment
+                continue;
+            }
+            mc.keyboardListener.onKeyEventImpl(input.getKeyCode(), 0, 1, mods);
+        }
+        for (String key : releasedKeys) {
+            InputMappings.Input input = InputMappings.getInputByName(key);
+            mc.keyboardListener.onKeyEventImpl(input.getKeyCode(), 0, 0, mods);
+        }
+        for (Character c : state.chars.toCharArray()) {
+            mc.keyboardListener.onCharEventImpl(c.charValue(), mods);
+        }
+        mc.keyboardListener.getState();
+    }
+
+    private void execMouseScroll(double dwheel) {
+        if (dwheel != 0.0) {
+            Minecraft mc = Minecraft.getInstance();
+            mc.mouseHelper.scrollCallbackImpl(mc.getMainWindow().getHandle(), 0, dwheel);
+        }
+    }
+
+    public Mode getMode() {return mode; }
+
+    public void loadWorldFromZip(String zipFile) {
+        Minecraft mc = Minecraft.getInstance();
+        List<String> zipEntries = ZipUtil.listZip(zipFile);
+        String tmpDir = System.getProperty("java.io.tmpdir");
+        Path saveBasePath = Paths.get(tmpDir, RandomHelper.getRandomHexString());
+        System.out.println("Unzipping " + zipFile + " -> " + saveBasePath);
+        // TODO maybe clean the temp save folder
+        ZipUtil.unzip(zipFile, saveBasePath.toString());
+        // zip folder structure is as follows:
+        // . -> saves -> New World Name -> <actual save data>
+        // thus, the world can be obtaines as below:
+        String saveName = zipEntries.get(0).split("/")[2];
+        // and, after the unzipping, we need to add "saves" to the basePath to
+        // obtain the correct folder
+        mc.loadWorld(saveBasePath.resolve("saves"), saveName);
+    }
+
+    public void loadWorldAndReplay(String prefix) {
+        // TODO handle file that is not at the beginning of the episode (e.g. tick counter of 6003)
+        loadWorldFromZip(prefix + ".zip");
+        sendFromFile(prefix + ".jsonl", 0);
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/settings/KeyBinding.java MCP-Reborn/src/main/java/net/minecraft/client/settings/KeyBinding.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/settings/KeyBinding.java	2024-03-10 15:44:47.662123453 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/settings/KeyBinding.java	2024-03-10 18:29:57.577071694 +0800
@@ -21,10 +21,10 @@
    private static final Set<String> KEYBIND_SET = Sets.newHashSet();
-   private static final Map<String, Integer> CATEGORY_ORDER = Util.make(Maps.newHashMap(), (p_205215_0_) -> {
-      p_205215_0_.put("key.categories.movement", 1);
-      p_205215_0_.put("key.categories.gameplay", 2);
-      p_205215_0_.put("key.categories.inventory", 3);
-      p_205215_0_.put("key.categories.creative", 4);
-      p_205215_0_.put("key.categories.multiplayer", 5);
-      p_205215_0_.put("key.categories.ui", 6);
-      p_205215_0_.put("key.categories.misc", 7);
+   private static final Map<String, Integer> CATEGORY_ORDER = Util.make(Maps.newHashMap(), (map) -> {
+      map.put("key.categories.movement", 1);
+      map.put("key.categories.gameplay", 2);
+      map.put("key.categories.inventory", 3);
+      map.put("key.categories.creative", 4);
+      map.put("key.categories.multiplayer", 5);
+      map.put("key.categories.ui", 6);
+      map.put("key.categories.misc", 7);
    });
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/util/ClientRecipeBook.java MCP-Reborn/src/main/java/net/minecraft/client/util/ClientRecipeBook.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/util/ClientRecipeBook.java	2024-03-10 15:44:47.666123464 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/util/ClientRecipeBook.java	2024-03-10 18:29:57.581071767 +0800
@@ -10,2 +10,3 @@
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
@@ -31,4 +32,4 @@
 
-   public void func_243196_a(Iterable<IRecipe<?>> p_243196_1_) {
-      Map<RecipeBookCategories, List<List<IRecipe<?>>>> map = func_243201_b(p_243196_1_);
+   public void func_243196_a(Iterable<IRecipe<?>> recipes) {
+      Map<RecipeBookCategories, List<List<IRecipe<?>>>> map = categorize(recipes);
       Map<RecipeBookCategories, List<RecipeList>> map1 = Maps.newHashMap();
@@ -47,3 +48,3 @@
 
-   private static Map<RecipeBookCategories, List<List<IRecipe<?>>>> func_243201_b(Iterable<IRecipe<?>> p_243201_0_) {
+   private static Map<RecipeBookCategories, List<List<IRecipe<?>>>> categorize(Iterable<IRecipe<?>> p_243201_0_) {
       Map<RecipeBookCategories, List<List<IRecipe<?>>>> map = Maps.newHashMap();
@@ -70,2 +71,3 @@
                list.add(irecipe);
+               list.sort(Comparator.comparing(r -> r.getRecipeOutput().toString()));
             }
@@ -73,3 +75,10 @@
       }
-
+      for (List<List<IRecipe<?>>> recipeList : map.values()) {
+         for (List<IRecipe<?>> subList : recipeList) {
+            if (subList.size() > 1) {
+               subList.sort(Comparator.comparing(r -> r.getRecipeOutput().getItem().toString()));
+            }
+         }
+         recipeList.sort(Comparator.comparing(r -> r.get(0).getRecipeOutput().getItem().toString()));
+      }
       return map;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/util/InputMappings.java MCP-Reborn/src/main/java/net/minecraft/client/util/InputMappings.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/util/InputMappings.java	2024-03-10 15:44:47.666123464 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/util/InputMappings.java	2024-03-10 18:29:57.581071767 +0800
@@ -64,7 +64,7 @@
 
-   public static void setMouseCallbacks(long p_216503_0_, GLFWCursorPosCallbackI p_216503_2_, GLFWMouseButtonCallbackI p_216503_3_, GLFWScrollCallbackI p_216503_4_, GLFWDropCallbackI p_216503_5_) {
-      GLFW.glfwSetCursorPosCallback(p_216503_0_, p_216503_2_);
-      GLFW.glfwSetMouseButtonCallback(p_216503_0_, p_216503_3_);
-      GLFW.glfwSetScrollCallback(p_216503_0_, p_216503_4_);
-      GLFW.glfwSetDropCallback(p_216503_0_, p_216503_5_);
+   public static void setMouseCallbacks(long handle, GLFWCursorPosCallbackI cursorPosCallback, GLFWMouseButtonCallbackI mouseButtonCallback, GLFWScrollCallbackI scrollCallback, GLFWDropCallbackI dropCallback) {
+      GLFW.glfwSetCursorPosCallback(handle, cursorPosCallback);
+      GLFW.glfwSetMouseButtonCallback(handle, mouseButtonCallback);
+      GLFW.glfwSetScrollCallback(handle, scrollCallback);
+      GLFW.glfwSetDropCallback(handle, dropCallback);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/util/Splashes.java MCP-Reborn/src/main/java/net/minecraft/client/util/Splashes.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/util/Splashes.java	2024-03-10 15:44:47.666123464 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/util/Splashes.java	2024-03-10 18:29:57.581071767 +0800
@@ -28,3 +28,3 @@
    private static final ResourceLocation SPLASHES_LOCATION = new ResourceLocation("texts/splashes.txt");
-   private static final Random RANDOM = new Random();
+   private static final Random RANDOM = new Random(0);
    private final List<String> possibleSplashes = Lists.newArrayList();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/client/world/ClientWorld.java MCP-Reborn/src/main/java/net/minecraft/client/world/ClientWorld.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/client/world/ClientWorld.java	2024-03-10 15:44:47.666123464 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/client/world/ClientWorld.java	2024-03-10 18:29:57.561071402 +0800
@@ -9,5 +9,4 @@
 import it.unimi.dsi.fastutil.objects.ObjectIterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
+
+import java.util.*;
 import java.util.function.BooleanSupplier;
@@ -80,2 +79,3 @@
    private final Int2ObjectMap<Entity> entitiesById = new Int2ObjectOpenHashMap<>();
+   private final Map<UUID, Integer> entitiesByUUID = new HashMap<>();
    private final ClientPlayNetHandler connection;
@@ -289,2 +289,3 @@
       this.entitiesById.put(entityIdIn, entityToSpawn);
+      this.entitiesByUUID.put(entityToSpawn.getUniqueID(), entityIdIn);
       this.getChunkProvider().getChunk(MathHelper.floor(entityToSpawn.getPosX() / 16.0D), MathHelper.floor(entityToSpawn.getPosZ() / 16.0D), ChunkStatus.FULL, true).addEntity(entityToSpawn);
@@ -295,2 +296,3 @@
       if (entity != null) {
+         this.entitiesByUUID.remove(entity.getUniqueID());
          entity.remove();
@@ -327,2 +329,7 @@
 
+   @Nullable
+   public Entity getEntityByUUID(UUID uuid) {
+      return this.entitiesById.get(this.entitiesByUUID.get(uuid));
+   }
+
    public void invalidateRegionAndSetBlock(BlockPos pos, BlockState state) {
@@ -337,3 +344,3 @@
       int i = 32;
-      Random random = new Random();
+      Random random = new Random(0);
       boolean flag = false;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/command/Commands.java MCP-Reborn/src/main/java/net/minecraft/command/Commands.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/command/Commands.java	2024-03-10 15:44:47.666123464 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/command/Commands.java	2024-03-10 18:29:57.593071986 +0800
@@ -169,3 +169,3 @@
 
-      if (envType.field_237220_e_) {
+      if (envType.dedicated) {
          BanIpCommand.register(this.dispatcher);
@@ -185,3 +185,3 @@
 
-      if (envType.field_237219_d_) {
+      if (envType.integrated) {
          PublishCommand.register(this.dispatcher);
@@ -355,8 +355,8 @@
 
-      private final boolean field_237219_d_;
-      private final boolean field_237220_e_;
+      private final boolean integrated;
+      private final boolean dedicated;
 
-      private EnvironmentType(boolean p_i232149_3_, boolean p_i232149_4_) {
-         this.field_237219_d_ = p_i232149_3_;
-         this.field_237220_e_ = p_i232149_4_;
+      private EnvironmentType(boolean integrated, boolean dedicated) {
+         this.integrated = integrated;
+         this.dedicated = dedicated;
       }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/command/impl/DebugCommand.java MCP-Reborn/src/main/java/net/minecraft/command/impl/DebugCommand.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/command/impl/DebugCommand.java	2024-03-10 15:44:47.670123474 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/command/impl/DebugCommand.java	2024-03-10 18:29:57.593071986 +0800
@@ -49,6 +49,6 @@
       MinecraftServer minecraftserver = source.getServer();
-      if (minecraftserver.func_240789_aP_()) {
+      if (minecraftserver.isProfiling()) {
          throw ALREADY_RUNNING_EXCEPTION.create();
       } else {
-         minecraftserver.func_240790_aQ_();
+         minecraftserver.startProfiling();
          source.sendFeedback(new TranslationTextComponent("commands.debug.started", "Started the debug profiler. Type '/debug stop' to stop it."), true);
@@ -60,3 +60,3 @@
       MinecraftServer minecraftserver = source.getServer();
-      if (!minecraftserver.func_240789_aP_()) {
+      if (!minecraftserver.isProfiling()) {
          throw NOT_RUNNING_EXCEPTION.create();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/command/impl/SpreadPlayersCommand.java MCP-Reborn/src/main/java/net/minecraft/command/impl/SpreadPlayersCommand.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/command/impl/SpreadPlayersCommand.java	2024-03-10 15:44:47.674123484 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/command/impl/SpreadPlayersCommand.java	2024-03-10 18:29:57.593071986 +0800
@@ -51,3 +51,3 @@
    private static int func_241070_a_(CommandSource p_241070_0_, Vector2f p_241070_1_, float p_241070_2_, float p_241070_3_, int p_241070_4_, boolean p_241070_5_, Collection<? extends Entity> p_241070_6_) throws CommandSyntaxException {
-      Random random = new Random();
+      Random random = new Random(0);
       double d0 = (double)(p_241070_1_.x - p_241070_3_);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/dispenser/IDispenseItemBehavior.java MCP-Reborn/src/main/java/net/minecraft/dispenser/IDispenseItemBehavior.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/dispenser/IDispenseItemBehavior.java	2024-03-10 15:44:47.682123506 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/dispenser/IDispenseItemBehavior.java	2024-03-10 18:29:57.581071767 +0800
@@ -4,2 +4,4 @@
 import java.util.Random;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.block.AbstractFireBlock;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/ai/brain/sensor/Sensor.java MCP-Reborn/src/main/java/net/minecraft/entity/ai/brain/sensor/Sensor.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/ai/brain/sensor/Sensor.java	2024-03-10 15:44:47.686123516 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/ai/brain/sensor/Sensor.java	2024-03-10 18:29:57.613072350 +0800
@@ -10,3 +10,3 @@
 public abstract class Sensor<E extends LivingEntity> {
-   private static final Random RANDOM = new Random();
+   private static final Random RANDOM = new Random(0);
    private static final EntityPredicate FRIENDLY_FIRE_WITH_VISIBILITY_CHECK = (new EntityPredicate()).setDistance(16.0D).allowFriendlyFire().setSkipAttackChecks();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/ai/RandomPositionGenerator.java MCP-Reborn/src/main/java/net/minecraft/entity/ai/RandomPositionGenerator.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/ai/RandomPositionGenerator.java	2024-03-10 15:44:47.686123516 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/ai/RandomPositionGenerator.java	2024-03-10 18:29:57.609072278 +0800
@@ -18,3 +18,3 @@
    public static Vector3d findRandomTarget(CreatureEntity entitycreatureIn, int xz, int y) {
-      return func_226339_a_(entitycreatureIn, xz, y, 0, (Vector3d)null, true, (double)((float)Math.PI / 2F), entitycreatureIn::getBlockPathWeight, false, 0, 0, true);
+      return findTarget(entitycreatureIn, xz, y, 0, (Vector3d)null, true, (double)((float)Math.PI / 2F), entitycreatureIn::getBlockPathWeight, false, 0, 0, true);
    }
@@ -23,3 +23,3 @@
    public static Vector3d findGroundTarget(CreatureEntity p_226338_0_, int p_226338_1_, int p_226338_2_, int p_226338_3_, @Nullable Vector3d p_226338_4_, double p_226338_5_) {
-      return func_226339_a_(p_226338_0_, p_226338_1_, p_226338_2_, p_226338_3_, p_226338_4_, true, p_226338_5_, p_226338_0_::getBlockPathWeight, true, 0, 0, false);
+      return findTarget(p_226338_0_, p_226338_1_, p_226338_2_, p_226338_3_, p_226338_4_, true, p_226338_5_, p_226338_0_::getBlockPathWeight, true, 0, 0, false);
    }
@@ -33,3 +33,3 @@
    public static Vector3d func_221024_a(CreatureEntity p_221024_0_, int p_221024_1_, int p_221024_2_, ToDoubleFunction<BlockPos> p_221024_3_) {
-      return func_226339_a_(p_221024_0_, p_221024_1_, p_221024_2_, 0, (Vector3d)null, false, 0.0D, p_221024_3_, true, 0, 0, true);
+      return findTarget(p_221024_0_, p_221024_1_, p_221024_2_, 0, (Vector3d)null, false, 0.0D, p_221024_3_, true, 0, 0, true);
    }
@@ -38,3 +38,3 @@
    public static Vector3d findAirTarget(CreatureEntity p_226340_0_, int p_226340_1_, int p_226340_2_, Vector3d p_226340_3_, float p_226340_4_, int p_226340_5_, int p_226340_6_) {
-      return func_226339_a_(p_226340_0_, p_226340_1_, p_226340_2_, 0, p_226340_3_, false, (double)p_226340_4_, p_226340_0_::getBlockPathWeight, true, p_226340_5_, p_226340_6_, true);
+      return findTarget(p_226340_0_, p_226340_1_, p_226340_2_, 0, p_226340_3_, false, (double)p_226340_4_, p_226340_0_::getBlockPathWeight, true, p_226340_5_, p_226340_6_, true);
    }
@@ -44,3 +44,3 @@
       Vector3d vector3d = p_234133_3_.subtract(p_234133_0_.getPosX(), p_234133_0_.getPosY(), p_234133_0_.getPosZ());
-      return func_226339_a_(p_234133_0_, p_234133_1_, p_234133_2_, 0, vector3d, false, (double)((float)Math.PI / 2F), p_234133_0_::getBlockPathWeight, true, 0, 0, true);
+      return findTarget(p_234133_0_, p_234133_1_, p_234133_2_, 0, vector3d, false, (double)((float)Math.PI / 2F), p_234133_0_::getBlockPathWeight, true, 0, 0, true);
    }
@@ -50,3 +50,3 @@
       Vector3d vector3d = targetVec3.subtract(entitycreatureIn.getPosX(), entitycreatureIn.getPosY(), entitycreatureIn.getPosZ());
-      return func_226339_a_(entitycreatureIn, xz, y, 0, vector3d, true, (double)((float)Math.PI / 2F), entitycreatureIn::getBlockPathWeight, false, 0, 0, true);
+      return findTarget(entitycreatureIn, xz, y, 0, vector3d, true, (double)((float)Math.PI / 2F), entitycreatureIn::getBlockPathWeight, false, 0, 0, true);
    }
@@ -56,3 +56,3 @@
       Vector3d vector3d = p_203155_3_.subtract(p_203155_0_.getPosX(), p_203155_0_.getPosY(), p_203155_0_.getPosZ());
-      return func_226339_a_(p_203155_0_, xz, p_203155_2_, 0, vector3d, true, p_203155_4_, p_203155_0_::getBlockPathWeight, false, 0, 0, true);
+      return findTarget(p_203155_0_, xz, p_203155_2_, 0, vector3d, true, p_203155_4_, p_203155_0_::getBlockPathWeight, false, 0, 0, true);
    }
@@ -62,3 +62,3 @@
       Vector3d vector3d = p_226344_4_.subtract(p_226344_0_.getPosX(), p_226344_0_.getPosY(), p_226344_0_.getPosZ());
-      return func_226339_a_(p_226344_0_, p_226344_1_, p_226344_2_, p_226344_3_, vector3d, false, p_226344_5_, p_226344_0_::getBlockPathWeight, true, 0, 0, false);
+      return findTarget(p_226344_0_, p_226344_1_, p_226344_2_, p_226344_3_, vector3d, false, p_226344_5_, p_226344_0_::getBlockPathWeight, true, 0, 0, false);
    }
@@ -68,3 +68,3 @@
       Vector3d vector3d = entitycreatureIn.getPositionVec().subtract(targetVec3);
-      return func_226339_a_(entitycreatureIn, xz, y, 0, vector3d, true, (double)((float)Math.PI / 2F), entitycreatureIn::getBlockPathWeight, false, 0, 0, true);
+      return findTarget(entitycreatureIn, xz, y, 0, vector3d, true, (double)((float)Math.PI / 2F), entitycreatureIn::getBlockPathWeight, false, 0, 0, true);
    }
@@ -74,3 +74,3 @@
       Vector3d vector3d = p_223548_0_.getPositionVec().subtract(p_223548_3_);
-      return func_226339_a_(p_223548_0_, p_223548_1_, p_223548_2_, 0, vector3d, false, (double)((float)Math.PI / 2F), p_223548_0_::getBlockPathWeight, true, 0, 0, true);
+      return findTarget(p_223548_0_, p_223548_1_, p_223548_2_, 0, vector3d, false, (double)((float)Math.PI / 2F), p_223548_0_::getBlockPathWeight, true, 0, 0, true);
    }
@@ -78,8 +78,10 @@
    @Nullable
-   private static Vector3d func_226339_a_(CreatureEntity p_226339_0_, int p_226339_1_, int p_226339_2_, int p_226339_3_, @Nullable Vector3d p_226339_4_, boolean p_226339_5_, double p_226339_6_, ToDoubleFunction<BlockPos> p_226339_8_, boolean p_226339_9_, int p_226339_10_, int p_226339_11_, boolean p_226339_12_) {
-      PathNavigator pathnavigator = p_226339_0_.getNavigator();
-      Random random = p_226339_0_.getRNG();
+   private static Vector3d findTarget(CreatureEntity creatureEntity, int p_226339_1_, int p_226339_2_, int p_226339_3_, @Nullable Vector3d p_226339_4_, boolean p_226339_5_, double p_226339_6_, ToDoubleFunction<BlockPos> p_226339_8_, boolean p_226339_9_, int p_226339_10_, int p_226339_11_, boolean p_226339_12_) {
+      PathNavigator pathnavigator = creatureEntity.getNavigator();
+      // ??? what is a good way to seed this random? creatureEntity.getRNG() does not
+      // seem to work
+      Random random = new Random(0);
       boolean flag;
-      if (p_226339_0_.detachHome()) {
-         flag = p_226339_0_.getHomePosition().withinDistance(p_226339_0_.getPositionVec(), (double)(p_226339_0_.getMaximumHomeDistance() + (float)p_226339_1_) + 1.0D);
+      if (creatureEntity.detachHome()) {
+         flag = creatureEntity.getHomePosition().withinDistance(creatureEntity.getPositionVec(), (double)(creatureEntity.getMaximumHomeDistance() + (float)p_226339_1_) + 1.0D);
       } else {
@@ -90,3 +92,3 @@
       double d0 = Double.NEGATIVE_INFINITY;
-      BlockPos blockpos = p_226339_0_.getPosition();
+      BlockPos blockpos = creatureEntity.getPosition();
 
@@ -98,5 +100,5 @@
             int l = blockpos1.getZ();
-            if (p_226339_0_.detachHome() && p_226339_1_ > 1) {
-               BlockPos blockpos2 = p_226339_0_.getHomePosition();
-               if (p_226339_0_.getPosX() > (double)blockpos2.getX()) {
+            if (creatureEntity.detachHome() && p_226339_1_ > 1) {
+               BlockPos blockpos2 = creatureEntity.getHomePosition();
+               if (creatureEntity.getPosX() > (double)blockpos2.getX()) {
                   j -= random.nextInt(p_226339_1_ / 2);
@@ -106,3 +108,3 @@
 
-               if (p_226339_0_.getPosZ() > (double)blockpos2.getZ()) {
+               if (creatureEntity.getPosZ() > (double)blockpos2.getZ()) {
                   l -= random.nextInt(p_226339_1_ / 2);
@@ -113,7 +115,7 @@
 
-            BlockPos blockpos3 = new BlockPos((double)j + p_226339_0_.getPosX(), (double)k + p_226339_0_.getPosY(), (double)l + p_226339_0_.getPosZ());
-            if (blockpos3.getY() >= 0 && blockpos3.getY() <= p_226339_0_.world.getHeight() && (!flag || p_226339_0_.isWithinHomeDistanceFromPosition(blockpos3)) && (!p_226339_12_ || pathnavigator.canEntityStandOnPos(blockpos3))) {
+            BlockPos blockpos3 = new BlockPos((double)j + creatureEntity.getPosX(), (double)k + creatureEntity.getPosY(), (double)l + creatureEntity.getPosZ());
+            if (blockpos3.getY() >= 0 && blockpos3.getY() <= creatureEntity.world.getHeight() && (!flag || creatureEntity.isWithinHomeDistanceFromPosition(blockpos3)) && (!p_226339_12_ || pathnavigator.canEntityStandOnPos(blockpos3))) {
                if (p_226339_9_) {
-                  blockpos3 = func_226342_a_(blockpos3, random.nextInt(p_226339_10_ + 1) + p_226339_11_, p_226339_0_.world.getHeight(), (p_226341_1_) -> {
-                     return p_226339_0_.world.getBlockState(p_226341_1_).getMaterial().isSolid();
+                  blockpos3 = func_226342_a_(blockpos3, random.nextInt(p_226339_10_ + 1) + p_226339_11_, creatureEntity.world.getHeight(), (p_226341_1_) -> {
+                     return creatureEntity.world.getBlockState(p_226341_1_).getMaterial().isSolid();
                   });
@@ -121,5 +123,5 @@
 
-               if (p_226339_5_ || !p_226339_0_.world.getFluidState(blockpos3).isTagged(FluidTags.WATER)) {
-                  PathNodeType pathnodetype = WalkNodeProcessor.func_237231_a_(p_226339_0_.world, blockpos3.toMutable());
-                  if (p_226339_0_.getPathPriority(pathnodetype) == 0.0F) {
+               if (p_226339_5_ || !creatureEntity.world.getFluidState(blockpos3).isTagged(FluidTags.WATER)) {
+                  PathNodeType pathnodetype = WalkNodeProcessor.func_237231_a_(creatureEntity.world, blockpos3.toMutable());
+                  if (creatureEntity.getPathPriority(pathnodetype) == 0.0F) {
                      double d1 = p_226339_8_.applyAsDouble(blockpos3);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/Entity.java MCP-Reborn/src/main/java/net/minecraft/entity/Entity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/Entity.java	2024-03-10 15:44:47.682123506 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/Entity.java	2024-03-10 18:29:57.605072205 +0800
@@ -5,2 +5,3 @@
 import com.google.common.collect.Sets;
+import com.minerl.multiagent.RandomHelper;
 import it.unimi.dsi.fastutil.objects.Object2DoubleArrayMap;
@@ -155,3 +156,3 @@
    public float entityCollisionReduction;
-   protected final Random rand = new Random();
+   protected Random rand = new Random(0);
    public int ticksExisted;
@@ -186,3 +187,3 @@
    private boolean invulnerable;
-   protected UUID entityUniqueID = MathHelper.getRandomUUID(this.rand);
+   protected UUID entityUniqueID = MathHelper.getRandomUUID(new Random());
    protected String cachedUniqueIdString = this.entityUniqueID.toString();
@@ -2813,2 +2814,6 @@
    }
+
+   public void setRandomSeed(long seed) {
+      this.rand.setSeed(seed);
+   }
 }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/item/ItemEntity.java MCP-Reborn/src/main/java/net/minecraft/entity/item/ItemEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/item/ItemEntity.java	2024-03-10 15:44:47.698123548 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/item/ItemEntity.java	2024-03-10 18:29:57.617072423 +0800
@@ -5,2 +5,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.entity.Entity;
@@ -43,3 +45,3 @@
       super(p_i50217_1_, world);
-      this.hoverStart = (float)(Math.random() * Math.PI * 2.0D);
+      this.hoverStart = (float)(RandomHelper.randomDouble() * Math.PI * 2.0D);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/LivingEntity.java MCP-Reborn/src/main/java/net/minecraft/entity/LivingEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/LivingEntity.java	2024-03-10 15:44:47.686123516 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/LivingEntity.java	2024-03-10 18:29:57.605072205 +0800
@@ -20,2 +20,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.advancements.CriteriaTriggers;
@@ -149,4 +151,2 @@
    public final int maxHurtResistantTime = 20;
-   public final float randomUnused2;
-   public final float randomUnused1;
    public float renderYawOffset;
@@ -206,6 +206,4 @@
       this.preventEntitySpawning = true;
-      this.randomUnused1 = (float)((Math.random() + 1.0D) * (double)0.01F);
       this.recenterBoundingBox();
-      this.randomUnused2 = (float)Math.random() * 12398.0F;
-      this.rotationYaw = (float)(Math.random() * (double)((float)Math.PI * 2F));
+      this.rotationYaw = (float) 0.0; // (float)(RandomHelper.nextDouble() * (double)((float)Math.PI * 2F));
       this.rotationYawHead = this.rotationYaw;
@@ -214,2 +212,3 @@
       this.brain = this.createBrain(new Dynamic<>(nbtdynamicops, nbtdynamicops.createMap(ImmutableMap.of(nbtdynamicops.createString("memories"), nbtdynamicops.emptyMap()))));
+      // System.out.println(type.toString());
    }
@@ -292,2 +291,3 @@
       super.baseTick();
+      this.rand.setSeed(getPosition().toLong() + world.getGameTime());
       this.world.getProfiler().startSection("livingEntityBaseTick");
@@ -1025,4 +1025,4 @@
                double d0;
-               for(d0 = entity1.getPosZ() - this.getPosZ(); d1 * d1 + d0 * d0 < 1.0E-4D; d0 = (Math.random() - Math.random()) * 0.01D) {
-                  d1 = (Math.random() - Math.random()) * 0.01D;
+               for(d0 = entity1.getPosZ() - this.getPosZ(); d1 * d1 + d0 * d0 < 1.0E-4D; d0 = (RandomHelper.nextDouble("attackEntityFrom") - RandomHelper.nextDouble("attackEntityFrom")) * 0.01D) {
+                  d1 = (RandomHelper.nextDouble("attackEntityFrom") - RandomHelper.nextDouble("attackEntityFrom")) * 0.01D;
                }
@@ -1032,3 +1032,3 @@
             } else {
-               this.attackedAtYaw = (float)((int)(Math.random() * 2.0D) * 180);
+               this.attackedAtYaw = (float)((int)(RandomHelper.nextDouble("attackEntityFrom") * 2.0D) * 180);
             }
@@ -1869,3 +1869,3 @@
 
-   public void travel(Vector3d travelVector) {
+      public void travel(Vector3d travelVector) {
       if (this.isServerWorld() || this.canPassengerSteer()) {
@@ -2772,3 +2772,3 @@
       for(int i = 0; i < count; ++i) {
-         Vector3d vector3d = new Vector3d(((double)this.rand.nextFloat() - 0.5D) * 0.1D, Math.random() * 0.1D + 0.1D, 0.0D);
+         Vector3d vector3d = new Vector3d(((double)this.rand.nextFloat() - 0.5D) * 0.1D, RandomHelper.nextDouble("itemParticles") * 0.1D + 0.1D, 0.0D);
          vector3d = vector3d.rotatePitch(-this.rotationPitch * ((float)Math.PI / 180F));
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/passive/FoxEntity.java MCP-Reborn/src/main/java/net/minecraft/entity/passive/FoxEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/passive/FoxEntity.java	2024-03-10 15:44:47.706123569 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/passive/FoxEntity.java	2024-03-10 18:29:57.609072278 +0800
@@ -13,2 +13,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.advancements.CriteriaTriggers;
@@ -251,3 +253,3 @@
             for(int i = 0; i < 8; ++i) {
-               Vector3d vector3d = (new Vector3d(((double)this.rand.nextFloat() - 0.5D) * 0.1D, Math.random() * 0.1D + 0.1D, 0.0D)).rotatePitch(-this.rotationPitch * ((float)Math.PI / 180F)).rotateYaw(-this.rotationYaw * ((float)Math.PI / 180F));
+               Vector3d vector3d = (new Vector3d(((double)this.rand.nextFloat() - 0.5D) * 0.1D, RandomHelper.randomDouble() * 0.1D + 0.1D, 0.0D)).rotatePitch(-this.rotationPitch * ((float)Math.PI / 180F)).rotateYaw(-this.rotationYaw * ((float)Math.PI / 180F));
                this.world.addParticle(new ItemParticleData(ParticleTypes.ITEM, itemstack), this.getPosX() + this.getLookVec().x / 2.0D, this.getPosY(), this.getPosZ() + this.getLookVec().z / 2.0D, vector3d.x, vector3d.y + 0.05D, vector3d.z);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/passive/PandaEntity.java MCP-Reborn/src/main/java/net/minecraft/entity/passive/PandaEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/passive/PandaEntity.java	2024-03-10 15:44:47.706123569 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/passive/PandaEntity.java	2024-03-10 18:29:57.609072278 +0800
@@ -9,2 +9,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.block.BlockState;
@@ -396,3 +398,3 @@
          for(int i = 0; i < 6; ++i) {
-            Vector3d vector3d = new Vector3d(((double)this.rand.nextFloat() - 0.5D) * 0.1D, Math.random() * 0.1D + 0.1D, ((double)this.rand.nextFloat() - 0.5D) * 0.1D);
+            Vector3d vector3d = new Vector3d(((double)this.rand.nextFloat() - 0.5D) * 0.1D, RandomHelper.randomDouble() * 0.1D + 0.1D, ((double)this.rand.nextFloat() - 0.5D) * 0.1D);
             vector3d = vector3d.rotatePitch(-this.rotationPitch * ((float)Math.PI / 180F));
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/player/ServerPlayerEntity.java MCP-Reborn/src/main/java/net/minecraft/entity/player/ServerPlayerEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/player/ServerPlayerEntity.java	2024-03-10 15:44:47.710123581 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/player/ServerPlayerEntity.java	2024-03-10 18:29:57.617072423 +0800
@@ -206,3 +206,3 @@
          int j1 = this.func_205735_q(i1);
-         int k1 = (new Random()).nextInt(i1);
+         int k1 = (new Random(0)).nextInt(i1);
 
@@ -927,6 +927,3 @@
       if (!(containerToSend.getSlot(slotInd) instanceof CraftingResultSlot)) {
-         if (containerToSend == this.container) {
-            CriteriaTriggers.INVENTORY_CHANGED.test(this, this.inventory, stack);
-         }
-
+         CriteriaTriggers.INVENTORY_CHANGED.test(this, this.inventory, stack);
          if (!this.isChangingQuantityOnly) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/projectile/FishingBobberEntity.java MCP-Reborn/src/main/java/net/minecraft/entity/projectile/FishingBobberEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/entity/projectile/FishingBobberEntity.java	2024-03-10 15:44:47.710123581 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/entity/projectile/FishingBobberEntity.java	2024-03-10 18:29:57.605072205 +0800
@@ -47,3 +47,3 @@
 public class FishingBobberEntity extends ProjectileEntity {
-   private final Random field_234596_b_ = new Random();
+   private final Random field_234596_b_ = new Random(0);
    private boolean field_234597_c_;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/inventory/container/EnchantmentContainer.java MCP-Reborn/src/main/java/net/minecraft/inventory/container/EnchantmentContainer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/inventory/container/EnchantmentContainer.java	2024-03-10 15:44:47.714123591 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/inventory/container/EnchantmentContainer.java	2024-03-10 18:29:57.589071913 +0800
@@ -34,3 +34,3 @@
    private final IWorldPosCallable worldPosCallable;
-   private final Random rand = new Random();
+   private final Random rand = new Random(0);
    private final IntReferenceHolder xpSeed = IntReferenceHolder.single();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/inventory/InventoryHelper.java MCP-Reborn/src/main/java/net/minecraft/inventory/InventoryHelper.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/inventory/InventoryHelper.java	2024-03-10 15:44:47.714123591 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/inventory/InventoryHelper.java	2024-03-10 18:29:57.589071913 +0800
@@ -12,3 +12,3 @@
 public class InventoryHelper {
-   private static final Random RANDOM = new Random();
+   private static final Random RANDOM = new Random(0);
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/item/BucketItem.java MCP-Reborn/src/main/java/net/minecraft/item/BucketItem.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/item/BucketItem.java	2024-03-10 15:44:47.718123601 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/item/BucketItem.java	2024-03-10 18:29:57.625072569 +0800
@@ -3,2 +3,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.advancements.CriteriaTriggers;
@@ -115,3 +117,3 @@
             for(int l = 0; l < 8; ++l) {
-               worldIn.addParticle(ParticleTypes.LARGE_SMOKE, (double)i + Math.random(), (double)j + Math.random(), (double)k + Math.random(), 0.0D, 0.0D, 0.0D);
+               worldIn.addParticle(ParticleTypes.LARGE_SMOKE, (double)i + RandomHelper.randomDouble(), (double)j + RandomHelper.randomDouble(), (double)k + RandomHelper.randomDouble(), 0.0D, 0.0D, 0.0D);
             }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/item/Item.java MCP-Reborn/src/main/java/net/minecraft/item/Item.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/item/Item.java	2024-03-10 15:44:47.718123601 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/item/Item.java	2024-03-10 18:29:57.625072569 +0800
@@ -46,3 +46,3 @@
    protected static final UUID ATTACK_SPEED_MODIFIER = UUID.fromString("FA233E1C-4180-4865-B01B-BCCE9785ACA3");
-   protected static final Random random = new Random();
+   protected static final Random random = new Random(0);
    protected final ItemGroup group;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/item/ItemModelsProperties.java MCP-Reborn/src/main/java/net/minecraft/item/ItemModelsProperties.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/item/ItemModelsProperties.java	2024-03-10 15:44:47.718123601 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/item/ItemModelsProperties.java	2024-03-10 18:29:57.625072569 +0800
@@ -6,2 +6,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.client.world.ClientWorld;
@@ -111,3 +113,3 @@
                   } else {
-                     d0 = Math.random();
+                     d0 = RandomHelper.randomDouble();
                   }
@@ -177,3 +179,3 @@
                   if (this.field_239440_b_.func_239448_a_(i)) {
-                     this.field_239440_b_.func_239449_a_(i, Math.random());
+                     this.field_239440_b_.func_239449_a_(i, RandomHelper.randomDouble());
                   }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/loot/LootContext.java MCP-Reborn/src/main/java/net/minecraft/loot/LootContext.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/loot/LootContext.java	2024-03-10 15:44:47.726123623 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/loot/LootContext.java	2024-03-10 18:29:57.589071913 +0800
@@ -188,3 +188,3 @@
                if (random == null) {
-                  random = new Random();
+                  random = new Random(0);
                }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/network/login/ServerLoginNetHandler.java MCP-Reborn/src/main/java/net/minecraft/network/login/ServerLoginNetHandler.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/network/login/ServerLoginNetHandler.java	2024-03-10 15:44:47.730123633 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/network/login/ServerLoginNetHandler.java	2024-03-10 18:29:57.633072715 +0800
@@ -39,3 +39,3 @@
    private static final Logger LOGGER = LogManager.getLogger();
-   private static final Random RANDOM = new Random();
+   private static final Random RANDOM = new Random(0);
    private final byte[] verifyToken = new byte[4];
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/network/NetworkManager.java MCP-Reborn/src/main/java/net/minecraft/network/NetworkManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/network/NetworkManager.java	2024-03-10 15:44:47.730123633 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/network/NetworkManager.java	2024-03-10 18:29:57.629072642 +0800
@@ -158,3 +158,3 @@
    public void sendPacket(IPacket<?> packetIn, @Nullable GenericFutureListener<? extends Future<? super Void>> p_201058_2_) {
-      if (this.isChannelOpen()) {
+       if (this.isChannelOpen()) {
          this.flushOutboundQueue();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/network/rcon/QueryThread.java MCP-Reborn/src/main/java/net/minecraft/network/rcon/QueryThread.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/network/rcon/QueryThread.java	2024-03-10 15:44:47.738123654 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/network/rcon/QueryThread.java	2024-03-10 18:29:57.633072715 +0800
@@ -278,3 +278,3 @@
          this.requestIdAsString = new String(this.requestId, StandardCharsets.UTF_8);
-         this.randomChallenge = (new Random()).nextInt(16777216);
+         this.randomChallenge = (new Random(0)).nextInt(16777216);
          this.challengeValue = String.format("\t%s%d\u0000", this.requestIdAsString, this.randomChallenge).getBytes(StandardCharsets.UTF_8);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/profiler/LongTickDetector.java MCP-Reborn/src/main/java/net/minecraft/profiler/LongTickDetector.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/profiler/LongTickDetector.java	2024-03-10 15:44:47.742123666 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/profiler/LongTickDetector.java	2024-03-10 18:29:57.625072569 +0800
@@ -14,12 +14,12 @@
    private final long field_233518_c_ = 0L;
-   private int field_233519_d_;
+   private int tickCounter;
    private final File field_233520_e_ = null;
-   private IResultableProfiler field_233521_f_;
+   private IResultableProfiler profiler;
 
-   public IProfiler func_233522_a_() {
-      this.field_233521_f_ = new Profiler(this.field_233517_b_, () -> {
-         return this.field_233519_d_;
+   public IProfiler getProfiler() {
+      this.profiler = new Profiler(this.field_233517_b_, () -> {
+         return this.tickCounter;
       }, false);
-      ++this.field_233519_d_;
-      return this.field_233521_f_;
+      ++this.tickCounter;
+      return this.profiler;
    }
@@ -27,5 +27,5 @@
    public void func_233525_b_() {
-      if (this.field_233521_f_ != EmptyProfiler.INSTANCE) {
-         IProfileResult iprofileresult = this.field_233521_f_.getResults();
-         this.field_233521_f_ = EmptyProfiler.INSTANCE;
+      if (this.profiler != EmptyProfiler.INSTANCE) {
+         IProfileResult iprofileresult = this.profiler.getResults();
+         this.profiler = EmptyProfiler.INSTANCE;
          if (iprofileresult.nanoTime() >= this.field_233518_c_) {
@@ -44,4 +44,4 @@
 
-   public static IProfiler func_233523_a_(IProfiler p_233523_0_, @Nullable LongTickDetector p_233523_1_) {
-      return p_233523_1_ != null ? IProfiler.func_233513_a_(p_233523_1_.func_233522_a_(), p_233523_0_) : p_233523_0_;
+   public static IProfiler getProfiler(IProfiler p_233523_0_, @Nullable LongTickDetector p_233523_1_) {
+      return p_233523_1_ != null ? IProfiler.func_233513_a_(p_233523_1_.getProfiler(), p_233523_0_) : p_233523_0_;
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/profiler/Profiler.java MCP-Reborn/src/main/java/net/minecraft/profiler/Profiler.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/profiler/Profiler.java	2024-03-10 15:44:47.742123666 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/profiler/Profiler.java	2024-03-10 18:29:57.625072569 +0800
@@ -36,10 +36,10 @@
    private Profiler.Section field_230079_k_;
-   private final boolean field_226230_l_;
+   private final boolean warnSlow;
 
-   public Profiler(LongSupplier p_i231482_1_, IntSupplier p_i231482_2_, boolean p_i231482_3_) {
-      this.startTime = p_i231482_1_.getAsLong();
-      this.field_233501_g_ = p_i231482_1_;
-      this.startTicks = p_i231482_2_.getAsInt();
-      this.currentTicks = p_i231482_2_;
-      this.field_226230_l_ = p_i231482_3_;
+   public Profiler(LongSupplier nanoTimeSupplier, IntSupplier tickSupplier, boolean warnSlow) {
+      this.startTime = nanoTimeSupplier.getAsLong();
+      this.field_233501_g_ = nanoTimeSupplier;
+      this.startTicks = tickSupplier.getAsInt();
+      this.currentTicks = tickSupplier;
+      this.warnSlow = warnSlow;
    }
@@ -104,3 +104,3 @@
          profiler$section.field_230083_b_ = profiler$section.field_230083_b_ + 1L;
-         if (this.field_226230_l_ && k > WARN_TIME_THRESHOLD) {
+         if (this.warnSlow && k > WARN_TIME_THRESHOLD) {
             LOGGER.warn("Something's taking too long! '{}' took aprox {} ms", () -> {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/profiler/TimeTracker.java MCP-Reborn/src/main/java/net/minecraft/profiler/TimeTracker.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/profiler/TimeTracker.java	2024-03-10 15:44:47.742123666 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/profiler/TimeTracker.java	2024-03-10 18:29:57.625072569 +0800
@@ -6,29 +6,29 @@
 public class TimeTracker {
-   private final LongSupplier field_233502_a_;
-   private final IntSupplier field_233503_b_;
-   private IResultableProfiler field_233504_c_ = EmptyProfiler.INSTANCE;
+   private final LongSupplier nanoTimeSupplier;
+   private final IntSupplier tickSupplier;
+   private IResultableProfiler profiler = EmptyProfiler.INSTANCE;
 
-   public TimeTracker(LongSupplier p_i231483_1_, IntSupplier p_i231483_2_) {
-      this.field_233502_a_ = p_i231483_1_;
-      this.field_233503_b_ = p_i231483_2_;
+   public TimeTracker(LongSupplier nanoTimeSupplier, IntSupplier tickSupplier) {
+      this.nanoTimeSupplier = nanoTimeSupplier;
+      this.tickSupplier = tickSupplier;
    }
 
-   public boolean func_233505_a_() {
-      return this.field_233504_c_ != EmptyProfiler.INSTANCE;
+   public boolean isTracking() {
+      return this.profiler != EmptyProfiler.INSTANCE;
    }
 
-   public void func_233506_b_() {
-      this.field_233504_c_ = EmptyProfiler.INSTANCE;
+   public void stopTracking() {
+      this.profiler = EmptyProfiler.INSTANCE;
    }
 
-   public void func_233507_c_() {
-      this.field_233504_c_ = new Profiler(this.field_233502_a_, this.field_233503_b_, true);
+   public void startTracking() {
+      this.profiler = new Profiler(this.nanoTimeSupplier, this.tickSupplier, true);
    }
 
-   public IProfiler func_233508_d_() {
-      return this.field_233504_c_;
+   public IProfiler getProfiler() {
+      return this.profiler;
    }
 
-   public IProfileResult func_233509_e_() {
-      return this.field_233504_c_.getResults();
+   public IProfileResult getResults() {
+      return this.profiler.getResults();
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/resources/FallbackResourceManager.java MCP-Reborn/src/main/java/net/minecraft/resources/FallbackResourceManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/resources/FallbackResourceManager.java	2024-03-10 15:44:47.746123676 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/resources/FallbackResourceManager.java	2024-03-10 18:29:57.585071840 +0800
@@ -31,2 +31,3 @@
       this.namespace = p_i226096_2_;
+      this.resourcePacks.add(new JavaResourcePack());
    }
@@ -47,3 +48,3 @@
 
-      for(int i = this.resourcePacks.size() - 1; i >= 0; --i) {
+      for(int i = 0; i < resourcePacks.size(); i++) {
          IResourcePack iresourcepack1 = this.resourcePacks.get(i);
@@ -104,3 +105,3 @@
          if (iresourcepack.resourceExists(this.type, resourceLocationIn)) {
-            InputStream inputstream = iresourcepack.resourceExists(this.type, resourcelocation) ? this.getInputStream(resourcelocation, iresourcepack) : null;
+            InputStream inputstream = iresourcepack.resourceExists(this.type, resourcelocation) ? this.getInputStream(resourcelocation, iresourcepack) : this.getInputStream(resourceLocationIn, iresourcepack);
             list.add(new SimpleResource(iresourcepack.getName(), resourceLocationIn, this.getInputStream(resourceLocationIn, iresourcepack), inputstream));
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/resources/JavaResourcePack.java MCP-Reborn/src/main/java/net/minecraft/resources/JavaResourcePack.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/resources/JavaResourcePack.java	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/resources/JavaResourcePack.java	2024-03-10 18:29:57.585071840 +0800
@@ -0,0 +1,88 @@
+package net.minecraft.resources;
+
+import net.minecraft.resources.data.IMetadataSectionSerializer;
+import net.minecraft.util.ResourceLocation;
+import org.apache.commons.io.IOUtils;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+public class JavaResourcePack implements IResourcePack {
+    private Set<String> resources;
+    private Set<String> namespaces;
+    private final String index = "index.txt";
+    public JavaResourcePack() {
+        try {
+            resources = IOUtils.readLines(this.getClass().getClassLoader().getResourceAsStream(index)).stream().collect(Collectors.toSet());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        namespaces = resources.stream().map(s -> getResourceLocation(s).getNamespace()).collect(Collectors.toSet());
+    }
+
+    @Override
+    public InputStream getRootResourceStream(String fileName) throws IOException {
+        return null;
+    }
+
+    @Override
+    public InputStream getResourceStream(ResourcePackType type, ResourceLocation location) throws IOException {
+        return this.getClass().getClassLoader().getResourceAsStream(pathjoin(location.getNamespace(), location.getPath()));
+    }
+
+    @Override
+    public Collection<ResourceLocation> getAllResourceLocations(ResourcePackType type, String namespaceIn, String pathIn, int maxDepthIn, Predicate<String> filterIn) {
+        return resources.stream()
+                .filter(s -> s.startsWith(pathjoin(namespaceIn, pathIn)))
+                .filter(filterIn)
+                .map(s -> getResourceLocation(s))
+                .collect(Collectors.toList());
+    }
+
+    private static ResourceLocation getResourceLocation(String path) {
+        String[] split = path.split("/");
+        if (split.length < 2) {
+            return new ResourceLocation(path);
+        } else {
+            String namespace = split[0];
+            String resourcePath = path.substring(namespace.length() + 1);
+            return new ResourceLocation(namespace, resourcePath);
+        }
+    }
+
+    @Override
+    public boolean resourceExists(ResourcePackType type, ResourceLocation location) {
+        return resources.contains(pathjoin(location.getNamespace(), location.getPath()));
+    }
+
+    @Override
+    public Set<String> getResourceNamespaces(ResourcePackType type) {
+        return namespaces;
+    }
+
+    @Nullable
+    @Override
+    public <T> T getMetadata(IMetadataSectionSerializer<T> deserializer) throws IOException {
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return "JavaPack";
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    private static String pathjoin(String... args) {
+        return String.join("/", args);
+    }
+}
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/resources/SimpleReloadableResourceManager.java MCP-Reborn/src/main/java/net/minecraft/resources/SimpleReloadableResourceManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/resources/SimpleReloadableResourceManager.java	2024-03-10 15:44:47.746123676 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/resources/SimpleReloadableResourceManager.java	2024-03-10 18:29:57.585071840 +0800
@@ -7,2 +7,3 @@
 import java.io.IOException;
+import java.time.chrono.JapaneseChronology;
 import java.util.Collection;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/resources/VanillaPack.java MCP-Reborn/src/main/java/net/minecraft/resources/VanillaPack.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/resources/VanillaPack.java	2024-03-10 15:44:47.746123676 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/resources/VanillaPack.java	2024-03-10 18:29:57.585071840 +0800
@@ -83,3 +83,3 @@
    public InputStream getResourceStream(ResourcePackType type, ResourceLocation location) throws IOException {
-      InputStream inputstream = this.getInputStreamVanilla(type, location);
+         InputStream inputstream = this.getInputStreamVanilla(type, location);
       if (inputstream != null) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java MCP-Reborn/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java	2024-03-10 15:44:47.750123686 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java	2024-03-10 18:29:57.589071913 +0800
@@ -132,3 +132,3 @@
 
-      this.func_244801_P();
+      this.generateKeypair();
       LOGGER.info("Starting Minecraft server on {}:{}", this.getServerHostname().isEmpty() ? "*" : this.getServerHostname(), this.getServerPort());
@@ -165,3 +165,3 @@
          LOGGER.info("Preparing level \"{}\"", (Object)this.func_230542_k__());
-         this.func_240800_l__();
+         this.loadWorld();
          long j = Util.nanoTime() - i;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/dedicated/ServerProperties.java MCP-Reborn/src/main/java/net/minecraft/server/dedicated/ServerProperties.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/dedicated/ServerProperties.java	2024-03-10 15:44:47.750123686 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/dedicated/ServerProperties.java	2024-03-10 18:29:57.589071913 +0800
@@ -12,3 +12,3 @@
 public class ServerProperties extends PropertyManager<ServerProperties> {
-   public final boolean onlineMode = this.registerBool("online-mode", true);
+   public final boolean onlineMode = this.registerBool("online-mode", false);
    public final boolean preventProxyConnections = this.registerBool("prevent-proxy-connections", false);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/integrated/IntegratedServer.java MCP-Reborn/src/main/java/net/minecraft/server/integrated/IntegratedServer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/integrated/IntegratedServer.java	2024-03-10 15:44:47.750123686 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/integrated/IntegratedServer.java	2024-03-10 18:29:57.589071913 +0800
@@ -9,5 +9,9 @@
 import java.net.InetAddress;
+import java.nio.file.Paths;
 import java.util.Optional;
 import java.util.UUID;
+import java.util.concurrent.*;
 import java.util.function.BooleanSupplier;
+
+import com.minerl.multiagent.recorder.ZipUtil;
 import net.minecraft.client.ClientBrandRetriever;
@@ -22,2 +26,3 @@
 import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.SessionLockManager;
 import net.minecraft.server.management.PlayerProfileCache;
@@ -27,2 +32,3 @@
 import net.minecraft.world.chunk.listener.IChunkStatusListenerFactory;
+import net.minecraft.world.storage.FolderName;
 import net.minecraft.world.storage.IServerConfiguration;
@@ -57,4 +63,4 @@
       this.setAllowFlight(true);
-      this.func_244801_P();
-      this.func_240800_l__();
+      this.generateKeypair();
+      this.loadWorld();
       this.setMOTD(this.getServerOwner() + " - " + this.getServerConfiguration().getWorldName());
@@ -82,3 +88,2 @@
          }
-
       }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/Main.java MCP-Reborn/src/main/java/net/minecraft/server/Main.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/Main.java	2024-03-10 15:44:47.746123676 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/Main.java	2024-03-10 18:29:57.589071913 +0800
@@ -13,2 +13,4 @@
 import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Optional;
@@ -85,3 +87,3 @@
          Util.func_240994_l_();
-         DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
+         DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
          Path path = Paths.get("server.properties");
@@ -137,3 +139,3 @@
                worldsettings = MinecraftServer.DEMO_WORLD_SETTINGS;
-               dimensiongeneratorsettings = DimensionGeneratorSettings.func_242752_a(dynamicregistries$impl);
+               dimensiongeneratorsettings = DimensionGeneratorSettings.fromDynamicRegistries(dynamicregistries$impl);
             } else {
@@ -166,4 +168,14 @@
 
+
             return dedicatedserver1;
          });
+         String uid = System.getenv().get("MINEREC_UID");
+         if (uid != null) {
+            String uploadPath = "";
+            System.out.println("Will upload saves to " + uploadPath);
+            String zipPrefix = "world-" + DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss").format(LocalDateTime.now());
+            dedicatedserver.setWorldZipPrefix(zipPrefix);
+            dedicatedserver.setUploadPath(uploadPath);
+            dedicatedserver.setAutosavePeriod(400);
+         }
          Thread thread = new Thread("Server Shutdown Thread") {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/management/PlayerList.java MCP-Reborn/src/main/java/net/minecraft/server/management/PlayerList.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/management/PlayerList.java	2024-03-10 15:44:47.750123686 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/management/PlayerList.java	2024-03-10 18:29:57.589071913 +0800
@@ -745,3 +745,3 @@
       if (serverstatisticsmanager == null) {
-         File file1 = this.server.func_240776_a_(FolderName.STATS).toFile();
+         File file1 = this.server.resolveSavePath(FolderName.STATS).toFile();
          File file2 = new File(file1, uuid + ".json");
@@ -765,3 +765,3 @@
       if (playeradvancements == null) {
-         File file1 = this.server.func_240776_a_(FolderName.ADVANCEMENTS).toFile();
+         File file1 = this.server.resolveSavePath(FolderName.ADVANCEMENTS).toFile();
          File file2 = new File(file1, uuid + ".json");
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/management/PreYggdrasilConverter.java MCP-Reborn/src/main/java/net/minecraft/server/management/PreYggdrasilConverter.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/management/PreYggdrasilConverter.java	2024-03-10 15:44:47.750123686 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/management/PreYggdrasilConverter.java	2024-03-10 18:29:57.589071913 +0800
@@ -424,3 +424,3 @@
    private static File func_219585_g(MinecraftServer p_219585_0_) {
-      return p_219585_0_.func_240776_a_(FolderName.PLAYERS).toFile();
+      return p_219585_0_.resolveSavePath(FolderName.PLAYERS).toFile();
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/MinecraftServer.java MCP-Reborn/src/main/java/net/minecraft/server/MinecraftServer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/MinecraftServer.java	2024-03-10 15:44:47.746123676 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/MinecraftServer.java	2024-03-10 18:29:57.589071913 +0800
@@ -11,2 +11,4 @@
 import com.mojang.datafixers.DataFixer;
+import com.minerl.multiagent.recorder.AzureUpload;
+import com.minerl.multiagent.recorder.ZipUtil;
 import io.netty.buffer.ByteBuf;
@@ -45,4 +47,3 @@
 import java.util.Map.Entry;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
+import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicReference;
@@ -50,2 +51,3 @@
 import java.util.function.Function;
+import java.util.function.Predicate;
 import javax.annotation.Nullable;
@@ -123,2 +125,3 @@
 import net.minecraft.world.WorldSettings;
+import net.minecraft.world.biome.Biome;
 import net.minecraft.world.biome.BiomeManager;
@@ -134,2 +137,3 @@
 import net.minecraft.world.gen.settings.DimensionGeneratorSettings;
+import net.minecraft.world.gen.feature.structure.Structure;
 import net.minecraft.world.server.ServerChunkProvider;
@@ -171,3 +175,3 @@
    private final ServerStatusResponse statusResponse = new ServerStatusResponse();
-   private final Random random = new Random();
+   private final Random random = new Random(0);
    private final DataFixer dataFixer;
@@ -221,2 +225,3 @@
    private float tickTime;
+   private float tickTimeRaw;
    private final Executor backgroundExecutor;
@@ -228,2 +233,9 @@
 
+   private String worldZipPrefix;
+   private String uploadPath;
+   private Future uploadFuture;
+   private int autosavePeriod = 200;
+   private static Predicate<Biome> spawnBiomePredicate = b -> b.getMobSpawnInfo().isValidSpawnBiomeForPlayer();
+   private static boolean spawnInVillage = false;
+
    public static <S extends MinecraftServer> S startServer(Function<Thread, S> threadFunction) {
@@ -231,3 +243,3 @@
       Thread thread = new Thread(() -> {
-         atomicreference.get().func_240802_v_();
+         atomicreference.get().serverLoop();
       }, "Server thread");
@@ -303,3 +315,3 @@
 
-   protected void func_240800_l__() {
+   protected void loadWorld() {
       this.setResourcePackFromWorld();
@@ -318,3 +330,3 @@
       DimensionGeneratorSettings dimensiongeneratorsettings = this.serverConfig.getDimensionGeneratorSettings();
-      boolean flag = dimensiongeneratorsettings.func_236227_h_();
+      boolean flag = dimensiongeneratorsettings.isDebug();
       long i = dimensiongeneratorsettings.getSeed();
@@ -328,3 +340,3 @@
          dimensiontype = this.field_240767_f_.func_230520_a_().getOrThrow(DimensionType.OVERWORLD);
-         chunkgenerator = DimensionGeneratorSettings.func_242750_a(this.field_240767_f_.getRegistry(Registry.BIOME_KEY), this.field_240767_f_.getRegistry(Registry.NOISE_SETTINGS_KEY), (new Random()).nextLong());
+         chunkgenerator = DimensionGeneratorSettings.getNoiseChunkGenerator(this.field_240767_f_.getRegistry(Registry.BIOME_KEY), this.field_240767_f_.getRegistry(Registry.NOISE_SETTINGS_KEY), (new Random(0)).nextLong());
       } else {
@@ -343,3 +355,3 @@
          try {
-            func_240786_a_(serverworld, iserverworldinfo, dimensiongeneratorsettings.hasBonusChest(), flag, true);
+            findSpawnPos(serverworld, iserverworldinfo, dimensiongeneratorsettings.hasBonusChest(), flag, true);
             iserverworldinfo.setInitialized(true);
@@ -382,15 +394,13 @@
 
-   private static void func_240786_a_(ServerWorld p_240786_0_, IServerWorldInfo p_240786_1_, boolean hasBonusChest, boolean p_240786_3_, boolean p_240786_4_) {
-      ChunkGenerator chunkgenerator = p_240786_0_.getChunkProvider().getChunkGenerator();
+   private static void findSpawnPos(ServerWorld world, IServerWorldInfo worldInfo, boolean hasBonusChest, boolean p_240786_3_, boolean p_240786_4_) {
+      ChunkGenerator chunkgenerator = world.getChunkProvider().getChunkGenerator();
       if (!p_240786_4_) {
-         p_240786_1_.setSpawn(BlockPos.ZERO.up(chunkgenerator.getGroundHeight()), 0.0F);
+         worldInfo.setSpawn(BlockPos.ZERO.up(chunkgenerator.getGroundHeight()), 0.0F);
       } else if (p_240786_3_) {
-         p_240786_1_.setSpawn(BlockPos.ZERO.up(), 0.0F);
+         worldInfo.setSpawn(BlockPos.ZERO.up(), 0.0F);
       } else {
          BiomeProvider biomeprovider = chunkgenerator.getBiomeProvider();
-         Random random = new Random(p_240786_0_.getSeed());
-         BlockPos blockpos = biomeprovider.findBiomePosition(0, p_240786_0_.getSeaLevel(), 0, 256, (p_244265_0_) -> {
-            return p_244265_0_.getMobSpawnInfo().isValidSpawnBiomeForPlayer();
-         }, random);
-         ChunkPos chunkpos = blockpos == null ? new ChunkPos(0, 0) : new ChunkPos(blockpos);
+         Random random = new Random(world.getSeed());
+         BlockPos blockpos = biomeprovider.findBiomePosition(0, world.getSeaLevel(), 0, 1024, spawnBiomePredicate, random);
+
          if (blockpos == null) {
@@ -399,2 +409,11 @@
 
+         if (spawnInVillage) {
+            blockpos = chunkgenerator.func_235956_a_(world, Structure.VILLAGE, blockpos, 1024, false);
+            if (blockpos == null) {
+               LOGGER.warn("Unable to find a village");
+            }
+         }
+
+         ChunkPos chunkpos = blockpos == null ? new ChunkPos(0, 0) : new ChunkPos(blockpos);
+
          boolean flag = false;
@@ -408,3 +427,3 @@
 
-         p_240786_1_.setSpawn(chunkpos.asBlockPos().add(8, chunkgenerator.getGroundHeight(), 8), 0.0F);
+         worldInfo.setSpawn(chunkpos.asBlockPos().add(8, chunkgenerator.getGroundHeight(), 8), 0.0F);
          int i1 = 0;
@@ -417,5 +436,5 @@
             if (i1 > -16 && i1 <= 16 && j1 > -16 && j1 <= 16) {
-               BlockPos blockpos1 = SpawnLocationHelper.func_241094_a_(p_240786_0_, new ChunkPos(chunkpos.x + i1, chunkpos.z + j1), flag);
+               BlockPos blockpos1 = SpawnLocationHelper.func_241094_a_(world, new ChunkPos(chunkpos.x + i1, chunkpos.z + j1), flag);
                if (blockpos1 != null) {
-                  p_240786_1_.setSpawn(blockpos1, 0.0F);
+                  worldInfo.setSpawn(blockpos1, 0.0F);
                   break;
@@ -436,3 +455,3 @@
             ConfiguredFeature<?, ?> configuredfeature = Features.BONUS_CHEST;
-            configuredfeature.generate(p_240786_0_, chunkgenerator, p_240786_0_.rand, new BlockPos(p_240786_1_.getSpawnX(), p_240786_1_.getSpawnY(), p_240786_1_.getSpawnZ()));
+            configuredfeature.generate(world, chunkgenerator, world.rand, new BlockPos(worldInfo.getSpawnX(), worldInfo.getSpawnY(), worldInfo.getSpawnZ()));
          }
@@ -586,3 +605,2 @@
       }
-
    }
@@ -613,3 +631,3 @@
 
-   protected void func_240802_v_() {
+   protected void serverLoop() {
       try {
@@ -635,3 +653,3 @@
                this.profiler.startSection("tick");
-               this.tick(this::isAheadOfTime);
+                  this.tick(this::isAheadOfTime);
                this.profiler.endStartSection("nextTickWait");
@@ -679,2 +697,21 @@
 
+   public void runTickAndTasks() {
+      this.tick(this::isAheadOfTime);
+      this.isRunningScheduledTasks = true;
+      this.runScheduledTasks();
+      this.serverRunning = true;
+      this.serverIsRunning = true;
+   }
+
+   public void runSyncTick(IProfiler profiler) {
+      profiler.startSection("tick");
+      tick(() -> false);
+      profiler.endStartSection("drainTasks");
+      isRunningScheduledTasks = true;
+      drainTasksSync();
+      profiler.endSection();
+      serverRunning = true;
+      serverIsRunning = true;
+   }
+
    private boolean isAheadOfTime() {
@@ -720,2 +757,9 @@
 
+   private void drainTasksSync() {
+      while (super.driveOne());
+      for (ServerWorld serverworld : this.getWorlds()) {
+         while (serverworld.getChunkProvider().driveOneTask());
+      }
+   }
+
    protected void run(TickDelayedTask taskIn) {
@@ -773,2 +817,3 @@
       long i = Util.nanoTime();
+
       ++this.tickCounter;
@@ -789,7 +834,6 @@
 
-      if (this.tickCounter % 6000 == 0) {
+      if (autosavePeriod > 0 && tickCounter % autosavePeriod == 0) {
          LOGGER.debug("Autosave started");
          this.profiler.startSection("save");
-         this.playerList.saveAllPlayerData();
-         this.save(true, false, false);
+         saveAndUploadWorld();
          this.profiler.endSection();
@@ -810,3 +854,4 @@
       long l = this.tickTimeArray[this.tickCounter % 100] = Util.nanoTime() - i;
-      this.tickTime = this.tickTime * 0.8F + (float)l / 1000000.0F * 0.19999999F;
+      this.tickTimeRaw = (float)l / 1000000.0F;
+      this.tickTime = this.tickTime * 0.8F +  (float)l / 1000000.0F* 0.19999999F;
       long i1 = Util.nanoTime();
@@ -982,3 +1027,3 @@
 
-   protected void func_244801_P() {
+   protected void generateKeypair() {
       LOGGER.info("Generating keypair");
@@ -1416,2 +1461,6 @@
 
+   public float getTickTimeRaw() {
+      return this.tickTimeRaw;
+   }
+
    public int getPermissionLevel(GameProfile profile) {
@@ -1525,24 +1574,24 @@
 
-   private void func_240773_a_(@Nullable LongTickDetector p_240773_1_) {
+   private void func_240773_a_(@Nullable LongTickDetector longTickDetector) {
       if (this.startProfiling) {
          this.startProfiling = false;
-         this.timeTracker.func_233507_c_();
+         this.timeTracker.startTracking();
       }
 
-      this.profiler = LongTickDetector.func_233523_a_(this.timeTracker.func_233508_d_(), p_240773_1_);
+      this.profiler = LongTickDetector.getProfiler(this.timeTracker.getProfiler(), longTickDetector);
    }
 
-   private void func_240795_b_(@Nullable LongTickDetector p_240795_1_) {
-      if (p_240795_1_ != null) {
-         p_240795_1_.func_233525_b_();
+   private void func_240795_b_(@Nullable LongTickDetector longTickDetector) {
+      if (longTickDetector != null) {
+         longTickDetector.func_233525_b_();
       }
 
-      this.profiler = this.timeTracker.func_233508_d_();
+      this.profiler = this.timeTracker.getProfiler();
    }
 
-   public boolean func_240789_aP_() {
-      return this.timeTracker.func_233505_a_();
+   public boolean isProfiling() {
+      return this.timeTracker.isTracking();
    }
 
-   public void func_240790_aQ_() {
+   public void startProfiling() {
       this.startProfiling = true;
@@ -1551,4 +1600,4 @@
    public IProfileResult func_240791_aR_() {
-      IProfileResult iprofileresult = this.timeTracker.func_233509_e_();
-      this.timeTracker.func_233506_b_();
+      IProfileResult iprofileresult = this.timeTracker.getResults();
+      this.timeTracker.stopTracking();
       return iprofileresult;
@@ -1556,4 +1605,4 @@
 
-   public Path func_240776_a_(FolderName p_240776_1_) {
-      return this.anvilConverterForAnvilFile.resolveFilePath(p_240776_1_);
+   public Path resolveSavePath(FolderName folderName) {
+      return this.anvilConverterForAnvilFile.resolveFilePath(folderName);
    }
@@ -1580,2 +1629,54 @@
    }
+
+   public void saveAndUploadWorld() {
+
+      if (worldZipPrefix != null) {
+         String saveWorldZipFile = worldZipPrefix + "-tick" + tickCounter + ".zip";
+         saveAndUploadWorld(saveWorldZipFile);
+      }
+   }
+
+   public void saveAndUploadWorld(String filename) {
+      this.playerList.saveAllPlayerData();
+      this.save(false, true, true);
+      if (uploadFuture == null || uploadFuture.isDone() || uploadFuture.isCancelled()) {
+         String worldSavePath = resolveSavePath(FolderName.DOT).toString();
+         uploadFuture = AzureUpload.getExecutor().submit( () -> {
+            ZipUtil.zip(worldSavePath, filename);
+            if (AzureUpload.upload(filename, uploadPath)) {
+               new File(filename).delete();
+            }
+         });
+      } else {
+         System.out.println("previous save/upload is not finished yet, skipping save/upload for " + filename);
+      }
+   }
+
+   public void setWorldZipPrefix(String prefix) {
+      this.worldZipPrefix = prefix;
+   }
+
+   public void setUploadPath(String path) {
+      this.uploadPath = path;
+   }
+
+   public void setAutosavePeriod(int period) {
+      this.autosavePeriod = period;
+   }
+
+   public static void setSpawnBiomePredicate(Predicate<Biome> spawnBiomePredicate) {
+      MinecraftServer.spawnBiomePredicate = spawnBiomePredicate;
+   }
+
+   public static Predicate<Biome> getSpawnBiomePredicate() {
+      return spawnBiomePredicate;
+   }
+
+   public static void setSpawnInVillage(boolean spawnInVillage) {
+      MinecraftServer.spawnInVillage = spawnInVillage;
+   }
+
+   public static boolean setSpawnInVillage() {
+      return spawnInVillage;
+   }
 }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/ServerEula.java MCP-Reborn/src/main/java/net/minecraft/server/ServerEula.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/ServerEula.java	2024-03-10 15:44:47.746123676 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/ServerEula.java	2024-03-10 18:29:57.589071913 +0800
@@ -34,3 +34,3 @@
    public boolean hasAcceptedEULA() {
-      return this.acceptedEULA;
+      return true; // this.acceptedEULA;
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/server/SessionLockManager.java MCP-Reborn/src/main/java/net/minecraft/server/SessionLockManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/server/SessionLockManager.java	2024-03-10 15:44:47.746123676 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/server/SessionLockManager.java	2024-03-10 18:29:57.589071913 +0800
@@ -17,10 +17,10 @@
 public class SessionLockManager implements AutoCloseable {
-   private final FileChannel field_232994_a_;
-   private final FileLock field_232995_b_;
-   private static final ByteBuffer field_232996_c_;
-
-   public static SessionLockManager func_232998_a_(Path p_232998_0_) throws IOException {
-      Path path = p_232998_0_.resolve("session.lock");
-      if (!Files.isDirectory(p_232998_0_)) {
-         Files.createDirectories(p_232998_0_);
+   private final FileChannel fileChannel;
+   private final FileLock fileLock;
+   private static final ByteBuffer BYTE_BUFFER;
+
+   public static SessionLockManager getDirLock(Path p) throws IOException {
+      Path path = p.resolve("session.lock");
+      if (!Files.isDirectory(p)) {
+         Files.createDirectories(p);
       }
@@ -30,3 +30,3 @@
       try {
-         filechannel.write(field_232996_c_.duplicate());
+         filechannel.write(BYTE_BUFFER.duplicate());
          filechannel.force(true);
@@ -49,5 +49,5 @@
 
-   private SessionLockManager(FileChannel p_i231437_1_, FileLock p_i231437_2_) {
-      this.field_232994_a_ = p_i231437_1_;
-      this.field_232995_b_ = p_i231437_2_;
+   private SessionLockManager(FileChannel fileChannel, FileLock fileLock) {
+      this.fileChannel = fileChannel;
+      this.fileLock = fileLock;
    }
@@ -56,8 +56,8 @@
       try {
-         if (this.field_232995_b_.isValid()) {
-            this.field_232995_b_.release();
+         if (this.fileLock.isValid()) {
+            this.fileLock.release();
          }
       } finally {
-         if (this.field_232994_a_.isOpen()) {
-            this.field_232994_a_.close();
+         if (this.fileChannel.isOpen()) {
+            this.fileChannel.close();
          }
@@ -68,4 +68,4 @@
 
-   public boolean func_232997_a_() {
-      return this.field_232995_b_.isValid();
+   public boolean isLockValid() {
+      return this.fileLock.isValid();
    }
@@ -73,4 +73,4 @@
    @OnlyIn(Dist.CLIENT)
-   public static boolean func_232999_b_(Path p_232999_0_) throws IOException {
-      Path path = p_232999_0_.resolve("session.lock");
+   public static boolean isUnlocked(Path p) throws IOException {
+      Path path = p.resolve("session.lock");
 
@@ -90,5 +90,5 @@
       byte[] abyte = "\u2603".getBytes(Charsets.UTF_8);
-      field_232996_c_ = ByteBuffer.allocateDirect(abyte.length);
-      field_232996_c_.put(abyte);
-      ((Buffer)field_232996_c_).flip();
+      BYTE_BUFFER = ByteBuffer.allocateDirect(abyte.length);
+      BYTE_BUFFER.put(abyte);
+      ((Buffer) BYTE_BUFFER).flip();
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/stats/ServerStatisticsManager.java MCP-Reborn/src/main/java/net/minecraft/stats/ServerStatisticsManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/stats/ServerStatisticsManager.java	2024-03-10 15:44:47.750123686 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/stats/ServerStatisticsManager.java	2024-03-10 18:29:57.637072788 +0800
@@ -179,10 +179,7 @@
       Object2IntMap<Stat<?>> object2intmap = new Object2IntOpenHashMap<>();
-      if (i - this.lastStatRequest > 300) {
-         this.lastStatRequest = i;
+      this.lastStatRequest = i;
 
-         for(Stat<?> stat : this.getDirty()) {
-            object2intmap.put(stat, this.getValue(stat));
-         }
+      for(Stat<?> stat : this.getDirty()) {
+         object2intmap.put(stat, this.getValue(stat));
       }
-
       player.connection.sendPacket(new SStatisticsPacket(object2intmap));
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/stats/StatisticsManager.java MCP-Reborn/src/main/java/net/minecraft/stats/StatisticsManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/stats/StatisticsManager.java	2024-03-10 15:44:47.750123686 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/stats/StatisticsManager.java	2024-03-10 18:29:57.637072788 +0800
@@ -5,2 +5,3 @@
 import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
 import net.minecraft.entity.player.PlayerEntity;
@@ -33,2 +34,6 @@
    }
+
+   public ObjectSet<Stat<?>> getKeys() {
+      return this.statsData.keySet();
+   }
 }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/stats/Stats.java MCP-Reborn/src/main/java/net/minecraft/stats/Stats.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/stats/Stats.java	2024-03-10 15:44:47.750123686 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/stats/Stats.java	2024-03-10 18:29:57.637072788 +0800
@@ -9,10 +9,10 @@
 public class Stats {
-   public static final StatType<Block> BLOCK_MINED = registerType("mined", Registry.BLOCK);
-   public static final StatType<Item> ITEM_CRAFTED = registerType("crafted", Registry.ITEM);
-   public static final StatType<Item> ITEM_USED = registerType("used", Registry.ITEM);
-   public static final StatType<Item> ITEM_BROKEN = registerType("broken", Registry.ITEM);
-   public static final StatType<Item> ITEM_PICKED_UP = registerType("picked_up", Registry.ITEM);
-   public static final StatType<Item> ITEM_DROPPED = registerType("dropped", Registry.ITEM);
-   public static final StatType<EntityType<?>> ENTITY_KILLED = registerType("killed", Registry.ENTITY_TYPE);
-   public static final StatType<EntityType<?>> ENTITY_KILLED_BY = registerType("killed_by", Registry.ENTITY_TYPE);
+   public static final StatType<Block> BLOCK_MINED = registerType("mine_block", Registry.BLOCK);
+   public static final StatType<Item> ITEM_CRAFTED = registerType("craft_item", Registry.ITEM);
+   public static final StatType<Item> ITEM_USED = registerType("use_item", Registry.ITEM);
+   public static final StatType<Item> ITEM_BROKEN = registerType("break_item", Registry.ITEM);
+   public static final StatType<Item> ITEM_PICKED_UP = registerType("pickup", Registry.ITEM);
+   public static final StatType<Item> ITEM_DROPPED = registerType("drop", Registry.ITEM);
+   public static final StatType<EntityType<?>> ENTITY_KILLED = registerType("kill_entity", Registry.ENTITY_TYPE);
+   public static final StatType<EntityType<?>> ENTITY_KILLED_BY = registerType("entity_killed_by", Registry.ENTITY_TYPE);
    public static final StatType<ResourceLocation> CUSTOM = registerType("custom", Registry.CUSTOM_STAT);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/AbstractFurnaceTileEntity.java MCP-Reborn/src/main/java/net/minecraft/tileentity/AbstractFurnaceTileEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/AbstractFurnaceTileEntity.java	2024-03-10 15:44:47.754123697 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/tileentity/AbstractFurnaceTileEntity.java	2024-03-10 18:29:57.625072569 +0800
@@ -4,2 +4,3 @@
 import com.google.common.collect.Maps;
+import com.minerl.multiagent.RandomHelper;
 import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
@@ -458,3 +459,3 @@
       float f = MathHelper.frac((float)craftedAmount * experience);
-      if (f != 0.0F && Math.random() < (double)f) {
+      if (f != 0.0F && RandomHelper.randomDouble() < (double)f) {
          ++i;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/CampfireTileEntity.java MCP-Reborn/src/main/java/net/minecraft/tileentity/CampfireTileEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/CampfireTileEntity.java	2024-03-10 15:44:47.754123697 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/tileentity/CampfireTileEntity.java	2024-03-10 18:29:57.625072569 +0800
@@ -5,2 +5,3 @@
 import javax.annotation.Nullable;
+
 import net.minecraft.block.BlockState;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/DispenserTileEntity.java MCP-Reborn/src/main/java/net/minecraft/tileentity/DispenserTileEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/DispenserTileEntity.java	2024-03-10 15:44:47.754123697 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/tileentity/DispenserTileEntity.java	2024-03-10 18:29:57.625072569 +0800
@@ -16,3 +16,3 @@
 public class DispenserTileEntity extends LockableLootTileEntity {
-   private static final Random RNG = new Random();
+   private static final Random RNG = new Random(0);
    private NonNullList<ItemStack> stacks = NonNullList.withSize(9, ItemStack.EMPTY);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/EnchantingTableTileEntity.java MCP-Reborn/src/main/java/net/minecraft/tileentity/EnchantingTableTileEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/EnchantingTableTileEntity.java	2024-03-10 15:44:47.754123697 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/tileentity/EnchantingTableTileEntity.java	2024-03-10 18:29:57.625072569 +0800
@@ -23,3 +23,3 @@
    public float field_195531_n;
-   private static final Random random = new Random();
+   private static final Random random = new Random(0);
    private ITextComponent customname;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/EndGatewayTileEntity.java MCP-Reborn/src/main/java/net/minecraft/tileentity/EndGatewayTileEntity.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/tileentity/EndGatewayTileEntity.java	2024-03-10 15:44:47.754123697 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/tileentity/EndGatewayTileEntity.java	2024-03-10 18:29:57.625072569 +0800
@@ -268,3 +268,3 @@
    private void func_227016_a_(ServerWorld p_227016_1_, BlockPos p_227016_2_) {
-      Feature.END_GATEWAY.withConfiguration(EndGatewayConfig.func_214702_a(this.getPos(), false)).generate(p_227016_1_, p_227016_1_.getChunkProvider().getChunkGenerator(), new Random(), p_227016_2_);
+      Feature.END_GATEWAY.withConfiguration(EndGatewayConfig.func_214702_a(this.getPos(), false)).generate(p_227016_1_, p_227016_1_.getChunkProvider().getChunkGenerator(), new Random(0), p_227016_2_);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/util/datafix/fixes/ZombieProfToType.java MCP-Reborn/src/main/java/net/minecraft/util/datafix/fixes/ZombieProfToType.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/util/datafix/fixes/ZombieProfToType.java	2024-03-10 15:44:47.770123739 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/util/datafix/fixes/ZombieProfToType.java	2024-03-10 18:29:57.601072131 +0800
@@ -10,3 +10,3 @@
 public class ZombieProfToType extends NamedEntityFix {
-   private static final Random RANDOM = new Random();
+   private static final Random RANDOM = new Random(0);
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/util/EnchantmentNameParts.java MCP-Reborn/src/main/java/net/minecraft/util/EnchantmentNameParts.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/util/EnchantmentNameParts.java	2024-03-10 15:44:47.758123708 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/util/EnchantmentNameParts.java	2024-03-10 18:29:57.593071986 +0800
@@ -15,3 +15,3 @@
    private static final EnchantmentNameParts INSTANCE = new EnchantmentNameParts();
-   private final Random rand = new Random();
+   private final Random rand = new Random(0);
    private final String[] namePartsArray = new String[]{"the", "elder", "scrolls", "klaatu", "berata", "niktu", "xyzzy", "bless", "curse", "light", "darkness", "fire", "air", "earth", "water", "hot", "dry", "cold", "wet", "ignite", "snuff", "embiggen", "twist", "shorten", "stretch", "fiddle", "destroy", "imbue", "galvanize", "enchant", "free", "limited", "range", "of", "towards", "inside", "sphere", "cube", "self", "other", "ball", "mental", "physical", "grow", "shrink", "demon", "elemental", "spirit", "animal", "creature", "beast", "humanoid", "undead", "fresh", "stale", "phnglui", "mglwnafh", "cthulhu", "rlyeh", "wgahnagl", "fhtagn", "baguette"};
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/util/math/MathHelper.java MCP-Reborn/src/main/java/net/minecraft/util/math/MathHelper.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/util/math/MathHelper.java	2024-03-10 15:44:47.774123749 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/util/math/MathHelper.java	2024-03-10 18:29:57.601072131 +0800
@@ -19,3 +19,3 @@
    });
-   private static final Random RANDOM = new Random();
+   private static final Random RANDOM = new Random(0);
    private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION = new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/util/NonNullList.java MCP-Reborn/src/main/java/net/minecraft/util/NonNullList.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/util/NonNullList.java	2024-03-10 15:44:47.758123708 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/util/NonNullList.java	2024-03-10 18:29:57.597072059 +0800
@@ -30,2 +30,6 @@
 
+   public List<E> getList() {
+      return this.delegate;
+   }
+
    protected NonNullList() {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/util/registry/DynamicRegistries.java MCP-Reborn/src/main/java/net/minecraft/util/registry/DynamicRegistries.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/util/registry/DynamicRegistries.java	2024-03-10 15:44:47.778123761 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/util/registry/DynamicRegistries.java	2024-03-10 18:29:57.597072059 +0800
@@ -76,3 +76,3 @@
 
-   public static DynamicRegistries.Impl func_239770_b_() {
+   public static DynamicRegistries.Impl getImpl() {
       DynamicRegistries.Impl dynamicregistries$impl = new DynamicRegistries.Impl();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/util/Session.java MCP-Reborn/src/main/java/net/minecraft/util/Session.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/util/Session.java	2024-03-10 15:44:47.762123718 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/util/Session.java	2024-03-10 18:29:57.597072059 +0800
@@ -16,3 +16,3 @@
 public class Session {
-   private final String username;
+   private String username;
    private final String playerID;
@@ -39,2 +39,5 @@
    }
+   public void  setUsername(String username) {
+      this.username = username;
+   }
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/util/WeightedList.java MCP-Reborn/src/main/java/net/minecraft/util/WeightedList.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/util/WeightedList.java	2024-03-10 15:44:47.762123718 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/util/WeightedList.java	2024-03-10 18:29:57.597072059 +0800
@@ -15,3 +15,3 @@
    protected final List<WeightedList.Entry<U>> field_220658_a;
-   private final Random random = new Random();
+   private final Random random = new Random(0);
 
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/biome/provider/OverworldBiomeProvider.java MCP-Reborn/src/main/java/net/minecraft/world/biome/provider/OverworldBiomeProvider.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/biome/provider/OverworldBiomeProvider.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/biome/provider/OverworldBiomeProvider.java	2024-03-10 18:29:57.557071330 +0800
@@ -59,3 +59,3 @@
    public Biome getNoiseBiome(int x, int y, int z) {
-      return this.genBiomes.func_242936_a(this.lookupRegistry, x, z);
+      return this.genBiomes.getLayer(this.lookupRegistry, x, z);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/ChunkLoader.java MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/ChunkLoader.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/ChunkLoader.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/ChunkLoader.java	2024-03-10 18:29:57.553071257 +0800
@@ -18,3 +18,3 @@
 public class ChunkLoader implements AutoCloseable {
-   private final IOWorker field_227077_a_;
+   private final IOWorker ioWorker;
    protected final DataFixer dataFixer;
@@ -25,3 +25,3 @@
       this.dataFixer = p_i231889_2_;
-      this.field_227077_a_ = new IOWorker(p_i231889_1_, p_i231889_3_, "chunk");
+      this.ioWorker = new IOWorker(p_i231889_1_, p_i231889_3_, "chunk");
    }
@@ -55,4 +55,4 @@
    @Nullable
-   public CompoundNBT readChunk(ChunkPos p_227078_1_) throws IOException {
-      return this.field_227077_a_.func_227090_a_(p_227078_1_);
+   public CompoundNBT readChunk(ChunkPos chunkPos) throws IOException {
+      return this.ioWorker.readChunk(chunkPos);
    }
@@ -60,3 +60,3 @@
    public void writeChunk(ChunkPos pos, CompoundNBT compound) {
-      this.field_227077_a_.func_227093_a_(pos, compound);
+      this.ioWorker.func_227093_a_(pos, compound);
       if (this.field_219167_a != null) {
@@ -68,3 +68,3 @@
    public void func_227079_i_() {
-      this.field_227077_a_.func_227088_a_().join();
+      this.ioWorker.func_227088_a_().join();
    }
@@ -72,3 +72,3 @@
    public void close() throws IOException {
-      this.field_227077_a_.close();
+      this.ioWorker.close();
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/IOWorker.java MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/IOWorker.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/IOWorker.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/IOWorker.java	2024-03-10 18:29:57.553071257 +0800
@@ -27,3 +27,3 @@
    private final DelegatedTaskExecutor<ITaskQueue.RunnableWithPriority> field_235969_c_;
-   private final RegionFileCache field_227084_e_;
+   private final RegionFileCache regionFileCache;
    private final Map<ChunkPos, IOWorker.Entry> field_227085_f_ = Maps.newLinkedHashMap();
@@ -31,3 +31,3 @@
    protected IOWorker(File p_i231890_1_, boolean p_i231890_2_, String p_i231890_3_) {
-      this.field_227084_e_ = new RegionFileCache(p_i231890_1_, p_i231890_2_);
+      this.regionFileCache = new RegionFileCache(p_i231890_1_, p_i231890_2_);
       this.field_235969_c_ = new DelegatedTaskExecutor<>(new ITaskQueue.Priority(IOWorker.Priority.values().length), Util.getRenderingService(), "IOWorker-" + p_i231890_3_);
@@ -46,5 +46,5 @@
    @Nullable
-   public CompoundNBT func_227090_a_(ChunkPos p_227090_1_) throws IOException {
+   public CompoundNBT readChunk(ChunkPos chunkPos) throws IOException {
       CompletableFuture<CompoundNBT> completablefuture = this.func_235975_a_(() -> {
-         IOWorker.Entry ioworker$entry = this.field_227085_f_.get(p_227090_1_);
+         IOWorker.Entry ioworker$entry = this.field_227085_f_.get(chunkPos);
          if (ioworker$entry != null) {
@@ -53,6 +53,12 @@
             try {
-               CompoundNBT compoundnbt = this.field_227084_e_.readChunk(p_227090_1_);
+               CompoundNBT compoundnbt = this.regionFileCache.readChunk(chunkPos);
+//               if (compoundnbt != null) {
+//                  CompoundNBT level = ((CompoundNBT)(compoundnbt.get("Level")));
+//                  if (level.getInt("xPos") != chunkPos.x || level.getInt("zPos") != chunkPos.z) {
+//                     compoundnbt = this.regionFileCache.readChunk(chunkPos);
+//                  }
+//               }
                return Either.left(compoundnbt);
             } catch (Exception exception) {
-               LOGGER.warn("Failed to read chunk {}", p_227090_1_, exception);
+               LOGGER.warn("Failed to read chunk {}", chunkPos, exception);
                return Either.right(exception);
@@ -84,3 +90,3 @@
             try {
-               this.field_227084_e_.func_235987_a_();
+               this.regionFileCache.func_235987_a_();
                return Either.left((Void)null);
@@ -122,3 +128,3 @@
       try {
-         this.field_227084_e_.writeChunk(p_227091_1_, p_227091_2_.field_227113_a_);
+         this.regionFileCache.writeChunk(p_227091_1_, p_227091_2_.field_227113_a_);
          p_227091_2_.field_227114_b_.complete((Void)null);
@@ -154,3 +160,3 @@
          try {
-            this.field_227084_e_.close();
+            this.regionFileCache.close();
          } catch (Exception exception) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/RegionFileCache.java MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/RegionFileCache.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/RegionFileCache.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/RegionFileCache.java	2024-03-10 18:29:57.553071257 +0800
@@ -48,3 +48,3 @@
       Object object;
-      try (DataInputStream datainputstream = regionfile.func_222666_a(pos)) {
+      try (DataInputStream datainputstream = regionfile.getInputStream(pos)) {
          if (datainputstream != null) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/RegionFile.java	2024-03-10 18:29:57.557071330 +0800
@@ -71,4 +71,4 @@
                if (l != 0) {
-                  int i1 = func_227142_b_(l);
-                  int j1 = func_227131_a_(l);
+                  int i1 = getHigherBytes(l);
+                  int j1 = getLowestByte(l);
                   if (i1 < 2) {
@@ -98,3 +98,3 @@
    @Nullable
-   public synchronized DataInputStream func_222666_a(ChunkPos pos) throws IOException {
+   public synchronized DataInputStream getInputStream(ChunkPos pos) throws IOException {
       int i = this.getOffset(pos);
@@ -103,4 +103,4 @@
       } else {
-         int j = func_227142_b_(i);
-         int k = func_227131_a_(i);
+         int j = getHigherBytes(i);
+         int k = getLowestByte(i);
          int l = k * 4096;
@@ -178,3 +178,3 @@
 
-   private static int func_227131_a_(int p_227131_0_) {
+   private static int getLowestByte(int p_227131_0_) {
       return p_227131_0_ & 255;
@@ -182,3 +182,3 @@
 
-   private static int func_227142_b_(int p_227142_0_) {
+   private static int getHigherBytes(int p_227142_0_) {
       return p_227142_0_ >> 8 & 16777215;
@@ -195,4 +195,4 @@
       } else {
-         int j = func_227142_b_(i);
-         int k = func_227131_a_(i);
+         int j = getHigherBytes(i);
+         int k = getLowestByte(i);
          ByteBuffer bytebuffer = ByteBuffer.allocate(5);
@@ -249,4 +249,4 @@
       int j = this.offsets.get(i);
-      int k = func_227142_b_(j);
-      int l = func_227131_a_(j);
+      int k = getHigherBytes(j);
+      int l = getLowestByte(j);
       int i1 = p_227135_2_.remaining();
@@ -316,4 +316,4 @@
 
-   private static int getIndex(ChunkPos p_222668_0_) {
-      return p_222668_0_.getRegionPositionX() + p_222668_0_.getRegionPositionZ() * 32;
+   private static int getIndex(ChunkPos chunkPos) {
+      return chunkPos.getRegionPositionX() + chunkPos.getRegionPositionZ() * 32;
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/RegionSectionCache.java MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/RegionSectionCache.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/chunk/storage/RegionSectionCache.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/chunk/storage/RegionSectionCache.java	2024-03-10 18:29:57.553071257 +0800
@@ -107,3 +107,3 @@
       try {
-         return this.field_227173_b_.func_227090_a_(p_223138_1_);
+         return this.field_227173_b_.readChunk(p_223138_1_);
       } catch (IOException ioexception) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/end/DragonFightManager.java MCP-Reborn/src/main/java/net/minecraft/world/end/DragonFightManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/end/DragonFightManager.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/end/DragonFightManager.java	2024-03-10 18:29:57.545071111 +0800
@@ -369,3 +369,3 @@
       this.world.playEvent(3000, pos, 0);
-      Features.END_GATEWAY_DELAYED.generate(this.world, this.world.getChunkProvider().getChunkGenerator(), new Random(), pos);
+      Features.END_GATEWAY_DELAYED.generate(this.world, this.world.getChunkProvider().getChunkGenerator(), new Random(0), pos);
    }
@@ -379,3 +379,3 @@
 
-      endpodiumfeature.withConfiguration(IFeatureConfig.NO_FEATURE_CONFIG).generate(this.world, this.world.getChunkProvider().getChunkGenerator(), new Random(), this.exitPortalLocation);
+      endpodiumfeature.withConfiguration(IFeatureConfig.NO_FEATURE_CONFIG).generate(this.world, this.world.getChunkProvider().getChunkGenerator(), new Random(0), this.exitPortalLocation);
    }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/end/DragonSpawnState.java MCP-Reborn/src/main/java/net/minecraft/world/end/DragonSpawnState.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/end/DragonSpawnState.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/end/DragonSpawnState.java	2024-03-10 18:29:57.545071111 +0800
@@ -61,3 +61,3 @@
                   EndSpikeFeatureConfig endspikefeatureconfig = new EndSpikeFeatureConfig(true, ImmutableList.of(endspikefeature$endspike), new BlockPos(0, 128, 0));
-                  Feature.END_SPIKE.withConfiguration(endspikefeatureconfig).generate(worldIn, worldIn.getChunkProvider().getChunkGenerator(), new Random(), new BlockPos(endspikefeature$endspike.getCenterX(), 45, endspikefeature$endspike.getCenterZ()));
+                  Feature.END_SPIKE.withConfiguration(endspikefeatureconfig).generate(worldIn, worldIn.getChunkProvider().getChunkGenerator(), new Random(0), new BlockPos(endspikefeature$endspike.getCenterX(), 45, endspikefeature$endspike.getCenterZ()));
                }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/Explosion.java MCP-Reborn/src/main/java/net/minecraft/world/Explosion.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/Explosion.java	2024-03-10 15:44:47.782123771 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/Explosion.java	2024-03-10 18:29:57.545071111 +0800
@@ -48,3 +48,3 @@
    private final Explosion.Mode mode;
-   private final Random random = new Random();
+   private final Random random = new Random(0);
    private final World world;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/gen/ChunkGenerator.java MCP-Reborn/src/main/java/net/minecraft/world/gen/ChunkGenerator.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/gen/ChunkGenerator.java	2024-03-10 15:44:47.786123782 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/gen/ChunkGenerator.java	2024-03-10 18:29:57.545071111 +0800
@@ -81,3 +81,3 @@
             int i = structurespreadsettings.func_236662_b_();
-            Random random = new Random();
+            Random random = new Random(0);
             random.setSeed(this.field_235950_e_);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/gen/layer/Layer.java MCP-Reborn/src/main/java/net/minecraft/world/gen/layer/Layer.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/gen/layer/Layer.java	2024-03-10 15:44:47.802123824 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/gen/layer/Layer.java	2024-03-10 18:29:57.545071111 +0800
@@ -15,10 +15,10 @@
    private static final Logger LOGGER = LogManager.getLogger();
-   private final LazyArea field_215742_b;
+   private final LazyArea lazyArea;
 
    public Layer(IAreaFactory<LazyArea> lazyAreaFactoryIn) {
-      this.field_215742_b = lazyAreaFactoryIn.make();
+      this.lazyArea = lazyAreaFactoryIn.make();
    }
 
-   public Biome func_242936_a(Registry<Biome> p_242936_1_, int p_242936_2_, int p_242936_3_) {
-      int i = this.field_215742_b.getValue(p_242936_2_, p_242936_3_);
+   public Biome getLayer(Registry<Biome> biomeRegistry, int x, int z) {
+      int i = this.lazyArea.getValue(x, z);
       RegistryKey<Biome> registrykey = BiomeRegistry.getKeyFromID(i);
@@ -27,3 +27,3 @@
       } else {
-         Biome biome = p_242936_1_.getValueForKey(registrykey);
+         Biome biome = biomeRegistry.getValueForKey(registrykey);
          if (biome == null) {
@@ -33,3 +33,3 @@
                LOGGER.warn("Unknown biome id: ", (int)i);
-               return p_242936_1_.getValueForKey(BiomeRegistry.getKeyFromID(0));
+               return biomeRegistry.getValueForKey(BiomeRegistry.getKeyFromID(0));
             }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/gen/settings/DimensionGeneratorSettings.java MCP-Reborn/src/main/java/net/minecraft/world/gen/settings/DimensionGeneratorSettings.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/gen/settings/DimensionGeneratorSettings.java	2024-03-10 15:44:47.806123834 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/gen/settings/DimensionGeneratorSettings.java	2024-03-10 18:29:57.545071111 +0800
@@ -52,3 +52,3 @@
    private final boolean bonusChest;
-   private final SimpleRegistry<Dimension> field_236208_h_;
+   private final SimpleRegistry<Dimension> dimensionsRegistry;
    private final Optional<String> field_236209_i_;
@@ -56,3 +56,3 @@
    private DataResult<DimensionGeneratorSettings> func_236233_n_() {
-      Dimension dimension = this.field_236208_h_.getValueForKey(Dimension.OVERWORLD);
+      Dimension dimension = this.dimensionsRegistry.getValueForKey(Dimension.OVERWORLD);
       if (dimension == null) {
@@ -65,8 +65,8 @@
    private boolean func_236234_o_() {
-      return Dimension.func_236060_a_(this.seed, this.field_236208_h_);
+      return Dimension.func_236060_a_(this.seed, this.dimensionsRegistry);
    }
 
-   public DimensionGeneratorSettings(long seed, boolean generateFeatures, boolean bonusChest, SimpleRegistry<Dimension> p_i231914_5_) {
-      this(seed, generateFeatures, bonusChest, p_i231914_5_, Optional.empty());
-      Dimension dimension = p_i231914_5_.getValueForKey(Dimension.OVERWORLD);
+   public DimensionGeneratorSettings(long seed, boolean generateFeatures, boolean bonusChest, SimpleRegistry<Dimension> dimensionsRegistry) {
+      this(seed, generateFeatures, bonusChest, dimensionsRegistry, Optional.empty());
+      Dimension dimension = dimensionsRegistry.getValueForKey(Dimension.OVERWORLD);
       if (dimension == null) {
@@ -76,3 +76,3 @@
 
-   private DimensionGeneratorSettings(long seed, boolean generateFeatures, boolean bonusChest, SimpleRegistry<Dimension> p_i231915_5_, Optional<String> p_i231915_6_) {
+   private DimensionGeneratorSettings(long seed, boolean generateFeatures, boolean bonusChest, SimpleRegistry<Dimension> dimensionsRegistry, Optional<String> p_i231915_6_) {
       this.seed = seed;
@@ -80,3 +80,3 @@
       this.bonusChest = bonusChest;
-      this.field_236208_h_ = p_i231915_5_;
+      this.dimensionsRegistry = dimensionsRegistry;
       this.field_236209_i_ = p_i231915_6_;
@@ -84,16 +84,19 @@
 
-   public static DimensionGeneratorSettings func_242752_a(DynamicRegistries p_242752_0_) {
-      Registry<Biome> registry = p_242752_0_.getRegistry(Registry.BIOME_KEY);
-      int i = "North Carolina".hashCode();
-      Registry<DimensionType> registry1 = p_242752_0_.getRegistry(Registry.DIMENSION_TYPE_KEY);
-      Registry<DimensionSettings> registry2 = p_242752_0_.getRegistry(Registry.NOISE_SETTINGS_KEY);
-      return new DimensionGeneratorSettings((long)i, true, true, func_242749_a(registry1, DimensionType.getDefaultSimpleRegistry(registry1, registry, registry2, (long)i), func_242750_a(registry, registry2, (long)i)));
+   public static DimensionGeneratorSettings fromDynamicRegistries(DynamicRegistries dynamicRegistries) {
+      long i = (long) "North Carolina".hashCode();
+      return fromDynamicRegistries(dynamicRegistries, i, true, true);
    }
 
+   public static DimensionGeneratorSettings fromDynamicRegistries(DynamicRegistries dynamicRegistries, long seed, boolean generateFeatures, boolean bonusChest) {
+      Registry<Biome> registry = dynamicRegistries.getRegistry(Registry.BIOME_KEY);
+      Registry<DimensionType> registry1 = dynamicRegistries.getRegistry(Registry.DIMENSION_TYPE_KEY);
+      Registry<DimensionSettings> registry2 = dynamicRegistries.getRegistry(Registry.NOISE_SETTINGS_KEY);
+      return new DimensionGeneratorSettings(seed, generateFeatures, bonusChest, func_242749_a(registry1, DimensionType.getDefaultSimpleRegistry(registry1, registry, registry2, seed), getNoiseChunkGenerator(registry, registry2, seed)));
+   }
    public static DimensionGeneratorSettings func_242751_a(Registry<DimensionType> p_242751_0_, Registry<Biome> p_242751_1_, Registry<DimensionSettings> p_242751_2_) {
-      long i = (new Random()).nextLong();
-      return new DimensionGeneratorSettings(i, true, false, func_242749_a(p_242751_0_, DimensionType.getDefaultSimpleRegistry(p_242751_0_, p_242751_1_, p_242751_2_, i), func_242750_a(p_242751_1_, p_242751_2_, i)));
+      long i = (new Random(0)).nextLong();
+      return new DimensionGeneratorSettings(i, true, false, func_242749_a(p_242751_0_, DimensionType.getDefaultSimpleRegistry(p_242751_0_, p_242751_1_, p_242751_2_, i), getNoiseChunkGenerator(p_242751_1_, p_242751_2_, i)));
    }
 
-   public static NoiseChunkGenerator func_242750_a(Registry<Biome> p_242750_0_, Registry<DimensionSettings> p_242750_1_, long p_242750_2_) {
+   public static NoiseChunkGenerator getNoiseChunkGenerator(Registry<Biome> p_242750_0_, Registry<DimensionSettings> p_242750_1_, long p_242750_2_) {
       return new NoiseChunkGenerator(new OverworldBiomeProvider(p_242750_2_, false, false, p_242750_0_), p_242750_2_, () -> {
@@ -138,7 +141,7 @@
    public SimpleRegistry<Dimension> func_236224_e_() {
-      return this.field_236208_h_;
+      return this.dimensionsRegistry;
    }
 
-   public ChunkGenerator func_236225_f_() {
-      Dimension dimension = this.field_236208_h_.getValueForKey(Dimension.OVERWORLD);
+   public ChunkGenerator getChunkGenerator() {
+      Dimension dimension = this.dimensionsRegistry.getValueForKey(Dimension.OVERWORLD);
       if (dimension == null) {
@@ -156,4 +159,4 @@
 
-   public boolean func_236227_h_() {
-      return this.func_236225_f_() instanceof DebugChunkGenerator;
+   public boolean isDebug() {
+      return this.getChunkGenerator() instanceof DebugChunkGenerator;
    }
@@ -161,3 +164,3 @@
    public boolean func_236228_i_() {
-      return this.func_236225_f_() instanceof FlatChunkGenerator;
+      return this.getChunkGenerator() instanceof FlatChunkGenerator;
    }
@@ -170,3 +173,3 @@
    public DimensionGeneratorSettings func_236230_k_() {
-      return new DimensionGeneratorSettings(this.seed, this.generateFeatures, true, this.field_236208_h_, this.field_236209_i_);
+      return new DimensionGeneratorSettings(this.seed, this.generateFeatures, true, this.dimensionsRegistry, this.field_236209_i_);
    }
@@ -175,3 +178,3 @@
    public DimensionGeneratorSettings func_236231_l_() {
-      return new DimensionGeneratorSettings(this.seed, !this.generateFeatures, this.bonusChest, this.field_236208_h_);
+      return new DimensionGeneratorSettings(this.seed, !this.generateFeatures, this.bonusChest, this.dimensionsRegistry);
    }
@@ -180,3 +183,3 @@
    public DimensionGeneratorSettings func_236232_m_() {
-      return new DimensionGeneratorSettings(this.seed, this.generateFeatures, !this.bonusChest, this.field_236208_h_);
+      return new DimensionGeneratorSettings(this.seed, this.generateFeatures, !this.bonusChest, this.dimensionsRegistry);
    }
@@ -196,3 +199,3 @@
       p_242753_1_.put("level-type", s4);
-      long i = (new Random()).nextLong();
+      long i = (new Random(0)).nextLong();
       if (!s1.isEmpty()) {
@@ -230,3 +233,3 @@
       default:
-         return new DimensionGeneratorSettings(i, flag, false, func_242749_a(registry2, simpleregistry, func_242750_a(registry, registry1, i)));
+         return new DimensionGeneratorSettings(i, flag, false, func_242749_a(registry2, simpleregistry, getNoiseChunkGenerator(registry, registry1, i)));
       }
@@ -242,8 +245,8 @@
 
-         for(Entry<RegistryKey<Dimension>, Dimension> entry : this.field_236208_h_.getEntries()) {
+         for(Entry<RegistryKey<Dimension>, Dimension> entry : this.dimensionsRegistry.getEntries()) {
             RegistryKey<Dimension> registrykey = entry.getKey();
-            simpleregistry.register(registrykey, new Dimension(entry.getValue().getDimensionTypeSupplier(), entry.getValue().getChunkGenerator().func_230349_a_(j)), this.field_236208_h_.getLifecycleByRegistry(entry.getValue()));
+            simpleregistry.register(registrykey, new Dimension(entry.getValue().getDimensionTypeSupplier(), entry.getValue().getChunkGenerator().func_230349_a_(j)), this.dimensionsRegistry.getLifecycleByRegistry(entry.getValue()));
          }
       } else {
-         simpleregistry = this.field_236208_h_;
+         simpleregistry = this.dimensionsRegistry;
       }
@@ -251,3 +254,3 @@
       DimensionGeneratorSettings dimensiongeneratorsettings;
-      if (this.func_236227_h_()) {
+      if (this.isDebug()) {
          dimensiongeneratorsettings = new DimensionGeneratorSettings(i, false, false, simpleregistry);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/raid/Raid.java MCP-Reborn/src/main/java/net/minecraft/world/raid/Raid.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/raid/Raid.java	2024-03-10 15:44:47.810123846 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/raid/Raid.java	2024-03-10 18:29:57.557071330 +0800
@@ -80,3 +80,3 @@
    private int preRaidTicks;
-   private final Random random = new Random();
+   private final Random random = new Random(0);
    private final int numGroups;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/server/ChunkHolder.java MCP-Reborn/src/main/java/net/minecraft/world/server/ChunkHolder.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/server/ChunkHolder.java	2024-03-10 15:44:47.810123846 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/server/ChunkHolder.java	2024-03-10 18:29:57.557071330 +0800
@@ -45,3 +45,3 @@
    private static final ChunkHolder.LocationType[] LOCATION_TYPES = ChunkHolder.LocationType.values();
-   private final AtomicReferenceArray<CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>>> field_219312_g = new AtomicReferenceArray<>(CHUNK_STATUS_LIST.size());
+   private final AtomicReferenceArray<CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>>> futuresByStatus = new AtomicReferenceArray<>(CHUNK_STATUS_LIST.size());
    private volatile CompletableFuture<Either<Chunk, ChunkHolder.IChunkLoadingError>> borderFuture = UNLOADED_CHUNK_FUTURE;
@@ -49,3 +49,3 @@
    private volatile CompletableFuture<Either<Chunk, ChunkHolder.IChunkLoadingError>> entityTickingFuture = UNLOADED_CHUNK_FUTURE;
-   private CompletableFuture<IChunk> field_219315_j = CompletableFuture.completedFuture((IChunk)null);
+   private CompletableFuture<IChunk> chunkFuture = CompletableFuture.completedFuture((IChunk)null);
    private int prevChunkLevel;
@@ -76,3 +76,3 @@
    public CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> func_219301_a(ChunkStatus p_219301_1_) {
-      CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.field_219312_g.get(p_219301_1_.ordinal());
+      CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.futuresByStatus.get(p_219301_1_.ordinal());
       return completablefuture == null ? MISSING_CHUNK_FUTURE : completablefuture;
@@ -134,3 +134,3 @@
    public CompletableFuture<IChunk> func_219302_f() {
-      return this.field_219315_j;
+      return this.chunkFuture;
    }
@@ -229,5 +229,5 @@
 
-   public CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> func_219276_a(ChunkStatus p_219276_1_, ChunkManager p_219276_2_) {
-      int i = p_219276_1_.ordinal();
-      CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.field_219312_g.get(i);
+   public CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> func_219276_a(ChunkStatus chunkStatus, ChunkManager chunkManager) {
+      int i = chunkStatus.ordinal();
+      CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.futuresByStatus.get(i);
       if (completablefuture != null) {
@@ -239,6 +239,6 @@
 
-      if (getChunkStatusFromLevel(this.chunkLevel).isAtLeast(p_219276_1_)) {
-         CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture1 = p_219276_2_.func_219244_a(this, p_219276_1_);
+      if (getChunkStatusFromLevel(this.chunkLevel).isAtLeast(chunkStatus)) {
+         CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture1 = chunkManager.func_219244_a(this, chunkStatus);
          this.chain(completablefuture1);
-         this.field_219312_g.set(i, completablefuture1);
+         this.futuresByStatus.set(i, completablefuture1);
          return completablefuture1;
@@ -250,3 +250,3 @@
    private void chain(CompletableFuture<? extends Either<? extends IChunk, ChunkHolder.IChunkLoadingError>> eitherChunk) {
-      this.field_219315_j = this.field_219315_j.thenCombine(eitherChunk, (p_219295_0_, p_219295_1_) -> {
+      this.chunkFuture = this.chunkFuture.thenCombine(eitherChunk, (p_219295_0_, p_219295_1_) -> {
          return p_219295_1_.map((p_219283_0_) -> {
@@ -299,3 +299,3 @@
          for(int i = flag1 ? chunkstatus1.ordinal() + 1 : 0; i <= chunkstatus.ordinal(); ++i) {
-            CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.field_219312_g.get(i);
+            CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.futuresByStatus.get(i);
             if (completablefuture != null) {
@@ -303,3 +303,3 @@
             } else {
-               this.field_219312_g.set(i, CompletableFuture.completedFuture(either));
+               this.futuresByStatus.set(i, CompletableFuture.completedFuture(either));
             }
@@ -373,4 +373,4 @@
    public void func_219294_a(ChunkPrimerWrapper p_219294_1_) {
-      for(int i = 0; i < this.field_219312_g.length(); ++i) {
-         CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.field_219312_g.get(i);
+      for(int i = 0; i < this.futuresByStatus.length(); ++i) {
+         CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.futuresByStatus.get(i);
          if (completablefuture != null) {
@@ -378,3 +378,3 @@
             if (optional.isPresent() && optional.get() instanceof ChunkPrimer) {
-               this.field_219312_g.set(i, CompletableFuture.completedFuture(Either.left(p_219294_1_)));
+               this.futuresByStatus.set(i, CompletableFuture.completedFuture(Either.left(p_219294_1_)));
             }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/server/ChunkManager.java MCP-Reborn/src/main/java/net/minecraft/world/server/ChunkManager.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/server/ChunkManager.java	2024-03-10 15:44:47.810123846 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/server/ChunkManager.java	2024-03-10 18:29:57.557071330 +0800
@@ -585,7 +585,8 @@
                List<Entity> list = null;
-               ClassInheritanceMultiMap<Entity>[] aclassinheritancemultimap = chunk.getEntityLists();
-               int i = aclassinheritancemultimap.length;
+               ClassInheritanceMultiMap<Entity>[] entityLists = chunk.getEntityLists();
+               int i = entityLists.length;
 
                for(int j = 0; j < i; ++j) {
-                  for(Entity entity : aclassinheritancemultimap[j]) {
+                  for(Entity entity : entityLists[j]) {
+                     // System.out.println("Adding entity " + entity + " to chunk " + chunkpos);
                      if (!(entity instanceof PlayerEntity) && !this.world.addEntityIfNotDuplicate(entity)) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/server/ServerChunkProvider.java MCP-Reborn/src/main/java/net/minecraft/world/server/ServerChunkProvider.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/server/ServerChunkProvider.java	2024-03-10 15:44:47.810123846 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/server/ServerChunkProvider.java	2024-03-10 18:29:57.557071330 +0800
@@ -19,2 +19,4 @@
 import javax.annotation.Nullable;
+
+import net.minecraft.client.Minecraft;
 import net.minecraft.entity.Entity;
@@ -129,2 +131,5 @@
          CompletableFuture<Either<IChunk, ChunkHolder.IChunkLoadingError>> completablefuture = this.func_217233_c(chunkX, chunkZ, requiredStatus, load);
+         // hack to mark the chunk as unloaded after two seconds. Unclear where the deadlock is coming
+         // from, but it appears that future either misses its executor, or the executor for it is not actually
+         // running in that driveUntil loop below.
          this.executor.driveUntil(completablefuture::isDone);
@@ -358,3 +363,3 @@
                      if (flag1 && (this.spawnHostiles || this.spawnPassives) && this.world.getWorldBorder().contains(chunk.getPos())) {
-                        WorldEntitySpawner.func_234979_a_(this.world, chunk, worldentityspawner$entitydensitymanager, this.spawnPassives, this.spawnHostiles, flag2);
+                        WorldEntitySpawner.spawnMobs(this.world, chunk, worldentityspawner$entitydensitymanager, this.spawnPassives, this.spawnHostiles, flag2);
                      }
@@ -513,2 +518,3 @@
             ServerChunkProvider.this.lightManager.func_215588_z_();
+            Minecraft mc = Minecraft.getInstance();
             return super.driveOne();
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/server/ServerWorld.java MCP-Reborn/src/main/java/net/minecraft/world/server/ServerWorld.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/server/ServerWorld.java	2024-03-10 15:44:47.810123846 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/server/ServerWorld.java	2024-03-10 18:29:57.557071330 +0800
@@ -8,2 +8,4 @@
 import com.google.common.collect.Sets;
+import com.minerl.multiagent.RandomHelper;
+import com.minerl.multiagent.recorder.PlayRecorder;
 import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
@@ -20,11 +22,3 @@
 import java.nio.file.Path;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Queue;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
 import java.util.concurrent.Executor;
@@ -473,3 +467,3 @@
       iprofiler.endStartSection("iceandsnow");
-      if (this.rand.nextInt(16) == 0) {
+      if (RandomHelper.getRandom("iceAndSnow").nextInt(16) == 0) {
          BlockPos blockpos2 = this.getHeight(Heightmap.Type.MOTION_BLOCKING, this.getBlockRandomPos(i, 0, j, 15));
@@ -501,3 +495,3 @@
                   if (blockstate.ticksRandomly()) {
-                     blockstate.randomTick(this, blockpos1, this.rand);
+                     blockstate.randomTick(this, blockpos1, RandomHelper.getRandom("randomBlockTick"));
                   }
@@ -506,3 +500,3 @@
                   if (fluidstate.ticksRandomly()) {
-                     fluidstate.randomTick(this, blockpos1, this.rand);
+                     fluidstate.randomTick(this, blockpos1, RandomHelper.getRandom("randomFluidTick"));
                   }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/spawner/WanderingTraderSpawner.java MCP-Reborn/src/main/java/net/minecraft/world/spawner/WanderingTraderSpawner.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/spawner/WanderingTraderSpawner.java	2024-03-10 15:44:47.814123856 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/spawner/WanderingTraderSpawner.java	2024-03-10 18:29:57.557071330 +0800
@@ -26,3 +26,3 @@
 public class WanderingTraderSpawner implements ISpecialSpawner {
-   private final Random random = new Random();
+   private final Random random = new Random(0);
    private final IServerWorldInfo field_234559_b_;
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/spawner/WorldEntitySpawner.java MCP-Reborn/src/main/java/net/minecraft/world/spawner/WorldEntitySpawner.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/spawner/WorldEntitySpawner.java	2024-03-10 15:44:47.814123856 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/spawner/WorldEntitySpawner.java	2024-03-10 18:29:57.557071330 +0800
@@ -2,2 +2,3 @@
 
+import com.minerl.multiagent.RandomHelper;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
@@ -56,3 +57,3 @@
    private static final int field_234960_b_ = (int)Math.pow(17.0D, 2.0D);
-   private static final EntityClassification[] field_234961_c_ = Stream.of(EntityClassification.values()).filter((p_234965_0_) -> {
+   private static final EntityClassification[] entityClassifications = Stream.of(EntityClassification.values()).filter((p_234965_0_) -> {
       return p_234965_0_ != EntityClassification.MISC;
@@ -104,11 +105,12 @@
 
-   public static void func_234979_a_(ServerWorld p_234979_0_, Chunk p_234979_1_, WorldEntitySpawner.EntityDensityManager p_234979_2_, boolean p_234979_3_, boolean p_234979_4_, boolean p_234979_5_) {
-      p_234979_0_.getProfiler().startSection("spawner");
-
-      for(EntityClassification entityclassification : field_234961_c_) {
-         if ((p_234979_3_ || !entityclassification.getPeacefulCreature()) && (p_234979_4_ || entityclassification.getPeacefulCreature()) && (p_234979_5_ || !entityclassification.getAnimal()) && p_234979_2_.func_234991_a_(entityclassification)) {
-            func_234967_a_(entityclassification, p_234979_0_, p_234979_1_, (p_234969_1_, p_234969_2_, p_234969_3_) -> {
-               return p_234979_2_.func_234989_a_(p_234969_1_, p_234969_2_, p_234969_3_);
+   public static void spawnMobs(ServerWorld world, Chunk chunk, WorldEntitySpawner.EntityDensityManager entityDensityManager, boolean spawnPassives, boolean spawnHostiles, boolean spawnAnimals) {
+      // System.out.println("spawning mobs for chunk " + chunk.getPos());
+      world.getProfiler().startSection("spawner");
+
+      for(EntityClassification entityclassification : entityClassifications) {
+         if ((spawnPassives || !entityclassification.getPeacefulCreature()) && (spawnHostiles || entityclassification.getPeacefulCreature()) && (spawnAnimals || !entityclassification.getAnimal()) && entityDensityManager.func_234991_a_(entityclassification)) {
+            func_234967_a_(entityclassification, world, chunk, (p_234969_1_, p_234969_2_, p_234969_3_) -> {
+               return entityDensityManager.func_234989_a_(p_234969_1_, p_234969_2_, p_234969_3_);
             }, (p_234970_1_, p_234970_2_) -> {
-               p_234979_2_.func_234990_a_(p_234970_1_, p_234970_2_);
+               entityDensityManager.func_234990_a_(p_234970_1_, p_234970_2_);
             });
@@ -117,3 +119,3 @@
 
-      p_234979_0_.getProfiler().endSection();
+      world.getProfiler().endSection();
    }
@@ -127,8 +129,11 @@
 
-   public static void func_234966_a_(EntityClassification p_234966_0_, ServerWorld p_234966_1_, IChunk p_234966_2_, BlockPos p_234966_3_, WorldEntitySpawner.IDensityCheck p_234966_4_, WorldEntitySpawner.IOnSpawnDensityAdder p_234966_5_) {
-      StructureManager structuremanager = p_234966_1_.func_241112_a_();
-      ChunkGenerator chunkgenerator = p_234966_1_.getChunkProvider().getChunkGenerator();
-      int i = p_234966_3_.getY();
-      BlockState blockstate = p_234966_2_.getBlockState(p_234966_3_);
-      if (!blockstate.isNormalCube(p_234966_2_, p_234966_3_)) {
+   public static void func_234966_a_(EntityClassification entityClassification, ServerWorld world, IChunk chunk, BlockPos blockPos, WorldEntitySpawner.IDensityCheck p_234966_4_, WorldEntitySpawner.IOnSpawnDensityAdder p_234966_5_) {
+      StructureManager structuremanager = world.func_241112_a_();
+      ChunkGenerator chunkgenerator = world.getChunkProvider().getChunkGenerator();
+      int i = blockPos.getY();
+      Random rand = world.rand;
+      // System.out.println("Spawning " + entityClassification + " entity for chunk " + chunk.getPos() + " @ block " + blockPos);
+      // Random rand = new Random(blockPos.toLong());
+      BlockState blockstate = chunk.getBlockState(blockPos);
+      if (!blockstate.isNormalCube(chunk, blockPos)) {
          BlockPos.Mutable blockpos$mutable = new BlockPos.Mutable();
@@ -137,4 +142,4 @@
          for(int k = 0; k < 3; ++k) {
-            int l = p_234966_3_.getX();
-            int i1 = p_234966_3_.getZ();
+            int l = blockPos.getX();
+            int i1 = blockPos.getZ();
             int j1 = 6;
@@ -142,3 +147,3 @@
             ILivingEntityData ilivingentitydata = null;
-            int k1 = MathHelper.ceil(p_234966_1_.rand.nextFloat() * 4.0F);
+            int k1 = MathHelper.ceil(rand.nextFloat() * 4.0F);
             int l1 = 0;
@@ -146,4 +151,4 @@
             for(int i2 = 0; i2 < k1; ++i2) {
-               l += p_234966_1_.rand.nextInt(6) - p_234966_1_.rand.nextInt(6);
-               i1 += p_234966_1_.rand.nextInt(6) - p_234966_1_.rand.nextInt(6);
+               l += rand.nextInt(6) - rand.nextInt(6);
+               i1 += rand.nextInt(6) - rand.nextInt(6);
                blockpos$mutable.setPos(l, i, i1);
@@ -151,8 +156,9 @@
                double d1 = (double)i1 + 0.5D;
-               PlayerEntity playerentity = p_234966_1_.getClosestPlayer(d0, (double)i, d1, -1.0D, false);
+               // System.out.println("d0, d1, blockpos = " + d0 + "," + d1 + ", " + blockPos);
+               PlayerEntity playerentity = world.getClosestPlayer(d0, (double)i, d1, -1.0D, false);
                if (playerentity != null) {
                   double d2 = playerentity.getDistanceSq(d0, (double)i, d1);
-                  if (func_234978_a_(p_234966_1_, p_234966_2_, blockpos$mutable, d2)) {
+                  if (func_234978_a_(world, chunk, blockpos$mutable, d2)) {
                      if (mobspawninfo$spawners == null) {
-                        mobspawninfo$spawners = func_234977_a_(p_234966_1_, structuremanager, chunkgenerator, p_234966_0_, p_234966_1_.rand, blockpos$mutable);
+                        mobspawninfo$spawners = func_234977_a_(world, structuremanager, chunkgenerator, entityClassification, rand, blockpos$mutable);
                         if (mobspawninfo$spawners == null) {
@@ -161,7 +167,7 @@
 
-                        k1 = mobspawninfo$spawners.minCount + p_234966_1_.rand.nextInt(1 + mobspawninfo$spawners.maxCount - mobspawninfo$spawners.minCount);
+                        k1 = mobspawninfo$spawners.minCount + rand.nextInt(1 + mobspawninfo$spawners.maxCount - mobspawninfo$spawners.minCount);
                      }
 
-                     if (func_234975_a_(p_234966_1_, p_234966_0_, structuremanager, chunkgenerator, mobspawninfo$spawners, blockpos$mutable, d2) && p_234966_4_.test(mobspawninfo$spawners.type, blockpos$mutable, p_234966_2_)) {
-                        MobEntity mobentity = func_234973_a_(p_234966_1_, mobspawninfo$spawners.type);
+                     if (func_234975_a_(world, entityClassification, structuremanager, chunkgenerator, mobspawninfo$spawners, blockpos$mutable, d2) && p_234966_4_.test(mobspawninfo$spawners.type, blockpos$mutable, chunk)) {
+                        MobEntity mobentity = func_234973_a_(world, mobspawninfo$spawners.type);
                         if (mobentity == null) {
@@ -170,9 +176,10 @@
 
-                        mobentity.setLocationAndAngles(d0, (double)i, d1, p_234966_1_.rand.nextFloat() * 360.0F, 0.0F);
-                        if (func_234974_a_(p_234966_1_, mobentity, d2)) {
-                           ilivingentitydata = mobentity.onInitialSpawn(p_234966_1_, p_234966_1_.getDifficultyForLocation(mobentity.getPosition()), SpawnReason.NATURAL, ilivingentitydata, (CompoundNBT)null);
+                        mobentity.setLocationAndAngles(d0, (double)i, d1, rand.nextFloat() * 360.0F, 0.0F);
+                        // System.out.println("Spawned " + mobentity);
+                        if (func_234974_a_(world, mobentity, d2)) {
+                           ilivingentitydata = mobentity.onInitialSpawn(world, world.getDifficultyForLocation(mobentity.getPosition()), SpawnReason.NATURAL, ilivingentitydata, (CompoundNBT)null);
                            ++j;
                            ++l1;
-                           p_234966_1_.func_242417_l(mobentity);
-                           p_234966_5_.run(mobentity, p_234966_2_);
+                           world.func_242417_l(mobentity);
+                           p_234966_5_.run(mobentity, chunk);
                            if (j >= mobentity.getMaxSpawnedInChunk()) {
@@ -267,8 +274,10 @@
 
-   private static BlockPos getRandomHeight(World worldIn, Chunk p_222262_1_) {
-      ChunkPos chunkpos = p_222262_1_.getPos();
-      int i = chunkpos.getXStart() + worldIn.rand.nextInt(16);
-      int j = chunkpos.getZStart() + worldIn.rand.nextInt(16);
-      int k = p_222262_1_.getTopBlockY(Heightmap.Type.WORLD_SURFACE, i, j) + 1;
-      int l = worldIn.rand.nextInt(k + 1);
+   private static BlockPos getRandomHeight(World worldIn, Chunk chunk) {
+      ChunkPos chunkpos = chunk.getPos();
+      Random rand = RandomHelper.getRandom("spawner" + chunkpos, 0);
+      // Random rand = new Random(chunkpos.asLong());
+      int i = chunkpos.getXStart() + rand.nextInt(16);
+      int j = chunkpos.getZStart() + rand.nextInt(16);
+      int k = chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE, i, j) + 1;
+      int l = rand.nextInt(k + 1);
       return new BlockPos(i, l, j);
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/storage/AnvilSaveConverter.java MCP-Reborn/src/main/java/net/minecraft/world/storage/AnvilSaveConverter.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/storage/AnvilSaveConverter.java	2024-03-10 15:44:47.814123856 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/storage/AnvilSaveConverter.java	2024-03-10 18:29:57.557071330 +0800
@@ -55,3 +55,3 @@
       LOGGER.info("Total conversion count is {}", (int)i);
-      DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.func_239770_b_();
+      DynamicRegistries.Impl dynamicregistries$impl = DynamicRegistries.getImpl();
       WorldSettingsImport<INBT> worldsettingsimport = WorldSettingsImport.create(NBTDynamicOps.INSTANCE, IResourceManager.Instance.INSTANCE, dynamicregistries$impl);
@@ -110,3 +110,3 @@
                   CompoundNBT compoundnbt;
-                  try (DataInputStream datainputstream = regionfile.func_222666_a(chunkpos)) {
+                  try (DataInputStream datainputstream = regionfile.getInputStream(chunkpos)) {
                      if (datainputstream == null) {
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/storage/SaveFormat.java MCP-Reborn/src/main/java/net/minecraft/world/storage/SaveFormat.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/storage/SaveFormat.java	2024-03-10 15:44:47.814123856 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/storage/SaveFormat.java	2024-03-10 18:29:57.557071330 +0800
@@ -129,3 +129,3 @@
                try {
-                  flag = SessionLockManager.func_232999_b_(file1.toPath());
+                  flag = SessionLockManager.isUnlocked(file1.toPath());
                } catch (Exception exception) {
@@ -257,3 +257,7 @@
    public SaveFormat.LevelSave getLevelSave(String saveName) throws IOException {
-      return new SaveFormat.LevelSave(saveName);
+      return getLevelSave(savesDir, saveName);
+   }
+
+   public SaveFormat.LevelSave getLevelSave(Path savePath, String saveName) throws IOException {
+      return new SaveFormat.LevelSave(savePath, saveName);
    }
@@ -266,6 +270,6 @@
 
-      public LevelSave(String saveName) throws IOException {
+      public LevelSave(Path savePath, String saveName) throws IOException {
          this.saveName = saveName;
-         this.saveDir = SaveFormat.this.savesDir.resolve(saveName);
-         this.saveDirLockManager = SessionLockManager.func_232998_a_(this.saveDir);
+         this.saveDir = savePath.resolve(saveName);
+         this.saveDirLockManager = null; // SessionLockManager.getDirLock(this.saveDir);
       }
@@ -287,5 +291,5 @@
       private void validateSaveDirLock() {
-         if (!this.saveDirLockManager.func_232997_a_()) {
-            throw new IllegalStateException("Lock is no longer valid");
-         }
+//         if (!this.saveDirLockManager.isLockValid()) {
+//            throw new IllegalStateException("Lock is no longer valid");
+//         }
       }
@@ -378,3 +382,3 @@
                         if (p_postVisitDirectory_1_.equals(LevelSave.this.saveDir)) {
-                           LevelSave.this.saveDirLockManager.close();
+                           // LevelSave.this.saveDirLockManager.close();
                            Files.deleteIfExists(path);
@@ -456,3 +460,3 @@
       public void close() throws IOException {
-         this.saveDirLockManager.close();
+         // this.saveDirLockManager.close();
       }
diff -u1 -rN MCP-Reborn-vanilla/src/main/java/net/minecraft/world/World.java MCP-Reborn/src/main/java/net/minecraft/world/World.java
--- MCP-Reborn-vanilla/src/main/java/net/minecraft/world/World.java	2024-03-10 15:44:47.782123771 +0800
+++ MCP-Reborn/src/main/java/net/minecraft/world/World.java	2024-03-10 18:29:57.545071111 +0800
@@ -13,2 +13,4 @@
 import javax.annotation.Nullable;
+
+import com.minerl.multiagent.RandomHelper;
 import net.minecraft.block.AbstractFireBlock;
@@ -79,3 +81,3 @@
    private int skylightSubtracted;
-   protected int updateLCG = (new Random()).nextInt();
+   protected int updateLCG = (new Random(0)).nextInt();
    protected final int DIST_HASH_MAGIC = 1013904223;
@@ -85,3 +87,3 @@
    protected float thunderingStrength;
-   public final Random rand = new Random();
+   public final Random rand = RandomHelper.getRandom();
    private final DimensionType dimensionType;
diff -u1 -rN MCP-Reborn-vanilla/src/main/resources/index.txt MCP-Reborn/src/main/resources/index.txt
--- MCP-Reborn-vanilla/src/main/resources/index.txt	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/resources/index.txt	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,2620 @@
+icons/icon_16x16.png
+icons/icon_32x32.png
+icons/minecraft.icns
+minecraft/icons/icon_16x16.png
+minecraft/icons/icon_32x32.png
+minecraft/icons/minecraft.icns
+minecraft/lang/af_za.json
+minecraft/lang/ar_sa.json
+minecraft/lang/ast_es.json
+minecraft/lang/az_az.json
+minecraft/lang/ba_ru.json
+minecraft/lang/bar.json
+minecraft/lang/be_by.json
+minecraft/lang/bg_bg.json
+minecraft/lang/br_fr.json
+minecraft/lang/brb.json
+minecraft/lang/bs_ba.json
+minecraft/lang/ca_es.json
+minecraft/lang/cs_cz.json
+minecraft/lang/cy_gb.json
+minecraft/lang/da_dk.json
+minecraft/lang/de_at.json
+minecraft/lang/de_ch.json
+minecraft/lang/de_de.json
+minecraft/lang/el_gr.json
+minecraft/lang/en_au.json
+minecraft/lang/en_ca.json
+minecraft/lang/en_gb.json
+minecraft/lang/en_nz.json
+minecraft/lang/en_pt.json
+minecraft/lang/en_ud.json
+minecraft/lang/enp.json
+minecraft/lang/enws.json
+minecraft/lang/eo_uy.json
+minecraft/lang/es_ar.json
+minecraft/lang/es_cl.json
+minecraft/lang/es_ec.json
+minecraft/lang/es_es.json
+minecraft/lang/es_mx.json
+minecraft/lang/es_uy.json
+minecraft/lang/es_ve.json
+minecraft/lang/esan.json
+minecraft/lang/et_ee.json
+minecraft/lang/eu_es.json
+minecraft/lang/fa_ir.json
+minecraft/lang/fi_fi.json
+minecraft/lang/fil_ph.json
+minecraft/lang/fo_fo.json
+minecraft/lang/fr_ca.json
+minecraft/lang/fr_fr.json
+minecraft/lang/fra_de.json
+minecraft/lang/fy_nl.json
+minecraft/lang/ga_ie.json
+minecraft/lang/gd_gb.json
+minecraft/lang/gl_es.json
+minecraft/lang/got_de.json
+minecraft/lang/gv_im.json
+minecraft/lang/haw_us.json
+minecraft/lang/he_il.json
+minecraft/lang/hi_in.json
+minecraft/lang/hr_hr.json
+minecraft/lang/hu_hu.json
+minecraft/lang/hy_am.json
+minecraft/lang/id_id.json
+minecraft/lang/ig_ng.json
+minecraft/lang/io_en.json
+minecraft/lang/is_is.json
+minecraft/lang/isv.json
+minecraft/lang/it_it.json
+minecraft/lang/ja_jp.json
+minecraft/lang/jbo_en.json
+minecraft/lang/ka_ge.json
+minecraft/lang/kab_kab.json
+minecraft/lang/kk_kz.json
+minecraft/lang/kn_in.json
+minecraft/lang/ko_kr.json
+minecraft/lang/ksh.json
+minecraft/lang/kw_gb.json
+minecraft/lang/la_la.json
+minecraft/lang/lb_lu.json
+minecraft/lang/li_li.json
+minecraft/lang/lol_us.json
+minecraft/lang/lt_lt.json
+minecraft/lang/lv_lv.json
+minecraft/lang/mi_nz.json
+minecraft/lang/mk_mk.json
+minecraft/lang/mn_mn.json
+minecraft/lang/moh_ca.json
+minecraft/lang/ms_my.json
+minecraft/lang/mt_mt.json
+minecraft/lang/nds_de.json
+minecraft/lang/nl_be.json
+minecraft/lang/nl_nl.json
+minecraft/lang/nn_no.json
+minecraft/lang/no_no.json
+minecraft/lang/nuk.json
+minecraft/lang/oc_fr.json
+minecraft/lang/oj_ca.json
+minecraft/lang/ovd.json
+minecraft/lang/pl_pl.json
+minecraft/lang/pt_br.json
+minecraft/lang/pt_pt.json
+minecraft/lang/qya_aa.json
+minecraft/lang/ro_ro.json
+minecraft/lang/ru_ru.json
+minecraft/lang/scn.json
+minecraft/lang/se_no.json
+minecraft/lang/sk_sk.json
+minecraft/lang/sl_si.json
+minecraft/lang/so_so.json
+minecraft/lang/sq_al.json
+minecraft/lang/sr_sp.json
+minecraft/lang/sv_se.json
+minecraft/lang/swg.json
+minecraft/lang/sxu.json
+minecraft/lang/szl.json
+minecraft/lang/ta_in.json
+minecraft/lang/th_th.json
+minecraft/lang/tl_ph.json
+minecraft/lang/tlh_aa.json
+minecraft/lang/tr_tr.json
+minecraft/lang/tt_ru.json
+minecraft/lang/tzl_tzl.json
+minecraft/lang/uk_ua.json
+minecraft/lang/val_es.json
+minecraft/lang/vec_it.json
+minecraft/lang/vi_vn.json
+minecraft/lang/yi_de.json
+minecraft/lang/yo_ng.json
+minecraft/lang/zh_cn.json
+minecraft/lang/zh_tw.json
+minecraft/resourcepacks/programmer_art.zip
+minecraft/sounds.json
+minecraft/sounds/ambient/cave/cave1.ogg
+minecraft/sounds/ambient/cave/cave10.ogg
+minecraft/sounds/ambient/cave/cave11.ogg
+minecraft/sounds/ambient/cave/cave12.ogg
+minecraft/sounds/ambient/cave/cave13.ogg
+minecraft/sounds/ambient/cave/cave14.ogg
+minecraft/sounds/ambient/cave/cave15.ogg
+minecraft/sounds/ambient/cave/cave16.ogg
+minecraft/sounds/ambient/cave/cave17.ogg
+minecraft/sounds/ambient/cave/cave18.ogg
+minecraft/sounds/ambient/cave/cave19.ogg
+minecraft/sounds/ambient/cave/cave2.ogg
+minecraft/sounds/ambient/cave/cave3.ogg
+minecraft/sounds/ambient/cave/cave4.ogg
+minecraft/sounds/ambient/cave/cave5.ogg
+minecraft/sounds/ambient/cave/cave6.ogg
+minecraft/sounds/ambient/cave/cave7.ogg
+minecraft/sounds/ambient/cave/cave8.ogg
+minecraft/sounds/ambient/cave/cave9.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/active1.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/active2.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/active3.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/active4.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/ambience.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/basaltground1.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/basaltground2.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/basaltground3.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/basaltground4.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/click1.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/click2.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/click3.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/click4.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/click5.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/click6.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/click7.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/click8.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/debris1.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/debris2.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/debris3.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/heavy_click1.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/heavy_click2.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/long_debris1.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/long_debris2.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/plode1.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/plode2.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/plode3.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/twist1.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/twist2.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/twist3.ogg
+minecraft/sounds/ambient/nether/basalt_deltas/twist4.ogg
+minecraft/sounds/ambient/nether/crimson_forest/addition1.ogg
+minecraft/sounds/ambient/nether/crimson_forest/addition2.ogg
+minecraft/sounds/ambient/nether/crimson_forest/addition3.ogg
+minecraft/sounds/ambient/nether/crimson_forest/ambience.ogg
+minecraft/sounds/ambient/nether/crimson_forest/mood1.ogg
+minecraft/sounds/ambient/nether/crimson_forest/mood2.ogg
+minecraft/sounds/ambient/nether/crimson_forest/mood3.ogg
+minecraft/sounds/ambient/nether/crimson_forest/mood4.ogg
+minecraft/sounds/ambient/nether/crimson_forest/particles1.ogg
+minecraft/sounds/ambient/nether/crimson_forest/particles2.ogg
+minecraft/sounds/ambient/nether/crimson_forest/particles3.ogg
+minecraft/sounds/ambient/nether/crimson_forest/shine1.ogg
+minecraft/sounds/ambient/nether/crimson_forest/shine2.ogg
+minecraft/sounds/ambient/nether/crimson_forest/shine3.ogg
+minecraft/sounds/ambient/nether/crimson_forest/shroom1.ogg
+minecraft/sounds/ambient/nether/crimson_forest/shroom2.ogg
+minecraft/sounds/ambient/nether/crimson_forest/shroom3.ogg
+minecraft/sounds/ambient/nether/crimson_forest/twang1.ogg
+minecraft/sounds/ambient/nether/crimson_forest/voom1.ogg
+minecraft/sounds/ambient/nether/crimson_forest/voom2.ogg
+minecraft/sounds/ambient/nether/nether_wastes/addition1.ogg
+minecraft/sounds/ambient/nether/nether_wastes/addition2.ogg
+minecraft/sounds/ambient/nether/nether_wastes/addition3.ogg
+minecraft/sounds/ambient/nether/nether_wastes/addition4.ogg
+minecraft/sounds/ambient/nether/nether_wastes/addition5.ogg
+minecraft/sounds/ambient/nether/nether_wastes/addition6.ogg
+minecraft/sounds/ambient/nether/nether_wastes/addition7.ogg
+minecraft/sounds/ambient/nether/nether_wastes/addition8.ogg
+minecraft/sounds/ambient/nether/nether_wastes/ambience.ogg
+minecraft/sounds/ambient/nether/nether_wastes/dark1.ogg
+minecraft/sounds/ambient/nether/nether_wastes/dark2.ogg
+minecraft/sounds/ambient/nether/nether_wastes/ground1.ogg
+minecraft/sounds/ambient/nether/nether_wastes/ground2.ogg
+minecraft/sounds/ambient/nether/nether_wastes/ground3.ogg
+minecraft/sounds/ambient/nether/nether_wastes/ground4.ogg
+minecraft/sounds/ambient/nether/nether_wastes/mood1.ogg
+minecraft/sounds/ambient/nether/nether_wastes/mood2.ogg
+minecraft/sounds/ambient/nether/nether_wastes/mood3.ogg
+minecraft/sounds/ambient/nether/nether_wastes/mood4.ogg
+minecraft/sounds/ambient/nether/nether_wastes/mood5.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/ambience.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/mood1.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/mood2.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/mood3.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/mood4.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/sand1.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/sand2.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/sand3.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/voices1.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/voices2.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/voices3.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/voices4.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/voices5.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/whisper1.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/whisper2.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/whisper3.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/whisper4.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/whisper5.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/whisper6.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/whisper7.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/whisper8.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/wind1.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/wind2.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/wind3.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/wind4.ogg
+minecraft/sounds/ambient/nether/soulsand_valley/with1.ogg
+minecraft/sounds/ambient/nether/warped_forest/addition1.ogg
+minecraft/sounds/ambient/nether/warped_forest/addition2.ogg
+minecraft/sounds/ambient/nether/warped_forest/addition3.ogg
+minecraft/sounds/ambient/nether/warped_forest/addition4.ogg
+minecraft/sounds/ambient/nether/warped_forest/addition5.ogg
+minecraft/sounds/ambient/nether/warped_forest/addition6.ogg
+minecraft/sounds/ambient/nether/warped_forest/ambience.ogg
+minecraft/sounds/ambient/nether/warped_forest/creak1.ogg
+minecraft/sounds/ambient/nether/warped_forest/creak2.ogg
+minecraft/sounds/ambient/nether/warped_forest/creak3.ogg
+minecraft/sounds/ambient/nether/warped_forest/creak4.ogg
+minecraft/sounds/ambient/nether/warped_forest/creak5.ogg
+minecraft/sounds/ambient/nether/warped_forest/enish1.ogg
+minecraft/sounds/ambient/nether/warped_forest/enish2.ogg
+minecraft/sounds/ambient/nether/warped_forest/enish3.ogg
+minecraft/sounds/ambient/nether/warped_forest/help1.ogg
+minecraft/sounds/ambient/nether/warped_forest/help2.ogg
+minecraft/sounds/ambient/nether/warped_forest/here1.ogg
+minecraft/sounds/ambient/nether/warped_forest/here2.ogg
+minecraft/sounds/ambient/nether/warped_forest/here3.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood1.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood2.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood3.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood4.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood5.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood6.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood7.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood8.ogg
+minecraft/sounds/ambient/nether/warped_forest/mood9.ogg
+minecraft/sounds/ambient/underwater/additions/animal1.ogg
+minecraft/sounds/ambient/underwater/additions/animal2.ogg
+minecraft/sounds/ambient/underwater/additions/bass_whale1.ogg
+minecraft/sounds/ambient/underwater/additions/bass_whale2.ogg
+minecraft/sounds/ambient/underwater/additions/bubbles1.ogg
+minecraft/sounds/ambient/underwater/additions/bubbles2.ogg
+minecraft/sounds/ambient/underwater/additions/bubbles3.ogg
+minecraft/sounds/ambient/underwater/additions/bubbles4.ogg
+minecraft/sounds/ambient/underwater/additions/bubbles5.ogg
+minecraft/sounds/ambient/underwater/additions/bubbles6.ogg
+minecraft/sounds/ambient/underwater/additions/crackles1.ogg
+minecraft/sounds/ambient/underwater/additions/crackles2.ogg
+minecraft/sounds/ambient/underwater/additions/dark1.ogg
+minecraft/sounds/ambient/underwater/additions/dark2.ogg
+minecraft/sounds/ambient/underwater/additions/dark3.ogg
+minecraft/sounds/ambient/underwater/additions/dark4.ogg
+minecraft/sounds/ambient/underwater/additions/driplets1.ogg
+minecraft/sounds/ambient/underwater/additions/driplets2.ogg
+minecraft/sounds/ambient/underwater/additions/earth_crack.ogg
+minecraft/sounds/ambient/underwater/additions/water1.ogg
+minecraft/sounds/ambient/underwater/additions/water2.ogg
+minecraft/sounds/ambient/underwater/enter1.ogg
+minecraft/sounds/ambient/underwater/enter2.ogg
+minecraft/sounds/ambient/underwater/enter3.ogg
+minecraft/sounds/ambient/underwater/exit1.ogg
+minecraft/sounds/ambient/underwater/exit2.ogg
+minecraft/sounds/ambient/underwater/exit3.ogg
+minecraft/sounds/ambient/underwater/underwater_ambience.ogg
+minecraft/sounds/ambient/weather/rain1.ogg
+minecraft/sounds/ambient/weather/rain2.ogg
+minecraft/sounds/ambient/weather/rain3.ogg
+minecraft/sounds/ambient/weather/rain4.ogg
+minecraft/sounds/ambient/weather/rain5.ogg
+minecraft/sounds/ambient/weather/rain6.ogg
+minecraft/sounds/ambient/weather/rain7.ogg
+minecraft/sounds/ambient/weather/rain8.ogg
+minecraft/sounds/ambient/weather/thunder1.ogg
+minecraft/sounds/ambient/weather/thunder2.ogg
+minecraft/sounds/ambient/weather/thunder3.ogg
+minecraft/sounds/block/ancient_debris/break1.ogg
+minecraft/sounds/block/ancient_debris/break2.ogg
+minecraft/sounds/block/ancient_debris/break3.ogg
+minecraft/sounds/block/ancient_debris/break4.ogg
+minecraft/sounds/block/ancient_debris/break5.ogg
+minecraft/sounds/block/bamboo/place1.ogg
+minecraft/sounds/block/bamboo/place2.ogg
+minecraft/sounds/block/bamboo/place3.ogg
+minecraft/sounds/block/bamboo/place4.ogg
+minecraft/sounds/block/bamboo/place5.ogg
+minecraft/sounds/block/bamboo/place6.ogg
+minecraft/sounds/block/bamboo/sapling_hit1.ogg
+minecraft/sounds/block/bamboo/sapling_hit2.ogg
+minecraft/sounds/block/bamboo/sapling_hit3.ogg
+minecraft/sounds/block/bamboo/sapling_hit4.ogg
+minecraft/sounds/block/bamboo/sapling_hit5.ogg
+minecraft/sounds/block/bamboo/sapling_place1.ogg
+minecraft/sounds/block/bamboo/sapling_place2.ogg
+minecraft/sounds/block/bamboo/sapling_place3.ogg
+minecraft/sounds/block/bamboo/sapling_place4.ogg
+minecraft/sounds/block/bamboo/sapling_place5.ogg
+minecraft/sounds/block/bamboo/sapling_place6.ogg
+minecraft/sounds/block/bamboo/step1.ogg
+minecraft/sounds/block/bamboo/step2.ogg
+minecraft/sounds/block/bamboo/step3.ogg
+minecraft/sounds/block/bamboo/step4.ogg
+minecraft/sounds/block/bamboo/step5.ogg
+minecraft/sounds/block/bamboo/step6.ogg
+minecraft/sounds/block/barrel/close.ogg
+minecraft/sounds/block/barrel/open1.ogg
+minecraft/sounds/block/barrel/open2.ogg
+minecraft/sounds/block/basalt/break1.ogg
+minecraft/sounds/block/basalt/break2.ogg
+minecraft/sounds/block/basalt/break3.ogg
+minecraft/sounds/block/basalt/break4.ogg
+minecraft/sounds/block/basalt/break5.ogg
+minecraft/sounds/block/basalt/step1.ogg
+minecraft/sounds/block/basalt/step2.ogg
+minecraft/sounds/block/basalt/step3.ogg
+minecraft/sounds/block/basalt/step4.ogg
+minecraft/sounds/block/basalt/step5.ogg
+minecraft/sounds/block/basalt/step6.ogg
+minecraft/sounds/block/beacon/activate.ogg
+minecraft/sounds/block/beacon/ambient.ogg
+minecraft/sounds/block/beacon/deactivate.ogg
+minecraft/sounds/block/beacon/power1.ogg
+minecraft/sounds/block/beacon/power2.ogg
+minecraft/sounds/block/beacon/power3.ogg
+minecraft/sounds/block/beehive/drip1.ogg
+minecraft/sounds/block/beehive/drip2.ogg
+minecraft/sounds/block/beehive/drip3.ogg
+minecraft/sounds/block/beehive/drip4.ogg
+minecraft/sounds/block/beehive/drip5.ogg
+minecraft/sounds/block/beehive/drip6.ogg
+minecraft/sounds/block/beehive/enter.ogg
+minecraft/sounds/block/beehive/exit.ogg
+minecraft/sounds/block/beehive/shear.ogg
+minecraft/sounds/block/beehive/work1.ogg
+minecraft/sounds/block/beehive/work2.ogg
+minecraft/sounds/block/beehive/work3.ogg
+minecraft/sounds/block/beehive/work4.ogg
+minecraft/sounds/block/bell/bell_use01.ogg
+minecraft/sounds/block/bell/bell_use02.ogg
+minecraft/sounds/block/bell/resonate.ogg
+minecraft/sounds/block/blastfurnace/blastfurnace1.ogg
+minecraft/sounds/block/blastfurnace/blastfurnace2.ogg
+minecraft/sounds/block/blastfurnace/blastfurnace3.ogg
+minecraft/sounds/block/blastfurnace/blastfurnace4.ogg
+minecraft/sounds/block/blastfurnace/blastfurnace5.ogg
+minecraft/sounds/block/bone_block/break1.ogg
+minecraft/sounds/block/bone_block/break2.ogg
+minecraft/sounds/block/bone_block/break3.ogg
+minecraft/sounds/block/bone_block/break4.ogg
+minecraft/sounds/block/bone_block/break5.ogg
+minecraft/sounds/block/bone_block/step1.ogg
+minecraft/sounds/block/bone_block/step2.ogg
+minecraft/sounds/block/bone_block/step3.ogg
+minecraft/sounds/block/bone_block/step4.ogg
+minecraft/sounds/block/bone_block/step5.ogg
+minecraft/sounds/block/brewing_stand/brew1.ogg
+minecraft/sounds/block/brewing_stand/brew2.ogg
+minecraft/sounds/block/bubble_column/bubble1.ogg
+minecraft/sounds/block/bubble_column/bubble2.ogg
+minecraft/sounds/block/bubble_column/bubble3.ogg
+minecraft/sounds/block/bubble_column/upwards_ambient1.ogg
+minecraft/sounds/block/bubble_column/upwards_ambient2.ogg
+minecraft/sounds/block/bubble_column/upwards_ambient3.ogg
+minecraft/sounds/block/bubble_column/upwards_ambient4.ogg
+minecraft/sounds/block/bubble_column/upwards_ambient5.ogg
+minecraft/sounds/block/bubble_column/upwards_inside.ogg
+minecraft/sounds/block/bubble_column/whirlpool_ambient1.ogg
+minecraft/sounds/block/bubble_column/whirlpool_ambient2.ogg
+minecraft/sounds/block/bubble_column/whirlpool_ambient3.ogg
+minecraft/sounds/block/bubble_column/whirlpool_ambient4.ogg
+minecraft/sounds/block/bubble_column/whirlpool_ambient5.ogg
+minecraft/sounds/block/bubble_column/whirlpool_inside.ogg
+minecraft/sounds/block/campfire/crackle1.ogg
+minecraft/sounds/block/campfire/crackle2.ogg
+minecraft/sounds/block/campfire/crackle3.ogg
+minecraft/sounds/block/campfire/crackle4.ogg
+minecraft/sounds/block/campfire/crackle5.ogg
+minecraft/sounds/block/campfire/crackle6.ogg
+minecraft/sounds/block/cauldron/dye1.ogg
+minecraft/sounds/block/cauldron/dye2.ogg
+minecraft/sounds/block/cauldron/dye3.ogg
+minecraft/sounds/block/chain/break1.ogg
+minecraft/sounds/block/chain/break2.ogg
+minecraft/sounds/block/chain/break3.ogg
+minecraft/sounds/block/chain/break4.ogg
+minecraft/sounds/block/chain/step1.ogg
+minecraft/sounds/block/chain/step2.ogg
+minecraft/sounds/block/chain/step3.ogg
+minecraft/sounds/block/chain/step4.ogg
+minecraft/sounds/block/chain/step5.ogg
+minecraft/sounds/block/chain/step6.ogg
+minecraft/sounds/block/chest/close.ogg
+minecraft/sounds/block/chest/close2.ogg
+minecraft/sounds/block/chest/close3.ogg
+minecraft/sounds/block/chest/open.ogg
+minecraft/sounds/block/chorus_flower/death1.ogg
+minecraft/sounds/block/chorus_flower/death2.ogg
+minecraft/sounds/block/chorus_flower/death3.ogg
+minecraft/sounds/block/chorus_flower/grow1.ogg
+minecraft/sounds/block/chorus_flower/grow2.ogg
+minecraft/sounds/block/chorus_flower/grow3.ogg
+minecraft/sounds/block/chorus_flower/grow4.ogg
+minecraft/sounds/block/composter/empty1.ogg
+minecraft/sounds/block/composter/empty2.ogg
+minecraft/sounds/block/composter/empty3.ogg
+minecraft/sounds/block/composter/fill1.ogg
+minecraft/sounds/block/composter/fill2.ogg
+minecraft/sounds/block/composter/fill3.ogg
+minecraft/sounds/block/composter/fill4.ogg
+minecraft/sounds/block/composter/fill_success1.ogg
+minecraft/sounds/block/composter/fill_success2.ogg
+minecraft/sounds/block/composter/fill_success3.ogg
+minecraft/sounds/block/composter/fill_success4.ogg
+minecraft/sounds/block/composter/ready1.ogg
+minecraft/sounds/block/composter/ready2.ogg
+minecraft/sounds/block/composter/ready3.ogg
+minecraft/sounds/block/composter/ready4.ogg
+minecraft/sounds/block/conduit/activate.ogg
+minecraft/sounds/block/conduit/ambient.ogg
+minecraft/sounds/block/conduit/attack1.ogg
+minecraft/sounds/block/conduit/attack2.ogg
+minecraft/sounds/block/conduit/attack3.ogg
+minecraft/sounds/block/conduit/deactivate.ogg
+minecraft/sounds/block/conduit/short1.ogg
+minecraft/sounds/block/conduit/short2.ogg
+minecraft/sounds/block/conduit/short3.ogg
+minecraft/sounds/block/conduit/short4.ogg
+minecraft/sounds/block/conduit/short5.ogg
+minecraft/sounds/block/conduit/short6.ogg
+minecraft/sounds/block/conduit/short7.ogg
+minecraft/sounds/block/conduit/short8.ogg
+minecraft/sounds/block/conduit/short9.ogg
+minecraft/sounds/block/enchantment_table/enchant1.ogg
+minecraft/sounds/block/enchantment_table/enchant2.ogg
+minecraft/sounds/block/enchantment_table/enchant3.ogg
+minecraft/sounds/block/end_portal/endportal.ogg
+minecraft/sounds/block/end_portal/eyeplace1.ogg
+minecraft/sounds/block/end_portal/eyeplace2.ogg
+minecraft/sounds/block/end_portal/eyeplace3.ogg
+minecraft/sounds/block/enderchest/close.ogg
+minecraft/sounds/block/enderchest/open.ogg
+minecraft/sounds/block/fence_gate/close1.ogg
+minecraft/sounds/block/fence_gate/close2.ogg
+minecraft/sounds/block/fence_gate/open1.ogg
+minecraft/sounds/block/fence_gate/open2.ogg
+minecraft/sounds/block/fletching_table/fletching_table1.ogg
+minecraft/sounds/block/fletching_table/fletching_table2.ogg
+minecraft/sounds/block/fungus/break1.ogg
+minecraft/sounds/block/fungus/break2.ogg
+minecraft/sounds/block/fungus/break3.ogg
+minecraft/sounds/block/fungus/break4.ogg
+minecraft/sounds/block/fungus/break5.ogg
+minecraft/sounds/block/fungus/break6.ogg
+minecraft/sounds/block/furnace/fire_crackle1.ogg
+minecraft/sounds/block/furnace/fire_crackle2.ogg
+minecraft/sounds/block/furnace/fire_crackle3.ogg
+minecraft/sounds/block/furnace/fire_crackle4.ogg
+minecraft/sounds/block/furnace/fire_crackle5.ogg
+minecraft/sounds/block/grindstone/grindstone1.ogg
+minecraft/sounds/block/grindstone/grindstone2.ogg
+minecraft/sounds/block/grindstone/grindstone3.ogg
+minecraft/sounds/block/honeyblock/break1.ogg
+minecraft/sounds/block/honeyblock/break2.ogg
+minecraft/sounds/block/honeyblock/break3.ogg
+minecraft/sounds/block/honeyblock/break4.ogg
+minecraft/sounds/block/honeyblock/break5.ogg
+minecraft/sounds/block/honeyblock/slide1.ogg
+minecraft/sounds/block/honeyblock/slide2.ogg
+minecraft/sounds/block/honeyblock/slide3.ogg
+minecraft/sounds/block/honeyblock/slide4.ogg
+minecraft/sounds/block/honeyblock/step1.ogg
+minecraft/sounds/block/honeyblock/step2.ogg
+minecraft/sounds/block/honeyblock/step3.ogg
+minecraft/sounds/block/honeyblock/step4.ogg
+minecraft/sounds/block/honeyblock/step5.ogg
+minecraft/sounds/block/iron_door/close1.ogg
+minecraft/sounds/block/iron_door/close2.ogg
+minecraft/sounds/block/iron_door/close3.ogg
+minecraft/sounds/block/iron_door/close4.ogg
+minecraft/sounds/block/iron_door/open1.ogg
+minecraft/sounds/block/iron_door/open2.ogg
+minecraft/sounds/block/iron_door/open3.ogg
+minecraft/sounds/block/iron_door/open4.ogg
+minecraft/sounds/block/iron_trapdoor/close1.ogg
+minecraft/sounds/block/iron_trapdoor/close2.ogg
+minecraft/sounds/block/iron_trapdoor/close3.ogg
+minecraft/sounds/block/iron_trapdoor/close4.ogg
+minecraft/sounds/block/iron_trapdoor/open1.ogg
+minecraft/sounds/block/iron_trapdoor/open2.ogg
+minecraft/sounds/block/iron_trapdoor/open3.ogg
+minecraft/sounds/block/iron_trapdoor/open4.ogg
+minecraft/sounds/block/lantern/break1.ogg
+minecraft/sounds/block/lantern/break2.ogg
+minecraft/sounds/block/lantern/break3.ogg
+minecraft/sounds/block/lantern/break4.ogg
+minecraft/sounds/block/lantern/break5.ogg
+minecraft/sounds/block/lantern/break6.ogg
+minecraft/sounds/block/lantern/place1.ogg
+minecraft/sounds/block/lantern/place2.ogg
+minecraft/sounds/block/lantern/place3.ogg
+minecraft/sounds/block/lantern/place4.ogg
+minecraft/sounds/block/lantern/place5.ogg
+minecraft/sounds/block/lantern/place6.ogg
+minecraft/sounds/block/lodestone/lock1.ogg
+minecraft/sounds/block/lodestone/lock2.ogg
+minecraft/sounds/block/lodestone/place1.ogg
+minecraft/sounds/block/lodestone/place2.ogg
+minecraft/sounds/block/lodestone/place3.ogg
+minecraft/sounds/block/lodestone/place4.ogg
+minecraft/sounds/block/nether_bricks/break1.ogg
+minecraft/sounds/block/nether_bricks/break2.ogg
+minecraft/sounds/block/nether_bricks/break3.ogg
+minecraft/sounds/block/nether_bricks/break4.ogg
+minecraft/sounds/block/nether_bricks/break5.ogg
+minecraft/sounds/block/nether_bricks/break6.ogg
+minecraft/sounds/block/nether_bricks/step1.ogg
+minecraft/sounds/block/nether_bricks/step2.ogg
+minecraft/sounds/block/nether_bricks/step3.ogg
+minecraft/sounds/block/nether_bricks/step4.ogg
+minecraft/sounds/block/nether_bricks/step5.ogg
+minecraft/sounds/block/nether_bricks/step6.ogg
+minecraft/sounds/block/nether_ore/break1.ogg
+minecraft/sounds/block/nether_ore/break2.ogg
+minecraft/sounds/block/nether_ore/break3.ogg
+minecraft/sounds/block/nether_ore/break4.ogg
+minecraft/sounds/block/nether_ore/step1.ogg
+minecraft/sounds/block/nether_ore/step2.ogg
+minecraft/sounds/block/nether_ore/step3.ogg
+minecraft/sounds/block/nether_ore/step4.ogg
+minecraft/sounds/block/nether_ore/step5.ogg
+minecraft/sounds/block/nether_sprouts/break1.ogg
+minecraft/sounds/block/nether_sprouts/break2.ogg
+minecraft/sounds/block/nether_sprouts/break3.ogg
+minecraft/sounds/block/nether_sprouts/break4.ogg
+minecraft/sounds/block/nether_sprouts/step1.ogg
+minecraft/sounds/block/nether_sprouts/step2.ogg
+minecraft/sounds/block/nether_sprouts/step3.ogg
+minecraft/sounds/block/nether_sprouts/step4.ogg
+minecraft/sounds/block/nether_sprouts/step5.ogg
+minecraft/sounds/block/netherite/break1.ogg
+minecraft/sounds/block/netherite/break2.ogg
+minecraft/sounds/block/netherite/break3.ogg
+minecraft/sounds/block/netherite/break4.ogg
+minecraft/sounds/block/netherite/step1.ogg
+minecraft/sounds/block/netherite/step2.ogg
+minecraft/sounds/block/netherite/step3.ogg
+minecraft/sounds/block/netherite/step4.ogg
+minecraft/sounds/block/netherite/step5.ogg
+minecraft/sounds/block/netherite/step6.ogg
+minecraft/sounds/block/netherrack/break1.ogg
+minecraft/sounds/block/netherrack/break2.ogg
+minecraft/sounds/block/netherrack/break3.ogg
+minecraft/sounds/block/netherrack/break4.ogg
+minecraft/sounds/block/netherrack/break5.ogg
+minecraft/sounds/block/netherrack/break6.ogg
+minecraft/sounds/block/netherrack/step1.ogg
+minecraft/sounds/block/netherrack/step2.ogg
+minecraft/sounds/block/netherrack/step3.ogg
+minecraft/sounds/block/netherrack/step4.ogg
+minecraft/sounds/block/netherrack/step5.ogg
+minecraft/sounds/block/netherrack/step6.ogg
+minecraft/sounds/block/netherwart/break1.ogg
+minecraft/sounds/block/netherwart/break2.ogg
+minecraft/sounds/block/netherwart/break3.ogg
+minecraft/sounds/block/netherwart/break4.ogg
+minecraft/sounds/block/netherwart/break5.ogg
+minecraft/sounds/block/netherwart/break6.ogg
+minecraft/sounds/block/netherwart/step1.ogg
+minecraft/sounds/block/netherwart/step2.ogg
+minecraft/sounds/block/netherwart/step3.ogg
+minecraft/sounds/block/netherwart/step4.ogg
+minecraft/sounds/block/netherwart/step5.ogg
+minecraft/sounds/block/nylium/break1.ogg
+minecraft/sounds/block/nylium/break2.ogg
+minecraft/sounds/block/nylium/break3.ogg
+minecraft/sounds/block/nylium/break4.ogg
+minecraft/sounds/block/nylium/break5.ogg
+minecraft/sounds/block/nylium/break6.ogg
+minecraft/sounds/block/nylium/step1.ogg
+minecraft/sounds/block/nylium/step2.ogg
+minecraft/sounds/block/nylium/step3.ogg
+minecraft/sounds/block/nylium/step4.ogg
+minecraft/sounds/block/nylium/step5.ogg
+minecraft/sounds/block/nylium/step6.ogg
+minecraft/sounds/block/pumpkin/carve1.ogg
+minecraft/sounds/block/pumpkin/carve2.ogg
+minecraft/sounds/block/respawn_anchor/ambient1.ogg
+minecraft/sounds/block/respawn_anchor/ambient2.ogg
+minecraft/sounds/block/respawn_anchor/ambient3.ogg
+minecraft/sounds/block/respawn_anchor/charge1.ogg
+minecraft/sounds/block/respawn_anchor/charge2.ogg
+minecraft/sounds/block/respawn_anchor/charge3.ogg
+minecraft/sounds/block/respawn_anchor/deplete1.ogg
+minecraft/sounds/block/respawn_anchor/deplete2.ogg
+minecraft/sounds/block/respawn_anchor/set_spawn1.ogg
+minecraft/sounds/block/respawn_anchor/set_spawn2.ogg
+minecraft/sounds/block/respawn_anchor/set_spawn3.ogg
+minecraft/sounds/block/roots/break1.ogg
+minecraft/sounds/block/roots/break2.ogg
+minecraft/sounds/block/roots/break3.ogg
+minecraft/sounds/block/roots/break4.ogg
+minecraft/sounds/block/roots/break5.ogg
+minecraft/sounds/block/roots/break6.ogg
+minecraft/sounds/block/roots/step1.ogg
+minecraft/sounds/block/roots/step2.ogg
+minecraft/sounds/block/roots/step3.ogg
+minecraft/sounds/block/roots/step4.ogg
+minecraft/sounds/block/roots/step5.ogg
+minecraft/sounds/block/scaffold/place1.ogg
+minecraft/sounds/block/scaffold/place2.ogg
+minecraft/sounds/block/scaffold/place3.ogg
+minecraft/sounds/block/scaffold/place4.ogg
+minecraft/sounds/block/shroomlight/break1.ogg
+minecraft/sounds/block/shroomlight/break2.ogg
+minecraft/sounds/block/shroomlight/break3.ogg
+minecraft/sounds/block/shroomlight/break4.ogg
+minecraft/sounds/block/shroomlight/break5.ogg
+minecraft/sounds/block/shroomlight/step1.ogg
+minecraft/sounds/block/shroomlight/step2.ogg
+minecraft/sounds/block/shroomlight/step3.ogg
+minecraft/sounds/block/shroomlight/step4.ogg
+minecraft/sounds/block/shroomlight/step5.ogg
+minecraft/sounds/block/shroomlight/step6.ogg
+minecraft/sounds/block/shulker_box/close.ogg
+minecraft/sounds/block/shulker_box/open.ogg
+minecraft/sounds/block/smithing_table/smithing_table1.ogg
+minecraft/sounds/block/smithing_table/smithing_table2.ogg
+minecraft/sounds/block/smithing_table/smithing_table3.ogg
+minecraft/sounds/block/smoker/smoker1.ogg
+minecraft/sounds/block/smoker/smoker2.ogg
+minecraft/sounds/block/smoker/smoker3.ogg
+minecraft/sounds/block/smoker/smoker4.ogg
+minecraft/sounds/block/smoker/smoker5.ogg
+minecraft/sounds/block/soul_sand/break1.ogg
+minecraft/sounds/block/soul_sand/break2.ogg
+minecraft/sounds/block/soul_sand/break3.ogg
+minecraft/sounds/block/soul_sand/break4.ogg
+minecraft/sounds/block/soul_sand/break5.ogg
+minecraft/sounds/block/soul_sand/break6.ogg
+minecraft/sounds/block/soul_sand/break7.ogg
+minecraft/sounds/block/soul_sand/break8.ogg
+minecraft/sounds/block/soul_sand/break9.ogg
+minecraft/sounds/block/soul_sand/step1.ogg
+minecraft/sounds/block/soul_sand/step2.ogg
+minecraft/sounds/block/soul_sand/step3.ogg
+minecraft/sounds/block/soul_sand/step4.ogg
+minecraft/sounds/block/soul_sand/step5.ogg
+minecraft/sounds/block/soul_soil/break1.ogg
+minecraft/sounds/block/soul_soil/break2.ogg
+minecraft/sounds/block/soul_soil/break3.ogg
+minecraft/sounds/block/soul_soil/break4.ogg
+minecraft/sounds/block/soul_soil/break5.ogg
+minecraft/sounds/block/soul_soil/break6.ogg
+minecraft/sounds/block/soul_soil/step1.ogg
+minecraft/sounds/block/soul_soil/step2.ogg
+minecraft/sounds/block/soul_soil/step3.ogg
+minecraft/sounds/block/soul_soil/step4.ogg
+minecraft/sounds/block/soul_soil/step5.ogg
+minecraft/sounds/block/stem/break1.ogg
+minecraft/sounds/block/stem/break2.ogg
+minecraft/sounds/block/stem/break3.ogg
+minecraft/sounds/block/stem/break4.ogg
+minecraft/sounds/block/stem/break5.ogg
+minecraft/sounds/block/stem/break6.ogg
+minecraft/sounds/block/stem/step1.ogg
+minecraft/sounds/block/stem/step2.ogg
+minecraft/sounds/block/stem/step3.ogg
+minecraft/sounds/block/stem/step4.ogg
+minecraft/sounds/block/stem/step5.ogg
+minecraft/sounds/block/stem/step6.ogg
+minecraft/sounds/block/sweet_berry_bush/break1.ogg
+minecraft/sounds/block/sweet_berry_bush/break2.ogg
+minecraft/sounds/block/sweet_berry_bush/break3.ogg
+minecraft/sounds/block/sweet_berry_bush/break4.ogg
+minecraft/sounds/block/sweet_berry_bush/place1.ogg
+minecraft/sounds/block/sweet_berry_bush/place2.ogg
+minecraft/sounds/block/sweet_berry_bush/place3.ogg
+minecraft/sounds/block/sweet_berry_bush/place4.ogg
+minecraft/sounds/block/sweet_berry_bush/place5.ogg
+minecraft/sounds/block/sweet_berry_bush/place6.ogg
+minecraft/sounds/block/vine/climb1.ogg
+minecraft/sounds/block/vine/climb2.ogg
+minecraft/sounds/block/vine/climb3.ogg
+minecraft/sounds/block/vine/climb4.ogg
+minecraft/sounds/block/vine/climb5.ogg
+minecraft/sounds/block/waterlily/place1.ogg
+minecraft/sounds/block/waterlily/place2.ogg
+minecraft/sounds/block/waterlily/place3.ogg
+minecraft/sounds/block/waterlily/place4.ogg
+minecraft/sounds/block/wooden_door/close.ogg
+minecraft/sounds/block/wooden_door/close2.ogg
+minecraft/sounds/block/wooden_door/close3.ogg
+minecraft/sounds/block/wooden_door/close4.ogg
+minecraft/sounds/block/wooden_door/close5.ogg
+minecraft/sounds/block/wooden_door/close6.ogg
+minecraft/sounds/block/wooden_door/open.ogg
+minecraft/sounds/block/wooden_door/open2.ogg
+minecraft/sounds/block/wooden_door/open3.ogg
+minecraft/sounds/block/wooden_door/open4.ogg
+minecraft/sounds/block/wooden_trapdoor/close1.ogg
+minecraft/sounds/block/wooden_trapdoor/close2.ogg
+minecraft/sounds/block/wooden_trapdoor/close3.ogg
+minecraft/sounds/block/wooden_trapdoor/open1.ogg
+minecraft/sounds/block/wooden_trapdoor/open2.ogg
+minecraft/sounds/block/wooden_trapdoor/open3.ogg
+minecraft/sounds/block/wooden_trapdoor/open4.ogg
+minecraft/sounds/block/wooden_trapdoor/open5.ogg
+minecraft/sounds/damage/fallbig.ogg
+minecraft/sounds/damage/fallsmall.ogg
+minecraft/sounds/damage/hit1.ogg
+minecraft/sounds/damage/hit2.ogg
+minecraft/sounds/damage/hit3.ogg
+minecraft/sounds/dig/cloth1.ogg
+minecraft/sounds/dig/cloth2.ogg
+minecraft/sounds/dig/cloth3.ogg
+minecraft/sounds/dig/cloth4.ogg
+minecraft/sounds/dig/coral1.ogg
+minecraft/sounds/dig/coral2.ogg
+minecraft/sounds/dig/coral3.ogg
+minecraft/sounds/dig/coral4.ogg
+minecraft/sounds/dig/grass1.ogg
+minecraft/sounds/dig/grass2.ogg
+minecraft/sounds/dig/grass3.ogg
+minecraft/sounds/dig/grass4.ogg
+minecraft/sounds/dig/gravel1.ogg
+minecraft/sounds/dig/gravel2.ogg
+minecraft/sounds/dig/gravel3.ogg
+minecraft/sounds/dig/gravel4.ogg
+minecraft/sounds/dig/sand1.ogg
+minecraft/sounds/dig/sand2.ogg
+minecraft/sounds/dig/sand3.ogg
+minecraft/sounds/dig/sand4.ogg
+minecraft/sounds/dig/snow1.ogg
+minecraft/sounds/dig/snow2.ogg
+minecraft/sounds/dig/snow3.ogg
+minecraft/sounds/dig/snow4.ogg
+minecraft/sounds/dig/stone1.ogg
+minecraft/sounds/dig/stone2.ogg
+minecraft/sounds/dig/stone3.ogg
+minecraft/sounds/dig/stone4.ogg
+minecraft/sounds/dig/wet_grass1.ogg
+minecraft/sounds/dig/wet_grass2.ogg
+minecraft/sounds/dig/wet_grass3.ogg
+minecraft/sounds/dig/wet_grass4.ogg
+minecraft/sounds/dig/wood1.ogg
+minecraft/sounds/dig/wood2.ogg
+minecraft/sounds/dig/wood3.ogg
+minecraft/sounds/dig/wood4.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed1.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed10.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed11.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed12.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed13.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed2.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed3.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed4.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed5.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed6.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed7.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed8.ogg
+minecraft/sounds/enchant/soulspeed/soulspeed9.ogg
+minecraft/sounds/enchant/thorns/hit1.ogg
+minecraft/sounds/enchant/thorns/hit2.ogg
+minecraft/sounds/enchant/thorns/hit3.ogg
+minecraft/sounds/enchant/thorns/hit4.ogg
+minecraft/sounds/entity/armorstand/break1.ogg
+minecraft/sounds/entity/armorstand/break2.ogg
+minecraft/sounds/entity/armorstand/break3.ogg
+minecraft/sounds/entity/armorstand/break4.ogg
+minecraft/sounds/entity/armorstand/hit1.ogg
+minecraft/sounds/entity/armorstand/hit2.ogg
+minecraft/sounds/entity/armorstand/hit3.ogg
+minecraft/sounds/entity/armorstand/hit4.ogg
+minecraft/sounds/entity/boat/paddle_land1.ogg
+minecraft/sounds/entity/boat/paddle_land2.ogg
+minecraft/sounds/entity/boat/paddle_land3.ogg
+minecraft/sounds/entity/boat/paddle_land4.ogg
+minecraft/sounds/entity/boat/paddle_land5.ogg
+minecraft/sounds/entity/boat/paddle_land6.ogg
+minecraft/sounds/entity/boat/paddle_water1.ogg
+minecraft/sounds/entity/boat/paddle_water2.ogg
+minecraft/sounds/entity/boat/paddle_water3.ogg
+minecraft/sounds/entity/boat/paddle_water4.ogg
+minecraft/sounds/entity/boat/paddle_water5.ogg
+minecraft/sounds/entity/boat/paddle_water6.ogg
+minecraft/sounds/entity/boat/paddle_water7.ogg
+minecraft/sounds/entity/boat/paddle_water8.ogg
+minecraft/sounds/entity/bobber/castfast.ogg
+minecraft/sounds/entity/bobber/retrieve1.ogg
+minecraft/sounds/entity/bobber/retrieve2.ogg
+minecraft/sounds/entity/bobber/retrieve3.ogg
+minecraft/sounds/entity/cow/milk1.ogg
+minecraft/sounds/entity/cow/milk2.ogg
+minecraft/sounds/entity/cow/milk3.ogg
+minecraft/sounds/entity/endereye/dead1.ogg
+minecraft/sounds/entity/endereye/dead2.ogg
+minecraft/sounds/entity/endereye/endereye_launch1.ogg
+minecraft/sounds/entity/endereye/endereye_launch2.ogg
+minecraft/sounds/entity/fish/flop1.ogg
+minecraft/sounds/entity/fish/flop2.ogg
+minecraft/sounds/entity/fish/flop3.ogg
+minecraft/sounds/entity/fish/flop4.ogg
+minecraft/sounds/entity/fish/hurt1.ogg
+minecraft/sounds/entity/fish/hurt2.ogg
+minecraft/sounds/entity/fish/hurt3.ogg
+minecraft/sounds/entity/fish/hurt4.ogg
+minecraft/sounds/entity/fish/swim1.ogg
+minecraft/sounds/entity/fish/swim2.ogg
+minecraft/sounds/entity/fish/swim3.ogg
+minecraft/sounds/entity/fish/swim4.ogg
+minecraft/sounds/entity/fish/swim5.ogg
+minecraft/sounds/entity/fish/swim6.ogg
+minecraft/sounds/entity/fish/swim7.ogg
+minecraft/sounds/entity/guardian/ambient1.ogg
+minecraft/sounds/entity/guardian/ambient2.ogg
+minecraft/sounds/entity/guardian/ambient3.ogg
+minecraft/sounds/entity/guardian/ambient4.ogg
+minecraft/sounds/entity/horse/eat1.ogg
+minecraft/sounds/entity/horse/eat2.ogg
+minecraft/sounds/entity/horse/eat3.ogg
+minecraft/sounds/entity/horse/eat4.ogg
+minecraft/sounds/entity/horse/eat5.ogg
+minecraft/sounds/entity/itemframe/add_item1.ogg
+minecraft/sounds/entity/itemframe/add_item2.ogg
+minecraft/sounds/entity/itemframe/add_item3.ogg
+minecraft/sounds/entity/itemframe/add_item4.ogg
+minecraft/sounds/entity/itemframe/break1.ogg
+minecraft/sounds/entity/itemframe/break2.ogg
+minecraft/sounds/entity/itemframe/break3.ogg
+minecraft/sounds/entity/itemframe/place1.ogg
+minecraft/sounds/entity/itemframe/place2.ogg
+minecraft/sounds/entity/itemframe/place3.ogg
+minecraft/sounds/entity/itemframe/place4.ogg
+minecraft/sounds/entity/itemframe/remove_item1.ogg
+minecraft/sounds/entity/itemframe/remove_item2.ogg
+minecraft/sounds/entity/itemframe/remove_item3.ogg
+minecraft/sounds/entity/itemframe/remove_item4.ogg
+minecraft/sounds/entity/itemframe/rotate_item1.ogg
+minecraft/sounds/entity/itemframe/rotate_item2.ogg
+minecraft/sounds/entity/itemframe/rotate_item3.ogg
+minecraft/sounds/entity/itemframe/rotate_item4.ogg
+minecraft/sounds/entity/leashknot/break1.ogg
+minecraft/sounds/entity/leashknot/break2.ogg
+minecraft/sounds/entity/leashknot/break3.ogg
+minecraft/sounds/entity/leashknot/place1.ogg
+minecraft/sounds/entity/leashknot/place2.ogg
+minecraft/sounds/entity/leashknot/place3.ogg
+minecraft/sounds/entity/painting/break1.ogg
+minecraft/sounds/entity/painting/break2.ogg
+minecraft/sounds/entity/painting/break3.ogg
+minecraft/sounds/entity/painting/place1.ogg
+minecraft/sounds/entity/painting/place2.ogg
+minecraft/sounds/entity/painting/place3.ogg
+minecraft/sounds/entity/painting/place4.ogg
+minecraft/sounds/entity/player/attack/crit1.ogg
+minecraft/sounds/entity/player/attack/crit2.ogg
+minecraft/sounds/entity/player/attack/crit3.ogg
+minecraft/sounds/entity/player/attack/knockback1.ogg
+minecraft/sounds/entity/player/attack/knockback2.ogg
+minecraft/sounds/entity/player/attack/knockback3.ogg
+minecraft/sounds/entity/player/attack/knockback4.ogg
+minecraft/sounds/entity/player/attack/strong1.ogg
+minecraft/sounds/entity/player/attack/strong2.ogg
+minecraft/sounds/entity/player/attack/strong3.ogg
+minecraft/sounds/entity/player/attack/strong4.ogg
+minecraft/sounds/entity/player/attack/strong5.ogg
+minecraft/sounds/entity/player/attack/strong6.ogg
+minecraft/sounds/entity/player/attack/sweep1.ogg
+minecraft/sounds/entity/player/attack/sweep2.ogg
+minecraft/sounds/entity/player/attack/sweep3.ogg
+minecraft/sounds/entity/player/attack/sweep4.ogg
+minecraft/sounds/entity/player/attack/sweep5.ogg
+minecraft/sounds/entity/player/attack/sweep6.ogg
+minecraft/sounds/entity/player/attack/sweep7.ogg
+minecraft/sounds/entity/player/attack/weak1.ogg
+minecraft/sounds/entity/player/attack/weak2.ogg
+minecraft/sounds/entity/player/attack/weak3.ogg
+minecraft/sounds/entity/player/attack/weak4.ogg
+minecraft/sounds/entity/player/hurt/berrybush_hurt1.ogg
+minecraft/sounds/entity/player/hurt/berrybush_hurt2.ogg
+minecraft/sounds/entity/player/hurt/drown1.ogg
+minecraft/sounds/entity/player/hurt/drown2.ogg
+minecraft/sounds/entity/player/hurt/drown3.ogg
+minecraft/sounds/entity/player/hurt/drown4.ogg
+minecraft/sounds/entity/player/hurt/fire_hurt1.ogg
+minecraft/sounds/entity/player/hurt/fire_hurt2.ogg
+minecraft/sounds/entity/player/hurt/fire_hurt3.ogg
+minecraft/sounds/entity/pufferfish/blow_out1.ogg
+minecraft/sounds/entity/pufferfish/blow_out2.ogg
+minecraft/sounds/entity/pufferfish/blow_up1.ogg
+minecraft/sounds/entity/pufferfish/blow_up2.ogg
+minecraft/sounds/entity/pufferfish/death1.ogg
+minecraft/sounds/entity/pufferfish/death2.ogg
+minecraft/sounds/entity/pufferfish/flop1.ogg
+minecraft/sounds/entity/pufferfish/flop2.ogg
+minecraft/sounds/entity/pufferfish/flop3.ogg
+minecraft/sounds/entity/pufferfish/flop4.ogg
+minecraft/sounds/entity/pufferfish/hurt1.ogg
+minecraft/sounds/entity/pufferfish/hurt2.ogg
+minecraft/sounds/entity/pufferfish/sting1.ogg
+minecraft/sounds/entity/pufferfish/sting2.ogg
+minecraft/sounds/entity/rabbit/attack1.ogg
+minecraft/sounds/entity/rabbit/attack2.ogg
+minecraft/sounds/entity/rabbit/attack3.ogg
+minecraft/sounds/entity/rabbit/attack4.ogg
+minecraft/sounds/entity/shulker/ambient1.ogg
+minecraft/sounds/entity/shulker/ambient2.ogg
+minecraft/sounds/entity/shulker/ambient3.ogg
+minecraft/sounds/entity/shulker/ambient4.ogg
+minecraft/sounds/entity/shulker/ambient5.ogg
+minecraft/sounds/entity/shulker/ambient6.ogg
+minecraft/sounds/entity/shulker/ambient7.ogg
+minecraft/sounds/entity/shulker/close1.ogg
+minecraft/sounds/entity/shulker/close2.ogg
+minecraft/sounds/entity/shulker/close3.ogg
+minecraft/sounds/entity/shulker/close4.ogg
+minecraft/sounds/entity/shulker/close5.ogg
+minecraft/sounds/entity/shulker/death1.ogg
+minecraft/sounds/entity/shulker/death2.ogg
+minecraft/sounds/entity/shulker/death3.ogg
+minecraft/sounds/entity/shulker/death4.ogg
+minecraft/sounds/entity/shulker/hurt1.ogg
+minecraft/sounds/entity/shulker/hurt2.ogg
+minecraft/sounds/entity/shulker/hurt3.ogg
+minecraft/sounds/entity/shulker/hurt4.ogg
+minecraft/sounds/entity/shulker/hurt_closed1.ogg
+minecraft/sounds/entity/shulker/hurt_closed2.ogg
+minecraft/sounds/entity/shulker/hurt_closed3.ogg
+minecraft/sounds/entity/shulker/hurt_closed4.ogg
+minecraft/sounds/entity/shulker/hurt_closed5.ogg
+minecraft/sounds/entity/shulker/open1.ogg
+minecraft/sounds/entity/shulker/open2.ogg
+minecraft/sounds/entity/shulker/open3.ogg
+minecraft/sounds/entity/shulker/open4.ogg
+minecraft/sounds/entity/shulker/open5.ogg
+minecraft/sounds/entity/shulker/shoot1.ogg
+minecraft/sounds/entity/shulker/shoot2.ogg
+minecraft/sounds/entity/shulker/shoot3.ogg
+minecraft/sounds/entity/shulker/shoot4.ogg
+minecraft/sounds/entity/shulker_bullet/hit1.ogg
+minecraft/sounds/entity/shulker_bullet/hit2.ogg
+minecraft/sounds/entity/shulker_bullet/hit3.ogg
+minecraft/sounds/entity/shulker_bullet/hit4.ogg
+minecraft/sounds/entity/snowman/death1.ogg
+minecraft/sounds/entity/snowman/death2.ogg
+minecraft/sounds/entity/snowman/death3.ogg
+minecraft/sounds/entity/snowman/hurt1.ogg
+minecraft/sounds/entity/snowman/hurt2.ogg
+minecraft/sounds/entity/snowman/hurt3.ogg
+minecraft/sounds/entity/squid/ambient1.ogg
+minecraft/sounds/entity/squid/ambient2.ogg
+minecraft/sounds/entity/squid/ambient3.ogg
+minecraft/sounds/entity/squid/ambient4.ogg
+minecraft/sounds/entity/squid/ambient5.ogg
+minecraft/sounds/entity/squid/death1.ogg
+minecraft/sounds/entity/squid/death2.ogg
+minecraft/sounds/entity/squid/death3.ogg
+minecraft/sounds/entity/squid/hurt1.ogg
+minecraft/sounds/entity/squid/hurt2.ogg
+minecraft/sounds/entity/squid/hurt3.ogg
+minecraft/sounds/entity/squid/hurt4.ogg
+minecraft/sounds/entity/squid/squirt1.ogg
+minecraft/sounds/entity/squid/squirt2.ogg
+minecraft/sounds/entity/squid/squirt3.ogg
+minecraft/sounds/entity/witch/ambient1.ogg
+minecraft/sounds/entity/witch/ambient2.ogg
+minecraft/sounds/entity/witch/ambient3.ogg
+minecraft/sounds/entity/witch/ambient4.ogg
+minecraft/sounds/entity/witch/ambient5.ogg
+minecraft/sounds/entity/witch/celebrate.ogg
+minecraft/sounds/entity/witch/death1.ogg
+minecraft/sounds/entity/witch/death2.ogg
+minecraft/sounds/entity/witch/death3.ogg
+minecraft/sounds/entity/witch/drink1.ogg
+minecraft/sounds/entity/witch/drink2.ogg
+minecraft/sounds/entity/witch/drink3.ogg
+minecraft/sounds/entity/witch/drink4.ogg
+minecraft/sounds/entity/witch/hurt1.ogg
+minecraft/sounds/entity/witch/hurt2.ogg
+minecraft/sounds/entity/witch/hurt3.ogg
+minecraft/sounds/entity/witch/throw1.ogg
+minecraft/sounds/entity/witch/throw2.ogg
+minecraft/sounds/entity/witch/throw3.ogg
+minecraft/sounds/event/raid/raidhorn_01.ogg
+minecraft/sounds/event/raid/raidhorn_02.ogg
+minecraft/sounds/event/raid/raidhorn_03.ogg
+minecraft/sounds/event/raid/raidhorn_04.ogg
+minecraft/sounds/fire/fire.ogg
+minecraft/sounds/fire/ignite.ogg
+minecraft/sounds/fireworks/blast1.ogg
+minecraft/sounds/fireworks/blast_far1.ogg
+minecraft/sounds/fireworks/largeblast1.ogg
+minecraft/sounds/fireworks/largeblast_far1.ogg
+minecraft/sounds/fireworks/launch1.ogg
+minecraft/sounds/fireworks/twinkle1.ogg
+minecraft/sounds/fireworks/twinkle_far1.ogg
+minecraft/sounds/item/armor/equip_chain1.ogg
+minecraft/sounds/item/armor/equip_chain2.ogg
+minecraft/sounds/item/armor/equip_chain3.ogg
+minecraft/sounds/item/armor/equip_chain4.ogg
+minecraft/sounds/item/armor/equip_chain5.ogg
+minecraft/sounds/item/armor/equip_chain6.ogg
+minecraft/sounds/item/armor/equip_diamond1.ogg
+minecraft/sounds/item/armor/equip_diamond2.ogg
+minecraft/sounds/item/armor/equip_diamond3.ogg
+minecraft/sounds/item/armor/equip_diamond4.ogg
+minecraft/sounds/item/armor/equip_diamond5.ogg
+minecraft/sounds/item/armor/equip_diamond6.ogg
+minecraft/sounds/item/armor/equip_generic1.ogg
+minecraft/sounds/item/armor/equip_generic2.ogg
+minecraft/sounds/item/armor/equip_generic3.ogg
+minecraft/sounds/item/armor/equip_generic4.ogg
+minecraft/sounds/item/armor/equip_generic5.ogg
+minecraft/sounds/item/armor/equip_generic6.ogg
+minecraft/sounds/item/armor/equip_gold1.ogg
+minecraft/sounds/item/armor/equip_gold2.ogg
+minecraft/sounds/item/armor/equip_gold3.ogg
+minecraft/sounds/item/armor/equip_gold4.ogg
+minecraft/sounds/item/armor/equip_gold5.ogg
+minecraft/sounds/item/armor/equip_gold6.ogg
+minecraft/sounds/item/armor/equip_iron1.ogg
+minecraft/sounds/item/armor/equip_iron2.ogg
+minecraft/sounds/item/armor/equip_iron3.ogg
+minecraft/sounds/item/armor/equip_iron4.ogg
+minecraft/sounds/item/armor/equip_iron5.ogg
+minecraft/sounds/item/armor/equip_iron6.ogg
+minecraft/sounds/item/armor/equip_leather1.ogg
+minecraft/sounds/item/armor/equip_leather2.ogg
+minecraft/sounds/item/armor/equip_leather3.ogg
+minecraft/sounds/item/armor/equip_leather4.ogg
+minecraft/sounds/item/armor/equip_leather5.ogg
+minecraft/sounds/item/armor/equip_leather6.ogg
+minecraft/sounds/item/armor/equip_netherite1.ogg
+minecraft/sounds/item/armor/equip_netherite2.ogg
+minecraft/sounds/item/armor/equip_netherite3.ogg
+minecraft/sounds/item/armor/equip_netherite4.ogg
+minecraft/sounds/item/axe/strip1.ogg
+minecraft/sounds/item/axe/strip2.ogg
+minecraft/sounds/item/axe/strip3.ogg
+minecraft/sounds/item/axe/strip4.ogg
+minecraft/sounds/item/book/close_put1.ogg
+minecraft/sounds/item/book/close_put2.ogg
+minecraft/sounds/item/book/open_flip1.ogg
+minecraft/sounds/item/book/open_flip2.ogg
+minecraft/sounds/item/book/open_flip3.ogg
+minecraft/sounds/item/bottle/drink_honey1.ogg
+minecraft/sounds/item/bottle/drink_honey2.ogg
+minecraft/sounds/item/bottle/drink_honey3.ogg
+minecraft/sounds/item/bottle/empty1.ogg
+minecraft/sounds/item/bottle/empty2.ogg
+minecraft/sounds/item/bottle/fill1.ogg
+minecraft/sounds/item/bottle/fill2.ogg
+minecraft/sounds/item/bottle/fill3.ogg
+minecraft/sounds/item/bottle/fill4.ogg
+minecraft/sounds/item/bottle/fill_dragonbreath1.ogg
+minecraft/sounds/item/bottle/fill_dragonbreath2.ogg
+minecraft/sounds/item/bucket/empty1.ogg
+minecraft/sounds/item/bucket/empty2.ogg
+minecraft/sounds/item/bucket/empty3.ogg
+minecraft/sounds/item/bucket/empty_fish1.ogg
+minecraft/sounds/item/bucket/empty_fish2.ogg
+minecraft/sounds/item/bucket/empty_fish3.ogg
+minecraft/sounds/item/bucket/empty_lava1.ogg
+minecraft/sounds/item/bucket/empty_lava2.ogg
+minecraft/sounds/item/bucket/empty_lava3.ogg
+minecraft/sounds/item/bucket/fill1.ogg
+minecraft/sounds/item/bucket/fill2.ogg
+minecraft/sounds/item/bucket/fill3.ogg
+minecraft/sounds/item/bucket/fill_fish1.ogg
+minecraft/sounds/item/bucket/fill_fish2.ogg
+minecraft/sounds/item/bucket/fill_fish3.ogg
+minecraft/sounds/item/bucket/fill_lava1.ogg
+minecraft/sounds/item/bucket/fill_lava2.ogg
+minecraft/sounds/item/bucket/fill_lava3.ogg
+minecraft/sounds/item/crossbow/loading_end.ogg
+minecraft/sounds/item/crossbow/loading_middle1.ogg
+minecraft/sounds/item/crossbow/loading_middle2.ogg
+minecraft/sounds/item/crossbow/loading_middle3.ogg
+minecraft/sounds/item/crossbow/loading_middle4.ogg
+minecraft/sounds/item/crossbow/loading_start.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick1_1.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick1_2.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick1_3.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick2_1.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick2_2.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick2_3.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick3_1.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick3_2.ogg
+minecraft/sounds/item/crossbow/quick_charge/quick3_3.ogg
+minecraft/sounds/item/crossbow/shoot1.ogg
+minecraft/sounds/item/crossbow/shoot2.ogg
+minecraft/sounds/item/crossbow/shoot3.ogg
+minecraft/sounds/item/elytra/elytra_loop.ogg
+minecraft/sounds/item/hoe/till1.ogg
+minecraft/sounds/item/hoe/till2.ogg
+minecraft/sounds/item/hoe/till3.ogg
+minecraft/sounds/item/hoe/till4.ogg
+minecraft/sounds/item/plant/crop1.ogg
+minecraft/sounds/item/plant/crop2.ogg
+minecraft/sounds/item/plant/crop3.ogg
+minecraft/sounds/item/plant/crop4.ogg
+minecraft/sounds/item/plant/crop5.ogg
+minecraft/sounds/item/plant/crop6.ogg
+minecraft/sounds/item/plant/netherwart1.ogg
+minecraft/sounds/item/plant/netherwart2.ogg
+minecraft/sounds/item/plant/netherwart3.ogg
+minecraft/sounds/item/plant/netherwart4.ogg
+minecraft/sounds/item/plant/netherwart5.ogg
+minecraft/sounds/item/plant/netherwart6.ogg
+minecraft/sounds/item/shield/block1.ogg
+minecraft/sounds/item/shield/block2.ogg
+minecraft/sounds/item/shield/block3.ogg
+minecraft/sounds/item/shield/block4.ogg
+minecraft/sounds/item/shield/block5.ogg
+minecraft/sounds/item/shovel/flatten1.ogg
+minecraft/sounds/item/shovel/flatten2.ogg
+minecraft/sounds/item/shovel/flatten3.ogg
+minecraft/sounds/item/shovel/flatten4.ogg
+minecraft/sounds/item/sweet_berries/pick_from_bush1.ogg
+minecraft/sounds/item/sweet_berries/pick_from_bush2.ogg
+minecraft/sounds/item/totem/use_totem.ogg
+minecraft/sounds/item/trident/ground_impact1.ogg
+minecraft/sounds/item/trident/ground_impact2.ogg
+minecraft/sounds/item/trident/ground_impact3.ogg
+minecraft/sounds/item/trident/ground_impact4.ogg
+minecraft/sounds/item/trident/pierce1.ogg
+minecraft/sounds/item/trident/pierce2.ogg
+minecraft/sounds/item/trident/pierce3.ogg
+minecraft/sounds/item/trident/return1.ogg
+minecraft/sounds/item/trident/return2.ogg
+minecraft/sounds/item/trident/return3.ogg
+minecraft/sounds/item/trident/riptide1.ogg
+minecraft/sounds/item/trident/riptide2.ogg
+minecraft/sounds/item/trident/riptide3.ogg
+minecraft/sounds/item/trident/throw1.ogg
+minecraft/sounds/item/trident/throw2.ogg
+minecraft/sounds/item/trident/thunder1.ogg
+minecraft/sounds/item/trident/thunder2.ogg
+minecraft/sounds/liquid/heavy_splash.ogg
+minecraft/sounds/liquid/lava.ogg
+minecraft/sounds/liquid/lavapop.ogg
+minecraft/sounds/liquid/splash.ogg
+minecraft/sounds/liquid/splash2.ogg
+minecraft/sounds/liquid/swim1.ogg
+minecraft/sounds/liquid/swim10.ogg
+minecraft/sounds/liquid/swim11.ogg
+minecraft/sounds/liquid/swim12.ogg
+minecraft/sounds/liquid/swim13.ogg
+minecraft/sounds/liquid/swim14.ogg
+minecraft/sounds/liquid/swim15.ogg
+minecraft/sounds/liquid/swim16.ogg
+minecraft/sounds/liquid/swim17.ogg
+minecraft/sounds/liquid/swim18.ogg
+minecraft/sounds/liquid/swim2.ogg
+minecraft/sounds/liquid/swim3.ogg
+minecraft/sounds/liquid/swim4.ogg
+minecraft/sounds/liquid/swim5.ogg
+minecraft/sounds/liquid/swim6.ogg
+minecraft/sounds/liquid/swim7.ogg
+minecraft/sounds/liquid/swim8.ogg
+minecraft/sounds/liquid/swim9.ogg
+minecraft/sounds/liquid/water.ogg
+minecraft/sounds/minecart/base.ogg
+minecraft/sounds/minecart/inside.ogg
+minecraft/sounds/mob/bat/death.ogg
+minecraft/sounds/mob/bat/hurt1.ogg
+minecraft/sounds/mob/bat/hurt2.ogg
+minecraft/sounds/mob/bat/hurt3.ogg
+minecraft/sounds/mob/bat/hurt4.ogg
+minecraft/sounds/mob/bat/idle1.ogg
+minecraft/sounds/mob/bat/idle2.ogg
+minecraft/sounds/mob/bat/idle3.ogg
+minecraft/sounds/mob/bat/idle4.ogg
+minecraft/sounds/mob/bat/loop.ogg
+minecraft/sounds/mob/bat/takeoff.ogg
+minecraft/sounds/mob/bee/aggressive1.ogg
+minecraft/sounds/mob/bee/aggressive2.ogg
+minecraft/sounds/mob/bee/aggressive3.ogg
+minecraft/sounds/mob/bee/death1.ogg
+minecraft/sounds/mob/bee/death2.ogg
+minecraft/sounds/mob/bee/hurt1.ogg
+minecraft/sounds/mob/bee/hurt2.ogg
+minecraft/sounds/mob/bee/hurt3.ogg
+minecraft/sounds/mob/bee/loop1.ogg
+minecraft/sounds/mob/bee/loop2.ogg
+minecraft/sounds/mob/bee/loop3.ogg
+minecraft/sounds/mob/bee/loop4.ogg
+minecraft/sounds/mob/bee/loop5.ogg
+minecraft/sounds/mob/bee/pollinate1.ogg
+minecraft/sounds/mob/bee/pollinate2.ogg
+minecraft/sounds/mob/bee/pollinate3.ogg
+minecraft/sounds/mob/bee/pollinate4.ogg
+minecraft/sounds/mob/bee/sting.ogg
+minecraft/sounds/mob/blaze/breathe1.ogg
+minecraft/sounds/mob/blaze/breathe2.ogg
+minecraft/sounds/mob/blaze/breathe3.ogg
+minecraft/sounds/mob/blaze/breathe4.ogg
+minecraft/sounds/mob/blaze/death.ogg
+minecraft/sounds/mob/blaze/hit1.ogg
+minecraft/sounds/mob/blaze/hit2.ogg
+minecraft/sounds/mob/blaze/hit3.ogg
+minecraft/sounds/mob/blaze/hit4.ogg
+minecraft/sounds/mob/cat/beg1.ogg
+minecraft/sounds/mob/cat/beg2.ogg
+minecraft/sounds/mob/cat/beg3.ogg
+minecraft/sounds/mob/cat/eat1.ogg
+minecraft/sounds/mob/cat/eat2.ogg
+minecraft/sounds/mob/cat/hiss1.ogg
+minecraft/sounds/mob/cat/hiss2.ogg
+minecraft/sounds/mob/cat/hiss3.ogg
+minecraft/sounds/mob/cat/hitt1.ogg
+minecraft/sounds/mob/cat/hitt2.ogg
+minecraft/sounds/mob/cat/hitt3.ogg
+minecraft/sounds/mob/cat/meow1.ogg
+minecraft/sounds/mob/cat/meow2.ogg
+minecraft/sounds/mob/cat/meow3.ogg
+minecraft/sounds/mob/cat/meow4.ogg
+minecraft/sounds/mob/cat/ocelot/death1.ogg
+minecraft/sounds/mob/cat/ocelot/death2.ogg
+minecraft/sounds/mob/cat/ocelot/death3.ogg
+minecraft/sounds/mob/cat/ocelot/idle1.ogg
+minecraft/sounds/mob/cat/ocelot/idle2.ogg
+minecraft/sounds/mob/cat/ocelot/idle3.ogg
+minecraft/sounds/mob/cat/ocelot/idle4.ogg
+minecraft/sounds/mob/cat/purr1.ogg
+minecraft/sounds/mob/cat/purr2.ogg
+minecraft/sounds/mob/cat/purr3.ogg
+minecraft/sounds/mob/cat/purreow1.ogg
+minecraft/sounds/mob/cat/purreow2.ogg
+minecraft/sounds/mob/cat/stray/idle1.ogg
+minecraft/sounds/mob/cat/stray/idle2.ogg
+minecraft/sounds/mob/cat/stray/idle3.ogg
+minecraft/sounds/mob/cat/stray/idle4.ogg
+minecraft/sounds/mob/chicken/hurt1.ogg
+minecraft/sounds/mob/chicken/hurt2.ogg
+minecraft/sounds/mob/chicken/plop.ogg
+minecraft/sounds/mob/chicken/say1.ogg
+minecraft/sounds/mob/chicken/say2.ogg
+minecraft/sounds/mob/chicken/say3.ogg
+minecraft/sounds/mob/chicken/step1.ogg
+minecraft/sounds/mob/chicken/step2.ogg
+minecraft/sounds/mob/cow/hurt1.ogg
+minecraft/sounds/mob/cow/hurt2.ogg
+minecraft/sounds/mob/cow/hurt3.ogg
+minecraft/sounds/mob/cow/say1.ogg
+minecraft/sounds/mob/cow/say2.ogg
+minecraft/sounds/mob/cow/say3.ogg
+minecraft/sounds/mob/cow/say4.ogg
+minecraft/sounds/mob/cow/step1.ogg
+minecraft/sounds/mob/cow/step2.ogg
+minecraft/sounds/mob/cow/step3.ogg
+minecraft/sounds/mob/cow/step4.ogg
+minecraft/sounds/mob/creeper/death.ogg
+minecraft/sounds/mob/creeper/say1.ogg
+minecraft/sounds/mob/creeper/say2.ogg
+minecraft/sounds/mob/creeper/say3.ogg
+minecraft/sounds/mob/creeper/say4.ogg
+minecraft/sounds/mob/dolphin/attack1.ogg
+minecraft/sounds/mob/dolphin/attack2.ogg
+minecraft/sounds/mob/dolphin/attack3.ogg
+minecraft/sounds/mob/dolphin/blowhole1.ogg
+minecraft/sounds/mob/dolphin/blowhole2.ogg
+minecraft/sounds/mob/dolphin/death1.ogg
+minecraft/sounds/mob/dolphin/death2.ogg
+minecraft/sounds/mob/dolphin/eat1.ogg
+minecraft/sounds/mob/dolphin/eat2.ogg
+minecraft/sounds/mob/dolphin/eat3.ogg
+minecraft/sounds/mob/dolphin/hurt1.ogg
+minecraft/sounds/mob/dolphin/hurt2.ogg
+minecraft/sounds/mob/dolphin/hurt3.ogg
+minecraft/sounds/mob/dolphin/idle1.ogg
+minecraft/sounds/mob/dolphin/idle2.ogg
+minecraft/sounds/mob/dolphin/idle3.ogg
+minecraft/sounds/mob/dolphin/idle4.ogg
+minecraft/sounds/mob/dolphin/idle5.ogg
+minecraft/sounds/mob/dolphin/idle6.ogg
+minecraft/sounds/mob/dolphin/idle_water1.ogg
+minecraft/sounds/mob/dolphin/idle_water10.ogg
+minecraft/sounds/mob/dolphin/idle_water2.ogg
+minecraft/sounds/mob/dolphin/idle_water3.ogg
+minecraft/sounds/mob/dolphin/idle_water4.ogg
+minecraft/sounds/mob/dolphin/idle_water5.ogg
+minecraft/sounds/mob/dolphin/idle_water6.ogg
+minecraft/sounds/mob/dolphin/idle_water7.ogg
+minecraft/sounds/mob/dolphin/idle_water8.ogg
+minecraft/sounds/mob/dolphin/idle_water9.ogg
+minecraft/sounds/mob/dolphin/jump1.ogg
+minecraft/sounds/mob/dolphin/jump2.ogg
+minecraft/sounds/mob/dolphin/jump3.ogg
+minecraft/sounds/mob/dolphin/play1.ogg
+minecraft/sounds/mob/dolphin/play2.ogg
+minecraft/sounds/mob/dolphin/splash1.ogg
+minecraft/sounds/mob/dolphin/splash2.ogg
+minecraft/sounds/mob/dolphin/splash3.ogg
+minecraft/sounds/mob/dolphin/swim1.ogg
+minecraft/sounds/mob/dolphin/swim2.ogg
+minecraft/sounds/mob/dolphin/swim3.ogg
+minecraft/sounds/mob/dolphin/swim4.ogg
+minecraft/sounds/mob/drowned/convert1.ogg
+minecraft/sounds/mob/drowned/convert2.ogg
+minecraft/sounds/mob/drowned/convert3.ogg
+minecraft/sounds/mob/drowned/death1.ogg
+minecraft/sounds/mob/drowned/death2.ogg
+minecraft/sounds/mob/drowned/hurt1.ogg
+minecraft/sounds/mob/drowned/hurt2.ogg
+minecraft/sounds/mob/drowned/hurt3.ogg
+minecraft/sounds/mob/drowned/idle1.ogg
+minecraft/sounds/mob/drowned/idle2.ogg
+minecraft/sounds/mob/drowned/idle3.ogg
+minecraft/sounds/mob/drowned/idle4.ogg
+minecraft/sounds/mob/drowned/idle5.ogg
+minecraft/sounds/mob/drowned/step1.ogg
+minecraft/sounds/mob/drowned/step2.ogg
+minecraft/sounds/mob/drowned/step3.ogg
+minecraft/sounds/mob/drowned/step4.ogg
+minecraft/sounds/mob/drowned/step5.ogg
+minecraft/sounds/mob/drowned/water/death1.ogg
+minecraft/sounds/mob/drowned/water/death2.ogg
+minecraft/sounds/mob/drowned/water/hurt1.ogg
+minecraft/sounds/mob/drowned/water/hurt2.ogg
+minecraft/sounds/mob/drowned/water/hurt3.ogg
+minecraft/sounds/mob/drowned/water/idle1.ogg
+minecraft/sounds/mob/drowned/water/idle2.ogg
+minecraft/sounds/mob/drowned/water/idle3.ogg
+minecraft/sounds/mob/drowned/water/idle4.ogg
+minecraft/sounds/mob/enderdragon/end.ogg
+minecraft/sounds/mob/enderdragon/growl1.ogg
+minecraft/sounds/mob/enderdragon/growl2.ogg
+minecraft/sounds/mob/enderdragon/growl3.ogg
+minecraft/sounds/mob/enderdragon/growl4.ogg
+minecraft/sounds/mob/enderdragon/hit1.ogg
+minecraft/sounds/mob/enderdragon/hit2.ogg
+minecraft/sounds/mob/enderdragon/hit3.ogg
+minecraft/sounds/mob/enderdragon/hit4.ogg
+minecraft/sounds/mob/enderdragon/wings1.ogg
+minecraft/sounds/mob/enderdragon/wings2.ogg
+minecraft/sounds/mob/enderdragon/wings3.ogg
+minecraft/sounds/mob/enderdragon/wings4.ogg
+minecraft/sounds/mob/enderdragon/wings5.ogg
+minecraft/sounds/mob/enderdragon/wings6.ogg
+minecraft/sounds/mob/endermen/death.ogg
+minecraft/sounds/mob/endermen/hit1.ogg
+minecraft/sounds/mob/endermen/hit2.ogg
+minecraft/sounds/mob/endermen/hit3.ogg
+minecraft/sounds/mob/endermen/hit4.ogg
+minecraft/sounds/mob/endermen/idle1.ogg
+minecraft/sounds/mob/endermen/idle2.ogg
+minecraft/sounds/mob/endermen/idle3.ogg
+minecraft/sounds/mob/endermen/idle4.ogg
+minecraft/sounds/mob/endermen/idle5.ogg
+minecraft/sounds/mob/endermen/portal.ogg
+minecraft/sounds/mob/endermen/portal2.ogg
+minecraft/sounds/mob/endermen/scream1.ogg
+minecraft/sounds/mob/endermen/scream2.ogg
+minecraft/sounds/mob/endermen/scream3.ogg
+minecraft/sounds/mob/endermen/scream4.ogg
+minecraft/sounds/mob/endermen/stare.ogg
+minecraft/sounds/mob/evocation_illager/cast1.ogg
+minecraft/sounds/mob/evocation_illager/cast2.ogg
+minecraft/sounds/mob/evocation_illager/celebrate.ogg
+minecraft/sounds/mob/evocation_illager/death1.ogg
+minecraft/sounds/mob/evocation_illager/death2.ogg
+minecraft/sounds/mob/evocation_illager/fangs.ogg
+minecraft/sounds/mob/evocation_illager/hurt1.ogg
+minecraft/sounds/mob/evocation_illager/hurt2.ogg
+minecraft/sounds/mob/evocation_illager/idle1.ogg
+minecraft/sounds/mob/evocation_illager/idle2.ogg
+minecraft/sounds/mob/evocation_illager/idle3.ogg
+minecraft/sounds/mob/evocation_illager/idle4.ogg
+minecraft/sounds/mob/evocation_illager/prepare_attack1.ogg
+minecraft/sounds/mob/evocation_illager/prepare_attack2.ogg
+minecraft/sounds/mob/evocation_illager/prepare_summon.ogg
+minecraft/sounds/mob/evocation_illager/prepare_wololo.ogg
+minecraft/sounds/mob/fox/aggro1.ogg
+minecraft/sounds/mob/fox/aggro2.ogg
+minecraft/sounds/mob/fox/aggro3.ogg
+minecraft/sounds/mob/fox/aggro4.ogg
+minecraft/sounds/mob/fox/aggro5.ogg
+minecraft/sounds/mob/fox/aggro6.ogg
+minecraft/sounds/mob/fox/aggro7.ogg
+minecraft/sounds/mob/fox/bite1.ogg
+minecraft/sounds/mob/fox/bite2.ogg
+minecraft/sounds/mob/fox/bite3.ogg
+minecraft/sounds/mob/fox/death1.ogg
+minecraft/sounds/mob/fox/death2.ogg
+minecraft/sounds/mob/fox/eat1.ogg
+minecraft/sounds/mob/fox/eat2.ogg
+minecraft/sounds/mob/fox/eat3.ogg
+minecraft/sounds/mob/fox/hurt1.ogg
+minecraft/sounds/mob/fox/hurt2.ogg
+minecraft/sounds/mob/fox/hurt3.ogg
+minecraft/sounds/mob/fox/hurt4.ogg
+minecraft/sounds/mob/fox/idle1.ogg
+minecraft/sounds/mob/fox/idle2.ogg
+minecraft/sounds/mob/fox/idle3.ogg
+minecraft/sounds/mob/fox/idle4.ogg
+minecraft/sounds/mob/fox/idle5.ogg
+minecraft/sounds/mob/fox/idle6.ogg
+minecraft/sounds/mob/fox/screech1.ogg
+minecraft/sounds/mob/fox/screech2.ogg
+minecraft/sounds/mob/fox/screech3.ogg
+minecraft/sounds/mob/fox/screech4.ogg
+minecraft/sounds/mob/fox/sleep1.ogg
+minecraft/sounds/mob/fox/sleep2.ogg
+minecraft/sounds/mob/fox/sleep3.ogg
+minecraft/sounds/mob/fox/sleep4.ogg
+minecraft/sounds/mob/fox/sleep5.ogg
+minecraft/sounds/mob/fox/sniff1.ogg
+minecraft/sounds/mob/fox/sniff2.ogg
+minecraft/sounds/mob/fox/sniff3.ogg
+minecraft/sounds/mob/fox/sniff4.ogg
+minecraft/sounds/mob/fox/spit1.ogg
+minecraft/sounds/mob/fox/spit2.ogg
+minecraft/sounds/mob/fox/spit3.ogg
+minecraft/sounds/mob/ghast/affectionate_scream.ogg
+minecraft/sounds/mob/ghast/charge.ogg
+minecraft/sounds/mob/ghast/death.ogg
+minecraft/sounds/mob/ghast/fireball4.ogg
+minecraft/sounds/mob/ghast/moan1.ogg
+minecraft/sounds/mob/ghast/moan2.ogg
+minecraft/sounds/mob/ghast/moan3.ogg
+minecraft/sounds/mob/ghast/moan4.ogg
+minecraft/sounds/mob/ghast/moan5.ogg
+minecraft/sounds/mob/ghast/moan6.ogg
+minecraft/sounds/mob/ghast/moan7.ogg
+minecraft/sounds/mob/ghast/scream1.ogg
+minecraft/sounds/mob/ghast/scream2.ogg
+minecraft/sounds/mob/ghast/scream3.ogg
+minecraft/sounds/mob/ghast/scream4.ogg
+minecraft/sounds/mob/ghast/scream5.ogg
+minecraft/sounds/mob/guardian/attack_loop.ogg
+minecraft/sounds/mob/guardian/curse.ogg
+minecraft/sounds/mob/guardian/elder_death.ogg
+minecraft/sounds/mob/guardian/elder_hit1.ogg
+minecraft/sounds/mob/guardian/elder_hit2.ogg
+minecraft/sounds/mob/guardian/elder_hit3.ogg
+minecraft/sounds/mob/guardian/elder_hit4.ogg
+minecraft/sounds/mob/guardian/elder_idle1.ogg
+minecraft/sounds/mob/guardian/elder_idle2.ogg
+minecraft/sounds/mob/guardian/elder_idle3.ogg
+minecraft/sounds/mob/guardian/elder_idle4.ogg
+minecraft/sounds/mob/guardian/flop1.ogg
+minecraft/sounds/mob/guardian/flop2.ogg
+minecraft/sounds/mob/guardian/flop3.ogg
+minecraft/sounds/mob/guardian/flop4.ogg
+minecraft/sounds/mob/guardian/guardian_death.ogg
+minecraft/sounds/mob/guardian/guardian_hit1.ogg
+minecraft/sounds/mob/guardian/guardian_hit2.ogg
+minecraft/sounds/mob/guardian/guardian_hit3.ogg
+minecraft/sounds/mob/guardian/guardian_hit4.ogg
+minecraft/sounds/mob/guardian/land_death.ogg
+minecraft/sounds/mob/guardian/land_hit1.ogg
+minecraft/sounds/mob/guardian/land_hit2.ogg
+minecraft/sounds/mob/guardian/land_hit3.ogg
+minecraft/sounds/mob/guardian/land_hit4.ogg
+minecraft/sounds/mob/guardian/land_idle1.ogg
+minecraft/sounds/mob/guardian/land_idle2.ogg
+minecraft/sounds/mob/guardian/land_idle3.ogg
+minecraft/sounds/mob/guardian/land_idle4.ogg
+minecraft/sounds/mob/hoglin/angry1.ogg
+minecraft/sounds/mob/hoglin/angry2.ogg
+minecraft/sounds/mob/hoglin/angry3.ogg
+minecraft/sounds/mob/hoglin/angry4.ogg
+minecraft/sounds/mob/hoglin/angry5.ogg
+minecraft/sounds/mob/hoglin/angry6.ogg
+minecraft/sounds/mob/hoglin/attack1.ogg
+minecraft/sounds/mob/hoglin/attack2.ogg
+minecraft/sounds/mob/hoglin/converted1.ogg
+minecraft/sounds/mob/hoglin/converted2.ogg
+minecraft/sounds/mob/hoglin/death1.ogg
+minecraft/sounds/mob/hoglin/death2.ogg
+minecraft/sounds/mob/hoglin/death3.ogg
+minecraft/sounds/mob/hoglin/hurt1.ogg
+minecraft/sounds/mob/hoglin/hurt2.ogg
+minecraft/sounds/mob/hoglin/hurt3.ogg
+minecraft/sounds/mob/hoglin/hurt4.ogg
+minecraft/sounds/mob/hoglin/idle1.ogg
+minecraft/sounds/mob/hoglin/idle10.ogg
+minecraft/sounds/mob/hoglin/idle11.ogg
+minecraft/sounds/mob/hoglin/idle2.ogg
+minecraft/sounds/mob/hoglin/idle3.ogg
+minecraft/sounds/mob/hoglin/idle4.ogg
+minecraft/sounds/mob/hoglin/idle5.ogg
+minecraft/sounds/mob/hoglin/idle6.ogg
+minecraft/sounds/mob/hoglin/idle7.ogg
+minecraft/sounds/mob/hoglin/idle8.ogg
+minecraft/sounds/mob/hoglin/idle9.ogg
+minecraft/sounds/mob/hoglin/retreat1.ogg
+minecraft/sounds/mob/hoglin/retreat2.ogg
+minecraft/sounds/mob/hoglin/retreat3.ogg
+minecraft/sounds/mob/hoglin/step1.ogg
+minecraft/sounds/mob/hoglin/step2.ogg
+minecraft/sounds/mob/hoglin/step3.ogg
+minecraft/sounds/mob/hoglin/step4.ogg
+minecraft/sounds/mob/hoglin/step5.ogg
+minecraft/sounds/mob/hoglin/step6.ogg
+minecraft/sounds/mob/horse/angry1.ogg
+minecraft/sounds/mob/horse/armor.ogg
+minecraft/sounds/mob/horse/breathe1.ogg
+minecraft/sounds/mob/horse/breathe2.ogg
+minecraft/sounds/mob/horse/breathe3.ogg
+minecraft/sounds/mob/horse/death.ogg
+minecraft/sounds/mob/horse/donkey/angry1.ogg
+minecraft/sounds/mob/horse/donkey/angry2.ogg
+minecraft/sounds/mob/horse/donkey/death.ogg
+minecraft/sounds/mob/horse/donkey/hit1.ogg
+minecraft/sounds/mob/horse/donkey/hit2.ogg
+minecraft/sounds/mob/horse/donkey/hit3.ogg
+minecraft/sounds/mob/horse/donkey/idle1.ogg
+minecraft/sounds/mob/horse/donkey/idle2.ogg
+minecraft/sounds/mob/horse/donkey/idle3.ogg
+minecraft/sounds/mob/horse/gallop1.ogg
+minecraft/sounds/mob/horse/gallop2.ogg
+minecraft/sounds/mob/horse/gallop3.ogg
+minecraft/sounds/mob/horse/gallop4.ogg
+minecraft/sounds/mob/horse/hit1.ogg
+minecraft/sounds/mob/horse/hit2.ogg
+minecraft/sounds/mob/horse/hit3.ogg
+minecraft/sounds/mob/horse/hit4.ogg
+minecraft/sounds/mob/horse/idle1.ogg
+minecraft/sounds/mob/horse/idle2.ogg
+minecraft/sounds/mob/horse/idle3.ogg
+minecraft/sounds/mob/horse/jump.ogg
+minecraft/sounds/mob/horse/land.ogg
+minecraft/sounds/mob/horse/leather.ogg
+minecraft/sounds/mob/horse/skeleton/death.ogg
+minecraft/sounds/mob/horse/skeleton/hit1.ogg
+minecraft/sounds/mob/horse/skeleton/hit2.ogg
+minecraft/sounds/mob/horse/skeleton/hit3.ogg
+minecraft/sounds/mob/horse/skeleton/hit4.ogg
+minecraft/sounds/mob/horse/skeleton/idle1.ogg
+minecraft/sounds/mob/horse/skeleton/idle2.ogg
+minecraft/sounds/mob/horse/skeleton/idle3.ogg
+minecraft/sounds/mob/horse/skeleton/water/gallop1.ogg
+minecraft/sounds/mob/horse/skeleton/water/gallop2.ogg
+minecraft/sounds/mob/horse/skeleton/water/gallop3.ogg
+minecraft/sounds/mob/horse/skeleton/water/gallop4.ogg
+minecraft/sounds/mob/horse/skeleton/water/idle1.ogg
+minecraft/sounds/mob/horse/skeleton/water/idle2.ogg
+minecraft/sounds/mob/horse/skeleton/water/idle3.ogg
+minecraft/sounds/mob/horse/skeleton/water/idle4.ogg
+minecraft/sounds/mob/horse/skeleton/water/idle5.ogg
+minecraft/sounds/mob/horse/skeleton/water/jump.ogg
+minecraft/sounds/mob/horse/skeleton/water/soft1.ogg
+minecraft/sounds/mob/horse/skeleton/water/soft2.ogg
+minecraft/sounds/mob/horse/skeleton/water/soft3.ogg
+minecraft/sounds/mob/horse/skeleton/water/soft4.ogg
+minecraft/sounds/mob/horse/skeleton/water/soft5.ogg
+minecraft/sounds/mob/horse/skeleton/water/soft6.ogg
+minecraft/sounds/mob/horse/soft1.ogg
+minecraft/sounds/mob/horse/soft2.ogg
+minecraft/sounds/mob/horse/soft3.ogg
+minecraft/sounds/mob/horse/soft4.ogg
+minecraft/sounds/mob/horse/soft5.ogg
+minecraft/sounds/mob/horse/soft6.ogg
+minecraft/sounds/mob/horse/wood1.ogg
+minecraft/sounds/mob/horse/wood2.ogg
+minecraft/sounds/mob/horse/wood3.ogg
+minecraft/sounds/mob/horse/wood4.ogg
+minecraft/sounds/mob/horse/wood5.ogg
+minecraft/sounds/mob/horse/wood6.ogg
+minecraft/sounds/mob/horse/zombie/death.ogg
+minecraft/sounds/mob/horse/zombie/hit1.ogg
+minecraft/sounds/mob/horse/zombie/hit2.ogg
+minecraft/sounds/mob/horse/zombie/hit3.ogg
+minecraft/sounds/mob/horse/zombie/hit4.ogg
+minecraft/sounds/mob/horse/zombie/idle1.ogg
+minecraft/sounds/mob/horse/zombie/idle2.ogg
+minecraft/sounds/mob/horse/zombie/idle3.ogg
+minecraft/sounds/mob/husk/convert1.ogg
+minecraft/sounds/mob/husk/convert2.ogg
+minecraft/sounds/mob/husk/death1.ogg
+minecraft/sounds/mob/husk/death2.ogg
+minecraft/sounds/mob/husk/hurt1.ogg
+minecraft/sounds/mob/husk/hurt2.ogg
+minecraft/sounds/mob/husk/idle1.ogg
+minecraft/sounds/mob/husk/idle2.ogg
+minecraft/sounds/mob/husk/idle3.ogg
+minecraft/sounds/mob/husk/step1.ogg
+minecraft/sounds/mob/husk/step2.ogg
+minecraft/sounds/mob/husk/step3.ogg
+minecraft/sounds/mob/husk/step4.ogg
+minecraft/sounds/mob/husk/step5.ogg
+minecraft/sounds/mob/illusion_illager/death1.ogg
+minecraft/sounds/mob/illusion_illager/death2.ogg
+minecraft/sounds/mob/illusion_illager/hurt1.ogg
+minecraft/sounds/mob/illusion_illager/hurt2.ogg
+minecraft/sounds/mob/illusion_illager/hurt3.ogg
+minecraft/sounds/mob/illusion_illager/idle1.ogg
+minecraft/sounds/mob/illusion_illager/idle2.ogg
+minecraft/sounds/mob/illusion_illager/idle3.ogg
+minecraft/sounds/mob/illusion_illager/idle4.ogg
+minecraft/sounds/mob/illusion_illager/mirror_move1.ogg
+minecraft/sounds/mob/illusion_illager/mirror_move2.ogg
+minecraft/sounds/mob/illusion_illager/prepare_blind.ogg
+minecraft/sounds/mob/illusion_illager/prepare_mirror.ogg
+minecraft/sounds/mob/irongolem/damage1.ogg
+minecraft/sounds/mob/irongolem/damage2.ogg
+minecraft/sounds/mob/irongolem/death.ogg
+minecraft/sounds/mob/irongolem/hit1.ogg
+minecraft/sounds/mob/irongolem/hit2.ogg
+minecraft/sounds/mob/irongolem/hit3.ogg
+minecraft/sounds/mob/irongolem/hit4.ogg
+minecraft/sounds/mob/irongolem/repair.ogg
+minecraft/sounds/mob/irongolem/throw.ogg
+minecraft/sounds/mob/irongolem/walk1.ogg
+minecraft/sounds/mob/irongolem/walk2.ogg
+minecraft/sounds/mob/irongolem/walk3.ogg
+minecraft/sounds/mob/irongolem/walk4.ogg
+minecraft/sounds/mob/llama/angry1.ogg
+minecraft/sounds/mob/llama/death1.ogg
+minecraft/sounds/mob/llama/death2.ogg
+minecraft/sounds/mob/llama/eat1.ogg
+minecraft/sounds/mob/llama/eat2.ogg
+minecraft/sounds/mob/llama/eat3.ogg
+minecraft/sounds/mob/llama/hurt1.ogg
+minecraft/sounds/mob/llama/hurt2.ogg
+minecraft/sounds/mob/llama/hurt3.ogg
+minecraft/sounds/mob/llama/idle1.ogg
+minecraft/sounds/mob/llama/idle2.ogg
+minecraft/sounds/mob/llama/idle3.ogg
+minecraft/sounds/mob/llama/idle4.ogg
+minecraft/sounds/mob/llama/idle5.ogg
+minecraft/sounds/mob/llama/spit1.ogg
+minecraft/sounds/mob/llama/spit2.ogg
+minecraft/sounds/mob/llama/step1.ogg
+minecraft/sounds/mob/llama/step2.ogg
+minecraft/sounds/mob/llama/step3.ogg
+minecraft/sounds/mob/llama/step4.ogg
+minecraft/sounds/mob/llama/step5.ogg
+minecraft/sounds/mob/llama/swag.ogg
+minecraft/sounds/mob/magmacube/big1.ogg
+minecraft/sounds/mob/magmacube/big2.ogg
+minecraft/sounds/mob/magmacube/big3.ogg
+minecraft/sounds/mob/magmacube/big4.ogg
+minecraft/sounds/mob/magmacube/jump1.ogg
+minecraft/sounds/mob/magmacube/jump2.ogg
+minecraft/sounds/mob/magmacube/jump3.ogg
+minecraft/sounds/mob/magmacube/jump4.ogg
+minecraft/sounds/mob/magmacube/small1.ogg
+minecraft/sounds/mob/magmacube/small2.ogg
+minecraft/sounds/mob/magmacube/small3.ogg
+minecraft/sounds/mob/magmacube/small4.ogg
+minecraft/sounds/mob/magmacube/small5.ogg
+minecraft/sounds/mob/mooshroom/convert1.ogg
+minecraft/sounds/mob/mooshroom/convert2.ogg
+minecraft/sounds/mob/mooshroom/eat1.ogg
+minecraft/sounds/mob/mooshroom/eat2.ogg
+minecraft/sounds/mob/mooshroom/eat3.ogg
+minecraft/sounds/mob/mooshroom/eat4.ogg
+minecraft/sounds/mob/mooshroom/milk1.ogg
+minecraft/sounds/mob/mooshroom/milk2.ogg
+minecraft/sounds/mob/mooshroom/milk3.ogg
+minecraft/sounds/mob/panda/aggressive/aggressive1.ogg
+minecraft/sounds/mob/panda/aggressive/aggressive2.ogg
+minecraft/sounds/mob/panda/aggressive/aggressive3.ogg
+minecraft/sounds/mob/panda/aggressive/aggressive4.ogg
+minecraft/sounds/mob/panda/bite1.ogg
+minecraft/sounds/mob/panda/bite2.ogg
+minecraft/sounds/mob/panda/bite3.ogg
+minecraft/sounds/mob/panda/cant_breed1.ogg
+minecraft/sounds/mob/panda/cant_breed2.ogg
+minecraft/sounds/mob/panda/cant_breed3.ogg
+minecraft/sounds/mob/panda/cant_breed4.ogg
+minecraft/sounds/mob/panda/cant_breed5.ogg
+minecraft/sounds/mob/panda/death1.ogg
+minecraft/sounds/mob/panda/death2.ogg
+minecraft/sounds/mob/panda/death3.ogg
+minecraft/sounds/mob/panda/death4.ogg
+minecraft/sounds/mob/panda/eat1.ogg
+minecraft/sounds/mob/panda/eat10.ogg
+minecraft/sounds/mob/panda/eat11.ogg
+minecraft/sounds/mob/panda/eat12.ogg
+minecraft/sounds/mob/panda/eat2.ogg
+minecraft/sounds/mob/panda/eat3.ogg
+minecraft/sounds/mob/panda/eat4.ogg
+minecraft/sounds/mob/panda/eat5.ogg
+minecraft/sounds/mob/panda/eat6.ogg
+minecraft/sounds/mob/panda/eat7.ogg
+minecraft/sounds/mob/panda/eat8.ogg
+minecraft/sounds/mob/panda/eat9.ogg
+minecraft/sounds/mob/panda/hurt1.ogg
+minecraft/sounds/mob/panda/hurt2.ogg
+minecraft/sounds/mob/panda/hurt3.ogg
+minecraft/sounds/mob/panda/hurt4.ogg
+minecraft/sounds/mob/panda/hurt5.ogg
+minecraft/sounds/mob/panda/hurt6.ogg
+minecraft/sounds/mob/panda/idle1.ogg
+minecraft/sounds/mob/panda/idle2.ogg
+minecraft/sounds/mob/panda/idle3.ogg
+minecraft/sounds/mob/panda/idle4.ogg
+minecraft/sounds/mob/panda/nosebreath1.ogg
+minecraft/sounds/mob/panda/nosebreath2.ogg
+minecraft/sounds/mob/panda/nosebreath3.ogg
+minecraft/sounds/mob/panda/pant1.ogg
+minecraft/sounds/mob/panda/pant2.ogg
+minecraft/sounds/mob/panda/pre_sneeze.ogg
+minecraft/sounds/mob/panda/sneeze1.ogg
+minecraft/sounds/mob/panda/sneeze2.ogg
+minecraft/sounds/mob/panda/sneeze3.ogg
+minecraft/sounds/mob/panda/step1.ogg
+minecraft/sounds/mob/panda/step2.ogg
+minecraft/sounds/mob/panda/step3.ogg
+minecraft/sounds/mob/panda/step4.ogg
+minecraft/sounds/mob/panda/step5.ogg
+minecraft/sounds/mob/panda/worried/worried1.ogg
+minecraft/sounds/mob/panda/worried/worried2.ogg
+minecraft/sounds/mob/panda/worried/worried3.ogg
+minecraft/sounds/mob/panda/worried/worried4.ogg
+minecraft/sounds/mob/panda/worried/worried5.ogg
+minecraft/sounds/mob/panda/worried/worried6.ogg
+minecraft/sounds/mob/parrot/death1.ogg
+minecraft/sounds/mob/parrot/death2.ogg
+minecraft/sounds/mob/parrot/death3.ogg
+minecraft/sounds/mob/parrot/death4.ogg
+minecraft/sounds/mob/parrot/eat1.ogg
+minecraft/sounds/mob/parrot/eat2.ogg
+minecraft/sounds/mob/parrot/eat3.ogg
+minecraft/sounds/mob/parrot/fly1.ogg
+minecraft/sounds/mob/parrot/fly2.ogg
+minecraft/sounds/mob/parrot/fly3.ogg
+minecraft/sounds/mob/parrot/fly4.ogg
+minecraft/sounds/mob/parrot/fly5.ogg
+minecraft/sounds/mob/parrot/fly6.ogg
+minecraft/sounds/mob/parrot/fly7.ogg
+minecraft/sounds/mob/parrot/fly8.ogg
+minecraft/sounds/mob/parrot/hurt1.ogg
+minecraft/sounds/mob/parrot/hurt2.ogg
+minecraft/sounds/mob/parrot/idle1.ogg
+minecraft/sounds/mob/parrot/idle2.ogg
+minecraft/sounds/mob/parrot/idle3.ogg
+minecraft/sounds/mob/parrot/idle4.ogg
+minecraft/sounds/mob/parrot/idle5.ogg
+minecraft/sounds/mob/parrot/idle6.ogg
+minecraft/sounds/mob/parrot/step1.ogg
+minecraft/sounds/mob/parrot/step2.ogg
+minecraft/sounds/mob/parrot/step3.ogg
+minecraft/sounds/mob/parrot/step4.ogg
+minecraft/sounds/mob/parrot/step5.ogg
+minecraft/sounds/mob/phantom/bite1.ogg
+minecraft/sounds/mob/phantom/bite2.ogg
+minecraft/sounds/mob/phantom/death1.ogg
+minecraft/sounds/mob/phantom/death2.ogg
+minecraft/sounds/mob/phantom/death3.ogg
+minecraft/sounds/mob/phantom/flap1.ogg
+minecraft/sounds/mob/phantom/flap2.ogg
+minecraft/sounds/mob/phantom/flap3.ogg
+minecraft/sounds/mob/phantom/flap4.ogg
+minecraft/sounds/mob/phantom/flap5.ogg
+minecraft/sounds/mob/phantom/flap6.ogg
+minecraft/sounds/mob/phantom/hurt1.ogg
+minecraft/sounds/mob/phantom/hurt2.ogg
+minecraft/sounds/mob/phantom/hurt3.ogg
+minecraft/sounds/mob/phantom/idle1.ogg
+minecraft/sounds/mob/phantom/idle2.ogg
+minecraft/sounds/mob/phantom/idle3.ogg
+minecraft/sounds/mob/phantom/idle4.ogg
+minecraft/sounds/mob/phantom/idle5.ogg
+minecraft/sounds/mob/phantom/swoop1.ogg
+minecraft/sounds/mob/phantom/swoop2.ogg
+minecraft/sounds/mob/phantom/swoop3.ogg
+minecraft/sounds/mob/phantom/swoop4.ogg
+minecraft/sounds/mob/pig/death.ogg
+minecraft/sounds/mob/pig/say1.ogg
+minecraft/sounds/mob/pig/say2.ogg
+minecraft/sounds/mob/pig/say3.ogg
+minecraft/sounds/mob/pig/step1.ogg
+minecraft/sounds/mob/pig/step2.ogg
+minecraft/sounds/mob/pig/step3.ogg
+minecraft/sounds/mob/pig/step4.ogg
+minecraft/sounds/mob/pig/step5.ogg
+minecraft/sounds/mob/piglin/admire1.ogg
+minecraft/sounds/mob/piglin/admire2.ogg
+minecraft/sounds/mob/piglin/angry1.ogg
+minecraft/sounds/mob/piglin/angry2.ogg
+minecraft/sounds/mob/piglin/angry3.ogg
+minecraft/sounds/mob/piglin/angry4.ogg
+minecraft/sounds/mob/piglin/celebrate1.ogg
+minecraft/sounds/mob/piglin/celebrate2.ogg
+minecraft/sounds/mob/piglin/celebrate3.ogg
+minecraft/sounds/mob/piglin/celebrate4.ogg
+minecraft/sounds/mob/piglin/converted1.ogg
+minecraft/sounds/mob/piglin/converted2.ogg
+minecraft/sounds/mob/piglin/death1.ogg
+minecraft/sounds/mob/piglin/death2.ogg
+minecraft/sounds/mob/piglin/death3.ogg
+minecraft/sounds/mob/piglin/death4.ogg
+minecraft/sounds/mob/piglin/hurt1.ogg
+minecraft/sounds/mob/piglin/hurt2.ogg
+minecraft/sounds/mob/piglin/hurt3.ogg
+minecraft/sounds/mob/piglin/idle1.ogg
+minecraft/sounds/mob/piglin/idle2.ogg
+minecraft/sounds/mob/piglin/idle3.ogg
+minecraft/sounds/mob/piglin/idle4.ogg
+minecraft/sounds/mob/piglin/idle5.ogg
+minecraft/sounds/mob/piglin/jealous1.ogg
+minecraft/sounds/mob/piglin/jealous2.ogg
+minecraft/sounds/mob/piglin/jealous3.ogg
+minecraft/sounds/mob/piglin/jealous4.ogg
+minecraft/sounds/mob/piglin/jealous5.ogg
+minecraft/sounds/mob/piglin/retreat1.ogg
+minecraft/sounds/mob/piglin/retreat2.ogg
+minecraft/sounds/mob/piglin/retreat3.ogg
+minecraft/sounds/mob/piglin/retreat4.ogg
+minecraft/sounds/mob/piglin/step1.ogg
+minecraft/sounds/mob/piglin/step2.ogg
+minecraft/sounds/mob/piglin/step3.ogg
+minecraft/sounds/mob/piglin/step4.ogg
+minecraft/sounds/mob/piglin/step5.ogg
+minecraft/sounds/mob/piglin_brute/angry1.ogg
+minecraft/sounds/mob/piglin_brute/angry2.ogg
+minecraft/sounds/mob/piglin_brute/angry3.ogg
+minecraft/sounds/mob/piglin_brute/angry4.ogg
+minecraft/sounds/mob/piglin_brute/angry5.ogg
+minecraft/sounds/mob/piglin_brute/death1.ogg
+minecraft/sounds/mob/piglin_brute/death2.ogg
+minecraft/sounds/mob/piglin_brute/death3.ogg
+minecraft/sounds/mob/piglin_brute/hurt1.ogg
+minecraft/sounds/mob/piglin_brute/hurt2.ogg
+minecraft/sounds/mob/piglin_brute/hurt3.ogg
+minecraft/sounds/mob/piglin_brute/hurt4.ogg
+minecraft/sounds/mob/piglin_brute/idle1.ogg
+minecraft/sounds/mob/piglin_brute/idle2.ogg
+minecraft/sounds/mob/piglin_brute/idle3.ogg
+minecraft/sounds/mob/piglin_brute/idle4.ogg
+minecraft/sounds/mob/piglin_brute/idle5.ogg
+minecraft/sounds/mob/piglin_brute/idle6.ogg
+minecraft/sounds/mob/piglin_brute/idle7.ogg
+minecraft/sounds/mob/piglin_brute/idle8.ogg
+minecraft/sounds/mob/piglin_brute/idle9.ogg
+minecraft/sounds/mob/piglin_brute/step1.ogg
+minecraft/sounds/mob/piglin_brute/step2.ogg
+minecraft/sounds/mob/piglin_brute/step3.ogg
+minecraft/sounds/mob/piglin_brute/step4.ogg
+minecraft/sounds/mob/piglin_brute/step5.ogg
+minecraft/sounds/mob/pillager/celebrate1.ogg
+minecraft/sounds/mob/pillager/celebrate2.ogg
+minecraft/sounds/mob/pillager/celebrate3.ogg
+minecraft/sounds/mob/pillager/celebrate4.ogg
+minecraft/sounds/mob/pillager/death1.ogg
+minecraft/sounds/mob/pillager/death2.ogg
+minecraft/sounds/mob/pillager/horn_celebrate.ogg
+minecraft/sounds/mob/pillager/hurt1.ogg
+minecraft/sounds/mob/pillager/hurt2.ogg
+minecraft/sounds/mob/pillager/hurt3.ogg
+minecraft/sounds/mob/pillager/idle1.ogg
+minecraft/sounds/mob/pillager/idle2.ogg
+minecraft/sounds/mob/pillager/idle3.ogg
+minecraft/sounds/mob/pillager/idle4.ogg
+minecraft/sounds/mob/polarbear/death1.ogg
+minecraft/sounds/mob/polarbear/death2.ogg
+minecraft/sounds/mob/polarbear/death3.ogg
+minecraft/sounds/mob/polarbear/hurt1.ogg
+minecraft/sounds/mob/polarbear/hurt2.ogg
+minecraft/sounds/mob/polarbear/hurt3.ogg
+minecraft/sounds/mob/polarbear/hurt4.ogg
+minecraft/sounds/mob/polarbear/idle1.ogg
+minecraft/sounds/mob/polarbear/idle2.ogg
+minecraft/sounds/mob/polarbear/idle3.ogg
+minecraft/sounds/mob/polarbear/idle4.ogg
+minecraft/sounds/mob/polarbear/step1.ogg
+minecraft/sounds/mob/polarbear/step2.ogg
+minecraft/sounds/mob/polarbear/step3.ogg
+minecraft/sounds/mob/polarbear/step4.ogg
+minecraft/sounds/mob/polarbear/warning1.ogg
+minecraft/sounds/mob/polarbear/warning2.ogg
+minecraft/sounds/mob/polarbear/warning3.ogg
+minecraft/sounds/mob/polarbear_baby/idle1.ogg
+minecraft/sounds/mob/polarbear_baby/idle2.ogg
+minecraft/sounds/mob/polarbear_baby/idle3.ogg
+minecraft/sounds/mob/polarbear_baby/idle4.ogg
+minecraft/sounds/mob/rabbit/bunnymurder.ogg
+minecraft/sounds/mob/rabbit/hop1.ogg
+minecraft/sounds/mob/rabbit/hop2.ogg
+minecraft/sounds/mob/rabbit/hop3.ogg
+minecraft/sounds/mob/rabbit/hop4.ogg
+minecraft/sounds/mob/rabbit/hurt1.ogg
+minecraft/sounds/mob/rabbit/hurt2.ogg
+minecraft/sounds/mob/rabbit/hurt3.ogg
+minecraft/sounds/mob/rabbit/hurt4.ogg
+minecraft/sounds/mob/rabbit/idle1.ogg
+minecraft/sounds/mob/rabbit/idle2.ogg
+minecraft/sounds/mob/rabbit/idle3.ogg
+minecraft/sounds/mob/rabbit/idle4.ogg
+minecraft/sounds/mob/ravager/bite1.ogg
+minecraft/sounds/mob/ravager/bite2.ogg
+minecraft/sounds/mob/ravager/bite3.ogg
+minecraft/sounds/mob/ravager/celebrate1.ogg
+minecraft/sounds/mob/ravager/celebrate2.ogg
+minecraft/sounds/mob/ravager/death1.ogg
+minecraft/sounds/mob/ravager/death2.ogg
+minecraft/sounds/mob/ravager/death3.ogg
+minecraft/sounds/mob/ravager/hurt1.ogg
+minecraft/sounds/mob/ravager/hurt2.ogg
+minecraft/sounds/mob/ravager/hurt3.ogg
+minecraft/sounds/mob/ravager/hurt4.ogg
+minecraft/sounds/mob/ravager/idle1.ogg
+minecraft/sounds/mob/ravager/idle2.ogg
+minecraft/sounds/mob/ravager/idle3.ogg
+minecraft/sounds/mob/ravager/idle4.ogg
+minecraft/sounds/mob/ravager/idle5.ogg
+minecraft/sounds/mob/ravager/idle6.ogg
+minecraft/sounds/mob/ravager/idle7.ogg
+minecraft/sounds/mob/ravager/idle8.ogg
+minecraft/sounds/mob/ravager/roar1.ogg
+minecraft/sounds/mob/ravager/roar2.ogg
+minecraft/sounds/mob/ravager/roar3.ogg
+minecraft/sounds/mob/ravager/roar4.ogg
+minecraft/sounds/mob/ravager/step1.ogg
+minecraft/sounds/mob/ravager/step2.ogg
+minecraft/sounds/mob/ravager/step3.ogg
+minecraft/sounds/mob/ravager/step4.ogg
+minecraft/sounds/mob/ravager/step5.ogg
+minecraft/sounds/mob/ravager/stun1.ogg
+minecraft/sounds/mob/ravager/stun2.ogg
+minecraft/sounds/mob/ravager/stun3.ogg
+minecraft/sounds/mob/sheep/say1.ogg
+minecraft/sounds/mob/sheep/say2.ogg
+minecraft/sounds/mob/sheep/say3.ogg
+minecraft/sounds/mob/sheep/shear.ogg
+minecraft/sounds/mob/sheep/step1.ogg
+minecraft/sounds/mob/sheep/step2.ogg
+minecraft/sounds/mob/sheep/step3.ogg
+minecraft/sounds/mob/sheep/step4.ogg
+minecraft/sounds/mob/sheep/step5.ogg
+minecraft/sounds/mob/silverfish/hit1.ogg
+minecraft/sounds/mob/silverfish/hit2.ogg
+minecraft/sounds/mob/silverfish/hit3.ogg
+minecraft/sounds/mob/silverfish/kill.ogg
+minecraft/sounds/mob/silverfish/say1.ogg
+minecraft/sounds/mob/silverfish/say2.ogg
+minecraft/sounds/mob/silverfish/say3.ogg
+minecraft/sounds/mob/silverfish/say4.ogg
+minecraft/sounds/mob/silverfish/step1.ogg
+minecraft/sounds/mob/silverfish/step2.ogg
+minecraft/sounds/mob/silverfish/step3.ogg
+minecraft/sounds/mob/silverfish/step4.ogg
+minecraft/sounds/mob/skeleton/death.ogg
+minecraft/sounds/mob/skeleton/hurt1.ogg
+minecraft/sounds/mob/skeleton/hurt2.ogg
+minecraft/sounds/mob/skeleton/hurt3.ogg
+minecraft/sounds/mob/skeleton/hurt4.ogg
+minecraft/sounds/mob/skeleton/say1.ogg
+minecraft/sounds/mob/skeleton/say2.ogg
+minecraft/sounds/mob/skeleton/say3.ogg
+minecraft/sounds/mob/skeleton/step1.ogg
+minecraft/sounds/mob/skeleton/step2.ogg
+minecraft/sounds/mob/skeleton/step3.ogg
+minecraft/sounds/mob/skeleton/step4.ogg
+minecraft/sounds/mob/slime/attack1.ogg
+minecraft/sounds/mob/slime/attack2.ogg
+minecraft/sounds/mob/slime/big1.ogg
+minecraft/sounds/mob/slime/big2.ogg
+minecraft/sounds/mob/slime/big3.ogg
+minecraft/sounds/mob/slime/big4.ogg
+minecraft/sounds/mob/slime/small1.ogg
+minecraft/sounds/mob/slime/small2.ogg
+minecraft/sounds/mob/slime/small3.ogg
+minecraft/sounds/mob/slime/small4.ogg
+minecraft/sounds/mob/slime/small5.ogg
+minecraft/sounds/mob/spider/death.ogg
+minecraft/sounds/mob/spider/say1.ogg
+minecraft/sounds/mob/spider/say2.ogg
+minecraft/sounds/mob/spider/say3.ogg
+minecraft/sounds/mob/spider/say4.ogg
+minecraft/sounds/mob/spider/step1.ogg
+minecraft/sounds/mob/spider/step2.ogg
+minecraft/sounds/mob/spider/step3.ogg
+minecraft/sounds/mob/spider/step4.ogg
+minecraft/sounds/mob/stray/death1.ogg
+minecraft/sounds/mob/stray/death2.ogg
+minecraft/sounds/mob/stray/hurt1.ogg
+minecraft/sounds/mob/stray/hurt2.ogg
+minecraft/sounds/mob/stray/hurt3.ogg
+minecraft/sounds/mob/stray/hurt4.ogg
+minecraft/sounds/mob/stray/idle1.ogg
+minecraft/sounds/mob/stray/idle2.ogg
+minecraft/sounds/mob/stray/idle3.ogg
+minecraft/sounds/mob/stray/idle4.ogg
+minecraft/sounds/mob/stray/step1.ogg
+minecraft/sounds/mob/stray/step2.ogg
+minecraft/sounds/mob/stray/step3.ogg
+minecraft/sounds/mob/stray/step4.ogg
+minecraft/sounds/mob/strider/death1.ogg
+minecraft/sounds/mob/strider/death2.ogg
+minecraft/sounds/mob/strider/death3.ogg
+minecraft/sounds/mob/strider/death4.ogg
+minecraft/sounds/mob/strider/eat1.ogg
+minecraft/sounds/mob/strider/eat2.ogg
+minecraft/sounds/mob/strider/eat3.ogg
+minecraft/sounds/mob/strider/happy1.ogg
+minecraft/sounds/mob/strider/happy2.ogg
+minecraft/sounds/mob/strider/happy3.ogg
+minecraft/sounds/mob/strider/happy4.ogg
+minecraft/sounds/mob/strider/happy5.ogg
+minecraft/sounds/mob/strider/hurt1.ogg
+minecraft/sounds/mob/strider/hurt2.ogg
+minecraft/sounds/mob/strider/hurt3.ogg
+minecraft/sounds/mob/strider/hurt4.ogg
+minecraft/sounds/mob/strider/idle1.ogg
+minecraft/sounds/mob/strider/idle2.ogg
+minecraft/sounds/mob/strider/idle3.ogg
+minecraft/sounds/mob/strider/idle4.ogg
+minecraft/sounds/mob/strider/idle5.ogg
+minecraft/sounds/mob/strider/idle6.ogg
+minecraft/sounds/mob/strider/retreat1.ogg
+minecraft/sounds/mob/strider/retreat2.ogg
+minecraft/sounds/mob/strider/retreat3.ogg
+minecraft/sounds/mob/strider/retreat4.ogg
+minecraft/sounds/mob/strider/retreat5.ogg
+minecraft/sounds/mob/strider/step1.ogg
+minecraft/sounds/mob/strider/step2.ogg
+minecraft/sounds/mob/strider/step3.ogg
+minecraft/sounds/mob/strider/step4.ogg
+minecraft/sounds/mob/strider/step5.ogg
+minecraft/sounds/mob/strider/step_lava1.ogg
+minecraft/sounds/mob/strider/step_lava2.ogg
+minecraft/sounds/mob/strider/step_lava3.ogg
+minecraft/sounds/mob/strider/step_lava4.ogg
+minecraft/sounds/mob/strider/step_lava5.ogg
+minecraft/sounds/mob/strider/step_lava6.ogg
+minecraft/sounds/mob/turtle/armor.ogg
+minecraft/sounds/mob/turtle/baby/death1.ogg
+minecraft/sounds/mob/turtle/baby/death2.ogg
+minecraft/sounds/mob/turtle/baby/egg_hatched1.ogg
+minecraft/sounds/mob/turtle/baby/egg_hatched2.ogg
+minecraft/sounds/mob/turtle/baby/egg_hatched3.ogg
+minecraft/sounds/mob/turtle/baby/hurt1.ogg
+minecraft/sounds/mob/turtle/baby/hurt2.ogg
+minecraft/sounds/mob/turtle/baby/shamble1.ogg
+minecraft/sounds/mob/turtle/baby/shamble2.ogg
+minecraft/sounds/mob/turtle/baby/shamble3.ogg
+minecraft/sounds/mob/turtle/baby/shamble4.ogg
+minecraft/sounds/mob/turtle/death1.ogg
+minecraft/sounds/mob/turtle/death2.ogg
+minecraft/sounds/mob/turtle/death3.ogg
+minecraft/sounds/mob/turtle/egg/drop_egg1.ogg
+minecraft/sounds/mob/turtle/egg/drop_egg2.ogg
+minecraft/sounds/mob/turtle/egg/egg_break1.ogg
+minecraft/sounds/mob/turtle/egg/egg_break2.ogg
+minecraft/sounds/mob/turtle/egg/egg_crack1.ogg
+minecraft/sounds/mob/turtle/egg/egg_crack2.ogg
+minecraft/sounds/mob/turtle/egg/egg_crack3.ogg
+minecraft/sounds/mob/turtle/egg/egg_crack4.ogg
+minecraft/sounds/mob/turtle/egg/egg_crack5.ogg
+minecraft/sounds/mob/turtle/egg/jump_egg1.ogg
+minecraft/sounds/mob/turtle/egg/jump_egg2.ogg
+minecraft/sounds/mob/turtle/egg/jump_egg3.ogg
+minecraft/sounds/mob/turtle/egg/jump_egg4.ogg
+minecraft/sounds/mob/turtle/hurt1.ogg
+minecraft/sounds/mob/turtle/hurt2.ogg
+minecraft/sounds/mob/turtle/hurt3.ogg
+minecraft/sounds/mob/turtle/hurt4.ogg
+minecraft/sounds/mob/turtle/hurt5.ogg
+minecraft/sounds/mob/turtle/idle1.ogg
+minecraft/sounds/mob/turtle/idle2.ogg
+minecraft/sounds/mob/turtle/idle3.ogg
+minecraft/sounds/mob/turtle/swim/swim1.ogg
+minecraft/sounds/mob/turtle/swim/swim2.ogg
+minecraft/sounds/mob/turtle/swim/swim3.ogg
+minecraft/sounds/mob/turtle/swim/swim4.ogg
+minecraft/sounds/mob/turtle/swim/swim5.ogg
+minecraft/sounds/mob/turtle/walk1.ogg
+minecraft/sounds/mob/turtle/walk2.ogg
+minecraft/sounds/mob/turtle/walk3.ogg
+minecraft/sounds/mob/turtle/walk4.ogg
+minecraft/sounds/mob/turtle/walk5.ogg
+minecraft/sounds/mob/vex/charge1.ogg
+minecraft/sounds/mob/vex/charge2.ogg
+minecraft/sounds/mob/vex/charge3.ogg
+minecraft/sounds/mob/vex/death1.ogg
+minecraft/sounds/mob/vex/death2.ogg
+minecraft/sounds/mob/vex/hurt1.ogg
+minecraft/sounds/mob/vex/hurt2.ogg
+minecraft/sounds/mob/vex/idle1.ogg
+minecraft/sounds/mob/vex/idle2.ogg
+minecraft/sounds/mob/vex/idle3.ogg
+minecraft/sounds/mob/vex/idle4.ogg
+minecraft/sounds/mob/villager/death.ogg
+minecraft/sounds/mob/villager/haggle1.ogg
+minecraft/sounds/mob/villager/haggle2.ogg
+minecraft/sounds/mob/villager/haggle3.ogg
+minecraft/sounds/mob/villager/hit1.ogg
+minecraft/sounds/mob/villager/hit2.ogg
+minecraft/sounds/mob/villager/hit3.ogg
+minecraft/sounds/mob/villager/hit4.ogg
+minecraft/sounds/mob/villager/idle1.ogg
+minecraft/sounds/mob/villager/idle2.ogg
+minecraft/sounds/mob/villager/idle3.ogg
+minecraft/sounds/mob/villager/no1.ogg
+minecraft/sounds/mob/villager/no2.ogg
+minecraft/sounds/mob/villager/no3.ogg
+minecraft/sounds/mob/villager/yes1.ogg
+minecraft/sounds/mob/villager/yes2.ogg
+minecraft/sounds/mob/villager/yes3.ogg
+minecraft/sounds/mob/vindication_illager/celebrate1.ogg
+minecraft/sounds/mob/vindication_illager/celebrate2.ogg
+minecraft/sounds/mob/vindication_illager/death1.ogg
+minecraft/sounds/mob/vindication_illager/death2.ogg
+minecraft/sounds/mob/vindication_illager/hurt1.ogg
+minecraft/sounds/mob/vindication_illager/hurt2.ogg
+minecraft/sounds/mob/vindication_illager/hurt3.ogg
+minecraft/sounds/mob/vindication_illager/idle1.ogg
+minecraft/sounds/mob/vindication_illager/idle2.ogg
+minecraft/sounds/mob/vindication_illager/idle3.ogg
+minecraft/sounds/mob/vindication_illager/idle4.ogg
+minecraft/sounds/mob/vindication_illager/idle5.ogg
+minecraft/sounds/mob/wandering_trader/appeared1.ogg
+minecraft/sounds/mob/wandering_trader/appeared2.ogg
+minecraft/sounds/mob/wandering_trader/death.ogg
+minecraft/sounds/mob/wandering_trader/disappeared1.ogg
+minecraft/sounds/mob/wandering_trader/disappeared2.ogg
+minecraft/sounds/mob/wandering_trader/drink_milk1.ogg
+minecraft/sounds/mob/wandering_trader/drink_milk2.ogg
+minecraft/sounds/mob/wandering_trader/drink_milk3.ogg
+minecraft/sounds/mob/wandering_trader/drink_milk4.ogg
+minecraft/sounds/mob/wandering_trader/drink_milk5.ogg
+minecraft/sounds/mob/wandering_trader/drink_potion.ogg
+minecraft/sounds/mob/wandering_trader/haggle1.ogg
+minecraft/sounds/mob/wandering_trader/haggle2.ogg
+minecraft/sounds/mob/wandering_trader/haggle3.ogg
+minecraft/sounds/mob/wandering_trader/hurt1.ogg
+minecraft/sounds/mob/wandering_trader/hurt2.ogg
+minecraft/sounds/mob/wandering_trader/hurt3.ogg
+minecraft/sounds/mob/wandering_trader/hurt4.ogg
+minecraft/sounds/mob/wandering_trader/idle1.ogg
+minecraft/sounds/mob/wandering_trader/idle2.ogg
+minecraft/sounds/mob/wandering_trader/idle3.ogg
+minecraft/sounds/mob/wandering_trader/idle4.ogg
+minecraft/sounds/mob/wandering_trader/idle5.ogg
+minecraft/sounds/mob/wandering_trader/no1.ogg
+minecraft/sounds/mob/wandering_trader/no2.ogg
+minecraft/sounds/mob/wandering_trader/no3.ogg
+minecraft/sounds/mob/wandering_trader/no4.ogg
+minecraft/sounds/mob/wandering_trader/no5.ogg
+minecraft/sounds/mob/wandering_trader/reappeared1.ogg
+minecraft/sounds/mob/wandering_trader/reappeared2.ogg
+minecraft/sounds/mob/wandering_trader/yes1.ogg
+minecraft/sounds/mob/wandering_trader/yes2.ogg
+minecraft/sounds/mob/wandering_trader/yes3.ogg
+minecraft/sounds/mob/wandering_trader/yes4.ogg
+minecraft/sounds/mob/wither/death.ogg
+minecraft/sounds/mob/wither/hurt1.ogg
+minecraft/sounds/mob/wither/hurt2.ogg
+minecraft/sounds/mob/wither/hurt3.ogg
+minecraft/sounds/mob/wither/hurt4.ogg
+minecraft/sounds/mob/wither/idle1.ogg
+minecraft/sounds/mob/wither/idle2.ogg
+minecraft/sounds/mob/wither/idle3.ogg
+minecraft/sounds/mob/wither/idle4.ogg
+minecraft/sounds/mob/wither/shoot.ogg
+minecraft/sounds/mob/wither/spawn.ogg
+minecraft/sounds/mob/wither_skeleton/death1.ogg
+minecraft/sounds/mob/wither_skeleton/death2.ogg
+minecraft/sounds/mob/wither_skeleton/hurt1.ogg
+minecraft/sounds/mob/wither_skeleton/hurt2.ogg
+minecraft/sounds/mob/wither_skeleton/hurt3.ogg
+minecraft/sounds/mob/wither_skeleton/hurt4.ogg
+minecraft/sounds/mob/wither_skeleton/idle1.ogg
+minecraft/sounds/mob/wither_skeleton/idle2.ogg
+minecraft/sounds/mob/wither_skeleton/idle3.ogg
+minecraft/sounds/mob/wither_skeleton/step1.ogg
+minecraft/sounds/mob/wither_skeleton/step2.ogg
+minecraft/sounds/mob/wither_skeleton/step3.ogg
+minecraft/sounds/mob/wither_skeleton/step4.ogg
+minecraft/sounds/mob/wolf/bark1.ogg
+minecraft/sounds/mob/wolf/bark2.ogg
+minecraft/sounds/mob/wolf/bark3.ogg
+minecraft/sounds/mob/wolf/death.ogg
+minecraft/sounds/mob/wolf/growl1.ogg
+minecraft/sounds/mob/wolf/growl2.ogg
+minecraft/sounds/mob/wolf/growl3.ogg
+minecraft/sounds/mob/wolf/howl1.ogg
+minecraft/sounds/mob/wolf/howl2.ogg
+minecraft/sounds/mob/wolf/hurt1.ogg
+minecraft/sounds/mob/wolf/hurt2.ogg
+minecraft/sounds/mob/wolf/hurt3.ogg
+minecraft/sounds/mob/wolf/panting.ogg
+minecraft/sounds/mob/wolf/shake.ogg
+minecraft/sounds/mob/wolf/step1.ogg
+minecraft/sounds/mob/wolf/step2.ogg
+minecraft/sounds/mob/wolf/step3.ogg
+minecraft/sounds/mob/wolf/step4.ogg
+minecraft/sounds/mob/wolf/step5.ogg
+minecraft/sounds/mob/wolf/whine.ogg
+minecraft/sounds/mob/zoglin/angry1.ogg
+minecraft/sounds/mob/zoglin/angry2.ogg
+minecraft/sounds/mob/zoglin/angry3.ogg
+minecraft/sounds/mob/zoglin/attack1.ogg
+minecraft/sounds/mob/zoglin/attack2.ogg
+minecraft/sounds/mob/zoglin/death1.ogg
+minecraft/sounds/mob/zoglin/death2.ogg
+minecraft/sounds/mob/zoglin/death3.ogg
+minecraft/sounds/mob/zoglin/hurt1.ogg
+minecraft/sounds/mob/zoglin/hurt2.ogg
+minecraft/sounds/mob/zoglin/hurt3.ogg
+minecraft/sounds/mob/zoglin/idle1.ogg
+minecraft/sounds/mob/zoglin/idle2.ogg
+minecraft/sounds/mob/zoglin/idle3.ogg
+minecraft/sounds/mob/zoglin/idle4.ogg
+minecraft/sounds/mob/zoglin/idle5.ogg
+minecraft/sounds/mob/zoglin/idle6.ogg
+minecraft/sounds/mob/zoglin/step1.ogg
+minecraft/sounds/mob/zoglin/step2.ogg
+minecraft/sounds/mob/zoglin/step3.ogg
+minecraft/sounds/mob/zoglin/step4.ogg
+minecraft/sounds/mob/zoglin/step5.ogg
+minecraft/sounds/mob/zombie/death.ogg
+minecraft/sounds/mob/zombie/hurt1.ogg
+minecraft/sounds/mob/zombie/hurt2.ogg
+minecraft/sounds/mob/zombie/infect.ogg
+minecraft/sounds/mob/zombie/metal1.ogg
+minecraft/sounds/mob/zombie/metal2.ogg
+minecraft/sounds/mob/zombie/metal3.ogg
+minecraft/sounds/mob/zombie/remedy.ogg
+minecraft/sounds/mob/zombie/say1.ogg
+minecraft/sounds/mob/zombie/say2.ogg
+minecraft/sounds/mob/zombie/say3.ogg
+minecraft/sounds/mob/zombie/step1.ogg
+minecraft/sounds/mob/zombie/step2.ogg
+minecraft/sounds/mob/zombie/step3.ogg
+minecraft/sounds/mob/zombie/step4.ogg
+minecraft/sounds/mob/zombie/step5.ogg
+minecraft/sounds/mob/zombie/unfect.ogg
+minecraft/sounds/mob/zombie/wood1.ogg
+minecraft/sounds/mob/zombie/wood2.ogg
+minecraft/sounds/mob/zombie/wood3.ogg
+minecraft/sounds/mob/zombie/wood4.ogg
+minecraft/sounds/mob/zombie/woodbreak.ogg
+minecraft/sounds/mob/zombie_villager/death.ogg
+minecraft/sounds/mob/zombie_villager/hurt1.ogg
+minecraft/sounds/mob/zombie_villager/hurt2.ogg
+minecraft/sounds/mob/zombie_villager/say1.ogg
+minecraft/sounds/mob/zombie_villager/say2.ogg
+minecraft/sounds/mob/zombie_villager/say3.ogg
+minecraft/sounds/mob/zombified_piglin/zpig1.ogg
+minecraft/sounds/mob/zombified_piglin/zpig2.ogg
+minecraft/sounds/mob/zombified_piglin/zpig3.ogg
+minecraft/sounds/mob/zombified_piglin/zpig4.ogg
+minecraft/sounds/mob/zombified_piglin/zpigangry1.ogg
+minecraft/sounds/mob/zombified_piglin/zpigangry2.ogg
+minecraft/sounds/mob/zombified_piglin/zpigangry3.ogg
+minecraft/sounds/mob/zombified_piglin/zpigangry4.ogg
+minecraft/sounds/mob/zombified_piglin/zpigdeath.ogg
+minecraft/sounds/mob/zombified_piglin/zpighurt1.ogg
+minecraft/sounds/mob/zombified_piglin/zpighurt2.ogg
+minecraft/sounds/music/game/calm1.ogg
+minecraft/sounds/music/game/calm2.ogg
+minecraft/sounds/music/game/calm3.ogg
+minecraft/sounds/music/game/creative/creative1.ogg
+minecraft/sounds/music/game/creative/creative2.ogg
+minecraft/sounds/music/game/creative/creative3.ogg
+minecraft/sounds/music/game/creative/creative4.ogg
+minecraft/sounds/music/game/creative/creative5.ogg
+minecraft/sounds/music/game/creative/creative6.ogg
+minecraft/sounds/music/game/end/boss.ogg
+minecraft/sounds/music/game/end/credits.ogg
+minecraft/sounds/music/game/end/end.ogg
+minecraft/sounds/music/game/hal1.ogg
+minecraft/sounds/music/game/hal2.ogg
+minecraft/sounds/music/game/hal3.ogg
+minecraft/sounds/music/game/hal4.ogg
+minecraft/sounds/music/game/nether/crimson_forest/chrysopoeia.ogg
+minecraft/sounds/music/game/nether/nether1.ogg
+minecraft/sounds/music/game/nether/nether2.ogg
+minecraft/sounds/music/game/nether/nether3.ogg
+minecraft/sounds/music/game/nether/nether4.ogg
+minecraft/sounds/music/game/nether/nether_wastes/rubedo.ogg
+minecraft/sounds/music/game/nether/soulsand_valley/so_below.ogg
+minecraft/sounds/music/game/nuance1.ogg
+minecraft/sounds/music/game/nuance2.ogg
+minecraft/sounds/music/game/piano1.ogg
+minecraft/sounds/music/game/piano2.ogg
+minecraft/sounds/music/game/piano3.ogg
+minecraft/sounds/music/game/water/axolotl.ogg
+minecraft/sounds/music/game/water/dragon_fish.ogg
+minecraft/sounds/music/game/water/shuniji.ogg
+minecraft/sounds/music/menu/menu1.ogg
+minecraft/sounds/music/menu/menu2.ogg
+minecraft/sounds/music/menu/menu3.ogg
+minecraft/sounds/music/menu/menu4.ogg
+minecraft/sounds/note/banjo.ogg
+minecraft/sounds/note/bass.ogg
+minecraft/sounds/note/bassattack.ogg
+minecraft/sounds/note/bd.ogg
+minecraft/sounds/note/bell.ogg
+minecraft/sounds/note/bit.ogg
+minecraft/sounds/note/cow_bell.ogg
+minecraft/sounds/note/didgeridoo.ogg
+minecraft/sounds/note/flute.ogg
+minecraft/sounds/note/guitar.ogg
+minecraft/sounds/note/harp.ogg
+minecraft/sounds/note/harp2.ogg
+minecraft/sounds/note/hat.ogg
+minecraft/sounds/note/icechime.ogg
+minecraft/sounds/note/iron_xylophone.ogg
+minecraft/sounds/note/pling.ogg
+minecraft/sounds/note/snare.ogg
+minecraft/sounds/note/xylobone.ogg
+minecraft/sounds/portal/portal.ogg
+minecraft/sounds/portal/travel.ogg
+minecraft/sounds/portal/trigger.ogg
+minecraft/sounds/random/anvil_break.ogg
+minecraft/sounds/random/anvil_land.ogg
+minecraft/sounds/random/anvil_use.ogg
+minecraft/sounds/random/bow.ogg
+minecraft/sounds/random/bowhit1.ogg
+minecraft/sounds/random/bowhit2.ogg
+minecraft/sounds/random/bowhit3.ogg
+minecraft/sounds/random/bowhit4.ogg
+minecraft/sounds/random/break.ogg
+minecraft/sounds/random/breath.ogg
+minecraft/sounds/random/burp.ogg
+minecraft/sounds/random/chestclosed.ogg
+minecraft/sounds/random/chestopen.ogg
+minecraft/sounds/random/classic_hurt.ogg
+minecraft/sounds/random/click.ogg
+minecraft/sounds/random/click_stereo.ogg
+minecraft/sounds/random/door_close.ogg
+minecraft/sounds/random/door_open.ogg
+minecraft/sounds/random/drink.ogg
+minecraft/sounds/random/eat1.ogg
+minecraft/sounds/random/eat2.ogg
+minecraft/sounds/random/eat3.ogg
+minecraft/sounds/random/explode1.ogg
+minecraft/sounds/random/explode2.ogg
+minecraft/sounds/random/explode3.ogg
+minecraft/sounds/random/explode4.ogg
+minecraft/sounds/random/fizz.ogg
+minecraft/sounds/random/fuse.ogg
+minecraft/sounds/random/glass1.ogg
+minecraft/sounds/random/glass2.ogg
+minecraft/sounds/random/glass3.ogg
+minecraft/sounds/random/levelup.ogg
+minecraft/sounds/random/orb.ogg
+minecraft/sounds/random/pop.ogg
+minecraft/sounds/random/splash.ogg
+minecraft/sounds/random/successful_hit.ogg
+minecraft/sounds/random/wood_click.ogg
+minecraft/sounds/records/11.ogg
+minecraft/sounds/records/13.ogg
+minecraft/sounds/records/blocks.ogg
+minecraft/sounds/records/cat.ogg
+minecraft/sounds/records/chirp.ogg
+minecraft/sounds/records/far.ogg
+minecraft/sounds/records/mall.ogg
+minecraft/sounds/records/mellohi.ogg
+minecraft/sounds/records/pigstep.ogg
+minecraft/sounds/records/stal.ogg
+minecraft/sounds/records/strad.ogg
+minecraft/sounds/records/wait.ogg
+minecraft/sounds/records/ward.ogg
+minecraft/sounds/step/cloth1.ogg
+minecraft/sounds/step/cloth2.ogg
+minecraft/sounds/step/cloth3.ogg
+minecraft/sounds/step/cloth4.ogg
+minecraft/sounds/step/coral1.ogg
+minecraft/sounds/step/coral2.ogg
+minecraft/sounds/step/coral3.ogg
+minecraft/sounds/step/coral4.ogg
+minecraft/sounds/step/coral5.ogg
+minecraft/sounds/step/coral6.ogg
+minecraft/sounds/step/grass1.ogg
+minecraft/sounds/step/grass2.ogg
+minecraft/sounds/step/grass3.ogg
+minecraft/sounds/step/grass4.ogg
+minecraft/sounds/step/grass5.ogg
+minecraft/sounds/step/grass6.ogg
+minecraft/sounds/step/gravel1.ogg
+minecraft/sounds/step/gravel2.ogg
+minecraft/sounds/step/gravel3.ogg
+minecraft/sounds/step/gravel4.ogg
+minecraft/sounds/step/ladder1.ogg
+minecraft/sounds/step/ladder2.ogg
+minecraft/sounds/step/ladder3.ogg
+minecraft/sounds/step/ladder4.ogg
+minecraft/sounds/step/ladder5.ogg
+minecraft/sounds/step/sand1.ogg
+minecraft/sounds/step/sand2.ogg
+minecraft/sounds/step/sand3.ogg
+minecraft/sounds/step/sand4.ogg
+minecraft/sounds/step/sand5.ogg
+minecraft/sounds/step/scaffold1.ogg
+minecraft/sounds/step/scaffold2.ogg
+minecraft/sounds/step/scaffold3.ogg
+minecraft/sounds/step/scaffold4.ogg
+minecraft/sounds/step/scaffold5.ogg
+minecraft/sounds/step/scaffold6.ogg
+minecraft/sounds/step/scaffold7.ogg
+minecraft/sounds/step/snow1.ogg
+minecraft/sounds/step/snow2.ogg
+minecraft/sounds/step/snow3.ogg
+minecraft/sounds/step/snow4.ogg
+minecraft/sounds/step/stone1.ogg
+minecraft/sounds/step/stone2.ogg
+minecraft/sounds/step/stone3.ogg
+minecraft/sounds/step/stone4.ogg
+minecraft/sounds/step/stone5.ogg
+minecraft/sounds/step/stone6.ogg
+minecraft/sounds/step/wet_grass1.ogg
+minecraft/sounds/step/wet_grass2.ogg
+minecraft/sounds/step/wet_grass3.ogg
+minecraft/sounds/step/wet_grass4.ogg
+minecraft/sounds/step/wet_grass5.ogg
+minecraft/sounds/step/wet_grass6.ogg
+minecraft/sounds/step/wood1.ogg
+minecraft/sounds/step/wood2.ogg
+minecraft/sounds/step/wood3.ogg
+minecraft/sounds/step/wood4.ogg
+minecraft/sounds/step/wood5.ogg
+minecraft/sounds/step/wood6.ogg
+minecraft/sounds/tile/piston/in.ogg
+minecraft/sounds/tile/piston/out.ogg
+minecraft/sounds/ui/cartography_table/drawmap1.ogg
+minecraft/sounds/ui/cartography_table/drawmap2.ogg
+minecraft/sounds/ui/cartography_table/drawmap3.ogg
+minecraft/sounds/ui/loom/select_pattern1.ogg
+minecraft/sounds/ui/loom/select_pattern2.ogg
+minecraft/sounds/ui/loom/select_pattern3.ogg
+minecraft/sounds/ui/loom/select_pattern4.ogg
+minecraft/sounds/ui/loom/select_pattern5.ogg
+minecraft/sounds/ui/loom/take_result1.ogg
+minecraft/sounds/ui/loom/take_result2.ogg
+minecraft/sounds/ui/stonecutter/cut1.ogg
+minecraft/sounds/ui/stonecutter/cut2.ogg
+minecraft/sounds/ui/toast/challenge_complete.ogg
+minecraft/sounds/ui/toast/in.ogg
+minecraft/sounds/ui/toast/out.ogg
+minecraft/textures/gui/title/background/panorama_0.png
+minecraft/textures/gui/title/background/panorama_1.png
+minecraft/textures/gui/title/background/panorama_2.png
+minecraft/textures/gui/title/background/panorama_3.png
+minecraft/textures/gui/title/background/panorama_4.png
+minecraft/textures/gui/title/background/panorama_5.png
+minecraft/textures/gui/title/background/panorama_overlay.png
+pack.mcmeta
+realms/lang/af_za.json
+realms/lang/ar_sa.json
+realms/lang/ast_es.json
+realms/lang/az_az.json
+realms/lang/ba_ru.json
+realms/lang/bar.json
+realms/lang/be_by.json
+realms/lang/bg_bg.json
+realms/lang/br_fr.json
+realms/lang/brb.json
+realms/lang/bs_ba.json
+realms/lang/ca_es.json
+realms/lang/cs_cz.json
+realms/lang/cy_gb.json
+realms/lang/da_dk.json
+realms/lang/de_at.json
+realms/lang/de_ch.json
+realms/lang/de_de.json
+realms/lang/el_gr.json
+realms/lang/en_au.json
+realms/lang/en_ca.json
+realms/lang/en_gb.json
+realms/lang/en_nz.json
+realms/lang/en_pt.json
+realms/lang/en_ud.json
+realms/lang/enp.json
+realms/lang/enws.json
+realms/lang/eo_uy.json
+realms/lang/es_ar.json
+realms/lang/es_cl.json
+realms/lang/es_ec.json
+realms/lang/es_es.json
+realms/lang/es_mx.json
+realms/lang/es_uy.json
+realms/lang/es_ve.json
+realms/lang/esan.json
+realms/lang/et_ee.json
+realms/lang/eu_es.json
+realms/lang/fa_ir.json
+realms/lang/fi_fi.json
+realms/lang/fil_ph.json
+realms/lang/fo_fo.json
+realms/lang/fr_ca.json
+realms/lang/fr_fr.json
+realms/lang/fra_de.json
+realms/lang/fy_nl.json
+realms/lang/ga_ie.json
+realms/lang/gd_gb.json
+realms/lang/gl_es.json
+realms/lang/got_de.json
+realms/lang/gv_im.json
+realms/lang/haw_us.json
+realms/lang/he_il.json
+realms/lang/hi_in.json
+realms/lang/hr_hr.json
+realms/lang/hu_hu.json
+realms/lang/hy_am.json
+realms/lang/id_id.json
+realms/lang/ig_ng.json
+realms/lang/io_en.json
+realms/lang/is_is.json
+realms/lang/isv.json
+realms/lang/it_it.json
+realms/lang/ja_jp.json
+realms/lang/jbo_en.json
+realms/lang/ka_ge.json
+realms/lang/kab_kab.json
+realms/lang/kk_kz.json
+realms/lang/kn_in.json
+realms/lang/ko_kr.json
+realms/lang/ksh.json
+realms/lang/kw_gb.json
+realms/lang/la_la.json
+realms/lang/lb_lu.json
+realms/lang/li_li.json
+realms/lang/lol_us.json
+realms/lang/lt_lt.json
+realms/lang/lv_lv.json
+realms/lang/mi_nz.json
+realms/lang/mk_mk.json
+realms/lang/mn_mn.json
+realms/lang/moh_ca.json
+realms/lang/ms_my.json
+realms/lang/mt_mt.json
+realms/lang/nds_de.json
+realms/lang/nl_be.json
+realms/lang/nl_nl.json
+realms/lang/nn_no.json
+realms/lang/no_no.json
+realms/lang/nuk.json
+realms/lang/oc_fr.json
+realms/lang/oj_ca.json
+realms/lang/ovd.json
+realms/lang/pl_pl.json
+realms/lang/pt_br.json
+realms/lang/pt_pt.json
+realms/lang/qya_aa.json
+realms/lang/ro_ro.json
+realms/lang/ru_ru.json
+realms/lang/scn.json
+realms/lang/se_no.json
+realms/lang/sk_sk.json
+realms/lang/sl_si.json
+realms/lang/so_so.json
+realms/lang/sq_al.json
+realms/lang/sr_sp.json
+realms/lang/sv_se.json
+realms/lang/swg.json
+realms/lang/sxu.json
+realms/lang/szl.json
+realms/lang/ta_in.json
+realms/lang/th_th.json
+realms/lang/tl_ph.json
+realms/lang/tlh_aa.json
+realms/lang/tr_tr.json
+realms/lang/tt_ru.json
+realms/lang/tzl_tzl.json
+realms/lang/uk_ua.json
+realms/lang/val_es.json
+realms/lang/vec_it.json
+realms/lang/vi_vn.json
+realms/lang/yi_de.json
+realms/lang/yo_ng.json
+realms/lang/zh_cn.json
+realms/lang/zh_tw.json
+realms/textures/gui/images/1.png
+realms/textures/gui/images/10.png
+realms/textures/gui/images/11.png
+realms/textures/gui/images/12.png
+realms/textures/gui/images/13.png
+realms/textures/gui/images/14.png
+realms/textures/gui/images/15.png
+realms/textures/gui/images/2.png
+realms/textures/gui/images/3.png
+realms/textures/gui/images/4.png
+realms/textures/gui/images/5.png
+realms/textures/gui/images/6.png
+realms/textures/gui/images/7.png
+realms/textures/gui/images/8.png
+realms/textures/gui/images/9.png
+cursors/mouse_cursor_white_4x4.png
+cursors/mouse_cursor_white_8x8.png
+cursors/mouse_cursor_white_16x16.png
diff -u1 -rN MCP-Reborn-vanilla/src/main/resources/MissionEnded.xsd MCP-Reborn/src/main/resources/MissionEnded.xsd
--- MCP-Reborn-vanilla/src/main/resources/MissionEnded.xsd	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/resources/MissionEnded.xsd	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="http://ProjectMalmo.microsoft.com"
+           xmlns="http://ProjectMalmo.microsoft.com"
+           elementFormDefault="qualified"
+           version="0.37">
+
+    <xs:include schemaLocation="MissionHandlers.xsd"/>
+
+    <xs:element name="MissionEnded">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Status" type="MissionResult"/>
+                <xs:element name="HumanReadableStatus" type="xs:string"/>
+                <xs:element ref="Reward" minOccurs="0" maxOccurs="1"/>
+                <xs:element ref="MissionDiagnostics" maxOccurs="1"/>
+            </xs:sequence>
+            <xs:attribute name="SchemaVersion" type="xs:token"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="MissionResult">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="ENDED"/>
+            <xs:enumeration value="PLAYER_DIED"/>
+            <xs:enumeration value="AGENT_QUIT"/>
+            <xs:enumeration value="MOD_FAILED_TO_INSTANTIATE_HANDLERS"/>
+            <xs:enumeration value="MOD_HAS_NO_WORLD_LOADED"/>
+            <xs:enumeration value="MOD_FAILED_TO_CREATE_WORLD"/>
+            <xs:enumeration value="MOD_HAS_NO_AGENT_AVAILABLE"/>
+            <xs:enumeration value="MOD_SERVER_UNREACHABLE"/>
+            <xs:enumeration value="MOD_SERVER_ABORTED_MISSION"/>
+            <xs:enumeration value="MOD_CONNECTION_FAILED"/>
+            <xs:enumeration value="MOD_CRASHED"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:element name="MissionDiagnostics">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="VideoData" minOccurs="0" maxOccurs="unbounded">
+                    <xs:complexType>
+                        <xs:attribute name="frameType" type="xs:string" use="required"/>
+                        <xs:attribute name="framesSent" type="xs:int" use="required"/>
+                        <xs:attribute name="averageFpsSent" type="xs:decimal" use="required"/>
+                        <xs:attribute name="framesReceived" type="xs:int"/>
+                        <xs:attribute name="framesWritten" type="xs:int"/>
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="Reward">
+        <xs:annotation>
+            <xs:documentation>
+                Stores multi-dimensional rewards as a map of int:double where dimension is a positive integer.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Value" minOccurs="1" maxOccurs="unbounded">
+                    <xs:complexType>
+                        <xs:attribute name="dimension" type="Dimension" use="required"/>
+                        <xs:attribute name="value" type="xs:decimal" use="required"/>
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+</xs:schema>
diff -u1 -rN MCP-Reborn-vanilla/src/main/resources/MissionHandlers.xsd MCP-Reborn/src/main/resources/MissionHandlers.xsd
--- MCP-Reborn-vanilla/src/main/resources/MissionHandlers.xsd	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/resources/MissionHandlers.xsd	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,3297 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="http://ProjectMalmo.microsoft.com"
+           xmlns="http://ProjectMalmo.microsoft.com"
+           xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
+           elementFormDefault="qualified"
+           jaxb:version="2.1"
+           version="0.37">
+
+    <xs:include schemaLocation="Types.xsd"/>
+    <xs:include schemaLocation="Mission.xsd"/>
+
+    <xs:complexType name="Pos">
+        <xs:annotation>
+            <xs:documentation>
+                An absolute position in the 3D Minecraft world.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="x" type="xs:float" use="required"/>
+        <xs:attribute name="y" type="xs:float" use="required"/>
+        <xs:attribute name="z" type="xs:float" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="PosAndDirection">
+        <xs:annotation>
+            <xs:documentation>
+                An absolute position in the 3D Minecraft world that includes yaw and pitch.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="Pos">
+                <xs:attribute name="yaw" use="optional" default="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Defaults to facing South (0). North is 180.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:restriction base="xs:float">
+                            <xs:minInclusive value="-360"/>
+                            <xs:maxInclusive value="360"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+                <xs:attribute name="pitch" use="optional" default="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Defaults to looking straight ahead (0). +90 = look down. -90 = look up.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:restriction base="xs:float">
+                            <xs:minInclusive value="-90"/>
+                            <xs:maxInclusive value="90"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <!--=============================================================================================================-->
+    <!--=========================================== SERVER-SIDE HANDLERS ============================================-->
+    <!--=============================================================================================================-->
+
+    <!--============================================= WORLD GENERATORS ==============================================-->
+
+    <xs:element name="FlatWorldGenerator">
+        <xs:annotation>
+            <xs:documentation>
+                Generates a superflat world with a specified preset string - see e.g.
+                [[http://www.minecraft101.net/superflat/]]
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="generatorString" use="optional" type="xs:string" default="3;7,2*3,2;1;village">
+                <xs:annotation>
+                    <xs:documentation>
+                        The superflat customization preset string.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="forceReset" use="optional" type="xs:boolean" default="false">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to true to force the world to be reloaded, otherwise the current world will be used
+                        (provided it matches the requested generator string).
+                        Force reloading is slow, but will guarantee that no world changes will carry over between
+                        missions.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="seed" use="optional" type="xs:string" default="">
+                <xs:annotation>
+                    <xs:documentation>
+                        The world seed - leave blank (default) to get a random world.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="destroyAfterUse" use="optional" type="xs:boolean" default="true">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to true to force the world data files to be deleted after the mission is done.
+                        Enabling this setting prevents the disk being filled with old worlds.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="DefaultWorldGenerator">
+        <xs:annotation>
+            <xs:documentation>
+                Generates the default terrain.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="seed" use="optional" type="xs:string" default="">
+                <xs:annotation>
+                    <xs:documentation>
+                        The world seed - leave blank (default) to get a random world.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="forceReset" use="optional" type="xs:boolean" default="false">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to true to force the world to be reloaded, otherwise the current world will be used
+                        (provided it matches the requested seed).
+                        Force reloading is slow, but will guarantee that no world changes will carry over between
+                        missions.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="generatorOptions" use="optional" type="xs:string" default="">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to the desired world generator string for your minecraft world.
+                        See https://minecraft.tools/en/custom.php for examples.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="destroyAfterUse" use="optional" type="xs:boolean" default="true">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to true to force the world data files to be deleted after the mission is done.
+                        Enabling this setting prevents the disk being filled with old worlds.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="FileWorldGenerator">
+        <xs:annotation>
+            <xs:documentation>
+                Loads a saved world from disk. You can find the saved worlds in "{{{ Minecraft\run\saves}}}". Use the
+                full path to one of those folders.
+
+                If Minecraft is running on a different machine then copy the folder to a readable network location and
+                update the path accordingly. Example:
+
+                {{{&lt;FileWorldGenerator src=&quot;\\\\machine-id\\shared\\ProjectMalmo\\saved_maps\\arena&quot; /&gt;}}}
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="src" use="required" type="xs:anyURI">
+                <xs:annotation>
+                    <xs:documentation>
+                        The path to the saved world folder.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="forceReset" use="optional" type="xs:boolean" default="false">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to true to force the world to be reloaded, otherwise the current world will be used
+                        (provided it matches the requested source filename).
+                        Force reloading is slow, but will guarantee that no world changes will carry over between
+                        missions.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="destroyAfterUse" use="optional" type="xs:boolean" default="true">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to true to force the world data files to be deleted after the mission is done.
+                        Enabling this setting prevents the disk being filled with old worlds.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="BiomeGenerator">
+        <xs:annotation>
+            <xs:documentation>
+                Generates a survival world with the specified biome.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="biome" use="optional" type="xs:int">
+                <xs:annotation>
+                    <xs:documentation>
+                        The biome type for the world. Each chunk will be loaded with the biome specified.
+
+                        If left blank, the world will be a normal survival world.
+
+                        Biome ID #'s can be found here: https://minecraft.gamepedia.com/Biome#Biome_IDs
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="forceReset" use="optional" type="xs:boolean" default="false">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to true to force the world to be reloaded, otherwise the current world will be used
+                        (provided it matches the requested seed).
+                        Force reloading is slow, but will guarantee that no world changes will carry over between
+                        missions.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="destroyAfterUse" use="optional" type="xs:boolean" default="true">
+                <xs:annotation>
+                    <xs:documentation>
+                        Set this to true to force the world data files to be deleted after the mission is done.
+                        Enabling this setting prevents the disk being filled with old worlds.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <!--============================================= WORLD DECORATORS ==============================================-->
+
+    <xs:simpleType name="BlockList">
+        <xs:list itemType="BlockType"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="ItemList">
+        <xs:list itemType="ItemType"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="BlockOrItemList">
+        <xs:list itemType="BlockOrItem"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="ColourList">
+        <xs:list itemType="Colour"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="VariantList">
+        <xs:list itemType="Variation"/>
+    </xs:simpleType>
+
+    <xs:complexType name="MazeBlock">
+        <xs:attribute name="type" use="optional" type="BlockList"/>
+        <xs:attribute name="variant" use="optional" type="VariantList"/>
+        <xs:attribute name="colour" use="optional" type="ColourList"/>
+        <xs:attribute name="height" use="optional" type="xs:int" default="1"/>
+        <xs:attribute name="heightVariance" use="optional" type="xs:int" default="0"/>
+    </xs:complexType>
+
+    <xs:complexType name="MazeTerminus">
+        <xs:complexContent>
+            <xs:extension base="MazeBlock">
+                <xs:attribute name="fixedToEdge" use="optional" type="xs:boolean" default="false"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="SnakeBlock">
+        <xs:attribute name="type" use="optional" type="BlockList"/>
+        <xs:attribute name="variant" use="optional" type="VariantList"/>
+        <xs:attribute name="colour" use="optional" type="ColourList"/>
+        <xs:attribute name="lifetime" use="optional" type="xs:int" default="1"/>
+        <xs:attribute name="lifetimeVariance" use="optional" type="xs:int" default="0"/>
+    </xs:complexType>
+
+    <xs:element name="MovingTargetDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Creates a moving two-block target which takes random moves within a specified arena. Can be linked to
+                the turn scheduler.
+                This can be made more general in the future, but is currently tailored specifically for the Malmo
+                collaborative challenge.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ArenaBounds" type="UnnamedGridDefinition">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Define the bounds of the arena within which the target can move.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="StartPos" type="Pos"/>
+                <xs:element name="Seed">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The master seed for the random number generator used to move the target.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:annotation>
+                            <xs:documentation>
+                                Either an integer number, or the string "random".
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:restriction base="xs:string">
+                            <xs:pattern value="random|[0-9]+"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="UpdateSpeed">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The length, in ticks, between each update, or the string "turnbased" to use the turn
+                            scheduler.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:annotation>
+                            <xs:documentation>
+                                Either an integer number, or the string "turnbased".
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:restriction base="xs:string">
+                            <xs:pattern value="turnbased|[0-9]+"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="PermeableBlocks" type="BlockSpec"/>
+                <xs:element name="BlockType" type="DrawBlockBasedObjectType"/>
+            </xs:all>
+            <xs:attribute name="requiresAirAbove" type="xs:boolean" default="true"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="SnakeDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Adds a snake made of blocks, that grows at one end and shrinks at the other.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="Seed">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The master seed for the random number generator used to make the snake.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:annotation>
+                            <xs:documentation>
+                                Either an integer number, or the string "random".
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:restriction base="xs:string">
+                            <xs:pattern value="random|[0-9]+"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="MaterialSeed" minOccurs="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Optional seed for determining block types.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:annotation>
+                            <xs:documentation>
+                                Either an integer number, or the string "random".
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:restriction base="xs:string">
+                            <xs:pattern value="random|[0-9]+"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="FreshBlock" type="SnakeBlock"/>
+                <xs:element name="StaleBlock" type="SnakeBlock"/>
+                <xs:element name="GapProbability">
+                    <xs:complexType>
+                        <xs:simpleContent>
+                            <xs:extension base="xs:decimal">
+                                <xs:attribute name="variance" type="xs:decimal" use="optional" default="0"/>
+                            </xs:extension>
+                        </xs:simpleContent>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="StairsProbability">
+                    <xs:complexType>
+                        <xs:simpleContent>
+                            <xs:extension base="xs:decimal">
+                                <xs:attribute name="variance" type="xs:decimal" use="optional" default="0"/>
+                            </xs:extension>
+                        </xs:simpleContent>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="TurnProbability">
+                    <xs:complexType>
+                        <xs:simpleContent>
+                            <xs:extension base="xs:decimal">
+                                <xs:attribute name="variance" type="xs:decimal" use="optional" default="0"/>
+                            </xs:extension>
+                        </xs:simpleContent>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="SizeAndPosition">
+                    <xs:complexType>
+                        <xs:attribute name="xOrigin" type="xs:int" default="0"/>
+                        <xs:attribute name="yOrigin" type="xs:int" default="55"/>
+                        <xs:attribute name="zOrigin" type="xs:int" default="0"/>
+                        <xs:attribute name="yMin" type="xs:int" default="32"/>
+                        <xs:attribute name="yMax" type="xs:int" default="250"/>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="SpeedInTicks" type="xs:int" default="6"/>
+                <xs:element name="MaxLength" type="xs:int" default="30"/>
+                <xs:element name="MaxStairLength" type="xs:int" default="20"/>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="MazeDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Adds a maze into the world.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="Seed">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The master seed for the random number generator used to make the maze.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:annotation>
+                            <xs:documentation>
+                                Either an integer number, or the string "random".
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:restriction base="xs:string">
+                            <xs:pattern value="random|[0-9]+"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="MaterialSeed" minOccurs="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Seed for the random number generator for determining block types - omit to allow master seed
+                            to control block types.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:annotation>
+                            <xs:documentation>
+                                Either an integer number, or the string "random".
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:restriction base="xs:string">
+                            <xs:pattern value="random|[0-9]+"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="AllowDiagonalMovement" type="xs:boolean" default="false"/>
+                <xs:element name="SizeAndPosition">
+                    <xs:complexType>
+                        <xs:attribute name="width" type="xs:int" default="10"/>
+                        <xs:attribute name="length" type="xs:int" default="10"/>
+                        <xs:attribute name="height" type="xs:int" default="100"/>
+                        <xs:attribute name="scale" type="xs:int" default="1"/>
+                        <xs:attribute name="xOrigin" type="xs:int" default="0"/>
+                        <xs:attribute name="yOrigin" type="xs:int" default="55"/>
+                        <xs:attribute name="zOrigin" type="xs:int" default="0"/>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="StartBlock" type="MazeTerminus"/>
+                <xs:element name="EndBlock" type="MazeTerminus"/>
+                <xs:element name="PathBlock" type="MazeBlock"/>
+                <xs:element name="FloorBlock" type="MazeBlock"/>
+                <xs:element name="GapBlock" type="MazeBlock"/>
+                <xs:element name="OptimalPathBlock" type="MazeBlock" minOccurs="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Omit this element if you want the optimal path to be unmarked.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="SubgoalBlock" type="MazeBlock" minOccurs="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Omit this element if you want the subgoal points to be unmarked.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="Waypoints" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:choice>
+                            <xs:element name="WaypointBlock" type="MazeBlock"/>
+                            <xs:element name="WaypointItem" type="BlockOrItemSpec"/>
+                        </xs:choice>
+                        <xs:attribute name="quantity" type="xs:int" use="required"/>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="GapProbability">
+                    <xs:complexType>
+                        <xs:simpleContent>
+                            <xs:extension base="xs:decimal">
+                                <xs:attribute name="variance" type="xs:decimal" use="optional" default="0"/>
+                            </xs:extension>
+                        </xs:simpleContent>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="AddQuitProducer" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="AddNavigationObservations" minOccurs="0" maxOccurs="1">
+                    <xs:complexType/>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="DrawObjectType">
+        <xs:annotation>
+            <xs:documentation>
+                Base class for all draw objects.
+            </xs:documentation>
+        </xs:annotation>
+    </xs:complexType>
+
+    <xs:complexType name="DrawBlockBasedObjectType">
+        <xs:annotation>
+            <xs:documentation>
+                Base class for all block-based draw objects.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawObjectType">
+                <xs:attribute name="type" type="BlockType" use="required"/>
+                <xs:attribute name="variant" type="Variation" use="optional"/>
+                <xs:attribute name="colour" type="Colour" use="optional"/>
+                <xs:attribute name="face" type="Facing" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:element name="DrawingDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Draws structures into the world.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element ref="DrawObjectType" maxOccurs="unbounded"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="DrawBlock">
+        <xs:annotation>
+            <xs:documentation>
+                Specify a block by location and type.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawBlockBasedObjectType">
+                <xs:attribute name="x" type="xs:int" use="required"/>
+                <xs:attribute name="y" type="xs:int" use="required"/>
+                <xs:attribute name="z" type="xs:int" use="required"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="DrawItem">
+        <xs:annotation>
+            <xs:documentation>
+                Specify an item by location and type.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawObjectType">
+                <xs:attribute name="x" type="xs:int" use="required"/>
+                <xs:attribute name="y" type="xs:int" use="required"/>
+                <xs:attribute name="z" type="xs:int" use="required"/>
+                <xs:attribute name="type" type="BlockOrItem" use="required"/>
+                <xs:attribute name="variant" type="Variation" use="optional"/>
+                <xs:attribute name="colour" type="Colour" use="optional"/>
+                <xs:attribute name="face" type="Facing" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="ContainedObjectType">
+        <xs:attribute name="type" use="required" type="BlockOrItem"/>
+        <xs:attribute name="variant" use="optional" type="Variation"/>
+        <xs:attribute name="colour" use="optional" type="Colour"/>
+        <xs:attribute name="quantity" type="xs:int" use="optional" default="1"/>
+    </xs:complexType>
+
+    <xs:complexType name="DrawContainer">
+        <xs:annotation>
+            <xs:documentation>
+                Specify a container item by location and type and contents.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawObjectType">
+                <xs:choice minOccurs="1" maxOccurs="unbounded">
+                    <xs:element name="Object" type="ContainedObjectType"/>
+                </xs:choice>
+                <xs:attribute name="x" type="xs:int" use="required"/>
+                <xs:attribute name="y" type="xs:int" use="required"/>
+                <xs:attribute name="z" type="xs:int" use="required"/>
+                <xs:attribute name="type" type="ContainerType" use="required"/>
+                <xs:attribute name="variant" type="Variation" use="optional"/>
+                <xs:attribute name="colour" type="Colour" use="optional"/>
+                <xs:attribute name="face" type="Facing" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="DrawSign">
+        <xs:annotation>
+            <xs:documentation>
+                Draw a sign at the required location with the specified text.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawObjectType">
+                <xs:attribute name="x" type="xs:int" use="required"/>
+                <xs:attribute name="y" type="xs:int" use="required"/>
+                <xs:attribute name="z" type="xs:int" use="required"/>
+                <xs:attribute name="type" type="SignType" use="required"/>
+                <xs:attribute name="variant" type="Variation" use="optional"/>
+                <xs:attribute name="colour" type="Colour" use="optional"/>
+                <xs:attribute name="face" type="Facing" use="optional"/>
+                <xs:attribute name="line1" type="xs:string" use="optional"/>
+                <xs:attribute name="line2" type="xs:string" use="optional"/>
+                <xs:attribute name="line3" type="xs:string" use="optional"/>
+                <xs:attribute name="line4" type="xs:string" use="optional"/>
+                <xs:attribute name="rotation" type="xs:int" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="DrawEntity">
+        <xs:annotation>
+            <xs:documentation>
+                Specify an entity by location and type.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawObjectType">
+                <xs:attribute name="x" type="xs:decimal" use="required"/>
+                <xs:attribute name="y" type="xs:decimal" use="required"/>
+                <xs:attribute name="z" type="xs:decimal" use="required"/>
+                <xs:attribute name="type" type="SpawnableTypes" use="required"/>
+                <xs:attribute name="yaw" type="xs:decimal" use="optional" default="0"/>
+                <xs:attribute name="pitch" type="xs:decimal" use="optional" default="0"/>
+                <xs:attribute name="xVel" type="xs:decimal" use="optional" default="0"/>
+                <xs:attribute name="yVel" type="xs:decimal" use="optional" default="0"/>
+                <xs:attribute name="zVel" type="xs:decimal" use="optional" default="0"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="DrawCuboid">
+        <xs:annotation>
+            <xs:documentation>
+                Specify a filled cuboid by inclusive coordinates and block type.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawBlockBasedObjectType">
+                <xs:attribute name="x1" type="xs:int" use="required"/>
+                <xs:attribute name="y1" type="xs:int" use="required"/>
+                <xs:attribute name="z1" type="xs:int" use="required"/>
+                <xs:attribute name="x2" type="xs:int" use="required"/>
+                <xs:attribute name="y2" type="xs:int" use="required"/>
+                <xs:attribute name="z2" type="xs:int" use="required"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="DrawSphere">
+        <xs:annotation>
+            <xs:documentation>
+                Specify a filled sphere by centre coordinates and inclusive radius.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawBlockBasedObjectType">
+                <xs:attribute name="x" type="xs:int" use="required"/>
+                <xs:attribute name="y" type="xs:int" use="required"/>
+                <xs:attribute name="z" type="xs:int" use="required"/>
+                <xs:attribute name="radius" type="xs:int" use="required"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="DrawLine">
+        <xs:annotation>
+            <xs:documentation>
+                Specify a line by start and end coordinates and thickness.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexContent>
+            <xs:extension base="DrawBlockBasedObjectType">
+                <xs:attribute name="x1" type="xs:int" use="required"/>
+                <xs:attribute name="y1" type="xs:int" use="required"/>
+                <xs:attribute name="z1" type="xs:int" use="required"/>
+                <xs:attribute name="x2" type="xs:int" use="required"/>
+                <xs:attribute name="y2" type="xs:int" use="required"/>
+                <xs:attribute name="z2" type="xs:int" use="required"/>
+                <xs:attribute name="steptype" type="BlockType" use="optional"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:element name="DrawObjectType" type="DrawObjectType"/>
+    <xs:element name="DrawEntity" type="DrawEntity" substitutionGroup="DrawObjectType"/>
+    <xs:element name="DrawItem" type="DrawItem" substitutionGroup="DrawObjectType"/>
+    <xs:element name="DrawLine" type="DrawLine" substitutionGroup="DrawObjectType"/>
+    <xs:element name="DrawSphere" type="DrawSphere" substitutionGroup="DrawObjectType"/>
+    <xs:element name="DrawBlock" type="DrawBlock" substitutionGroup="DrawObjectType"/>
+    <xs:element name="DrawCuboid" type="DrawCuboid" substitutionGroup="DrawObjectType"/>
+    <xs:element name="DrawContainer" type="DrawContainer" substitutionGroup="DrawObjectType"/>
+    <xs:element name="DrawSign" type="DrawSign" substitutionGroup="DrawObjectType"/>
+
+    <xs:element name="ClassroomDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Adds a series of joined rooms into the world.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice>
+                <xs:element name="complexity">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element name="building" type="ZeroToOne"/>
+                            <xs:element name="path" type="ZeroToOne"/>
+                            <xs:element name="division" type="ZeroToOne"/>
+                            <xs:element name="obstacle" type="ZeroToOne"/>
+                            <xs:element name="hint" type="ZeroToOne"/>
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="specification">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element name="width" type="BuildingSize"/>
+                            <xs:element name="height" type="BuildingSize"/>
+                            <xs:element name="length" type="BuildingSize"/>
+                            <xs:element name="pathLength" type="NonNegative"/>
+                            <xs:element name="divisions">
+                                <xs:complexType>
+                                    <xs:all>
+                                        <xs:element name="southNorth" type="NonNegative"/>
+                                        <xs:element name="eastWest" type="NonNegative"/>
+                                        <xs:element name="aboveBelow" type="NonNegative"/>
+                                    </xs:all>
+                                </xs:complexType>
+                            </xs:element>
+                            <xs:element name="horizontalObstacles" type="HorizontalObstacles"/>
+                            <xs:element name="verticalObstacles" type="VerticalObstacles"/>
+                            <xs:element name="hintLikelihood" type="ZeroToOne"/>
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
+            </xs:choice>
+            <xs:attribute name="seed" type="xs:string" use="optional"/>
+            <xs:attribute name="palette" type="PaletteEnum" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="PaletteEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="default"/>
+            <xs:enumeration value="random"/>
+            <xs:enumeration value="dungeon"/>
+            <xs:enumeration value="pyramid"/>
+            <xs:enumeration value="igloo"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="HorizontalObstacles">
+        <xs:all>
+            <xs:element name="gap" type="NonNegative"/>
+            <xs:element name="bridge" type="NonNegative"/>
+            <xs:element name="door" type="NonNegative"/>
+            <xs:element name="puzzle" type="NonNegative"/>
+            <xs:element name="jump" type="NonNegative"/>
+        </xs:all>
+    </xs:complexType>
+
+    <xs:complexType name="VerticalObstacles">
+        <xs:all>
+            <xs:element name="stairs" type="NonNegative"/>
+            <xs:element name="ladder" type="NonNegative"/>
+            <xs:element name="jump" type="NonNegative"/>
+        </xs:all>
+    </xs:complexType>
+
+    <xs:simpleType name="BuildingSize">
+        <xs:restriction base="xs:int">
+            <xs:minInclusive value="7"/>
+            <xs:maxInclusive value="50"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="ZeroToOne">
+        <xs:restriction base="xs:double">
+            <xs:minInclusive value="0"/>
+            <xs:maxInclusive value="1"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="NonNegative">
+        <xs:restriction base="xs:int">
+            <xs:minInclusive value="0"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:element name="AnimationDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Basic animation created by repeatedly applying a DrawingDecorator at different positions.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:choice>
+                    <xs:element name="Parametric">
+                        <xs:annotation>
+                            <xs:documentation>
+                                Create an animation where the (x,y,z) position are determined by parametric equations.
+                                Recognised tokens are:
+
+                                * basic arithmetic operations: +, -, /, *, % (modulo), ^ (to the power of)
+                                * basic trig: sin, cos, tan, asin, acos, atan
+                                * abs (absolute value)
+                                * rand - replaces with a random float between 0 and 1
+                                * t - the integer time variable, incremented with each time interval
+
+                                For example, to create a structure which orbits in the x-z plane about the point
+                                (100,100,100) at a radius of 20, use:
+                                "100+20*cos(t)" and "100+20*sin(t)" for the x and z strings.
+
+                                The parser is not robust to mismatched brackets, typos, unrecognised tokens etc, and
+                                will fail silently.
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:complexType>
+                            <xs:all>
+                                <xs:element name="x" type="xs:string"/>
+                                <xs:element name="y" type="xs:string"/>
+                                <xs:element name="z" type="xs:string"/>
+                            </xs:all>
+                            <xs:attribute name="seed">
+                                <xs:annotation>
+                                    <xs:documentation>
+                                        The master seed for the random number generator used for any stochastic
+                                        elements.
+                                    </xs:documentation>
+                                </xs:annotation>
+                                <xs:simpleType>
+                                    <xs:annotation>
+                                        <xs:documentation>
+                                            Either an integer number, or the string "random".
+                                        </xs:documentation>
+                                    </xs:annotation>
+                                    <xs:restriction base="xs:string">
+                                        <xs:pattern value="random|[0-9]+"/>
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:attribute>
+                        </xs:complexType>
+                    </xs:element>
+                    <xs:element name="Linear">
+                        <xs:annotation>
+                            <xs:documentation>
+                                Create an animation where the (x,y,z) position is determined by the starting position, a
+                                constant velocity, and a bounding box.
+
+                                Each time step, the position is updated by adding the velocity values. If the object
+                                goes outside of the bounding box in one dimension, that dimension's velocity will be
+                                flipped to reverse the direction.
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:complexType>
+                            <xs:all>
+                                <xs:element name="CanvasBounds" type="UnnamedGridDefinition">
+                                    <xs:annotation>
+                                        <xs:documentation>
+                                            Define the bounds of the canvas within which to move the object.
+                                        </xs:documentation>
+                                    </xs:annotation>
+                                </xs:element>
+                                <xs:element name="InitialPos" type="Pos">
+                                    <xs:annotation>
+                                        <xs:documentation>
+                                            Define the starting position of the drawing's origin.
+                                        </xs:documentation>
+                                    </xs:annotation>
+                                </xs:element>
+                                <xs:element name="InitialVelocity" type="Pos">
+                                    <xs:annotation>
+                                        <xs:documentation>
+                                            Define the initial velocity of the drawing's origin.
+                                        </xs:documentation>
+                                    </xs:annotation>
+                                </xs:element>
+                            </xs:all>
+                        </xs:complexType>
+                    </xs:element>
+                </xs:choice>
+                <xs:element ref="DrawingDecorator">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Define the drawing, relative to (0,0,0), which will be drawn for each frame of the
+                            animation.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+            </xs:sequence>
+            <xs:attribute name="ticksPerUpdate" type="xs:int" default="1">
+                <xs:annotation>
+                    <xs:documentation>
+                        The number of server ticks between each update of the animation.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="BuildBattleDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Sets up a build battle area, with a source structure that can't be edited, and a goal structure, with
+                optional recolouring of blocks to indicate correct/incorrect placement.
+
+                NOTE: Make sure to add a {{{ RewardForStructureCopying }}} handler to the AgentHandlers if you want your
+                agent to be rewarded for contributing to the build.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="GoalStructureBounds" type="UnnamedGridDefinition">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Define the bounds of the structure to be copied. Anything in this volume when the mission
+                            starts will be used as the blueprint - eg anything drawn here using the
+                            {{{ DrawingDecorator }}}, etc.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="PlayerStructureBounds" type="UnnamedGridDefinition">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Define the bounds in which the agent should build their copy.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="BlockTypeOnCorrectPlacement" type="DrawBlockBasedObjectType" minOccurs="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If present, correctly placed blocks (in the source and the copy) will be changed to this
+                            block type.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="BlockTypeOnIncorrectPlacement" type="DrawBlockBasedObjectType" minOccurs="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If present, incorrectly placed blocks (in the copy only) will be changed to this block type.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="RandomPlacement">
+        <xs:all>
+            <xs:annotation>
+                <xs:documentation>
+                    Helps specify where random blocks should be placed in the world.
+                </xs:documentation>
+            </xs:annotation>
+            <xs:element name="origin" type="Pos" minOccurs="0" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>
+                        Specifies the origin point or spawn point in the Minecraft world.
+
+                        If omitted, set to the world spawn point.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="minRadius" type="xs:decimal" minOccurs="1" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>
+                        Specifies the minumum radius of the circle around which the block may be randomly placed.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="maxRadius" type="xs:decimal" minOccurs="1" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>
+                        Specifies the maximum radius of the circle around which the block may be randomly placed.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="block" type="BlockType" minOccurs="1" maxOccurs="1">
+                <xs:annotation>
+                    <xs:documentation>
+                        Specifies the block type of the block to be randomly placed.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="placement" minOccurs="0" maxOccurs="1">
+                <xs:simpleType>
+                    <xs:annotation>
+                        <xs:documentation>
+                            Specifies the placement behavior of the block. Default is "surface."
+
+                            String of "sphere" will place equally randomly in a sphere of radius specified.
+
+                            String of "circle" will place randomly in a circle at the y-coordinate specified.
+
+                            String of "surface" will place randomly in a circle, then raise to the highest available
+                            block on the surface.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:restriction base="xs:string">
+                        <xs:enumeration value="sphere"/>
+                        <xs:enumeration value="circle"/>
+                        <xs:enumeration value="surface"/>
+                        <xs:enumeration value="fixed_surface"/>
+                    </xs:restriction>
+                </xs:simpleType>
+            </xs:element>
+            <xs:element name="randomizedRadius" type="xs:boolean" minOccurs="0" maxOccurs="1" default="false">
+                <xs:annotation>
+                    <xs:documentation>
+                        Sets whether or not the target block radius should be randomized within a certain radius.
+                        If false, will set the default target radius to the given radius.
+                        If true, will set the default target radius randomly within the bound specified below.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="minRandomizedRadius" type="xs:decimal" minOccurs="0" maxOccurs="1" default="10">
+                <xs:annotation>
+                    <xs:documentation>
+                        The minimum distance for a randomly placed target block.
+
+                        If omitted, and randomization of radius enabled, set to 10 blocks.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="maxRandomizedRadius" type="xs:decimal" minOccurs="0" maxOccurs="1" default="64">
+                <xs:annotation>
+                    <xs:documentation>
+                        The maximum distance for a randomly placed target block.
+
+                        If omitted, and randomization of radius enabled, set to 64 blocks.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+        </xs:all>
+    </xs:complexType>
+
+    <xs:element name="MarkingDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Adds two distinct "marker" blocks diagonally at world start. Can be used to mark a build area for an
+                agent
+                receiving an exact blueprint
+
+                Agent is teleported to a random point within a radius around the marker blocks.
+
+                This can be used together with ObservationFromGrid to provide an observation for the structure placed
+                around the marker blocks.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="origin" type="Pos" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Specifies the origin point or spawn point in the Minecraft world.
+
+                            If omitted, set to the world spawn point.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="block1" type="BlockType" minOccurs="1" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Specifies the block type of the first marker block to be randomly placed.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="block2" type="BlockType" minOccurs="1" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Specifies the block type of the second marker block to be randomly placed.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="placement" minOccurs="0" maxOccurs="1">
+                    <xs:simpleType>
+                        <xs:annotation>
+                            <xs:documentation>
+                                Specifies the placement behavior of the agent. Default is "surface."
+
+                                String of "sphere" will place equally randomly in a sphere of radius specified.
+
+                                String of "circle" will place randomly in a circle at the y-coordinate specified.
+
+                                String of "surface" will place randomly in a circle, then raise to the highest available
+                                block on the surface.
+                            </xs:documentation>
+                        </xs:annotation>
+                        <xs:restriction base="xs:string">
+                            <xs:enumeration value="sphere"/>
+                            <xs:enumeration value="circle"/>
+                            <xs:enumeration value="surface"/>
+                            <xs:enumeration value="fixed_surface"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="randomizedRadius" type="xs:boolean" minOccurs="0" maxOccurs="1" default="false">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Sets whether or not the agent radius should be randomized within a certain radius.
+                            If false, will set the default target radius to the given radius.
+                            If true, will set the default target radius randomly within the bound specified below.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="minRandomizedRadius" type="xs:decimal" minOccurs="0" maxOccurs="1" default="10">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The minimum distance for a randomly placed agent.
+
+                            If omitted, and randomization of radius enabled, set to 10 blocks.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="maxRandomizedRadius" type="xs:decimal" minOccurs="0" maxOccurs="1" default="64">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The maximum distance for a randomly placed agent.
+
+                            If omitted, and randomization of radius enabled, set to 64 blocks.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="NavigationDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Adds a specified block to the world and sets compass to that block.
+
+                Block is placed randomly along a radius around the origin specified.
+
+                Can force the block to appear at the highest available y-value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="randomPlacementProperties" type="RandomPlacement">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Properties for placing a block in the world randomly.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="randomizeCompassLocation" type="xs:boolean" minOccurs="1" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Sets whether or not the compass location should be randomized within a certain radius.
+                            If false, will set the compass location to the block that was randomly placed.
+                            If true, will set the compass location to a random spot within the radius specified below.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="minRandomizedDistance" type="xs:decimal" minOccurs="0" maxOccurs="1" default="0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The minimum distance a randomized compass location must be from the block that was randomly
+                            placed.
+
+                            If omitted, set to 0 blocks.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="maxRandomizedDistance" type="xs:decimal" minOccurs="0" maxOccurs="1" default="8">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The maximum distance a randomized compass location must be from the block that was randomly
+                            placed.
+
+                            If omitted, set to 8 blocks.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="RandomizedStartDecorator">
+        <xs:annotation>
+            <xs:documentation>
+                Adds a random starting position for agents uniformly chosen at random from (0,0) to (Integer.max,
+                Inteegr.max)
+
+                Spawn height is chosen using the default minecraft spawning rules - getTopSolidOrLiquidBlock()
+                preventing spawning
+
+                on trees or non-solid blocks
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="test" type="xs:string" use="optional" default=""/>
+        </xs:complexType>
+    </xs:element>
+
+    <!--============================================== QUIT PRODUCERS ===============================================-->
+
+    <xs:element name="ServerQuitFromTimeUp">
+        <xs:annotation>
+            <xs:documentation>
+                Specifies a time limit that applies to all agents.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="timeLimitMs" type="xs:decimal" use="required"/>
+            <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ServerQuitWhenAnyAgentFinishes">
+        <xs:annotation>
+            <xs:documentation>
+                Specifies that the mission ends when any of the agents finish.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+        </xs:complexType>
+    </xs:element>
+
+    <!--=============================================================================================================-->
+    <!--=========================================== CLIENT-SIDE HANDLERS ============================================-->
+    <!--=============================================================================================================-->
+
+    <!--============================================== VIDEO PRODUCERS ==============================================-->
+
+    <xs:element name="DepthProducer">
+        <xs:annotation>
+            <xs:documentation>
+                Requests that 32bpp depth frames be sent.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Width" type="xs:int"/>
+                <xs:element name="Height" type="xs:int"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="LuminanceProducer">
+        <xs:annotation>
+            <xs:documentation>
+                Requests an 8bpp grayscale image.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Width" type="xs:int"/>
+                <xs:element name="Height" type="xs:int"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="HexColour">
+        <xs:restriction base="xs:hexBinary">
+            <xs:length value="3"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="MobWithColour">
+        <xs:attribute name="type" type="MobList" use="required"/>
+        <xs:attribute name="colour" use="required" type="HexColour"/>
+    </xs:complexType>
+
+    <xs:element name="ColourMapProducer">
+        <xs:annotation>
+            <xs:documentation>
+                Requests a 24bpp colour map - each object/entity represented by a solid block of colour.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Width" type="xs:int"/>
+                <xs:element name="Height" type="xs:int"/>
+                <xs:choice minOccurs="0" maxOccurs="unbounded">
+                    <xs:element name="ColourSpec" type="MobWithColour"/>
+                </xs:choice>
+            </xs:sequence>
+            <xs:attribute name="skyColour" type="HexColour" default="fbceb1"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="VideoProducer">
+        <xs:annotation>
+            <xs:documentation>
+                Requests that video frames be sent.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Width" type="xs:int"/>
+                <xs:element name="Height" type="xs:int"/>
+                <xs:element name="DepthScaling" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:attribute name="min" default="0">
+                            <xs:simpleType>
+                                <xs:restriction base="xs:decimal">
+                                    <xs:minInclusive value="0"/>
+                                    <xs:maxInclusive value="1"/>
+                                </xs:restriction>
+                            </xs:simpleType>
+                        </xs:attribute>
+                        <xs:attribute name="max" default="1">
+                            <xs:simpleType>
+                                <xs:restriction base="xs:decimal">
+                                    <xs:minInclusive value="0"/>
+                                    <xs:maxInclusive value="1"/>
+                                </xs:restriction>
+                            </xs:simpleType>
+                        </xs:attribute>
+                        <xs:attribute name="autoscale" type="xs:boolean" default="true">
+                            <xs:annotation>
+                                <xs:documentation>
+                                    Set to false to specify the min and max depths manually. Default is true, where uses
+                                    the min and max depths in the scene.
+                                </xs:documentation>
+                            </xs:annotation>
+                        </xs:attribute>
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+            <xs:attribute name="want_depth" type="xs:boolean" default="false">
+                <xs:annotation>
+                    <xs:documentation>
+                        If true, returns depth in the fourth channel as RGBDRGBD.... Else as RGBRGB...
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="viewpoint" default="0">
+                <xs:annotation>
+                    <xs:documentation>
+                        Sets the camera viewpoint. 0 = first-person, 1 = behind, 2 = facing.
+                    </xs:documentation>
+                </xs:annotation>
+                <xs:simpleType>
+                    <xs:restriction base="xs:int">
+                        <xs:minInclusive value="0"/>
+                        <xs:maxInclusive value="2"/>
+                    </xs:restriction>
+                </xs:simpleType>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <!--============================================== AUDIO PRODUCERS ==============================================-->
+
+    <!--============================================= COMMAND HANDLERS ==============================================-->
+
+    <!-- Command lists -->
+
+    <xs:simpleType name="ContinuousMovementCommand">
+        <xs:annotation>
+            <xs:documentation>
+                Commands for smooth movement. Some examples:
+
+                "{{{ move 0.5}}}" - start moving forward at 50% of the normal walking speed (-ve = backward, +ve =
+                forward).
+
+                "{{{ strafe -1}}}" - start moving left at 100% of the normal walking speed (-ve = left, +ve = right).
+
+                "{{{ pitch 0.1}}}" - start tilting the agent's head down at 10% of the maximum speed (-ve = up, +ve =
+                down). The maximum speed is set by {{{ turnSpeedDegs }}} in {{{ ContinuousMovementCommands }}}.
+
+                "{{{ turn 0.1}}}" - start turning right at 10% of the maximum speed (-ve = anti-clockwise/left, +ve =
+                clockwise/right). The maximum speed is set by {{{ turnSpeedDegs }}} in {{{ ContinuousMovementCommands }}}.
+
+                "{{{ jump 1}}}" - start jumping (1 = start, 0 = stop).
+
+                "{{{ crouch 1}}}" - start crouching (1 = start, 0 = stop).
+
+                "{{{ attack 1}}}" - start attacking (1 = start, 0 = stop). The 'attack' command is for destroying blocks
+                and attacking mobs.
+
+                "{{{ use 1}}}" - start 'use'ing (1 = start, 0 = stop). The 'use' command is for placing blocks and for
+                other things too.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="move"/>
+            <xs:enumeration value="strafe"/>
+            <xs:enumeration value="pitch"/>
+            <xs:enumeration value="turn"/>
+            <xs:enumeration value="jump"/>
+            <xs:enumeration value="crouch"/>
+            <xs:enumeration value="attack"/>
+            <xs:enumeration value="use"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="AbsoluteMovementCommand">
+        <xs:annotation>
+            <xs:documentation>
+                Commands to set position and orientation directly. Some examples:
+
+                "{{{ tp 23.5 1 -34.5}}}" - teleport the agent to the absolute position x y z (space delimited).
+
+                "{{{ tpx 230}}}" - set the agent's x coordinate, without altering the y and z.
+
+                "{{{ tpy 103.2}}}" - set the agent's y coordinate, without altering the x and z.
+
+                "{{{ tpz -32.5}}}" - set the agent's z coordinate, without altering the x and y.
+
+                "{{{ setYaw 30}}}" - set the agent's body orientation to be 30 degrees clockwise from south.
+
+                "{{{ setPitch 20}}}" - set the agent's body orientation to be 20 degrees down from horizontal.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="tpx"/>
+            <xs:enumeration value="tpy"/>
+            <xs:enumeration value="tpz"/>
+            <xs:enumeration value="tp"/>
+            <xs:enumeration value="setYaw"/>
+            <xs:enumeration value="setPitch"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="DiscreteMovementCommand">
+        <xs:annotation>
+            <xs:documentation>
+                Commands for moving and turning in discrete increments. Some examples:
+
+                "{{{ move 1}}}" - move the agent one block forwards (1 = forwards, -1 = backwards).
+
+                "{{{ jumpmove 1}}}" - move the agent one block up and forwards (1 = forwards, -1 = backwards).
+
+                "{{{ strafe 1}}}" - move the agent one block sideways (1 = right, -1 = left).
+
+                "{{{ jumpstrafe 1}}}" - move the agent one block up and sideways (1 = right, -1 = left).
+
+                "{{{ turn 1}}}" - rotate the agent 90 degrees right (1 = right, -1 = left).
+
+                "{{{ movenorth 1}}}" - move the agent one block north.
+
+                "{{{ moveeast 1}}}" - move the agent one block east.
+
+                "{{{ movesouth 1}}}" - move the agent one block south.
+
+                "{{{ movewest 1}}}" - move the agent one block west.
+
+                "{{{ jumpnorth 1}}}" - move the agent one block up and north.
+
+                "{{{ jumpeast 1}}}" - move the agent one block up and east.
+
+                "{{{ jumpsouth 1}}}" - move the agent one block up and south.
+
+                "{{{ jumpwest 1}}}" - move the agent one block up and west.
+
+                "{{{ jump 1}}}" - move the agent one block up.
+
+                "{{{ look 1}}}" - look down by 45 degrees (-ve = up, +ve = down).
+
+                "{{{ attack 1}}}" - destroy the block that has focus.
+
+                "{{{ use 1}}}" - place the held block item on the block face that has focus.
+
+                "{{{ jumpuse }}}" - simultaneously jump and place the held block on the block face that has focus.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="move"/>
+            <xs:enumeration value="jumpmove"/>
+            <xs:enumeration value="strafe"/>
+            <xs:enumeration value="jumpstrafe"/>
+            <xs:enumeration value="turn"/>
+            <xs:enumeration value="movenorth"/>
+            <xs:enumeration value="moveeast"/>
+            <xs:enumeration value="movesouth"/>
+            <xs:enumeration value="movewest"/>
+            <xs:enumeration value="jumpnorth"/>
+            <xs:enumeration value="jumpeast"/>
+            <xs:enumeration value="jumpsouth"/>
+            <xs:enumeration value="jumpwest"/>
+            <xs:enumeration value="jump"/>
+            <xs:enumeration value="look"/>
+            <xs:enumeration value="attack"/>
+            <xs:enumeration value="use"/>
+            <xs:enumeration value="jumpuse"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="InventoryCommand">
+        <xs:annotation>
+            <xs:documentation>
+                Commands for changing the contents of the inventory and hotbar.
+
+                To move items around in the inventory you can use {{{ swapInventoryItems }}}. For example, to swap
+                the contents of slots 13 and 14, issue this command:
+
+                "{{{ swapInventoryItems 13 14}}}"
+
+                Note that inventory slots are numbered from 0 to 39.
+                0-8 are the hotbar slots (which correspond to the hotbar commands hotbar.1-hotbar.9 - _note the offset_)
+                9-35 are the rest of the inventory (visible when you press 'E' in the game)
+                36-39 are the armour slots.
+
+                So to move an item out of the hotbar, say:
+
+                "{{{ swapInventoryItems 3 30}}}"
+
+                Other commands:
+
+                "{{{ combineInventoryItems x y}}}" - will attempt to combine the stacks in slots x and y, and leave the
+                results in slot x. Eg if there are ten blocks of granite in slot 4, and 57 blocks of granite in slot 12,
+                then {{{ combineInventoryItems 4 12}}} will result in 64 (the max) blocks of granite in slot 4, and the
+                remainder in slot 12. If the slots can't be combined (they are different types, or the first slot is
+                full) then nothing will happen.
+
+                "{{{ discardCurrentItem }}}" - discards the currently held item.
+
+                To select a hotbar slot:
+
+                "{{{ hotbar.1 1}}}"
+                "{{{ hotbar.1 0}}}"
+
+                Send both commands to select hotbar slot 1 as the currently-held tool. This affects the attack and use
+                commands
+                - e.g. if the agent does 'use' while holding a block item it will place the block into the world.
+
+                If the agent is currently pointed at a container item - eg a chest, shulker box, dispenser etc - then
+                the swap and combine commands
+                can be extended to allow access to the container's inventory. To do this, simply prefix the slot number
+                with the name of the foreign
+                inventory (which will be provided by the {{{ ObservationFromFullInventory }}} observations).
+
+                So to move an item out of the hotbar and into slot 0 of a chest, say:
+
+                "{{{ swapInventoryItems 3 Chest:0}}}"
+
+                Note that this is the same as writing
+
+                "{{{ swapInventoryItems Inventory:3 Chest:0}}}"
+
+                "Inventory" is the name of the player's inventory.
+
+                See inventory_test.py for a working example of this.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="swapInventoryItems"/>
+            <xs:enumeration value="combineInventoryItems"/>
+            <xs:enumeration value="discardCurrentItem"/>
+            <xs:enumeration value="hotbar.1"/>
+            <xs:enumeration value="hotbar.2"/>
+            <xs:enumeration value="hotbar.3"/>
+            <xs:enumeration value="hotbar.4"/>
+            <xs:enumeration value="hotbar.5"/>
+            <xs:enumeration value="hotbar.6"/>
+            <xs:enumeration value="hotbar.7"/>
+            <xs:enumeration value="hotbar.8"/>
+            <xs:enumeration value="hotbar.9"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="SimpleCraftCommand">
+        <xs:annotation>
+            <xs:documentation>
+                A command for simple crafting:
+
+                "{{{ craft carpet PINK}}}"
+
+                etc.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="craft"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="NearbyCraftCommand">
+        <xs:annotation>
+            <xs:documentation>
+                A command for simple nearby crafting:
+
+                "{{{ craftNearby carpet PINK}}}"
+
+                etc.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="craftNearby"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="NearbySmeltCommand">
+        <xs:annotation>
+            <xs:documentation>
+                A command for simple smelting:
+
+                "{{{ smeltNearby carpet PINK}}}"
+
+                etc.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="smeltNearby"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="EquipCommand">
+        <xs:annotation>
+            <xs:documentation>
+                A command for equiping items in the agent's inventory:
+
+                Will look in the agent's inventory. If the item exists, will swap that stack with the agents currently
+                equiped hotbard slot.
+
+                For basic objects, use BlockTypes found in Types.xsd. Eg:
+
+                "{{{ equip diamond_pickaxe}}}" will attempt to equip a diamond_pickaxe from the agent's inventory.
+
+                for more control over colours, types, etc, add a Variation or Colour. Eg:
+
+                "{{{ equip carpet PINK}}}"
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="equip"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="PlaceCommand">
+        <xs:annotation>
+            <xs:documentation>
+                A command for placing blocks in the agent's inventory:
+
+                Will look in the agent's inventory. If the block exists, will try to place the block in the world.
+
+                For basic objects, use BlockTypes found in Types.xsd. Eg:
+
+                "{{{ place diamond_block}}}" will attempt to place a diamond block from the agent's inventory.
+
+                for more control over colours, types, etc, add a Variation or Colour. Eg:
+
+                "{{{ place carpet PINK}}}"
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="place"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="ChatCommand">
+        <xs:annotation>
+            <xs:documentation>
+                A command for broadcasting text messages to the other players. An example:
+
+                "{{{ chat I have found diamonds!}}}" - broadcasts the string "{{{ I have found diamonds!}}}".
+
+                Chat messages from other players can be observed using {{{ ObservationFromChat }}}.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="chat"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="MissionQuitCommand">
+        <xs:annotation>
+            <xs:documentation>
+                A command for ending the mission, example:
+
+                "{{{ quit }}}" - terminates the current mission.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="quit"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="HumanLevelCommand">
+        <xs:annotation>
+            <xs:documentation>
+                Commands for controlling Minecraft at the level of keyboard and mouse events.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="forward"/>
+            <xs:enumeration value="left"/>
+            <xs:enumeration value="back"/>
+            <xs:enumeration value="right"/>
+            <xs:enumeration value="jump"/>
+            <xs:enumeration value="sneak"/>
+            <xs:enumeration value="sprint"/>
+            <xs:enumeration value="inventory"/>
+            <xs:enumeration value="swapHands"/>
+            <xs:enumeration value="drop"/>
+            <xs:enumeration value="use"/>
+            <xs:enumeration value="attack"/>
+            <xs:enumeration value="pickItem"/>
+            <xs:enumeration value="hotbar.1"/>
+            <xs:enumeration value="hotbar.2"/>
+            <xs:enumeration value="hotbar.3"/>
+            <xs:enumeration value="hotbar.4"/>
+            <xs:enumeration value="hotbar.5"/>
+            <xs:enumeration value="hotbar.6"/>
+            <xs:enumeration value="hotbar.7"/>
+            <xs:enumeration value="hotbar.8"/>
+            <xs:enumeration value="hotbar.9"/>
+            <xs:enumeration value="moveMouse"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="Command">
+        <xs:union
+                memberTypes="ContinuousMovementCommand AbsoluteMovementCommand DiscreteMovementCommand InventoryCommand ChatCommand SimpleCraftCommand MissionQuitCommand HumanLevelCommand"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="CommandList">
+        <xs:list itemType="Command"/>
+    </xs:simpleType>
+
+    <xs:complexType name="CommandListModifier">
+        <xs:annotation>
+            <xs:documentation>
+                Allow-list/deny-list base class - restricted by each command handler to only allow that handler's
+                commands.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice maxOccurs="unbounded">
+            <xs:element name="command" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:choice>
+        <xs:attribute name="type" use="optional" default="deny-list">
+            <xs:simpleType>
+                <xs:restriction base="xs:string">
+                    <xs:enumeration value="deny-list"/>
+                    <xs:enumeration value="allow-list"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+
+    <!-- The actual command handlers.-->
+
+    <xs:element name="ContinuousMovementCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept commands that control smooth movement.
+
+                Commands take the form of "verb &lt;value&gt;" e.g. "{{{ move 1}}}" to move forwards with normal speed.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="ContinuousMovementCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+            <xs:attribute name="turnSpeedDegs" type="xs:decimal" use="optional" default="180">
+                <xs:annotation>
+                    <xs:documentation>
+                        This sets the maximum speed for both turning the agent and adjusting the camera pitch, in
+                        degrees per second.
+
+                        The turn and pitch command values are both scaled by this - eg "{{{ turn -0.5}}}" to turn left
+                        (anti-clockwise) at half this maximum speed.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AbsoluteMovementCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept commands to set the player's position and orientation directly.
+
+                Commands take the form of "verb &lt;value&gt;" e.g. "{{{ tpx 13}}}" to set the x-coordinate to 13.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="AbsoluteMovementCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="DiscreteMovementCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept commands that control movement in discrete jumps.
+
+                Commands take the form of "verb &lt;value&gt;" e.g. "{{{ move 1}}}" to move forwards one square.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="DiscreteMovementCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+            <xs:attribute name="autoFall" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="autoJump" type="xs:boolean" use="optional" default="false"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="InventoryCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept commands that control the player's inventory.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="InventoryCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="SimpleCraftCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept simple commands that implement a basic form of crafting.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ViewToCraft" type="xs:boolean" minOccurs="0" maxOccurs="1" default="false"/>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="SimpleCraftCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="NearbyCraftCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept simple commands that implement a basic form of crafting. Success of
+                the craft command depends on the presence of a nearby crafting table previously placed by the agent and
+                within reach and in field of view.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ViewToCraft" type="xs:boolean" minOccurs="0" maxOccurs="1" default="false"/>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="NearbyCraftCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="NearbySmeltCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept simple commands that implement a basic form of smelting. Success of
+                the smelt command depends on the presence of a nearby furnace previously placed by the agent and within
+                reach and in field of view. Fails when a command corresponds to an item not able to be smelted. Each
+                command takes fuel as if the agent had placed the items in a furnace.
+
+                Eg:
+
+                If called on 16 iron ore, requires 2 coal, 2 charcoal, 11 planks, etc.
+
+                If called twice separately on 8 iron ore, requires 1 coal and then 1 coal, etc.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ViewToSmelt" type="xs:boolean" minOccurs="0" maxOccurs="1" default="false"/>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="NearbySmeltCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="EquipCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept commands that allow for equiping items of a given type in the players
+                inventory. If the specified block is contained in the agent's inventory, then the agent will attempt
+                to swap it to the current hotbar slot.
+
+                If a non-block is specified, the command fails.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="EquipCommand" minOccurs="0" maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="PlaceCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept commands that allow for the placement of blocks. If the specified
+                block is contained in the agent's inventory, then the agent will attempt to place the block in the
+                world.
+
+                If a non-block is specified, the command fails.
+
+                If a block is specified, the block will swap to the hotbar, be placed, and then swapped back, so no
+                changes to the inventory are made besides losing a block.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="PlaceCommand" minOccurs="0" maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ChatCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept commands that send chat messages to the other players.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="ChatCommand" minOccurs="0" maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="MissionQuitCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept a command that quits the mission.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="MissionQuitCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+            <xs:attribute name="quitDescription" type="xs:string" use="optional" default=""/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="TurnBasedCommands">
+        <xs:annotation>
+            <xs:documentation>
+                Allows a user to specify that certain commands must be sent on a turn-by-turn basis - ie, in a
+                multi-agent mission, placing the {{{ DiscreteMovementCommand }}} handler inside the TurnBasedCommands
+                section will mean that each agent must take it in turns to send a discrete movement command. See
+                turn_based_test.py in the Python Samples for a demonstration/explanation of this.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:group ref="TurnBasedApplicableCommandHandlers"/>
+            <xs:attribute name="requestedPosition">
+                <xs:simpleType>
+                    <xs:restriction base="xs:integer">
+                        <xs:minInclusive value="0"/>
+                    </xs:restriction>
+                </xs:simpleType>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="HumanLevelCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept commands on the level of mouse and keyboard events.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="ModifierList" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:restriction base="CommandListModifier">
+                                <xs:choice maxOccurs="unbounded">
+                                    <xs:element name="command" type="HumanLevelCommand" minOccurs="0"
+                                                maxOccurs="unbounded"/>
+                                </xs:choice>
+                            </xs:restriction>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="PauseCommand">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will accept the pause command for payusing missions.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="CameraCommands">
+        <xs:annotation>
+            <xs:documentation>
+                This command accepts camera commands. The command is "camera {yaw} {pitch}"
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+    <!--=========================================== OBSERVATION PRODUCERS ===========================================-->
+
+    <xs:element name="ObservationFromRecentCommands">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that say what commands have been acted on since the last
+                report, in the JSON element {{{ CommandsSinceLastObservation }}}.
+
+                Note that the commands returned might not yet have taken effect, depending on the command and the way in
+                which Minecraft responds to it -
+                but they will have been processed by the command handling chain.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromAchievements">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that corespond to each achievemnet available in game.
+                IMPORTANT - given Malmo by-passes the Gui for most events this handler will attempt to give the player
+                the OPEN_INVENTORY acheivemnt to not prevent the accumulation of later achievemnts.
+
+                Achievemnts will be returned as individual entires with a boolean value under the property key
+                "achievements"
+
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromEquippedItem">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return an observation of the equipped item by the agent along with its item
+                durribility if available.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromTurnScheduler">
+        <xs:annotation>
+            <xs:documentation>
+                Automatically addd by Malmo when the user specifies the {{{ TurnBasedCommands }}} handler. This provides
+                vital observations back to the agent to allow them to make use of the turn scheduler. When it is the
+                agent's turn, the JSON will contain {{{ turn_number }}} - an integer which tracks the number of turns
+                the
+                agent has taken, and {{{ turn_key }}} - a one-shot key which must be passed back to Malmo as a parameter
+                in {{{ sendCommand }}} in order for the command to be accepted.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromSubgoalPositionList">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that indicate the direction to follow to the next
+                subgoal.
+                The value to turn by is returned in the JSON element {{{ yawDelta }}}.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Point" type="PointWithToleranceAndDescription" minOccurs="1" maxOccurs="unbounded"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ObservationFromHotBar">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that say what is in the hotbar.
+
+                Up to four values are returned for each slot, if not empty: e.g. {{{ Hotbar_1_size }}} and
+                {{{ Hotbar_1_item }}} containing the number and
+                type of the item(s) in the slot, respectively, and {{{ Hotbar_1_colour }}} and {{{ Hotbar_1_variant }}}
+                if
+                the item has a colour/variation. Hotbar slots are numbered 0 to 8 inclusive.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromFullStats">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return several observations:
+
+                * Achievement statistics: {{{ DistanceTravelled }}}, {{{ TimeAlive }}}, {{{ MobsKilled }}},
+                {{{ PlayersKilled }}}, {{{ DamageTaken }}}, {{{ DamageDealt }}}
+                * Life statistics: {{{ Life }}}, {{{ Score }}}, {{{ Food }}}, {{{ Air }}}, {{{ XP }}}, {{{ IsAlive }}},
+                {{{ Name }}}
+                * Position statistics: {{{ XPos }}}, {{{ YPos }}}, {{{ ZPos }}}, {{{ Pitch }}}, {{{ Yaw }}}
+                * Environment statistics: {{{ WorldTime }}} - current time in ticks, {{{ TotalTime }}} - total world
+                time,
+                unaffected by ServerInitialConditions
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromHuman">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return low-level keyboard and mouse events.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromSystem">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return information on the current performance of the Minecraft system - eg
+                tick speeds, etc.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromRay">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return a JSON object called "LineOfSight", containing observations about the
+                block or entity which is currently in the centre of the screen:
+
+                * Hit details: {{{ hitType }}} - will be "block" for a block, "entity" for an entity (eg spider, rabbit
+                etc) or "item" for a free-floating item that can be picked up. {{{ inRange }}} will be true if the
+                block/entity is within the agent's reach - ie attacking or using will have an effect on the object.
+                {{{ distance }}} gives the straight-line distance from the agent.
+                * Position: {{{ x }}}, {{{ y }}}, {{{ z }}} - in the case of block hits, will be the precise point when
+                the
+                ray intercepts the block. {{{ yaw }}}, {{{ pitch }}} are also added for entities.
+                * Type information: {{{ type }}}, {{{ colour }}}, {{{ variant }}}, {{{ facing }}}
+                * Extra properties: in the case of block types, any extra properties will be returned by their minecraft
+                name, prefixed with "prop_" (eg, for leaves, "prop_decayable" and "prop_check_decay") - this is the same
+                data as can be seen by exploring Minecraft with the F3 debug information displayed. For floating items,
+                the stack size is returned in {{{ stackSize }}}
+                * NBTTagCompound: for tile entity blocks, optionally returns a json object called "NBTTagCompound" which
+                contains the entity's entire NBTTagCompound - eg useful for reading the text off signs, etc. Set
+                {{{ includeNBT }}} to true to receive this data.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="includeNBT" type="xs:boolean" use="optional" default="false"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ObservationFromFullInventory">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that describe the contents of the player's inventory.
+                There are two modes - "flat" (the default) is provided for backwards compatibility, and behaves like
+                this:
+
+                The inventory contents are returned in a flat format in the root of the json observations.
+                Up to four values are returned for each slot, if not empty: e.g. {{{ InventorySlot_0_size }}} and
+                {{{ InventorySlot_0_item }}} containing the number and
+                type of the item(s) in the slot, respectively, and {{{ InventorySlot_0_colour }}} and
+                {{{ InventorySlot_0_variant }}} if the item has a colour/variation.
+                Inventory slots are numbered 0 to 39 inclusive.
+                If there is a container item available (eg the player is pointed at a chest), this will be returned in
+                the same way, but "InventorySlot"
+                will be replaced by "ContainerNameSlot" - eg {{{ ShulkerBoxSlot_0_item }}} etc.
+
+                If {{{ flat }}} is false (recommended), the data is returned as an array of objects, one for each item
+                in
+                the inventory/inventories.
+                The JSON array is called "inventory", and each item in the array will contain:
+                * {{{ type }}} - the type of the object in that ItemStack
+                * {{{ colour }}} - the colour, if relevant
+                * {{{ variant }}} - the variant, if relevant
+                * {{{ quantity }}} - the number of objects in the ItemStack
+                * {{{ index }}} - the slot number
+                * {{{ inventory }}} - the name of the inventory - will be "Inventory" for the player, or, for example,
+                "ShulkerBox", "Chest" etc, if a container is available.
+                This index and inventory information can be used to specify the item in the {{{ InventoryCommands }}} -
+                items are specified as inventory:index -
+                eg "ShulkerBox:12"
+
+                In addition to this information, whether {{{ flat }}} is true or false, an array called
+                "inventoriesAvailable" will also be returned.
+                This will contain a list of all the inventories available (usually just the player's, but if the player
+                is pointed at a container, this
+                will also be available.)
+                For each inventory, an object will be returned that specifies:
+                * {{{ name }}} - the inventory name (same as will be returned in the {{{ inventory }}} field for any
+                items
+                in that inventory)
+                * {{{ size }}} - the number of slots the inventory provides.
+
+                For a working example please see inventory_test.py in the Python samples folder.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="flat" type="xs:boolean" use="optional" default="true"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ObservationFromDiscreteCell">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return an observation of the player's position that is unique for every cell
+                on the x/z plane.
+                This is useful for discrete-movement missions where we need to uniquely identify their location but
+                don't mind how.
+
+                The observation will contain the JSON element {{{ cell }}} containing e.g. {{{ (2,4) }}} if the player
+                is
+                standing at any location where
+                x = 2 and z = 4.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:complexType name="NamedPoint">
+        <xs:complexContent>
+            <xs:extension base="Pos">
+                <xs:attribute name="name" type="xs:Name" use="required"/>
+                <!-- ObservationFromDistance handler will return JSON with property "distanceFrom<name>" -->
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:element name="ObservationFromDistance">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return an observation that specifies the distance to a named location.
+
+                A JSON element {{{ distanceFromNAME }}} will be returned (where {{{ NAME }}} is replaced with the name
+                of
+                the NamedPoint),
+                with a value that is the distance.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Marker" type="NamedPoint"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="GridDefinition">
+        <xs:annotation>
+            <xs:documentation>
+                {{{ name }}} - Each grid has a text label to identify it.
+
+                {{{ absoluteCoords }}} - If true, the min and max coordinates of the grid are interpreted as world
+                coordinates. If false (the default)
+                then the coordinates are relative to the player.
+                {{{ projectDown }}} if true, generate a 2d top-down projection; that is, take a top-most non-air
+                block for each x and z position
+                {{{ atSpawn }}} if true, coordinates are given relative to world spawn point
+
+                {{{ min }}}, {{{ max }}} - The corners of the grid.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="min" type="Pos"/>
+            <xs:element name="max" type="Pos"/>
+        </xs:sequence>
+        <xs:attribute name="name" type="xs:Name" use="required"/>
+        <xs:attribute name="absoluteCoords" type="xs:boolean" use="optional" default="false"/>
+        <xs:attribute name="projectDown" type="xs:boolean" use="optional" default="false"/>
+        <xs:attribute name="atSpawn" type="xs:boolean" use="optional" default="false"/>
+    </xs:complexType>
+
+    <xs:element name="ObservationFromGrid">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that say what the nearby blocks are.
+
+                For each {{{ Grid }}} entry, a named JSON element will be returned with a 1D array of block types, in
+                order along the x, then z, then y axes.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Grid" type="GridDefinition"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="RangeDefinition">
+        <xs:annotation>
+            <xs:documentation>
+                Used by {{{ ObservationFromNearbyEntities }}}. Defines the range within which entities will be returned.
+                Eg a range of 10,1,10 will return all entities within +/-10 blocks of the agent in the x and z axes, and
+                within +/-1 block vertically.
+
+                {{{ update_frequency }}} is measured in Minecraft world ticks, and allows the user to specify how often
+                they would like to receive each observation. A value of 20, under normal Minecraft running conditions,
+                for example, would return the entity list once per second.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="name" type="xs:Name" use="required"/>
+        <xs:attribute name="xrange" type="xs:decimal" use="required"/>
+        <xs:attribute name="yrange" type="xs:decimal" use="required"/>
+        <xs:attribute name="zrange" type="xs:decimal" use="required"/>
+        <xs:attribute name="update_frequency" type="xs:int" use="optional" default="1"/>
+    </xs:complexType>
+
+    <xs:element name="ObservationFromNearbyEntities">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that list the positions of all entities that fall within
+                the given ranges of the agent.
+
+                A JSON array will be returned for each range requested, named using the {{{ name }}} attribute of the
+                range. Within the array will be a series of elements, one for each entity, containing the following:
+
+                - name: a string describing the entity (eg from Types.xsd)
+
+                - x: the x position of the entity
+
+                - y: the y position of the entity
+
+                - z: the z position of the entity
+
+                - quantity: if items have been grouped together by Minecraft, this indicates the number in the stack
+
+                - colour: if the item is a tile entity, with a colour, this will be present to describe the colour
+
+                - variation: optional string to describe the variation - eg the type of egg, or brick, etc (see
+                Types.xsd)
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Range" type="RangeDefinition"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ObservationFromChat">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that say what chat messages have occurred and from which
+                player.
+                It will also return messages for any title or subtitle change (eg via Minecraft's title command)
+
+                A JSON element {{{ Chat }}} will be returned, with a list of chat strings.
+                In the same way, title changes and subtitle changes will be returned in {{{ Title }}} and
+                {{{ Subtitle }}}
+                respectively.
+
+                Note that unless the AgentHost ObservationsPolicy is set to KEEP_ALL_OBSERVATIONS it is likely that chat
+                messages will be missed.
+                The default policy is LATEST_OBSERVATION_ONLY.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+    <xs:element name="ObservationFromCompass">
+        <xs:annotation>
+            <xs:documentation>
+                When present, the Mod will return observations that detail how the agent is facing and what position the
+                agent is in with respect to a compass.
+
+                A JSON element will be returned for the compass in the agent's inventory. The JSON will contain the
+                following
+
+                - set: boolean on whether the compass is set
+
+                - compass-x: the x coordinate value of the set compass, null if not set
+
+                - compass-y: the y coordinate value of the set compass, null if not set
+
+                - compass-z: the z coordinate value of the set compass, null if not set
+
+                - relative-x: the relative x coordinate value of the player to the compass, null if not set
+
+                - relative-y: the relative y coordinate value of the player to the compass, null if not set
+
+                - relative-z: the relative z coordinate value of the player to the compass, null if not set
+
+                - offset: the number of degrees the agent is facing away from the direction the compass is pointing
+
+                - normalized-offset: the number of degrees the agent is facing away, with the agent considered 0 degrees
+
+                - distance: the distance from the agent's location to the compass's location
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+
+    <xs:element name="FileBasedPerformanceProducer">
+        <xs:annotation>
+            <xs:documentation>
+                When present, writes reward performance and number of samples to a directory.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType/>
+    </xs:element>
+
+
+    <!--============================================= REWARD PRODUCERS ==============================================-->
+
+    <xs:simpleType name="Dimension">
+        <xs:annotation>
+            <xs:documentation>
+                For multi-dimensional rewards, specifies the dimension to allocate this reward to. All rewards on this
+                dimension will be summed.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:int">
+            <xs:minInclusive value="0"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:attributeGroup name="RewardProducerAttributes">
+        <!-- Common attributes that are shared by all reward producers. -->
+        <xs:attribute name="dimension" type="Dimension" use="optional" default="0"/>
+    </xs:attributeGroup>
+
+    <xs:complexType name="PointWithReward">
+        <xs:complexContent>
+            <xs:extension base="Pos">
+                <xs:attribute name="reward" type="xs:decimal" use="required"/>
+                <xs:attribute name="tolerance" type="xs:decimal" use="required"/>
+                <xs:attribute name="oneshot" type="xs:boolean" use="optional" default="true"/>
+                <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="MobWithReward">
+        <xs:attribute name="type" type="MobList" use="required"/>
+        <xs:attribute name="reward" type="xs:decimal" use="required"/>
+        <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+    </xs:complexType>
+
+    <xs:element name="RewardForDamagingEntity">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when an entity is damaged.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Mob" type="MobWithReward"/>
+            </xs:choice>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="RewardForReachingPosition">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when a specified position is reached by the agent.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Marker" type="PointWithReward"/>
+            </xs:choice>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="BlockSpec">
+        <xs:attribute name="type" use="required" type="BlockList"/>
+        <xs:attribute name="amount" use="optional" type="xs:int" default="1"/>
+        <xs:attribute name="variant" use="optional" type="VariantList"/>
+        <xs:attribute name="colour" use="optional" type="ColourList"/>
+    </xs:complexType>
+
+    <xs:complexType name="BlockOrItemSpec">
+        <xs:attribute name="type" use="required" type="BlockOrItemList"/>
+        <xs:attribute name="amount" use="optional" type="xs:int" default="1"/>
+        <xs:attribute name="variant" use="optional" type="VariantList"/>
+        <xs:attribute name="colour" use="optional" type="ColourList"/>
+    </xs:complexType>
+
+    <xs:complexType name="BlockSpecWithRewardAndBehaviour">
+        <xs:complexContent>
+            <xs:extension base="BlockSpec">
+                <xs:attribute name="reward" use="required" type="xs:decimal"/>
+                <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+                <xs:attribute name="behaviour" type="Behaviour" use="optional" default="oncePerBlock"/>
+                <xs:attribute name="cooldownInMs" type="xs:decimal" use="optional" default="1000"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="BlockOrItemSpecWithReward">
+        <xs:complexContent>
+            <xs:extension base="BlockOrItemSpec">
+                <xs:attribute name="reward" use="required" type="xs:decimal"/>
+                <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:simpleType name="Behaviour">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="onceOnly"/>
+            <xs:enumeration value="oncePerBlock"/>
+            <xs:enumeration value="oncePerTimeSpan"/>
+            <xs:enumeration value="constant"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:element name="RewardForTouchingBlockType">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a rewards when an agent comes in contact with a specific block type.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Block" type="BlockSpecWithRewardAndBehaviour"/>
+            </xs:choice>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="RewardForSendingCommand">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the agent issues a command.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="reward" type="xs:decimal" use="optional" default="-1.0"/>
+            <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="ChatMatchSpec">
+        <xs:attribute name="description" use="required" type="xs:string"/>
+        <xs:attribute name="reward" use="required" type="xs:decimal"/>
+        <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+        <xs:attribute name="regex" use="required" type="xs:string"/>
+    </xs:complexType>
+
+    <xs:element name="RewardForSendingMatchingChatMessage">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the agent sends a chat message that matches a given regular expression (supports
+                Java regex syntax).
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="ChatMatch" type="ChatMatchSpec"/>
+            </xs:choice>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="ItemSpec">
+        <xs:attribute name="reward" use="required" type="xs:decimal"/>
+        <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+        <xs:attribute name="type" use="required" type="BlockOrItemList"/>
+    </xs:complexType>
+
+    <xs:element name="RewardForCollectingItem">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the agent collects a specific item.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithReward"/>
+            </xs:choice>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="RewardForCollectingItemQuantity">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the agent collects a specific item.
+
+                If Sparse is set to true, will only give full reward on collecting entire amount.
+
+                Otherwise, will give the reward amount notated for each item collected up to the amount noted.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithReward"/>
+            </xs:choice>
+            <xs:attribute name="sparse" type="xs:boolean" use="optional" default="true"/>
+            <xs:attribute name="once" type="xs:boolean" use="optional" default="false"/>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="RewardForPossessingItem">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the agent collects a specific item amount.
+
+                If Sparse is set to true, will only give reward once, when the agent first possesses the entire amount.
+
+                Otherwise, will give the reward amount notated every time a multiple of the specified amount is
+                possessed.
+
+                If excludeLoops is set to true, will only give reward for possesion if the maximum the agent has
+                previously
+                attained is increaded, and that increse crosses a multiple of the specified amount. (Note this only
+                applies
+                when sparse is set to false - when sparse is true this has no effect)
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithReward"/>
+            </xs:choice>
+            <xs:attribute name="sparse" type="xs:boolean" use="optional" default="true"/>
+            <xs:attribute name="excludeLoops" type="xs:boolean" use="optional" default="true"/>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="RewardForCraftingItem">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the agent crafts a specific item.
+
+                If Sparse is set to true, will only give full reward on crafting of entire amount.
+
+                Otherwise, will give the reward amount notated for each item crafted up to the amount noted.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithReward"/>
+            </xs:choice>
+            <xs:attribute name="Sparse" type="xs:boolean" use="optional" default="true"/>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="RewardForSmeltingItem">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the agent smelts a specific item.
+
+                If Sparse is set to true, will only give full reward on crafting of entire amount.
+
+                Otherwise, will give the reward amount notated for each item crafted up to the amount noted.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithReward"/>
+            </xs:choice>
+            <xs:attribute name="Sparse" type="xs:boolean" use="optional" default="true"/>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="RewardForDiscardingItem">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when an agent discards a specific item.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithReward"/>
+            </xs:choice>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="MissionEndRewardCase">
+        <xs:attribute name="reward" use="required" type="xs:decimal"/>
+        <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+        <xs:attribute name="description" use="required" type="xs:string"/>
+    </xs:complexType>
+
+    <xs:element name="RewardForMissionEnd">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the mission ends for a specified reason.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Reward" type="MissionEndRewardCase"/>
+            </xs:choice>
+            <xs:attribute name="rewardForDeath" type="xs:decimal" use="optional" default="0.0"/>
+            <xs:attribute name="rewardForDeathDistribution" type="xs:string" use="optional" default=""/>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="RewardDensityForBuildAndBreak">
+        <xs:annotation>
+            <xs:documentation>
+                Reward type for {{{ RewardForStructureCopying }}}:
+
+                - {{{ PER_BLOCK }}} - reward will be given whenever a block is placed/destroyed in the goal volume, and
+                will be determined by {{{ RewardScale }}}. An additional reward of {{{ RewardForCompletion }}} will be
+                added
+                the *first* time the copy is correctly completed.
+
+                - {{{ MISSION_END }}} - no reward will be given until the mission ends. Reward will be scaled from 0-1,
+                where 0 means "no blocks correct" and 1 means "all blocks correct", and then multipled by
+                {{{ RewardScale }}}.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="PER_BLOCK"/>
+            <xs:enumeration value="MISSION_END"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="UnnamedGridDefinition">
+        <xs:sequence>
+            <xs:element name="min">
+                <xs:complexType>
+                    <xs:attribute name="x" type="xs:int" use="required"/>
+                    <xs:attribute name="y" type="xs:int" use="required"/>
+                    <xs:attribute name="z" type="xs:int" use="required"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="max">
+                <xs:complexType>
+                    <xs:attribute name="x" type="xs:int" use="required"/>
+                    <xs:attribute name="y" type="xs:int" use="required"/>
+                    <xs:attribute name="z" type="xs:int" use="required"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:element name="RewardForStructureCopying">
+        <xs:annotation>
+            <xs:documentation>
+                Sends a reward when the agent copies blocks from a given structure.
+
+                NOTE: This will do nothing unless you have set up a {{{ BuildBattleDecorator }}} on the server.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="RewardDensity" type="RewardDensityForBuildAndBreak"/>
+                <xs:element name="AddQuitProducer" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If present, the mission will end when the copy exactly matches the original. Set
+                            {{{ description }}} to the quit code you'd like to receive when this happens. (See
+                            {{{ RewardForMissionEnd }}}.)
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:complexType>
+                        <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+                    </xs:complexType>
+                </xs:element>
+            </xs:all>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+            <xs:attribute name="rewardScale" type="xs:decimal" use="optional" default="5.0">
+                <xs:annotation>
+                    <xs:documentation>
+                        This is the reward to be added or deducted for each block event.
+
+                        The rewards are calculated as follows:
+                        - For destroying a correct block: -1 * rewardScale
+                        - For destroying an incorrect block: +1 * rewardScale
+                        - For placing a correct block: +1 * rewardScale
+                        - For placing an incorrect block: -1 * rewardScale
+
+                        If {{{ RewardDensity }}} is set to {{{ {PER_BLOCK }}}, this reward will arrive after each block
+                        transaction. If it's set to {{{ MISSION_END }}}, the individual rewards will not be sent, but
+                        will
+                        be summed throughout the course of the mission, and the resulting total will be sent as the
+                        final reward.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="rewardDistribution" type="xs:string" use="optional" default=""/>
+            <xs:attribute name="rewardForCompletion" type="xs:decimal" use="optional" default="200.0">
+                <xs:annotation>
+                    <xs:documentation>
+                        This is the extra reward to be added when the copy exactly matches the original. (Will only be
+                        applied once.)
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="RewardDensityForTimeTaken">
+        <xs:annotation>
+            <xs:documentation>
+                Reward type for {{{ RewardForTimeTaken }}}:
+
+                - {{{ PER_TICK }}} - only the reward delta will be sent as a reward, at each tick. The initial reward
+                will
+                be ignored.
+
+                - {{{ PER_TICK_ACCUMULATED }}} - the initial reward will be adjusted by the delta at each tick, and the
+                current total sent. Eg: if the initial reward is 1000 and the delta is -1, the agent will receive
+                rewards of 1000,999,998,997 for the first four ticks.
+
+                - {{{ MISSION_END }}} - the initial reward will be adjusted by the delta at each tick, but no reward
+                will
+                be sent until the mission ends. Eg: if the initial reward is 1000, the delta is -1, and the mission runs
+                for 800 ticks, then final reward sent will be 200.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="PER_TICK"/>
+            <xs:enumeration value="PER_TICK_ACCUMULATED"/>
+            <xs:enumeration value="MISSION_END"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:element name="RewardForTimeTaken">
+        <xs:annotation>
+            <xs:documentation>
+                Reward that is dependent on time. Can be received per tick, or just once at the end.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+            <xs:attribute name="initialReward" type="xs:decimal" use="required"/>
+            <xs:attribute name="delta" type="xs:decimal" use="required"/>
+            <xs:attribute name="density" type="RewardDensityForTimeTaken" use="required"/>
+            <xs:attribute name="rewardDistribution" type="xs:string" use="optional" default=""/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="RewardDensityForDistanceTraveledToCompassTarget">
+        <xs:annotation>
+            <xs:documentation>
+                Reward type for {{{ RewardForDistanceTraveledToCompassTarget }}}:
+
+                <xs:simpleType name="Command">
+                    <xs:union
+                            memberTypes="ContinuousMovementCommand AbsoluteMovementCommand DiscreteMovementCommand InventoryCommand ChatCommand SimpleCraftCommand NearbyCraftCommand NearbySmeltCommand MissionQuitCommand HumanLevelCommand"/>
+                </xs:simpleType>
+
+                - {{{ PER_TICK_ACCUMULATED }}} - the reward will be adjusted by the delta at each tick, and the current
+                total sent. E.G. If the agent moves forward to the target and then back to start the reward will be 0
+
+                - {{{ MISSION_END }}} - the reward will be adjusted by the delta at each tick, but no reward will be
+                sent
+                until the mission ends. Eg: if the initial reward is 1000, the delta is -1, and the mission runs for 800
+                ticks, then final reward sent will be 200.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="PER_TICK"/>
+            <xs:enumeration value="PER_TICK_ACCUMULATED"/>
+            <xs:enumeration value="MISSION_END"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:element name="RewardForDistanceTraveledToCompassTarget">
+        <xs:annotation>
+            <xs:documentation>
+                Reward that is dependent on distance to compass target. Can be received per tick, or just once at the
+                end.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+            <xs:attribute name="rewardPerBlock" type="xs:decimal" use="required"/>
+            <xs:attribute name="density" type="RewardDensityForDistanceTraveledToCompassTarget" use="required"/>
+            <xs:attribute name="rewardDistribution" type="xs:string" use="optional" default=""/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="MobWithDescriptionAndReward">
+        <xs:complexContent>
+            <xs:extension base="MobWithDescription">
+                <xs:attribute name="reward" use="required" type="xs:decimal"/>
+                <xs:attribute name="distribution" type="xs:string" use="optional" default=""/>
+                <xs:attribute name="oneshot" type="xs:boolean" use="optional" default="true"/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:element name="RewardForCatchingMob">
+        <xs:annotation>
+            <xs:documentation>
+                Reward for cornering a mob, such that it cannot move from its current square without passing through an
+                agent.
+                If {{{ global }}} is true then the agent doesn't have to be involved in catching the mob; otherwise they
+                must be adjacent to the mob.
+                For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north,
+                south, east or west of them for them to move into. (An air block is considered occupied if there is an
+                agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in
+                which mobs can escape by jumping or passing through agents.
+                If {{{ oneshot }}} is true, the reward will only be counted once per entity.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Mob" type="MobWithDescriptionAndReward"/>
+            </xs:choice>
+            <xs:attributeGroup ref="RewardProducerAttributes"/>
+        </xs:complexType>
+    </xs:element>
+
+    <!--============================================== QUIT PRODUCERS ===============================================-->
+
+    <xs:complexType name="PointWithToleranceAndDescription">
+        <xs:complexContent>
+            <xs:extension base="Pos">
+                <xs:attribute name="tolerance" type="xs:decimal" use="optional" default="0.5"/>
+                <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="BlockSpecWithDescription">
+        <xs:complexContent>
+            <xs:extension base="BlockSpec">
+                <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:complexType name="BlockOrItemSpecWithDescription">
+        <xs:complexContent>
+            <xs:extension base="BlockOrItemSpec">
+                <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+            </xs:extension>
+        </xs:complexContent>
+    </xs:complexType>
+
+    <xs:element name="AgentQuitFromReachingPosition">
+        <xs:annotation>
+            <xs:documentation>
+                When this is included the agent's mission will end when they reach a specified position.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Marker" type="PointWithToleranceAndDescription"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentQuitFromTimeUp">
+        <xs:annotation>
+            <xs:documentation>
+                When this is included the agent's mission will end when a certain amount of time has elapsed.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="timeLimitMs" type="xs:decimal" use="required"/>
+            <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentQuitFromTouchingBlockType">
+        <xs:annotation>
+            <xs:documentation>
+                When this is included the agent's mission will end when they come in contact with a specified block
+                type.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Block" type="BlockSpecWithDescription"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentQuitFromCollectingItem">
+        <xs:annotation>
+            <xs:documentation>
+                When this is included the agent's mission will end when they collect (or craft) a specified item.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithDescription"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentQuitFromCollectingItemQuantity">
+        <xs:annotation>
+            <xs:documentation>
+                When this is included the agent's mission will end when they collect (or craft) a specified number of
+                the item.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithDescription"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentQuitFromCraftingItem">
+        <xs:annotation>
+            <xs:documentation>
+                When this is included the agent's mission will end when they craft a specified item.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithDescription"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentQuitFromSmeltingItem">
+        <xs:annotation>
+            <xs:documentation>
+                When this is included the agent's mission will end when they smelt a specified item.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithDescription"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentQuitFromPossessingItem">
+        <xs:annotation>
+            <xs:documentation>
+                When this is included the agent's mission will end when they possess the specified item in their
+                inventory all at once.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Item" type="BlockOrItemSpecWithDescription"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="MobList">
+        <xs:list itemType="EntityTypes"/>
+    </xs:simpleType>
+
+    <xs:complexType name="MobWithDescription">
+        <xs:attribute name="type" type="MobList" use="required"/>
+        <xs:attribute name="description" type="xs:string" use="optional" default=""/>
+        <xs:attribute name="global" type="xs:boolean" use="optional" default="false"/>
+    </xs:complexType>
+
+    <xs:element name="AgentQuitFromCatchingMob">
+        <xs:annotation>
+            <xs:documentation>
+                Agent's mission will end when they corner a mob. If {{{ global }}} is true then the agent doesn't have
+                to
+                be involved in catching the mob; otherwise they must be adjacent to the mob.
+                For our purposes, a mob is deemed "caught" if there are no unoccupied air blocks immediately north,
+                south, east or west of them for them to move into. (An air block is considered occupied if there is an
+                agent standing in it.) This does not necessarily correspond to Minecraft's definition of caught, in
+                which mobs can escape by jumping or passing through agents.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="1" maxOccurs="unbounded">
+                <xs:element name="Mob" type="MobWithDescription"/>
+            </xs:choice>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:complexType name="CommandQuota">
+        <xs:annotation>
+            <xs:documentation>
+                Set up a quota for a group of commands. {{{ AgentQuitFromReachingCommandQuota }}} will fire once the
+                quota
+                is exceeded.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="commands" use="required" type="CommandList">
+            <xs:annotation>
+                <xs:documentation>
+                    List of commands, separated by spaces, that will share this quota. Commands must be valid members of
+                    {{{ ContinuousMovementCommand }}}, {{{ AbsoluteMovementCommand }}}, {{{ DiscreteMovementCommand }}},
+                    {{{ InventoryCommand }}}, or {{{ ChatCommand }}}.
+
+                    For instance, if the command list contains {{{ moveeast }}}, {{{ movenorth }}}, {{{ movesouth }}}
+                    and
+                    {{{ movewest }}}, then the mission will end once the summed total usage of all four commands reaches
+                    the quota - even if the agent never used {{{ movesouth }}}.
+                </xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="quota" use="required" type="xs:int">
+            <xs:annotation>
+                <xs:documentation>
+                    Total number of usages allocated for this command group.
+                </xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="description" type="xs:string" use="optional" default="">
+            <xs:annotation>
+                <xs:documentation>
+                    String that will be returned from the {{{ AgentQuitFromReachingCommandQuota }}} if this quota is
+                    reached. This can be used in {{{ RewardForMissionEnd }}}, and is also returned in the
+                    {{{ MissionEnded }}} message.
+                </xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+
+    <xs:element name="AgentQuitFromReachingCommandQuota">
+        <xs:annotation>
+            <xs:documentation>
+                Count the commands acted on by the Mod, and signal the end of the mission when the defined quota of
+                commands has been reached.
+
+                A total number of commands can be specified, and/or groups of commands can be given their own quota.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:choice minOccurs="0" maxOccurs="unbounded">
+                <xs:element name="Quota" type="CommandQuota"/>
+            </xs:choice>
+            <xs:attribute name="total" type="xs:int" use="optional">
+                <xs:annotation>
+                    <xs:documentation>
+                        Total number of commands allowed before the mission ends. (Note that a command must be acted on
+                        to be counted - sending malformed commands won't affect the total.)
+
+                        The check for total command use takes precedence over the individual group quotas, if used.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="description" type="xs:string" use="optional" default="">
+                <xs:annotation>
+                    <xs:documentation>
+                        String that will be returned from the {{{ AgentQuitFromReachingCommandQuota }}} if the total
+                        allowed command usage is reached. This can be used in {{{ RewardForMissionEnd }}}, and is also
+                        returned in the {{{ MissionEnded }}} message.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
+        </xs:complexType>
+    </xs:element>
+
+</xs:schema>
diff -u1 -rN MCP-Reborn-vanilla/src/main/resources/MissionInit.xsd MCP-Reborn/src/main/resources/MissionInit.xsd
--- MCP-Reborn-vanilla/src/main/resources/MissionInit.xsd	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/resources/MissionInit.xsd	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="http://ProjectMalmo.microsoft.com"
+           xmlns="http://ProjectMalmo.microsoft.com"
+           elementFormDefault="qualified"
+           version="0.37">
+
+    <xs:include schemaLocation="Mission.xsd"/>
+
+    <xs:element name="MissionInit">
+        <xs:annotation>
+            <xs:documentation>
+                A MissionInit message tells the agent and client to talk to each other to complete the supplied mission.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element ref="Mission"/>
+                <xs:element name="ExperimentUID" type="xs:string">
+                    <xs:annotation>
+                        <xs:documentation>
+                            A unique ID to disambiguate experiments.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element ref="MinecraftServerConnection" minOccurs="0" maxOccurs="1"/>
+                <xs:element name="ClientRole" type="xs:int">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The role this client will play in a multi-agent mission.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element ref="ClientAgentConnection"/>
+            </xs:sequence>
+            <xs:attribute name="SchemaVersion" type="xs:token"/>
+            <xs:attribute name="PlatformVersion" type="xs:token"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="MinecraftServerConnection">
+        <xs:annotation>
+            <xs:documentation>
+                The MinecraftServerConnection is used to tell other clients where to find their server.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:attribute name="address" use="required" type="xs:string"/>
+            <xs:attribute name="port" use="required" type="xs:int"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ClientAgentConnection">
+        <xs:annotation>
+            <xs:documentation>
+                A ClientAgentConnection tells the client and the agent the IP address and ports they should talk to each
+                other on.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="ClientIPAddress" type="xs:string"/>
+                <xs:element name="ClientMissionControlPort" type="xs:int"/>
+                <xs:element name="ClientCommandsPort" type="xs:int"/>
+                <xs:element name="AgentIPAddress" type="xs:string"/>
+                <xs:element name="AgentMissionControlPort" type="xs:int"/>
+                <xs:element name="AgentVideoPort" type="xs:int"/>
+                <xs:element name="AgentDepthPort" type="xs:int"/>
+                <xs:element name="AgentLuminancePort" type="xs:int"/>
+                <xs:element name="AgentObservationsPort" type="xs:int"/>
+                <xs:element name="AgentRewardsPort" type="xs:int"/>
+                <xs:element name="AgentColourMapPort" type="xs:int"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+</xs:schema>
diff -u1 -rN MCP-Reborn-vanilla/src/main/resources/Mission.xsd MCP-Reborn/src/main/resources/Mission.xsd
--- MCP-Reborn-vanilla/src/main/resources/Mission.xsd	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/resources/Mission.xsd	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,702 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="http://ProjectMalmo.microsoft.com"
+           xmlns="http://ProjectMalmo.microsoft.com"
+           xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
+           elementFormDefault="qualified"
+           jaxb:version="2.1"
+           version="0.37">
+
+    <xs:include schemaLocation="Types.xsd"/>
+    <xs:include schemaLocation="MissionHandlers.xsd"/>
+
+    <xs:element name="Mission">
+        <xs:annotation>
+            <xs:documentation>
+                A mission definition has a description, a starting point and some kind of scoring system. It also
+                defines the agents and roles involved.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element ref="About"/>
+                <xs:element ref="ModSettings" minOccurs="0" maxOccurs="1"/>
+                <xs:element ref="ServerSection" minOccurs="1" maxOccurs="1"/>
+                <xs:element ref="AgentSection" minOccurs="1" maxOccurs="unbounded"/>
+            </xs:sequence>
+            <xs:attribute name="SchemaVersion" type="xs:token"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ModSettings">
+        <xs:annotation>
+            <xs:documentation>
+                Settings here are independent of the mission but affect how it is run.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="MsPerTick" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Use this to overclock Minecraft - to make Minecraft time run faster than normal.
+
+                            This sets the delay that Minecraft uses between world ticks, in milliseconds. The default is
+                            50ms per tick (20Hz).
+                            Double-speed would be 25ms/tick (40Hz). For best results, stick to reasonable values (that
+                            the server
+                            might be able to cope with), which also result in a whole number of ticks per second.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:restriction base="xs:int">
+                            <xs:minInclusive value="1"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="FrameSkip" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Use this to overclock syncronous mode Minecraft - to make Minecraft run more than one tick
+                            per env step.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:restriction base="xs:int">
+                            <xs:minInclusive value="1"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:element>
+                <xs:element name="PrioritiseOffscreenRendering" type="xs:boolean" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If set to true, the Minecraft window will only be updated once per second during the run of
+                            the mission. This will allow the
+                            render pipeline to run much faster, resulting in the platform receiving frames at a higher
+                            rate.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ServerSection">
+        <xs:annotation>
+            <xs:documentation>
+                The Minecraft server stores the world and keeps track of the blocks that have changed. Each Minecraft
+                client connects to the server when it needs to
+                change blocks or ask what blocks are present, for example. With multi-agent missions there may be many
+                clients, all connected to a single server.
+
+                Settings here affect the shared world that the clients experience.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name='HumanInteraction' minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:all>
+                            <xs:element name="Port" type='xs:string' minOccurs="1" maxOccurs="1"/>
+                            <xs:element name="MaxPlayers" type="xs:int" minOccurs="1" maxOccurs="1" default="1"/>
+                        </xs:all>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element ref="ServerInitialConditions" minOccurs="0" maxOccurs="1"/>
+                <xs:element ref="ServerHandlers"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentSection">
+        <xs:annotation>
+            <xs:documentation>
+                Each agent is specified in one of these sections, detailing the way they experience the world and the
+                actions they can perform to interact with it.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Name" type="xs:string"/>
+                <xs:element ref="AgentStart"/>
+                <xs:element ref="AgentHandlers"/>
+            </xs:sequence>
+            <xs:attribute name="mode" use="optional" type="GameMode" default="Survival"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="About">
+        <xs:annotation>
+            <xs:documentation>
+                Description of the mission.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="Summary" type="xs:string"/>
+                <xs:element name="Description" type="xs:string" minOccurs="0"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="AgentStart">
+        <xs:annotation>
+            <xs:documentation>
+                Defines the initial conditions of the player. This includes the location and the contents of their
+                inventory.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element name="BreakSpeedMultiplier" type="xs:float" minOccurs="0" maxOccurs="1" default="1.0">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Multiply the agent break speed by this amount (greater is faster).
+                            TODO: should this really be in the AgentStart?
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="PreferredSpawnBiome" type="xs:string" minOccurs="0" maxOccurs="1">
+                   <xs:annotation>
+                        <xs:documentation>
+                            Preferred spawn biome category for the agent. World generation is not affected by this
+                            option, only starting location of the agent. As such, incompatible with Placement.
+                            Spawning in this biome is not guaranteed because a) Minecraft will search in 1024 radius circle
+                            for this biome, and place agent in a default biome if the requested biome is not found
+                            b) there is certain randomness in the agent placement, so even if the biome is found, agent may
+                            be placed near, but not in the requested biome.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="SpawnInVillage" type="xs:boolean" default="false"  minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If true, try to spawn the agent to a village.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="DoneOnDeath" type="xs:boolean" default="false"  minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If true, environment will return done=True when agent dies.
+                            (Note: this is in AgentStart as quit section is not supported by current Java code)
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="Placement" type="PosAndDirection" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The player's starting location; if unspecified, will be the game's randomly-chosen spawn
+                            point.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="Velocity" type="Pos" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            The player's starting velocity; if unspecified, will be 0
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="NearPlayer" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:all>
+                            <xs:element name="Name" type="xs:string" minOccurs="1" maxOccurs="1">
+                                <xs:annotation>
+                                    <xs:documentation>
+                                        Start the player next to player with this name
+                                    </xs:documentation>
+                                </xs:annotation>
+                            </xs:element>
+                            <xs:element name="MinDistance" type="xs:double">
+                                <xs:annotation>
+                                    <xs:documentation>
+                                        Min horizontal distance to the anchor player
+                                    </xs:documentation>
+                                </xs:annotation>
+                            </xs:element>
+                            <xs:element name="MaxDistance" type="xs:double">
+                                <xs:annotation>
+                                    <xs:documentation>
+                                        Max horizontal distance to the anchor player
+                                    </xs:documentation>
+                                </xs:annotation>
+                            </xs:element>
+                            <xs:element name="MaxVertDistance" type="xs:double">
+                                <xs:annotation>
+                                    <xs:documentation>
+                                        Max vertical distance to the anchor player
+                                    </xs:documentation>
+                                </xs:annotation>
+                            </xs:element>
+                             <xs:element name="LookingAt" type="xs:boolean">
+                                <xs:annotation>
+                                    <xs:documentation>
+                                        If true, orient this player such to be looking at the anchor player.
+                                        Does not always guarantee visibility if there are obstacles in the way
+                                    </xs:documentation>
+                                </xs:annotation>
+                            </xs:element>
+                        </xs:all>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="Inventory" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:choice minOccurs="0"
+                                   maxOccurs="unbounded">    <!-- minOccurs = 0 to allow an empty Inventory -->
+                            <xs:element ref="InventoryObject"/>
+                        </xs:choice>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="EnderBoxInventory" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:choice minOccurs="0"
+                                   maxOccurs="unbounded">    <!-- minOccurs = 0 to allow an empty Inventory -->
+                            <xs:element ref="InventoryObject"/>
+                        </xs:choice>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="StartingHealth" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:attribute name="health" type="xs:float" use="optional"/>
+                        <xs:attribute name="maxHealth" type="xs:float" default="20" />
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="StartingFood" minOccurs="0" maxOccurs="1">
+                    <xs:complexType>
+                        <xs:attribute name="food" type="xs:int" default="20" />
+                        <xs:attribute name="foodSaturation" type="xs:float" use="optional" />
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="LowLevelInputs" type="xs:boolean" default="false" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If set to true, inputs to malmo are disabled and instead given via a virtual keyboard and mouse (instead of higher level commands)
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="GuiScale" type="xs:float" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Overrides default gui scale
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="GammaSetting" type="xs:float" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Overrides gamma setting (brightness)
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="FOVSetting" type="xs:float" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Overrides field of view setting
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="LoadWorldFile" type="xs:string" default="false" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If provided, load the world from this file
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="WorldSeed" type="xs:long" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If provided, use this seed for world generation. Ignored if the world is loaded from a save file
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="BonusChest" type="xs:boolean" default="false"  minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Whether bonus chest is present near agent starting point in the generated world. Ignored if the world is loaded from a save file
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="GenerateFeatures" type="xs:boolean" default="true"  minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Whether world features are generated. Ignored if the world is loaded from file.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="EnableRecorder" type="xs:boolean" default="false" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Whether minerec should record video and the actions taken in the environment
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="FakeCursorSize" type="xs:int" default="0" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Size of the fake mouse cursor (in pixels) to be shown in gui. Set to 0 to disable the fake
+                            mouse cursor. When the value is below 16, cursor sprite is cropped
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="MultiplayerUsername" type="xs:string" default="0" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Username to use when playing online
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ServerInitialConditions">
+        <xs:annotation>
+            <xs:documentation>
+                Defines the initial conditions of the world.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element ref="Time" minOccurs="0" maxOccurs="1"/>
+                <xs:element ref="Weather" minOccurs="0" maxOccurs="1"/>
+                <xs:element name="RemoteServer" type="xs:string" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Address of a server to connect to
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+
+                <xs:element name="AllowSpawning" type="xs:boolean" minOccurs="0" maxOccurs="1" default="false">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Set this to true to allow mobs (animals and hostiles) to spawn during a mission.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+                <xs:element name="AllowedMobs" minOccurs="0" maxOccurs="1">
+                    <xs:annotation>
+                        <xs:documentation>
+                            If AllowSpawning is set to true, use this to specify a list of the allowed mobs. Only those
+                            mobs which are on the list will be allowed to spawn. If no list is specified, normal
+                            spawning behaviour will take place. Note that these settings *do* effect mob_spawner blocks.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:simpleType>
+                        <xs:list itemType="EntityTypes"/>
+                    </xs:simpleType>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="Time">
+        <xs:annotation>
+            <xs:documentation>
+                Minecraft has a day-night cycle that affects the light levels and the appearance of hostile mobs.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:all>
+                <xs:element ref="StartTime" minOccurs="0" maxOccurs="1"/>
+                <xs:element name="AllowPassageOfTime" type="xs:boolean" minOccurs="0" maxOccurs="1" default="true">
+                    <xs:annotation>
+                        <xs:documentation>
+                            Set this to false to stop the day-night cycle. The sun position and weather will remain
+                            fixed for the duration of the mission.
+                        </xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+            </xs:all>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="StartTime">
+        <xs:annotation>
+            <xs:documentation>
+                Specifies the time of day at the start of the mission, in thousandths of an hour.
+
+                0 = dawn
+
+                6000 = noon
+
+                18000 = midnight
+
+                Time in Minecraft travels (by default) 72 times faster than real-time. Thus a Minecraft day lasts 20
+                minutes.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:simpleType>
+            <xs:restriction base="xs:int">
+                <xs:minInclusive value="0"/>
+                <xs:maxInclusive value="23999"/>
+            </xs:restriction>
+        </xs:simpleType>
+    </xs:element>
+
+    <xs:element name="Weather" default="normal">
+        <xs:annotation>
+            <xs:documentation>
+                Specifies the type of weather at the start of the mission.
+
+                "normal" = let Minecraft do what it normally does
+
+                "clear" = start with clear weather
+
+                "rain" = start with rain (or snow, in a cold biome)
+
+                "thunder" = start with thundery weather
+            </xs:documentation>
+        </xs:annotation>
+        <xs:simpleType>
+            <xs:restriction base="xs:string">
+                <xs:enumeration value="normal"/>
+                <xs:enumeration value="clear"/>
+                <xs:enumeration value="rain"/>
+                <xs:enumeration value="thunder"/>
+            </xs:restriction>
+        </xs:simpleType>
+    </xs:element>
+
+    <xs:complexType name="InventoryObjectType">
+        <xs:attribute name="slot" use="required">
+            <xs:simpleType>
+                <xs:restriction base="xs:int">
+                    <xs:minInclusive value="0"/>
+                    <xs:maxInclusive value="39"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="type" use="required" type="BlockOrItem"/>
+        <xs:attribute name="variant" use="optional" type="Variation"/>
+        <xs:attribute name="colour" use="optional" type="Colour"/>
+        <xs:attribute name="quantity" default="1">
+            <xs:simpleType>
+                <xs:restriction base="xs:int">
+                    <xs:minInclusive value="1"/>
+                    <xs:maxInclusive value="64"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+
+    <xs:element name="InventoryObject" type="InventoryObjectType">
+        <xs:annotation>
+            <xs:documentation>
+                Defines an item or block in the inventory.
+            </xs:documentation>
+        </xs:annotation>
+    </xs:element>
+
+    <xs:element name="InventoryItem" substitutionGroup="InventoryObject">
+        <xs:annotation>
+            <xs:documentation>
+                Defines an item in the inventory - deprecated, use InventoryObject instead
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:complexContent>
+                <xs:extension base="InventoryObjectType"/>
+            </xs:complexContent>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="InventoryBlock" substitutionGroup="InventoryObject">
+        <xs:annotation>
+            <xs:documentation>
+                Defines a block in the inventory - deprecated, use InventoryObject instead
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:complexContent>
+                <xs:extension base="InventoryObjectType"/>
+            </xs:complexContent>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="GameMode">
+        <xs:annotation>
+            <xs:documentation>
+                In survival mode the player can be hurt and experience hunger and must collect blocks. In creative mode
+                they can fly and have an infinite supply of blocks.
+                In spectator mode the player cannot change anything and can fly through blocks.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="Survival"/>
+            <xs:enumeration value="Creative"/>
+            <xs:enumeration value="Adventure"/>
+            <xs:enumeration value="Spectator"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <!--=============================================================================================================-->
+    <!--=========================================== MISSION HANDLER TYPES ===========================================-->
+    <!--=============================================================================================================-->
+    <!--======= When new mission handlers are created, make sure they are added to the correct section below ======= -->
+    <!--=============================================================================================================-->
+
+    <xs:group name="AgentMissionHandlers">
+        <xs:annotation>
+            <xs:documentation>
+                Lists the available Mission Handlers for an agent. Each can only be used once.
+            </xs:documentation>
+            <xs:appinfo>
+                <jaxb:property name="AgentMissionHandlers"/>
+            </xs:appinfo>
+        </xs:annotation>
+        <xs:all>
+            <xs:element ref="ObservationFromAchievements" minOccurs="0"/>
+            <xs:element ref="ObservationFromChat" minOccurs="0"/>
+            <xs:element ref="ObservationFromCompass" minOccurs="0"/>
+            <xs:element ref="ObservationFromDiscreteCell" minOccurs="0"/>
+            <xs:element ref="ObservationFromDistance" minOccurs="0"/>
+            <xs:element ref="ObservationFromEquippedItem" minOccurs="0"/>
+            <xs:element ref="ObservationFromFullInventory" minOccurs="0"/>
+            <xs:element ref="ObservationFromFullStats" minOccurs="0"/>
+            <xs:element ref="ObservationFromGrid" minOccurs="0"/>
+            <xs:element ref="ObservationFromHotBar" minOccurs="0"/>
+            <xs:element ref="ObservationFromHuman" minOccurs="0"/>
+            <xs:element ref="ObservationFromNearbyEntities" minOccurs="0"/>
+            <xs:element ref="ObservationFromRay" minOccurs="0"/>
+            <xs:element ref="ObservationFromRecentCommands" minOccurs="0"/>
+            <xs:element ref="ObservationFromSubgoalPositionList" minOccurs="0"/>
+            <xs:element ref="ObservationFromSystem" minOccurs="0"/>
+            <xs:element ref="ObservationFromTurnScheduler" minOccurs="0"/>
+
+            <xs:element ref="VideoProducer" minOccurs="0"/>
+            <xs:element ref="DepthProducer" minOccurs="0"/>
+            <xs:element ref="LuminanceProducer" minOccurs="0"/>
+            <xs:element ref="ColourMapProducer" minOccurs="0"/>
+
+            <xs:element ref="FileBasedPerformanceProducer" minOccurs="0"/>
+
+            <xs:element ref="RewardForTouchingBlockType" minOccurs="0"/>
+            <xs:element ref="RewardForSendingCommand" minOccurs="0"/>
+            <xs:element ref="RewardForSendingMatchingChatMessage" minOccurs="0"/>
+            <xs:element ref="RewardForCollectingItem" minOccurs="0"/>
+            <xs:element ref="RewardForCollectingItemQuantity" minOccurs="0"/>
+            <xs:element ref="RewardForPossessingItem" minOccurs="0"/>
+            <xs:element ref="RewardForCraftingItem" minOccurs="0"/>
+            <xs:element ref="RewardForSmeltingItem" minOccurs="0"/>
+            <xs:element ref="RewardForDiscardingItem" minOccurs="0"/>
+            <xs:element ref="RewardForReachingPosition" minOccurs="0"/>
+            <xs:element ref="RewardForMissionEnd" minOccurs="0"/>
+            <xs:element ref="RewardForStructureCopying" minOccurs="0"/>
+            <xs:element ref="RewardForTimeTaken" minOccurs="0"/>
+            <xs:element ref="RewardForCatchingMob" minOccurs="0"/>
+            <xs:element ref="RewardForDamagingEntity" minOccurs="0"/>
+            <xs:element ref="RewardForDistanceTraveledToCompassTarget" minOccurs="0"/>
+
+            <xs:element ref="ContinuousMovementCommands" minOccurs="0"/>
+            <xs:element ref="AbsoluteMovementCommands" minOccurs="0"/>
+            <xs:element ref="DiscreteMovementCommands" minOccurs="0"/>
+            <xs:element ref="InventoryCommands" minOccurs="0"/>
+            <xs:element ref="ChatCommands" minOccurs="0"/>
+            <xs:element ref="SimpleCraftCommands" minOccurs="0"/>
+            <xs:element ref="NearbyCraftCommands" minOccurs="0"/>
+            <xs:element ref="NearbySmeltCommands" minOccurs="0"/>
+            <xs:element ref="PlaceCommands" minOccurs="0"/>
+            <xs:element ref="EquipCommands" minOccurs="0"/>
+            <xs:element ref="MissionQuitCommands" minOccurs="0"/>
+            <xs:element ref="TurnBasedCommands" minOccurs="0"/>
+            <xs:element ref="HumanLevelCommands" minOccurs="0"/>
+            <xs:element ref="PauseCommand" minOccurs="0"/>
+            <xs:element ref="CameraCommands" minOccurs="0"/>
+            <!-- When adding a new command handler, make sure to update MissionSpec::getListOfCommandHandlers and MissionSpec::getAllowedCommands, and add to TurnBasedApplicableCommandHandlers (below) if appropriate -->
+
+            <xs:element ref="AgentQuitFromTimeUp" minOccurs="0"/>
+            <xs:element ref="AgentQuitFromReachingPosition" minOccurs="0"/>
+            <xs:element ref="AgentQuitFromTouchingBlockType" minOccurs="0"/>
+            <xs:element ref="AgentQuitFromCollectingItem" minOccurs="0"/>
+            <xs:element ref="AgentQuitFromPossessingItem" minOccurs="0"/>
+            <xs:element ref="AgentQuitFromCraftingItem" minOccurs="0"/>
+            <xs:element ref="AgentQuitFromSmeltingItem" minOccurs="0"/>
+            <xs:element ref="AgentQuitFromReachingCommandQuota" minOccurs="0"/>
+            <xs:element ref="AgentQuitFromCatchingMob" minOccurs="0"/>
+        </xs:all>
+    </xs:group>
+
+    <xs:group name="TurnBasedApplicableCommandHandlers">
+        <xs:annotation>
+            <xs:documentation>
+                Lists the command handlers which are applicable to a turn-based scenario.
+            </xs:documentation>
+            <xs:appinfo>
+                <jaxb:property name="TurnBasedApplicableCommandHandlers"/>
+            </xs:appinfo>
+        </xs:annotation>
+        <xs:all>
+            <xs:element ref="AbsoluteMovementCommands" minOccurs="0"/>
+            <xs:element ref="DiscreteMovementCommands" minOccurs="0"/>
+            <xs:element ref="InventoryCommands" minOccurs="0"/>
+            <xs:element ref="ChatCommands" minOccurs="0"/>
+            <xs:element ref="SimpleCraftCommands" minOccurs="0"/>
+            <xs:element ref="MissionQuitCommands" minOccurs="0"/>
+        </xs:all>
+    </xs:group>
+
+    <xs:element name="AgentHandlers">
+        <xs:annotation>
+            <xs:documentation>
+                Defines the Mission Handlers block for an individual agent.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:group ref="AgentMissionHandlers"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ServerHandlers">
+        <xs:annotation>
+            <xs:documentation>
+                Defines the Mission Handlers block for the server.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:choice>
+                    <xs:annotation>
+                        <xs:appinfo>
+                            <jaxb:property name="WorldGenerator"/>
+                        </xs:appinfo>
+                    </xs:annotation>
+                    <xs:element ref="FlatWorldGenerator"/>
+                    <xs:element ref="FileWorldGenerator"/>
+                    <xs:element ref="DefaultWorldGenerator"/>
+                    <xs:element ref="BiomeGenerator"/>
+                </xs:choice>
+                <xs:choice minOccurs="0" maxOccurs="unbounded">
+                    <xs:annotation>
+                        <xs:appinfo>
+                            <jaxb:property name="WorldDecorators"/>
+                        </xs:appinfo>
+                    </xs:annotation>
+                    <xs:element ref="DrawingDecorator"/>
+                    <xs:element ref="AnimationDecorator"/>
+                    <xs:element ref="MazeDecorator"/>
+                    <xs:element ref="ClassroomDecorator"/>
+                    <xs:element ref="SnakeDecorator"/>
+                    <xs:element ref="MovingTargetDecorator"/>
+                    <xs:element ref="BuildBattleDecorator"/>
+                    <xs:element ref="MarkingDecorator"/>
+                    <xs:element ref="NavigationDecorator"/>
+                    <xs:element ref="RandomizedStartDecorator"/>
+                </xs:choice>
+                <xs:choice minOccurs="0" maxOccurs="unbounded">
+                    <xs:annotation>
+                        <xs:appinfo>
+                            <jaxb:property name="ServerQuitProducers"/>
+                        </xs:appinfo>
+                    </xs:annotation>
+                    <xs:element ref="ServerQuitFromTimeUp"/>
+                    <xs:element ref="ServerQuitWhenAnyAgentFinishes"/>
+                </xs:choice>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+</xs:schema>
diff -u1 -rN MCP-Reborn-vanilla/src/main/resources/schemas.index MCP-Reborn/src/main/resources/schemas.index
--- MCP-Reborn-vanilla/src/main/resources/schemas.index	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/resources/schemas.index	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,5 @@
+Types.xsd
+MissionInit.xsd
+MissionHandlers.xsd
+MissionEnded.xsd
+Mission.xsd
diff -u1 -rN MCP-Reborn-vanilla/src/main/resources/Types.xsd MCP-Reborn/src/main/resources/Types.xsd
--- MCP-Reborn-vanilla/src/main/resources/Types.xsd	1970-01-01 08:00:00.000000000 +0800
+++ MCP-Reborn/src/main/resources/Types.xsd	2024-03-10 18:29:57.641072860 +0800
@@ -0,0 +1,849 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="http://ProjectMalmo.microsoft.com"
+           xmlns="http://ProjectMalmo.microsoft.com"
+           xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
+           elementFormDefault="qualified"
+           jaxb:version="2.1"
+           version="0.37">
+    <xs:simpleType name="Colour">
+        <xs:annotation>
+            <xs:documentation>
+                The sixteen block colours allowed in the Minecraft world: {{{ WHITE }}}, {{{ ORANGE }}}, {{{ MAGENTA }}},
+                {{{ LIGHT_BLUE }}}, {{{ YELLOW }}}, {{{ LIME }}}, {{{ PINK }}},
+                {{{ GRAY }}}, {{{ SILVER }}}, {{{ CYAN }}}, {{{ PURPLE }}}, {{{ BLUE }}}, {{{ BROWN }}}, {{{ GREEN }}},
+                {{{ RED }}}, {{{ BLACK }}}.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="WHITE"/>
+            <xs:enumeration value="ORANGE"/>
+            <xs:enumeration value="MAGENTA"/>
+            <xs:enumeration value="LIGHT_BLUE"/>
+            <xs:enumeration value="YELLOW"/>
+            <xs:enumeration value="LIME"/>
+            <xs:enumeration value="PINK"/>
+            <xs:enumeration value="GRAY"/>
+            <xs:enumeration value="SILVER"/>
+            <xs:enumeration value="CYAN"/>
+            <xs:enumeration value="PURPLE"/>
+            <xs:enumeration value="BLUE"/>
+            <xs:enumeration value="BROWN"/>
+            <xs:enumeration value="GREEN"/>
+            <xs:enumeration value="RED"/>
+            <xs:enumeration value="BLACK"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="Facing">
+        <xs:annotation>
+            <xs:documentation>
+                Enum to specify block faces: {{{ DOWN }}}, {{{ UP }}}, {{{ NORTH }}}, {{{ SOUTH }}}, {{{ WEST }}},
+                {{{ EAST }}}, {{{ UP_X }}}, {{{ DOWN_X }}}, {{{ UP_Z }}}, {{{ DOWN_Z }}}.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="DOWN"/>
+            <xs:enumeration value="UP"/>
+            <xs:enumeration value="NORTH"/>
+            <xs:enumeration value="SOUTH"/>
+            <xs:enumeration value="WEST"/>
+            <xs:enumeration value="EAST"/>
+            <xs:enumeration value="UP_X"/>
+            <xs:enumeration value="DOWN_X"/>
+            <xs:enumeration value="UP_Z"/>
+            <xs:enumeration value="DOWN_Z"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="ItemType">
+        <xs:annotation>
+            <xs:documentation>
+                The types of item allowed in the Minecraft world.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="iron_shovel"/>
+            <xs:enumeration value="iron_pickaxe"/>
+            <xs:enumeration value="iron_axe"/>
+            <xs:enumeration value="flint_and_steel"/>
+            <xs:enumeration value="apple"/>
+            <xs:enumeration value="bow"/>
+            <xs:enumeration value="arrow"/>
+            <xs:enumeration value="coal"/>
+            <xs:enumeration value="diamond"/>
+            <xs:enumeration value="iron_ingot"/>
+            <xs:enumeration value="gold_ingot"/>
+            <xs:enumeration value="iron_sword"/>
+            <xs:enumeration value="wooden_sword"/>
+            <xs:enumeration value="wooden_shovel"/>
+            <xs:enumeration value="wooden_pickaxe"/>
+            <xs:enumeration value="wooden_axe"/>
+            <xs:enumeration value="stone_sword"/>
+            <xs:enumeration value="stone_shovel"/>
+            <xs:enumeration value="stone_pickaxe"/>
+            <xs:enumeration value="stone_axe"/>
+            <xs:enumeration value="diamond_sword"/>
+            <xs:enumeration value="diamond_shovel"/>
+            <xs:enumeration value="diamond_pickaxe"/>
+            <xs:enumeration value="diamond_axe"/>
+            <xs:enumeration value="stick"/>
+            <xs:enumeration value="bowl"/>
+            <xs:enumeration value="mushroom_stew"/>
+            <xs:enumeration value="golden_sword"/>
+            <xs:enumeration value="golden_shovel"/>
+            <xs:enumeration value="golden_pickaxe"/>
+            <xs:enumeration value="golden_axe"/>
+            <xs:enumeration value="string"/>
+            <xs:enumeration value="feather"/>
+            <xs:enumeration value="gunpowder"/>
+            <xs:enumeration value="wooden_hoe"/>
+            <xs:enumeration value="stone_hoe"/>
+            <xs:enumeration value="iron_hoe"/>
+            <xs:enumeration value="diamond_hoe"/>
+            <xs:enumeration value="golden_hoe"/>
+            <xs:enumeration value="wheat_seeds"/>
+            <xs:enumeration value="wheat"/>
+            <xs:enumeration value="bread"/>
+            <xs:enumeration value="leather_helmet"/>
+            <xs:enumeration value="leather_chestplate"/>
+            <xs:enumeration value="leather_leggings"/>
+            <xs:enumeration value="leather_boots"/>
+            <xs:enumeration value="chainmail_helmet"/>
+            <xs:enumeration value="chainmail_chestplate"/>
+            <xs:enumeration value="chainmail_leggings"/>
+            <xs:enumeration value="chainmail_boots"/>
+            <xs:enumeration value="iron_helmet"/>
+            <xs:enumeration value="iron_chestplate"/>
+            <xs:enumeration value="iron_leggings"/>
+            <xs:enumeration value="iron_boots"/>
+            <xs:enumeration value="diamond_helmet"/>
+            <xs:enumeration value="diamond_chestplate"/>
+            <xs:enumeration value="diamond_leggings"/>
+            <xs:enumeration value="diamond_boots"/>
+            <xs:enumeration value="golden_helmet"/>
+            <xs:enumeration value="golden_chestplate"/>
+            <xs:enumeration value="golden_leggings"/>
+            <xs:enumeration value="golden_boots"/>
+            <xs:enumeration value="flint"/>
+            <xs:enumeration value="porkchop"/>
+            <xs:enumeration value="cooked_porkchop"/>
+            <xs:enumeration value="painting"/>
+            <xs:enumeration value="golden_apple"/>
+            <xs:enumeration value="sign"/>
+            <xs:enumeration value="wooden_door"/>
+            <xs:enumeration value="bucket"/>
+            <xs:enumeration value="bucket"/>
+            <xs:enumeration value="water_bucket"/>
+            <xs:enumeration value="lava_bucket"/>
+            <xs:enumeration value="minecart"/>
+            <xs:enumeration value="saddle"/>
+            <xs:enumeration value="iron_door"/>
+            <xs:enumeration value="redstone"/>
+            <xs:enumeration value="snowball"/>
+            <xs:enumeration value="boat"/>
+            <xs:enumeration value="leather"/>
+            <xs:enumeration value="milk_bucket"/>
+            <xs:enumeration value="brick"/>
+            <xs:enumeration value="clay_ball"/>
+            <xs:enumeration value="reeds"/>
+            <xs:enumeration value="paper"/>
+            <xs:enumeration value="book"/>
+            <xs:enumeration value="slime_ball"/>
+            <xs:enumeration value="chest_minecart"/>
+            <xs:enumeration value="furnace_minecart"/>
+            <xs:enumeration value="egg"/>
+            <xs:enumeration value="compass"/>
+            <xs:enumeration value="fishing_rod"/>
+            <xs:enumeration value="clock"/>
+            <xs:enumeration value="glowstone_dust"/>
+            <xs:enumeration value="fish"/>
+            <xs:enumeration value="cooked_fish"/>
+            <xs:enumeration value="dye"/>
+            <xs:enumeration value="bone"/>
+            <xs:enumeration value="sugar"/>
+            <xs:enumeration value="cake"/>
+            <xs:enumeration value="bed"/>
+            <xs:enumeration value="repeater"/>
+            <xs:enumeration value="cookie"/>
+            <xs:enumeration value="filled_map"/>
+            <xs:enumeration value="shears"/>
+            <xs:enumeration value="melon"/>
+            <xs:enumeration value="pumpkin_seeds"/>
+            <xs:enumeration value="melon_seeds"/>
+            <xs:enumeration value="beef"/>
+            <xs:enumeration value="cooked_beef"/>
+            <xs:enumeration value="chicken"/>
+            <xs:enumeration value="cooked_chicken"/>
+            <xs:enumeration value="rotten_flesh"/>
+            <xs:enumeration value="ender_pearl"/>
+            <xs:enumeration value="blaze_rod"/>
+            <xs:enumeration value="ghast_tear"/>
+            <xs:enumeration value="gold_nugget"/>
+            <xs:enumeration value="nether_wart"/>
+            <xs:enumeration value="potion"/>
+            <xs:enumeration value="glass_bottle"/>
+            <xs:enumeration value="spider_eye"/>
+            <xs:enumeration value="fermented_spider_eye"/>
+            <xs:enumeration value="blaze_powder"/>
+            <xs:enumeration value="magma_cream"/>
+            <xs:enumeration value="brewing_stand"/>
+            <xs:enumeration value="cauldron"/>
+            <xs:enumeration value="ender_eye"/>
+            <xs:enumeration value="speckled_melon"/>
+            <xs:enumeration value="spawn_egg"/>
+            <xs:enumeration value="experience_bottle"/>
+            <xs:enumeration value="fire_charge"/>
+            <xs:enumeration value="writable_book"/>
+            <xs:enumeration value="written_book"/>
+            <xs:enumeration value="emerald"/>
+            <xs:enumeration value="item_frame"/>
+            <xs:enumeration value="flower_pot"/>
+            <xs:enumeration value="carrot"/>
+            <xs:enumeration value="potato"/>
+            <xs:enumeration value="baked_potato"/>
+            <xs:enumeration value="poisonous_potato"/>
+            <xs:enumeration value="map"/>
+            <xs:enumeration value="golden_carrot"/>
+            <xs:enumeration value="skull"/>
+            <xs:enumeration value="carrot_on_a_stick"/>
+            <xs:enumeration value="nether_star"/>
+            <xs:enumeration value="pumpkin_pie"/>
+            <xs:enumeration value="fireworks"/>
+            <xs:enumeration value="firework_charge"/>
+            <xs:enumeration value="enchanted_book"/>
+            <xs:enumeration value="comparator"/>
+            <xs:enumeration value="netherbrick"/>
+            <xs:enumeration value="quartz"/>
+            <xs:enumeration value="tnt_minecart"/>
+            <xs:enumeration value="hopper_minecart"/>
+            <xs:enumeration value="prismarine_shard"/>
+            <xs:enumeration value="prismarine_crystals"/>
+            <xs:enumeration value="rabbit"/>
+            <xs:enumeration value="cooked_rabbit"/>
+            <xs:enumeration value="rabbit_stew"/>
+            <xs:enumeration value="rabbit_foot"/>
+            <xs:enumeration value="rabbit_hide"/>
+            <xs:enumeration value="armor_stand"/>
+            <xs:enumeration value="iron_horse_armor"/>
+            <xs:enumeration value="golden_horse_armor"/>
+            <xs:enumeration value="diamond_horse_armor"/>
+            <xs:enumeration value="lead"/>
+            <xs:enumeration value="name_tag"/>
+            <xs:enumeration value="command_block_minecart"/>
+            <xs:enumeration value="mutton"/>
+            <xs:enumeration value="cooked_mutton"/>
+            <xs:enumeration value="banner"/>
+            <xs:enumeration value="spruce_door"/>
+            <xs:enumeration value="birch_door"/>
+            <xs:enumeration value="jungle_door"/>
+            <xs:enumeration value="acacia_door"/>
+            <xs:enumeration value="dark_oak_door"/>
+            <xs:enumeration value="chorus_fruit"/>
+            <xs:enumeration value="chorus_fruit_popped"/>
+            <xs:enumeration value="beetroot"/>
+            <xs:enumeration value="beetroot_seeds"/>
+            <xs:enumeration value="beetroot_soup"/>
+            <xs:enumeration value="dragon_breath"/>
+            <xs:enumeration value="splash_potion"/>
+            <xs:enumeration value="spectral_arrow"/>
+            <xs:enumeration value="tipped_arrow"/>
+            <xs:enumeration value="lingering_potion"/>
+            <xs:enumeration value="shield"/>
+            <xs:enumeration value="elytra"/>
+            <xs:enumeration value="spruce_boat"/>
+            <xs:enumeration value="birch_boat"/>
+            <xs:enumeration value="jungle_boat"/>
+            <xs:enumeration value="acacia_boat"/>
+            <xs:enumeration value="dark_oak_boat"/>
+            <xs:enumeration value="totem_of_undying"/>
+            <xs:enumeration value="shulker_shell"/>
+            <xs:enumeration value="iron_nugget"/>
+            <xs:enumeration value="record_13"/>
+            <xs:enumeration value="record_cat"/>
+            <xs:enumeration value="record_blocks"/>
+            <xs:enumeration value="record_chirp"/>
+            <xs:enumeration value="record_far"/>
+            <xs:enumeration value="record_mall"/>
+            <xs:enumeration value="record_mellohi"/>
+            <xs:enumeration value="record_stal"/>
+            <xs:enumeration value="record_strad"/>
+            <xs:enumeration value="record_ward"/>
+            <xs:enumeration value="record_11"/>
+            <xs:enumeration value="record_wait"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="BlockType">
+        <xs:annotation>
+            <xs:documentation>
+                The block types allowed in the Minecraft world.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="air"/>
+            <xs:enumeration value="stone"/>
+            <xs:enumeration value="grass"/>
+            <xs:enumeration value="dirt"/>
+            <xs:enumeration value="cobblestone"/>
+            <xs:enumeration value="planks"/>
+            <xs:enumeration value="sapling"/>
+            <xs:enumeration value="bedrock"/>
+            <xs:enumeration value="flowing_water"/>
+            <xs:enumeration value="water"/>
+            <xs:enumeration value="flowing_lava"/>
+            <xs:enumeration value="lava"/>
+            <xs:enumeration value="sand"/>
+            <xs:enumeration value="gravel"/>
+            <xs:enumeration value="gold_ore"/>
+            <xs:enumeration value="iron_ore"/>
+            <xs:enumeration value="coal_ore"/>
+            <xs:enumeration value="log"/>
+            <xs:enumeration value="leaves"/>
+            <xs:enumeration value="sponge"/>
+            <xs:enumeration value="glass"/>
+            <xs:enumeration value="lapis_ore"/>
+            <xs:enumeration value="lapis_block"/>
+            <xs:enumeration value="dispenser"/>
+            <xs:enumeration value="sandstone"/>
+            <xs:enumeration value="noteblock"/>
+            <xs:enumeration value="bed"/>
+            <xs:enumeration value="golden_rail"/>
+            <xs:enumeration value="detector_rail"/>
+            <xs:enumeration value="sticky_piston"/>
+            <xs:enumeration value="web"/>
+            <xs:enumeration value="tallgrass"/>
+            <xs:enumeration value="deadbush"/>
+            <xs:enumeration value="piston"/>
+            <xs:enumeration value="piston_head"/>
+            <xs:enumeration value="wool"/>
+            <xs:enumeration value="piston_extension"/>
+            <xs:enumeration value="yellow_flower"/>
+            <xs:enumeration value="red_flower"/>
+            <xs:enumeration value="brown_mushroom"/>
+            <xs:enumeration value="red_mushroom"/>
+            <xs:enumeration value="gold_block"/>
+            <xs:enumeration value="iron_block"/>
+            <xs:enumeration value="double_stone_slab"/>
+            <xs:enumeration value="stone_slab"/>
+            <xs:enumeration value="brick_block"/>
+            <xs:enumeration value="tnt"/>
+            <xs:enumeration value="bookshelf"/>
+            <xs:enumeration value="mossy_cobblestone"/>
+            <xs:enumeration value="obsidian"/>
+            <xs:enumeration value="torch"/>
+            <xs:enumeration value="fire"/>
+            <xs:enumeration value="mob_spawner"/>
+            <xs:enumeration value="oak_stairs"/>
+            <xs:enumeration value="chest"/>
+            <xs:enumeration value="redstone_wire"/>
+            <xs:enumeration value="diamond_ore"/>
+            <xs:enumeration value="diamond_block"/>
+            <xs:enumeration value="crafting_table"/>
+            <xs:enumeration value="wheat"/>
+            <xs:enumeration value="farmland"/>
+            <xs:enumeration value="furnace"/>
+            <xs:enumeration value="lit_furnace"/>
+            <xs:enumeration value="standing_sign"/>
+            <xs:enumeration value="wooden_door"/>
+            <xs:enumeration value="ladder"/>
+            <xs:enumeration value="rail"/>
+            <xs:enumeration value="stone_stairs"/>
+            <xs:enumeration value="wall_sign"/>
+            <xs:enumeration value="lever"/>
+            <xs:enumeration value="stone_pressure_plate"/>
+            <xs:enumeration value="iron_door"/>
+            <xs:enumeration value="wooden_pressure_plate"/>
+            <xs:enumeration value="redstone_ore"/>
+            <xs:enumeration value="lit_redstone_ore"/>
+            <xs:enumeration value="unlit_redstone_torch"/>
+            <xs:enumeration value="redstone_torch"/>
+            <xs:enumeration value="stone_button"/>
+            <xs:enumeration value="snow_layer"/>
+            <xs:enumeration value="ice"/>
+            <xs:enumeration value="snow"/>
+            <xs:enumeration value="cactus"/>
+            <xs:enumeration value="clay"/>
+            <xs:enumeration value="reeds"/>
+            <xs:enumeration value="jukebox"/>
+            <xs:enumeration value="fence"/>
+            <xs:enumeration value="pumpkin"/>
+            <xs:enumeration value="netherrack"/>
+            <xs:enumeration value="soul_sand"/>
+            <xs:enumeration value="glowstone"/>
+            <xs:enumeration value="portal"/>
+            <xs:enumeration value="lit_pumpkin"/>
+            <xs:enumeration value="cake"/>
+            <xs:enumeration value="unpowered_repeater"/>
+            <xs:enumeration value="powered_repeater"/>
+            <xs:enumeration value="stained_glass"/>
+            <xs:enumeration value="trapdoor"/>
+            <xs:enumeration value="monster_egg"/>
+            <xs:enumeration value="stonebrick"/>
+            <xs:enumeration value="brown_mushroom_block"/>
+            <xs:enumeration value="red_mushroom_block"/>
+            <xs:enumeration value="iron_bars"/>
+            <xs:enumeration value="glass_pane"/>
+            <xs:enumeration value="melon_block"/>
+            <xs:enumeration value="pumpkin_stem"/>
+            <xs:enumeration value="melon_stem"/>
+            <xs:enumeration value="vine"/>
+            <xs:enumeration value="fence_gate"/>
+            <xs:enumeration value="brick_stairs"/>
+            <xs:enumeration value="stone_brick_stairs"/>
+            <xs:enumeration value="mycelium"/>
+            <xs:enumeration value="waterlily"/>
+            <xs:enumeration value="nether_brick"/>
+            <xs:enumeration value="nether_brick_fence"/>
+            <xs:enumeration value="nether_brick_stairs"/>
+            <xs:enumeration value="nether_wart"/>
+            <xs:enumeration value="enchanting_table"/>
+            <xs:enumeration value="brewing_stand"/>
+            <xs:enumeration value="cauldron"/>
+            <xs:enumeration value="end_portal"/>
+            <xs:enumeration value="end_portal_frame"/>
+            <xs:enumeration value="end_stone"/>
+            <xs:enumeration value="dragon_egg"/>
+            <xs:enumeration value="redstone_lamp"/>
+            <xs:enumeration value="lit_redstone_lamp"/>
+            <xs:enumeration value="double_wooden_slab"/>
+            <xs:enumeration value="wooden_slab"/>
+            <xs:enumeration value="cocoa"/>
+            <xs:enumeration value="sandstone_stairs"/>
+            <xs:enumeration value="emerald_ore"/>
+            <xs:enumeration value="ender_chest"/>
+            <xs:enumeration value="tripwire_hook"/>
+            <xs:enumeration value="tripwire"/>
+            <xs:enumeration value="emerald_block"/>
+            <xs:enumeration value="spruce_stairs"/>
+            <xs:enumeration value="birch_stairs"/>
+            <xs:enumeration value="jungle_stairs"/>
+            <xs:enumeration value="command_block"/>
+            <xs:enumeration value="beacon"/>
+            <xs:enumeration value="cobblestone_wall"/>
+            <xs:enumeration value="flower_pot"/>
+            <xs:enumeration value="carrots"/>
+            <xs:enumeration value="potatoes"/>
+            <xs:enumeration value="wooden_button"/>
+            <xs:enumeration value="skull"/>
+            <xs:enumeration value="anvil"/>
+            <xs:enumeration value="trapped_chest"/>
+            <xs:enumeration value="light_weighted_pressure_plate"/>
+            <xs:enumeration value="heavy_weighted_pressure_plate"/>
+            <xs:enumeration value="unpowered_comparator"/>
+            <xs:enumeration value="powered_comparator"/>
+            <xs:enumeration value="daylight_detector"/>
+            <xs:enumeration value="redstone_block"/>
+            <xs:enumeration value="quartz_ore"/>
+            <xs:enumeration value="hopper"/>
+            <xs:enumeration value="quartz_block"/>
+            <xs:enumeration value="quartz_stairs"/>
+            <xs:enumeration value="activator_rail"/>
+            <xs:enumeration value="dropper"/>
+            <xs:enumeration value="stained_hardened_clay"/>
+            <xs:enumeration value="stained_glass_pane"/>
+            <xs:enumeration value="leaves2"/>
+            <xs:enumeration value="log2"/>
+            <xs:enumeration value="acacia_stairs"/>
+            <xs:enumeration value="dark_oak_stairs"/>
+            <xs:enumeration value="slime"/>
+            <xs:enumeration value="barrier"/>
+            <xs:enumeration value="iron_trapdoor"/>
+            <xs:enumeration value="prismarine"/>
+            <xs:enumeration value="sea_lantern"/>
+            <xs:enumeration value="hay_block"/>
+            <xs:enumeration value="carpet"/>
+            <xs:enumeration value="hardened_clay"/>
+            <xs:enumeration value="coal_block"/>
+            <xs:enumeration value="packed_ice"/>
+            <xs:enumeration value="double_plant"/>
+            <xs:enumeration value="standing_banner"/>
+            <xs:enumeration value="wall_banner"/>
+            <xs:enumeration value="daylight_detector_inverted"/>
+            <xs:enumeration value="red_sandstone"/>
+            <xs:enumeration value="red_sandstone_stairs"/>
+            <xs:enumeration value="double_stone_slab2"/>
+            <xs:enumeration value="stone_slab2"/>
+            <xs:enumeration value="spruce_fence_gate"/>
+            <xs:enumeration value="birch_fence_gate"/>
+            <xs:enumeration value="jungle_fence_gate"/>
+            <xs:enumeration value="dark_oak_fence_gate"/>
+            <xs:enumeration value="acacia_fence_gate"/>
+            <xs:enumeration value="spruce_fence"/>
+            <xs:enumeration value="birch_fence"/>
+            <xs:enumeration value="jungle_fence"/>
+            <xs:enumeration value="dark_oak_fence"/>
+            <xs:enumeration value="acacia_fence"/>
+            <xs:enumeration value="spruce_door"/>
+            <xs:enumeration value="birch_door"/>
+            <xs:enumeration value="jungle_door"/>
+            <xs:enumeration value="acacia_door"/>
+            <xs:enumeration value="dark_oak_door"/>
+            <xs:enumeration value="end_rod"/>
+            <xs:enumeration value="chorus_plant"/>
+            <xs:enumeration value="chorus_flower"/>
+            <xs:enumeration value="purpur_block"/>
+            <xs:enumeration value="purpur_pillar"/>
+            <xs:enumeration value="purpur_stairs"/>
+            <xs:enumeration value="purpur_double_slab"/>
+            <xs:enumeration value="purpur_slab"/>
+            <xs:enumeration value="end_bricks"/>
+            <xs:enumeration value="beetroots"/>
+            <xs:enumeration value="grass_path"/>
+            <xs:enumeration value="end_gateway"/>
+            <xs:enumeration value="repeating_command_block"/>
+            <xs:enumeration value="chain_command_block"/>
+            <xs:enumeration value="frosted_ice"/>
+            <xs:enumeration value="magma"/>
+            <xs:enumeration value="nether_wart_block"/>
+            <xs:enumeration value="red_nether_brick"/>
+            <xs:enumeration value="bone_block"/>
+            <xs:enumeration value="structure_void"/>
+            <xs:enumeration value="observer"/>
+            <xs:enumeration value="white_shulker_box"/>
+            <xs:enumeration value="orange_shulker_box"/>
+            <xs:enumeration value="magenta_shulker_box"/>
+            <xs:enumeration value="light_blue_shulker_box"/>
+            <xs:enumeration value="yellow_shulker_box"/>
+            <xs:enumeration value="lime_shulker_box"/>
+            <xs:enumeration value="pink_shulker_box"/>
+            <xs:enumeration value="gray_shulker_box"/>
+            <xs:enumeration value="silver_shulker_box"/>
+            <xs:enumeration value="cyan_shulker_box"/>
+            <xs:enumeration value="purple_shulker_box"/>
+            <xs:enumeration value="blue_shulker_box"/>
+            <xs:enumeration value="brown_shulker_box"/>
+            <xs:enumeration value="green_shulker_box"/>
+            <xs:enumeration value="red_shulker_box"/>
+            <xs:enumeration value="black_shulker_box"/>
+            <xs:enumeration value="structure_block"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="BlockOrItem">
+        <xs:union memberTypes="BlockType ItemType"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="SignType">
+        <xs:annotation>
+            <xs:documentation>
+                Subset of the block types which act as signs
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="standing_sign"/>
+            <xs:enumeration value="wall_sign"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="ContainerType">
+        <xs:annotation>
+            <xs:documentation>
+                Subset of the block types which act as containers, for use in DrawContainer object
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="dispenser"/>
+            <xs:enumeration value="chest"/>
+            <xs:enumeration value="trapped_chest"/>
+            <xs:enumeration value="hopper"/>
+            <xs:enumeration value="dropper"/>
+            <xs:enumeration value="white_shulker_box"/>
+            <xs:enumeration value="orange_shulker_box"/>
+            <xs:enumeration value="magenta_shulker_box"/>
+            <xs:enumeration value="light_blue_shulker_box"/>
+            <xs:enumeration value="yellow_shulker_box"/>
+            <xs:enumeration value="lime_shulker_box"/>
+            <xs:enumeration value="pink_shulker_box"/>
+            <xs:enumeration value="gray_shulker_box"/>
+            <xs:enumeration value="silver_shulker_box"/>
+            <xs:enumeration value="cyan_shulker_box"/>
+            <xs:enumeration value="purple_shulker_box"/>
+            <xs:enumeration value="blue_shulker_box"/>
+            <xs:enumeration value="brown_shulker_box"/>
+            <xs:enumeration value="green_shulker_box"/>
+            <xs:enumeration value="red_shulker_box"/>
+            <xs:enumeration value="black_shulker_box"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="StoneTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Types of stone for stone blocks/items
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="stone"/>
+            <xs:enumeration value="granite"/>
+            <xs:enumeration value="smooth_granite"/>
+            <xs:enumeration value="diorite"/>
+            <xs:enumeration value="smooth_diorite"/>
+            <xs:enumeration value="andesite"/>
+            <xs:enumeration value="smooth_andesite"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="WoodTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Types of wood for wooden blocks/items
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="oak"/>
+            <xs:enumeration value="spruce"/>
+            <xs:enumeration value="birch"/>
+            <xs:enumeration value="jungle"/>
+            <xs:enumeration value="acacia"/>
+            <xs:enumeration value="dark_oak"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="FlowerTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Types of flower
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="dandelion"/>
+            <xs:enumeration value="poppy"/>
+            <xs:enumeration value="blue_orchid"/>
+            <xs:enumeration value="allium"/>
+            <xs:enumeration value="houstonia"/>
+            <xs:enumeration value="red_tulip"/>
+            <xs:enumeration value="orange_tulip"/>
+            <xs:enumeration value="white_tulip"/>
+            <xs:enumeration value="pink_tulip"/>
+            <xs:enumeration value="oxeye_daisy"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="MonsterEggTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Types of monster egg blocks
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="cobblestone"/>
+            <xs:enumeration value="stone_brick"/>
+            <xs:enumeration value="mossy_brick"/>
+            <xs:enumeration value="cracked_brick"/>
+            <xs:enumeration value="chiseled_brick"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="ShapeTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Shapes for, eg, rails
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="north_south"/>
+            <xs:enumeration value="east_west"/>
+            <xs:enumeration value="north_east"/>
+            <xs:enumeration value="north_west"/>
+            <xs:enumeration value="south_east"/>
+            <xs:enumeration value="south_west"/>
+            <xs:enumeration value="ascending_east"/>
+            <xs:enumeration value="ascending_west"/>
+            <xs:enumeration value="ascending_north"/>
+            <xs:enumeration value="ascending_south"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="HalfTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Specify which half - for stairs or double blocks (doors, tall flowers, etc)
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <!-- for stairs -->
+            <xs:enumeration value="top"/>
+            <xs:enumeration value="bottom"/>
+            <!-- for bed -->
+            <xs:enumeration value="head"/>
+            <xs:enumeration value="foot"/>
+            <!-- for doors -->
+            <xs:enumeration value="upper"/>
+            <xs:enumeration value="lower"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="EntityTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Types of entity for eggs, spawners etc.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="ElderGuardian"/>
+            <xs:enumeration value="WitherSkeleton"/>
+            <xs:enumeration value="Stray"/>
+            <xs:enumeration value="Husk"/>
+            <xs:enumeration value="ZombieVillager"/>
+            <xs:enumeration value="SkeletonHorse"/>
+            <xs:enumeration value="ZombieHorse"/>
+            <xs:enumeration value="EvocationIllager"/>
+            <xs:enumeration value="VindicationIllager"/>
+            <xs:enumeration value="Vex"/>
+            <xs:enumeration value="Creeper"/>
+            <xs:enumeration value="Skeleton"/>
+            <xs:enumeration value="Spider"/>
+            <xs:enumeration value="Giant"/>
+            <xs:enumeration value="Zombie"/>
+            <xs:enumeration value="Slime"/>
+            <xs:enumeration value="Ghast"/>
+            <xs:enumeration value="PigZombie"/>
+            <xs:enumeration value="Enderman"/>
+            <xs:enumeration value="CaveSpider"/>
+            <xs:enumeration value="Silverfish"/>
+            <xs:enumeration value="Blaze"/>
+            <xs:enumeration value="LavaSlime"/>    <!-- Magma Cube -->
+            <xs:enumeration value="EnderDragon"/>
+            <xs:enumeration value="WitherBoss"/>
+            <xs:enumeration value="Bat"/>
+            <xs:enumeration value="Witch"/>
+            <xs:enumeration value="Endermite"/>
+            <xs:enumeration value="Guardian"/>
+            <xs:enumeration value="Shulker"/>
+            <xs:enumeration value="Donkey"/>
+            <xs:enumeration value="Mule"/>
+            <xs:enumeration value="Pig"/>
+            <xs:enumeration value="Sheep"/>
+            <xs:enumeration value="Cow"/>
+            <xs:enumeration value="Chicken"/>
+            <xs:enumeration value="Squid"/>
+            <xs:enumeration value="Wolf"/>
+            <xs:enumeration value="MushroomCow"/>
+            <xs:enumeration value="SnowMan"/>
+            <xs:enumeration value="Ozelot"/>
+            <xs:enumeration value="VillagerGolem"/>
+            <xs:enumeration value="Horse"/>
+            <xs:enumeration value="Rabbit"/>
+            <xs:enumeration value="PolarBear"/>
+            <xs:enumeration value="Llama"/>
+            <xs:enumeration value="Villager"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="NonLivingEntityTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Non-living entities, such as boats, minecarts etc.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="MinecartRideable"/>
+            <xs:enumeration value="MinecartChest"/>
+            <xs:enumeration value="MinecartFurnace"/>
+            <xs:enumeration value="MinecartTNT"/>
+            <xs:enumeration value="MinecartSpawner"/>
+            <xs:enumeration value="MinecartHopper"/>
+            <xs:enumeration value="MinecartCommandBlock"/>
+            <xs:enumeration value="Boat"/>
+            <xs:enumeration value="ArmorStand"/>
+            <xs:enumeration value="ItemFrame"/>
+            <xs:enumeration value="EnderCrystal"/>
+            <xs:enumeration value="LeashKnot"/>
+            <xs:enumeration value="Painting"/>
+            <xs:enumeration value="PrimedTnt"/>
+            <xs:enumeration value="FallingSand"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="ProjectileEntityTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Non-living entities which tend to represent projectiles thrown by other mobs, etc.
+                Doesn't make much sense to spawn/draw these, but the agent could encounter them in the wild.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="XPOrb"/>
+            <xs:enumeration value="AreaEffectCloud"/>
+            <xs:enumeration value="ThrownEgg"/>
+            <xs:enumeration value="Arrow"/>
+            <xs:enumeration value="Snowball"/>
+            <xs:enumeration value="Fireball"/>
+            <xs:enumeration value="SmallFireball"/>
+            <xs:enumeration value="ThrownEnderpearl"/>
+            <xs:enumeration value="EyeOfEnderSignal"/>
+            <xs:enumeration value="ThrownPotion"/>
+            <xs:enumeration value="ThrownExpBottle"/>
+            <xs:enumeration value="WitherSkull"/>
+            <xs:enumeration value="FireworksRocketEntity"/>
+            <xs:enumeration value="SpectralArrow"/>
+            <xs:enumeration value="ShulkerBullet"/>
+            <xs:enumeration value="DragonFireball"/>
+            <xs:enumeration value="EvocationFangs"/>
+            <xs:enumeration value="LlamaSpit"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="NoteTypes">
+        <xs:annotation>
+            <xs:documentation>
+                Note values for creating tuned noteblocks
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="F_sharp_3"/>
+            <xs:enumeration value="G3"/>
+            <xs:enumeration value="G_sharp_3"/>
+            <xs:enumeration value="A3"/>
+            <xs:enumeration value="A_sharp_3"/>
+            <xs:enumeration value="B3"/>
+            <xs:enumeration value="C4"/>
+            <xs:enumeration value="C_sharp_4"/>
+            <xs:enumeration value="D4"/>
+            <xs:enumeration value="D_sharp_4"/>
+            <xs:enumeration value="E4"/>
+            <xs:enumeration value="F4"/>
+            <xs:enumeration value="F_sharp_4"/>
+            <xs:enumeration value="G4"/>
+            <xs:enumeration value="G_sharp_4"/>
+            <xs:enumeration value="A4"/>
+            <xs:enumeration value="A_sharp_4"/>
+            <xs:enumeration value="B4"/>
+            <xs:enumeration value="C5"/>
+            <xs:enumeration value="C_sharp_5"/>
+            <xs:enumeration value="D5"/>
+            <xs:enumeration value="D_sharp_5"/>
+            <xs:enumeration value="E5"/>
+            <xs:enumeration value="F5"/>
+            <xs:enumeration value="F_sharp_5"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="SpawnableTypes">
+        <xs:annotation>
+            <xs:documentation>
+                An entity that can be placed using the DrawingDecorator.
+            </xs:documentation>
+            <xs:appinfo>
+                <jaxb:class name="SpawnableTypes"/>  <!-- needed or jaxb just binds it to a String -->
+            </xs:appinfo>
+        </xs:annotation>
+        <xs:union memberTypes="EntityTypes NonLivingEntityTypes"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="Variation">
+        <xs:annotation>
+            <xs:documentation>
+                Variants of block types, item types, entities etc
+            </xs:documentation>
+            <xs:appinfo>
+                <jaxb:class name="Variation"/>   <!-- needed or jaxb just binds BlockVariant to a String -->
+            </xs:appinfo>
+        </xs:annotation>
+        <xs:union
+                memberTypes="StoneTypes WoodTypes FlowerTypes EntityTypes ShapeTypes MonsterEggTypes HalfTypes NoteTypes"/>
+    </xs:simpleType>
+
+</xs:schema>
