diff --git a/.classpath b/.classpath
index 3faebf037..5f118aa08 100644
--- a/.classpath
+++ b/.classpath
@@ -5,5 +5,5 @@
-
diff --git a/.env b/.env
index e69de29bb..0b84832cc 100644
--- a/.env
+++ b/.env
@@ -0,0 +1 @@
+PYTHONPATH=.:$PYTHONPATH
\ No newline at end of file
diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml
new file mode 100644
index 000000000..2036fc6ee
--- /dev/null
+++ b/.github/workflows/build_test.yml
@@ -0,0 +1,139 @@
+name: Build wheels
+
+on: [workflow_dispatch] #, pull_request]
+
+# todo: Support windows
+# todo: make new docker image which includes java
+# todo: get s3 upload to work.
+# todo: support sdist!
+
+jobs:
+ build_wheels_linux:
+ name: ${{ matrix.os }} ${{ matrix.pver }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-18.04] #, windows-latest, macos-latest]
+ pver: ["cp35*x86_64", "cp36*x86_64", "cp37*x86_64", "cp38*x86_64"] # Todo add 39.
+ env:
+ # build using the manylinux2014 image
+ CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
+ CIBW_MANYLINUX_I686_IMAGE: manylinux2014
+ CIBW_BUILD: "${{ matrix.pver }}"
+ CIBW_SKIP: cp27-* pp27-*
+ CIBW_BEFORE_BUILD_LINUX: |
+ yum install -y \
+ java-1.8.0-openjdk-devel
+ # Why the fuck is there no headless-devel package like on debian!
+
+ # Add the Java dependencies to the
+ echo "$(/opt/python/cp37-cp37m/bin/pip show auditwheel | grep "Location:" | cut -d ':' -f2)/auditwheel/policy/policy.json" | xargs \
+ python -c "import sys; import json; import os; fname = sys.argv[1]; fread = open(fname, 'r'); obj = json.load(fread); fread.close(); print(obj[-1]); [o['lib_whitelist'].extend('libXcursor.so.1, libXfixes.so.3, libXi.so.6, libXrandr.so.2, libXtst.so.6, libXxf86vm.so.1, libawt.so, libawt_xawt.so, libjava.so, libjawt.so, libjvm.so, libncurses.so.5, libtinfo.so.5, libverify.so'.split(', ')) for o in obj]; print(obj[-1]['lib_whitelist']); fwrite = open(fname, 'w'); json.dump(obj, fwrite); fwrite.close(); fread = open(fname, 'r'); print(json.load(fread)[-1])"
+
+
+ CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel -v repair -w {dest_dir} {wheel}"
+ CIBW_BEFORE_BUILD: pip install .
+
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: 'recursive'
+
+ - name: Declare some variables
+ id: vars
+ shell: bash
+ run: |
+ echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
+ echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
+
+ - uses: actions/setup-python@v2
+ name: Install Python
+ with:
+ python-version: '3.7'
+
+ - uses: actions/setup-java@v1.3.0 # This is for MacOS and Windows
+ with:
+ java-version: '8' # The JDK version to make available on the path.
+ java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk
+ architecture: x64 # (x64 or x86) - defaults to x64
+
+ # TODO Run testing here! (Or we could run it in a different workflow.)
+
+
+ - name: Install cibuildwheel
+ run: |
+ python -m pip install cibuildwheel==1.5.2
+
+
+ - name: Build wheels
+ run: |
+ python -m cibuildwheel --output-dir wheelhouse
+
+ - uses: actions/upload-artifact@v2
+ with:
+ path: ./wheelhouse/*.whl
+
+ - uses: shallwefootball/s3-upload-action@master
+ with:
+ aws_key_id: ${{ secrets.AWS_KEY_ID }}
+ aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
+ aws_bucket: ${{ secrets.AWS_MINERL_BUCKET }}
+ source_dir: './wheelhouse'
+ destination_dir: builds/${{ steps.vars.outputs.branch }}/${{ steps.vars.outputs.sha_short }}/
+
+ build_sdist:
+ name: Build source distribution
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: 'recursive'
+
+
+ - name: Declare some variables
+ id: vars
+ shell: bash
+ run: |
+ echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
+ echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
+
+ - uses: actions/setup-python@v2
+ name: Install Python
+ with:
+ python-version: '3.7'
+
+ - name: Build sdist
+ run: python setup.py sdist
+
+ - uses: actions/upload-artifact@v2
+ with:
+ path: dist/*.tar.gz
+
+ - uses: shallwefootball/s3-upload-action@master
+ with:
+ aws_key_id: ${{ secrets.AWS_KEY_ID }}
+ aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
+ aws_bucket: ${{ secrets.AWS_MINERL_BUCKET }}
+ source_dir: './dist/'
+ destination_dir: builds/${{ steps.vars.outputs.branch }}/${{ steps.vars.outputs.sha_short }}/
+
+
+
+ # # upload_pypi:
+ # # needs: [build_wheels, build_sdist]
+ # # runs-on: ubuntu-latest
+ # # # upload to PyPI on every tag starting with 'v'
+ # # # if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
+ # # # alternatively, to publish when a GitHub Release is created, use the following rule:
+ # # if: github.event_name == 'release' && github.event.action == 'published'
+ # # steps:
+ # # - uses: actions/download-artifact@v2
+ # # with:
+ # # name: artifact
+ # # path: dist
+
+ # # - uses: pypa/gh-action-pypi-publish@master
+ # # with:
+ # # user: __token__
+ # # password: ${{ secrets.pypi_password }}
+ # # To test: repository_url: https://test.pypi.org/legacy/
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c25deec21..a34218222 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@@ -181,9 +182,7 @@ celerybeat-schedule
# Environments
.env
.venv
-env/
venv/
-ENV/
env.bak/
venv.bak/
@@ -236,3 +235,57 @@ minerl_data/pipeline/tests_data/
t/
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# C extensions
+*.so
+
+venv/*
+*logs*
+
+# Distribution / packaging
+.Python
+build/
+_build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+build/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# IDE files
+.idea/
+.vscode
+
+*.DS_store
+*.iml
+*Minecraft.ipr
+*Minecraft.iws
+
+*/logs/
+
+tests/logs/
+.vscode/settings.json
+*/performance
+
+
+# Jenkins
+jenkins_home/
+
+#eclipse
+Malmo/Minecraft/*.launch
+*minerl_watchers/
+caches/
+native/
+wrapper/
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 9cca4d8a7..000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "minerl/env/Malmo"]
- path = minerl/env/Malmo
- url = https://github.com/cmu-rl/Malmo.git
diff --git a/.travis.yml b/.travis.yml
index 5b2b1ee91..a4a6f0c19 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,7 +26,8 @@ addons:
env:
- MINERL_DATA_ROOT=$HOME/data
install:
-- pip install .
+- export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/
+- pip install -e .
- pip install pytest-xdist
- python -c "import logging; import minerl; logging.basicConfig(level=logging.DEBUG); minerl.data.download(minimal=True)"
services: xvfb
diff --git a/Jenkinsfile b/Jenkinsfile
deleted file mode 100644
index cd0707167..000000000
--- a/Jenkinsfile
+++ /dev/null
@@ -1,64 +0,0 @@
-pipeline {
- agent any
- stages {
- stage('Requirements') {
- steps {
- sh 'pip3 install -r requirements.txt --user'
- sh 'git submodule update --init'
- sh 'pip3 install -e . --upgrade --user'
- }
- }
- stage('Download data') {
- steps {
- sh 'python3 -c "import logging; import minerl; logging.basicConfig(level=logging.DEBUG); minerl.data.download(directory=\'./data\', minimal=True)"'
- }
- }
- stage('Run PyTest') {
- parallel {
- stage('Basic MineRL') {
- agent any
- steps {
- catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
- sh '''
- export "MINERL_DATA_DIR=${WORKSPACE}/data"
- pytest -n 8 --junitxml=./results/basic_report.xml --ignore=minerl/env/Malmo --ignore=tests/excluded --ignore=tests/local --ignore=minerl/dependencies'''
- }
- }
- }
- stage('PySmartDL') {
- steps {
- catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
- dir(path: 'minerl/dependencies/pySmartDL/') {
- sh '''
- export "MINERL_DATA_DIR=${WORKSPACE}/data"
- pytest --junitxml=./results/pysmartdl_report.xml --ignore=minerl/env/Malmo --ignore=tests/excluded'''
- }
- }
- }
- }
- stage('Advanced MineRL') {
- steps {
- catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
- sh '''
- export "PYTHONPATH=${WORKSPACE}:${PYTHONPATH}"
- export "MINERL_DATA_DIR=${WORKSPACE}/data"
- pytest --junitxml=./results/advanced_report.xml ./tests/local
- '''
- }
- }
- }
- }
- }
- stage('Cleanup') {
- steps {
- sh 'rm -rf ./data'
- sh 'rm -rf /tmp/pySmartDL'
- junit '**/results/*.xml'
- sh 'rm -rf ./results'
- }
- }
- }
- environment {
- DISPLAY = ':0'
- }
-}
diff --git a/MANIFEST.in b/MANIFEST.in
index b2da7f51f..30cff32c5 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,20 @@
-recursive-include projectname *.py
-include README
+include README.md
include requirements.txt
-include LICENSE
\ No newline at end of file
+include LICENSE
+recursive-include minerl/herobraine/env_specs/ *
+include minerl/herobraine/hero/mc_constants.json
+include minerl/herobraine/hero/mission.xml.j2
+include minerl/env/info.npz
+recursive-include minerl/data/assets *
+recursive-include minerl/Malmo/Minecraft *
+recursive-include minerl/Malmo/Schemas *
+recursive-include minerl/data/pipeline/parser *
+prune minerl/Malmo/Minecraft/.gradle
+prune minerl/Malmo/Minecraft/bin
+prune minerl/Malmo/Minecraft/build
+prune minerl/Malmo/Minecraft/run/gradle
+prune minerl/Malmo/Minecraft/.minecraft
+prune minerl/Malmo/Minecraft/.minecraftserver
+prune minerl/Malmo/Minecraft/run/saves
+global-exclude *.lock
+global-exclude *fuse_hidden*
\ No newline at end of file
diff --git a/docs/source/environments/index.rst b/docs/source/environments/index.rst
index 94e80687b..89b7ab215 100644
--- a/docs/source/environments/index.rst
+++ b/docs/source/environments/index.rst
@@ -59,7 +59,7 @@ Basic Environments
- envspec = gym.spec(id)
+ envspec = gym.spec(id)._kwargs['enc_spec']
print("______________")
@@ -67,12 +67,12 @@ Basic Environments
print("______________")
if 'docstr' in envspec._kwargs:
- print(envspec._kwargs['docstr'])
+ print(envspec.get_docstring())
- action_space = prep_space(envspec._kwargs['action_space'])
- state_space = prep_space(envspec._kwargs['observation_space'])
+ action_space = prep_space(envspec.action_space)
+ state_space = prep_space(envspec.observation_space)
print(".......")
print("Observation Space")
@@ -162,20 +162,21 @@ Competition Environments
- envspec = gym.spec(id)
+ envspec = gym.spec(id)._kwargs['enc_spec']
print("______________")
print("{}".format(id))
print("______________")
-
+
if 'docstr' in envspec._kwargs:
- print(envspec._kwargs['docstr'])
+ print(envspec.get_docstring())
+
+ action_space = prep_space(envspec.action_space)
+ state_space = prep_space(envspec.observation_space)
- action_space = prep_space(envspec._kwargs['action_space'])
- state_space = prep_space(envspec._kwargs['observation_space'])
print(".......")
print("Observation Space")
diff --git a/minerl/Malmo/.gitattributes b/minerl/Malmo/.gitattributes
new file mode 100755
index 000000000..1ff0c4230
--- /dev/null
+++ b/minerl/Malmo/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/minerl/Malmo/.gitignore b/minerl/Malmo/.gitignore
new file mode 100755
index 000000000..c15fc6425
--- /dev/null
+++ b/minerl/Malmo/.gitignore
@@ -0,0 +1,227 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+x64/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Roslyn cache directories
+*.ide/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+## TODO: Comment the next line if you want to checkin your
+## web deploy settings but do note that will include unencrypted
+## passwords
+#*.pubxml
+
+# NuGet Packages Directory
+packages/*
+## TODO: If the tool you use requires repositories.config
+## uncomment the next line
+#!packages/repositories.config
+/ExperimentStudio/packages
+/Installer/packages
+/ExperimentLauncher/packages
+/HumanAction/packages
+/CSharpAgents/Sample/packages
+/InstancePlayer/packages
+/MalmoPlatform/packages
+
+# Enable "build/" folder in the NuGet Packages folder since
+# NuGet packages use it for MSBuild targets.
+# This line needs to be after the ignore of the build folder
+# (and the packages folder if the line above has been uncommented)
+!packages/build/
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.bak
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# LightSwitch generated files
+GeneratedArtifacts/
+_Pvt_Extensions/
+ModelManifest.xml
+
+# Eclipse/Java files
+*.class
+.classpath
+.gradle
+.settings
+.project
+/Mod/eclipse
+
+# IntelliJ files
+.idea
+
+# Python
+*.pyc
+
+# Thing specific to Project Malmo
+Schemas/xs3p.xsl
+Minecraft/run/config/malmomodCLIENT.cfg
+Minecraft/Minecraft_Client.launch
+
+.vscode/
+Minecraft/run/logs
+Minecraft/run/readme.txt
+Minecraft/run/user*
+Minecraft/run/.mixin.out
+Minecraft/run/config/splash.properties
+Minecraft/.minecraft
+Minecraft/.minecraftserver
+Malmo/samples/Python_examples/.fuse_hidden*
+Malmo/samples/Python_examples/MalmoPython.so
diff --git a/minerl/Malmo/.gitrepo b/minerl/Malmo/.gitrepo
new file mode 100644
index 000000000..f6b551bf0
--- /dev/null
+++ b/minerl/Malmo/.gitrepo
@@ -0,0 +1,12 @@
+; DO NOT EDIT (unless you know what you are doing)
+;
+; This subdirectory is a git "subrepo", and this file is maintained by the
+; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
+;
+[subrepo]
+ remote = git@github.com:cmu-rl/Malmo
+ branch = minerl
+ commit = 8d81e2b309841deac9bedcafe75157844874f6ec
+ parent = 39d109600bd11089b9fbef90ec01ab920b828958
+ method = merge
+ cmdver = 0.4.0
diff --git a/minerl/Malmo/LICENSE.txt b/minerl/Malmo/LICENSE.txt
new file mode 100644
index 000000000..c42635beb
--- /dev/null
+++ b/minerl/Malmo/LICENSE.txt
@@ -0,0 +1,18 @@
+MIT License
+
+Copyright (c) 2016, 2018 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.
diff --git a/minerl/Malmo/Minecraft/CMakeLists.txt b/minerl/Malmo/Minecraft/CMakeLists.txt
new file mode 100755
index 000000000..2c1238a25
--- /dev/null
+++ b/minerl/Malmo/Minecraft/CMakeLists.txt
@@ -0,0 +1,77 @@
+# ------------------------------------------------------------------------------------------------
+# 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.
+# ------------------------------------------------------------------------------------------------
+
+if( MSVC )
+ set( GRADLE ${CMAKE_CURRENT_SOURCE_DIR}/gradlew.bat )
+else()
+ set( GRADLE ${CMAKE_CURRENT_SOURCE_DIR}/gradlew )
+endif()
+
+# We currently do an in-source build for the Mod, because of gradle.
+add_custom_target( Minecraft
+ ALL
+ COMMAND ${GRADLE} setupDecompWorkspace build testClasses -x test --stacktrace -Pversion=${MALMO_VERSION}
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/main/resources/version.properties
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "Building Minecraft..."
+)
+
+# Write the Malmo version number into a properties file for the Mod and gradle to use:
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/main/resources/version.properties
+ COMMAND ${CMAKE_COMMAND} -E echo "malmomod.version=" ${MALMO_VERSION} >
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main/resources/version.properties
+ COMMENT "Creating version file..."
+)
+
+set( TEST_MOD OFF )
+if( TEST_MOD )
+ add_test(
+ NAME MinecraftTests
+ COMMAND ${GRADLE} test --info
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+ set_tests_properties( MinecraftTests PROPERTIES ENVIRONMENT "MALMO_XSD_PATH=$ENV{MALMO_XSD_PATH}" )
+endif()
+
+# install the Minecraft folder
+execute_process(
+ COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_VARIABLE GIT_BRANCH
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+execute_process(
+ COMMAND ${GIT_EXECUTABLE} ls-tree -r ${GIT_BRANCH} --name-only .
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ OUTPUT_VARIABLE MOD_FILES
+)
+STRING(REGEX REPLACE "\n" ";" MOD_FILES "${MOD_FILES}")
+list(REMOVE_ITEM MOD_FILES launch_minecraft_in_background.py)
+list(REMOVE_ITEM MOD_FILES CMakeLists.txt)
+foreach ( file ${MOD_FILES} )
+ get_filename_component( dir ${file} PATH )
+ install( FILES ${file} DESTINATION Minecraft/${dir} )
+endforeach()
+install( PROGRAMS gradlew DESTINATION Minecraft/ )
+install( PROGRAMS launchClient.sh DESTINATION Minecraft/ )
+# Make sure the version number is part of the installation:
+install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/main/resources/version.properties DESTINATION Minecraft/src/main/resources )
+install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/build/libs/MalmoMod-${MALMO_VERSION}.jar DESTINATION "Mod" )
+
diff --git a/minerl/Malmo/Minecraft/CREDITS-fml.txt b/minerl/Malmo/Minecraft/CREDITS-fml.txt
new file mode 100755
index 000000000..97122f5d3
--- /dev/null
+++ b/minerl/Malmo/Minecraft/CREDITS-fml.txt
@@ -0,0 +1,28 @@
+This is Forge Mod Loader.
+
+You can find the source code at all times at https://github.com/MinecraftForge/FML
+
+This minecraft mod is a clean open source implementation of a mod loader for minecraft servers
+and minecraft clients.
+
+The code is authored by cpw.
+
+It began by partially implementing an API defined by the client side ModLoader, authored by Risugami.
+http://www.minecraftforum.net/topic/75440-
+This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader.
+
+It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge.
+http://www.minecraftforge.net/
+
+Additionally, it contains an implementation of topological sort based on that
+published at http://keithschwarz.com/interesting/code/?dir=topological-sort
+
+It also contains code from the Maven project for performing versioned dependency
+resolution. http://maven.apache.org/
+
+It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/
+with credit to it's authors.
+
+Forge Mod Loader downloads components from the Minecraft Coder Pack
+(http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team.
+
diff --git a/minerl/Malmo/Minecraft/LICENSE-new.txt b/minerl/Malmo/Minecraft/LICENSE-new.txt
new file mode 100755
index 000000000..be2c9e66d
--- /dev/null
+++ b/minerl/Malmo/Minecraft/LICENSE-new.txt
@@ -0,0 +1,483 @@
+Minecraft Forge is licensed under the terms of the LGPL 2.1 found
+here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and copied
+below.
+
+A note on authorship:
+All source artifacts are property of their original author, with
+the exclusion of the contents of the patches directory and others
+copied from it from time to time. Authorship of the contents of
+the patches directory is retained by the Minecraft Forge project.
+This is because the patches are partially machine generated
+artifacts, and are changed heavily due to the way forge works.
+Individual attribution within them is impossible.
+
+Consent:
+All contributions to Forge must consent to the release of any
+patch content to the Forge project.
+
+A note on infectivity:
+The LGPL is chosen specifically so that projects may depend on Forge
+features without being infected with its license. That is the
+purpose of the LGPL. Mods and others using this code via ordinary
+Java mechanics for referencing libraries are specifically not bound
+by Forge's license for the Mod code.
+
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/minerl/Malmo/Minecraft/MinecraftForge-Credits.txt b/minerl/Malmo/Minecraft/MinecraftForge-Credits.txt
new file mode 100755
index 000000000..d0de5a521
--- /dev/null
+++ b/minerl/Malmo/Minecraft/MinecraftForge-Credits.txt
@@ -0,0 +1,26 @@
+* Eloraam *
+
+* FlowerChild *
+
+* Hawkye *
+
+* MALfunction84 *
+
+Submitted the sleep handler code for his mod (Somnia) and others to use.
+
+* Scokeev9 *
+
+Gave permission for ScotTools API to be integrated into MCF, and also supported the Forge by converting his mods to use it.
+
+ScotTools Background: ScotTools was an API that enabled modders to add blocks to harvesting levels (and many other ease-of-use features to create new tools), and the first tool API that used block material for block breaking efficiency which allowed blocks from mods that didn't use ScotTools API to break with the correct speed.
+
+* SpaceToad *
+
+* LexManos *
+
+* cpw *
+
+* Minecraft Coder Pack (MCP) *
+Forge Mod Loader and Minecraft Forge have permission to distribute and automatically download components of MCP and distribute MCP data files.
+This permission is not transitive and others wishing to redistribute the Minecraft Forge source independently should seek permission of MCP or
+remove the MCP data files and request their users to download MCP separately.
diff --git a/minerl/Malmo/Minecraft/Minecraft_Server.launch b/minerl/Malmo/Minecraft/Minecraft_Server.launch
new file mode 100644
index 000000000..e83242ab9
--- /dev/null
+++ b/minerl/Malmo/Minecraft/Minecraft_Server.launch
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/minerl/Malmo/Minecraft/Paulscode IBXM Library License.txt b/minerl/Malmo/Minecraft/Paulscode IBXM Library License.txt
new file mode 100755
index 000000000..d4884b071
--- /dev/null
+++ b/minerl/Malmo/Minecraft/Paulscode IBXM Library License.txt
@@ -0,0 +1,10 @@
+IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of mumart nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/minerl/Malmo/Minecraft/Paulscode SoundSystem CodecIBXM License.txt b/minerl/Malmo/Minecraft/Paulscode SoundSystem CodecIBXM License.txt
new file mode 100755
index 000000000..a68a49478
--- /dev/null
+++ b/minerl/Malmo/Minecraft/Paulscode SoundSystem CodecIBXM License.txt
@@ -0,0 +1,40 @@
+SoundSystem CodecIBXM Class License:
+
+You are free to use this class for any purpose, commercial or otherwise.
+You may modify this class or source code, and distribute it any way you
+like, provided the following conditions are met:
+
+1) You may not falsely claim to be the author of this class or any
+ unmodified portion of it.
+2) You may not copyright this class or a modified version of it and then
+ sue me for copyright infringement.
+3) If you modify the source code, you must clearly document the changes
+ made before redistributing the modified source code, so other users know
+ it is not the original code.
+4) You are not required to give me credit for this class in any derived
+ work, but if you do, you must also mention my website:
+ http://www.paulscode.com
+5) I the author will not be responsible for any damages (physical,
+ financial, or otherwise) caused by the use if this class or any
+ portion of it.
+6) I the author do not guarantee, warrant, or make any representations,
+ either expressed or implied, regarding the use of this class or any
+ portion of it.
+
+Author: Paul Lamb
+http://www.paulscode.com
+
+
+This software is based on or using the IBXM library available from
+http://www.geocities.com/sunet2000/
+
+
+IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of mumart nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/minerl/Malmo/Minecraft/README.txt b/minerl/Malmo/Minecraft/README.txt
new file mode 100755
index 000000000..bf31cf997
--- /dev/null
+++ b/minerl/Malmo/Minecraft/README.txt
@@ -0,0 +1,71 @@
+-------------------------------------------
+Source installation information for modders
+-------------------------------------------
+This code follows the Minecraft Forge installation methodology. It will apply
+some small patches to the vanilla MCP source code, giving you and it access
+to some of the data and functions you need to build a successful mod.
+
+Note also that the patches are built against "unrenamed" MCP source code (aka
+srgnames) - this means that you will not be able to read them directly against
+normal code.
+
+Source pack installation information:
+
+Standalone source installation
+==============================
+
+Step 1: Open your command-line and browse to the folder where you extracted the zip file.
+
+Step 2: Copy eclipse-ORIGINAL to eclipse. (This helps avoid polluting the git status.)
+
+Step 3: Once you have a command window up in the folder that the downloaded material was placed, type:
+
+Windows: "gradlew setupDecompWorkspace"
+Linux/Mac OS: "./gradlew setupDecompWorkspace"
+
+Step 4: After all that finished, you're left with a choice.
+For eclipse, run "gradlew eclipse" (./gradlew eclipse if you are on Mac/Linux)
+
+If you preffer to use IntelliJ, steps are a little different.
+1. Open IDEA, and import project.
+2. Select your build.gradle file and have it import.
+3. Once it's finished you must close IntelliJ and run the following command:
+
+"gradlew genIntellijRuns" (./gradlew genIntellijRuns if you are on Mac/Linux)
+
+4. For overclocking to work, the JVM needs this argument:
+ -Dfml.coreMods.load=com.microsoft.Malmo.OverclockingPlugin
+ Add it using whatever method IntelliJ provides.
+
+Step 5: Run "gradlew build" ("./gradlew build" if you are on Mac/Linux)
+
+Step 6: Open Eclipse and switch your workspace to /eclipse/ (if you use IDEA, it should automatically start on your project)
+
+Step 7: Right click on the project root ('MDKExample') and select Refactor > Rename... and enter 'Minecraft' as the new name.
+
+Step 8: To debug or run, use the Java Application > Minecraft_Client target.
+
+If at any point you are missing libraries in your IDE, or you've run into problems you can run "gradlew --refresh-dependencies" to refresh the local cache. "gradlew clean" to reset everything {this does not effect your code} and then start the processs again.
+
+Should it still not work,
+Refer to #ForgeGradle on EsperNet for more information about the gradle environment.
+
+Tip:
+If you do not care about seeing Minecraft's source code you can replace "setupDecompWorkspace" with one of the following:
+"setupDevWorkspace": Will patch, deobfusicated, and gather required assets to run minecraft, but will not generated human readable source code.
+"setupCIWorkspace": Same as Dev but will not download any assets. This is useful in build servers as it is the fastest because it does the least work.
+
+Tip:
+When using Decomp workspace, the Minecraft source code is NOT added to your workspace in a editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes and usually can be accessed under the 'referenced libraries' section of your IDE.
+
+Forge source installation
+=========================
+MinecraftForge ships with this code and installs it as part of the forge
+installation process, no further action is required on your part.
+
+LexManos' Install Video
+=======================
+https://www.youtube.com/watch?v=8VEdtQLuLO0&feature=youtu.be
+
+For more details update more often refer to the Forge Forums:
+http://www.minecraftforge.net/forum/index.php/topic,14048.0.html
diff --git a/minerl/Malmo/Minecraft/build.gradle b/minerl/Malmo/Minecraft/build.gradle
new file mode 100755
index 000000000..88b281ba7
--- /dev/null
+++ b/minerl/Malmo/Minecraft/build.gradle
@@ -0,0 +1,349 @@
+// For those who want the bleeding edge
+buildscript {
+ repositories {
+
+ maven { url 'https://jitpack.io' }
+ jcenter()
+ mavenCentral()
+ maven {
+ url = "http://files.minecraftforge.net/maven"
+ }
+ maven {
+ name = "sonatype"
+ url = "https://oss.sonatype.org/content/repositories/snapshots/"
+ }
+ }
+ dependencies {
+ classpath 'org.ow2.asm:asm:6.0'
+ classpath('com.github.SpongePowered:MixinGradle:dcfaf61'){ // 0.6
+ // Because forgegradle requires 6.0 (not -debug-all) while mixingradle depends on 5.0
+ // and putting mixin right here will place it before forge in the class loader
+ exclude group: 'org.ow2.asm', module: 'asm-debug-all'
+ }
+ classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT'
+ }
+}
+
+plugins {
+ id 'java'
+ id 'com.github.johnrengelman.shadow' version '1.2.4'
+}
+
+// For versions >= 1.8
+apply plugin: 'net.minecraftforge.gradle.forge'
+apply plugin: 'org.spongepowered.mixin'
+// For versions < 1.8
+// apply plugin: 'forge'
+
+// ext {
+// mixinSrg = new File(project.buildDir, 'tmp/mixins/mixins.srg')
+// mixinRefMap = new File(project.buildDir, 'tmp/mixins/mixins.malmo.refmap.json')
+// }
+
+
+// Read the version number from the Mod's version properties file.
+if (!file('src/main/resources/version.properties').exists()) {
+ ant.fail("version.properties file is missing - this is created automatically by CMake. If you are building from source, make sure you have built the full source tree, not just the Minecraft folder.")
+}
+def propFile = file('src/main/resources/version.properties')
+def versionProp = new Properties()
+versionProp.load(propFile.newReader())
+version = versionProp['malmomod.version']
+
+group= "com.microsoft.MalmoMod" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
+archivesBaseName = "MalmoMod"
+
+
+
+minecraft {
+ version = "1.11.2-13.20.0.2228"
+ runDir = "run"
+
+ // the mappings can be changed at any time, and must be in the following format.
+ // snapshot_YYYYMMDD snapshot are built nightly.
+ // stable_# stables are built at the discretion of the MCP team.
+ // Use non-default mappings at your own risk. they may not allways work.
+ // simply re-run your setup task after changing the mappings to update your workspace.
+ mappings = "snapshot_20161220"
+ makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
+}
+
+
+runClient {
+ if (project.hasProperty("runDir")) {
+ minecraft.runDir = project.getProperty("runDir")
+ }
+}
+
+repositories {
+ maven {
+ name = "SpongePowered Repo"
+ url = "http://repo.spongepowered.org/maven/"
+ }
+ maven { url 'https://jitpack.io' }
+}
+
+// Add the overclocking plugin to the manifest so that it is loaded when running in a non-dev environment (eg from the launcher)
+jar {
+
+ manifest {
+ attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker',
+ 'TweakOrder': '0',
+ 'FMLCorePluginContainsFMLMod': 'true',
+ 'FMLCorePlugin': 'com.microsoft.Malmo.OverclockingPlugin',
+ 'FMLAT': 'malmomod_at.cfg'
+ }
+ manifest {
+ attributes 'FMLCorePlugin': 'com.microsoft.Malmo.OverclockingPlugin',
+ 'FMLCorePluginContainsFMLMod': 'true'
+ }
+}
+
+// And add to the jvm args so that it is also loaded when running using gradle runClient:
+JavaExec exec = project.getTasks().getByName("runClient")
+exec.jvmArgs(["-Dfml.coreMods.load=com.microsoft.Malmo.OverclockingPlugin","-Xmx2G"])
+// ForgeGradle automatically sets the runClient task's outputs to be the runDir above (eg "run"). This
+// means that gradle will helpfully try to take a snapshot of the complete contents of the run folder in order
+// to carry out up-to-date checks for any tasks that depend on runClient.
+// The run folder also contains the logs folder, which, on Windows at least, can contain locked
+// TCP log files, which gradle won't be able to checksum, meaning that the runClient task will fail
+// if there is already a Minecraft running and logging.
+
+// To fix this issue, uncomment the following to set the outputs to a dummy value:
+// exec.getOutputs().files.setFrom(file("dummy_value"))
+
+// We also force the up-to-date check to return false, since the user will ALWAYS want runClient to run.
+exec.getOutputs().upToDateWhen( { return false } )
+
+dependencies {
+ // compile 'com.github.SpongePowered:Mixin:404f5da' // 0.7.5-SNAPSHOT
+ // ^ mixin doesn't compile on jitpack, so we'll have to depend on the SNAPSHOT and build it manually for reprod
+ //shade group: 'org.apache.commons', name: 'com.springsource.org.apache.commons.codec', version: '1.6.0'
+ compile 'org.spongepowered:mixin:0.7.5-SNAPSHOT'
+
+}
+
+processResources
+{
+ // this will ensure that this task is redone when the versions change.
+ inputs.property "version", project.version
+ inputs.property "mcversion", project.minecraft.version
+
+ // replace stuff in mcmod.info, nothing else
+ from(sourceSets.main.resources.srcDirs) {
+ include 'mcmod.info'
+
+ // replace version and mcversion
+ expand 'version':project.version, 'mcversion':project.minecraft.version
+ }
+
+ // copy everything else, thats not the mcmod.info
+ from(sourceSets.main.resources.srcDirs) {
+ exclude 'mcmod.info'
+ }
+}
+
+if (JavaVersion.current().isJava8Compatible())
+{
+ allprojects
+ {
+ tasks.withType(Javadoc)
+ {
+ options.addStringOption('Xdoclint:none', '-quiet')
+ }
+ }
+}
+
+// Mixin stuff.
+
+sourceSets {
+ main {
+ ext.refMap = "mixins.malmomod.refmap.json"
+ }
+}
+
+
+
+task copySrg(type: Copy, dependsOn: 'genSrgs') {
+ from {project.tasks.genSrgs.mcpToSrg}
+ into 'build'
+}
+
+setupDecompWorkspace.dependsOn copySrg
+setupDevWorkspace.dependsOn copySrg
+project.tasks.idea.dependsOn copySrg
+
+
+// Mixin uses multiple HashMaps to generate the refmap.
+// HashMaps are unordered collections and as such do not produce deterministic output.
+// To fix that, we simply sort the refmap json file.
+import groovy.json.JsonSlurper
+import groovy.json.JsonOutput
+compileJava.doLast {
+ File refmapFile = compileJava.ext.refMapFile
+ if (refmapFile.exists()) {
+ def ordered
+ ordered = {
+ if (it instanceof Map) {
+ def sorted = new TreeMap(it)
+ sorted.replaceAll { k, v -> ordered(v) }
+ sorted
+ } else if (it instanceof List) {
+ it.replaceAll { v -> ordered(v) }
+ } else {
+ it
+ }
+ }
+ def json = JsonOutput.toJson(ordered(new JsonSlurper().parse(refmapFile)))
+ refmapFile.withWriter { it.write json }
+ }
+}
+
+
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
+import java.util.zip.ZipOutputStream
+
+import org.objectweb.asm.*
+
+import static org.objectweb.asm.Opcodes.ASM5
+
+// MC binaries were complied with a java version that produces invalid class files under certain circumstances
+// This causes setupCIWorkspace to be insufficient for compiling.
+// Related JDK bug: https://bugs.openjdk.java.net/browse/JDK-8066725
+// As a workaround, to use setupCIWorkspace on Drone, we modify the bin jar in-place and remove all parameter annotations.
+// WARNING: This piece of code ignores any and all gradle conventions and will probably fail horribly when run outside
+// of a single-use environment (e.g. Drone). Use setupDecompWorkspace for normal use.
+def annotationWorkaround = {
+ println "Applying RuntimeInvisibleParameterAnnotations workaround..."
+ File jar = getOutJar()
+ File tmp = new File((File) getTemporaryDir(), "workaround.jar")
+ tmp.withOutputStream {
+ new ZipOutputStream(it).withStream { dst ->
+ new ZipFile(jar).withCloseable { src ->
+ src.entries().each {
+ if (it.name.startsWith("net/minecraft/") && it.name.endsWith(".class")) {
+ def cw = new ClassWriter(0)
+ def cv = new ClassVisitor(ASM5, cw) {
+ @Override
+ MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ return new MethodVisitor(ASM5, cv.visitMethod(access, name, desc, signature, exceptions)) {
+ @Override
+ AnnotationVisitor visitParameterAnnotation(int parameter, String pdesc, boolean visible) {
+ return null // Strip all parameter annotations
+ }
+ }
+ }
+ }
+ new ClassReader(src.getInputStream(it)).accept(cv, 0)
+ dst.putNextEntry(new ZipEntry(it.name))
+ dst.write(cw.toByteArray())
+ } else {
+ dst.putNextEntry(it)
+ dst.write(src.getInputStream(it).bytes)
+ }
+ }
+ }
+ }
+ }
+ jar.delete()
+ tmp.renameTo(jar)
+}
+
+tasks.deobfMcMCP.doLast annotationWorkaround
+
+
+
+// -------------- Task 'jaxb' runs xjc to make java files from XSD files --------------------
+
+gradle.projectsEvaluated {
+ // compileJava target isn't available until after forge has initialized things
+ compileJava.dependsOn copySrg
+ compileJava.dependsOn jaxb
+ compileJava.dependsOn copyModToClient
+ compileJava.dependsOn copyModToServer
+ 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 '../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')
+ }
+}
+
+// -------------- Task 'copyMds' copied the .md files into the javadoc folder --------------------
+
+task copyMds(type: Copy) {
+ from 'src/main/java/'
+ into 'build/docs/javadoc'
+ include ('**/*.md')
+}
+
+// -------------- Task 'copyMod' copies the Mod file into the Minecraft client and server mods folders --------------------
+
+task copyModToClient(type: Copy) {
+ from 'build/libs/'
+ into '../Minecraft/.minecraft/mods'
+ include ('*.jar')
+}
+
+task copyModToServer(type: Copy) {
+ from 'build/libs/'
+ into '../Minecraft/.minecraftserver/mods'
+ include ('*.jar')
+}
+
+// Interesting. We've packaged forge gradle into the full jar and use gradle inherently??
+shadowJar {
+ classifier = 'fat'
+ configurations = [project.configurations.all]
+ manifest {
+ attributes "Main-Class": "com.microsoft.Malmo.Launcher.GradleStart"
+ }
+}
+
+javadoc.dependsOn copyMds
diff --git a/minerl/Malmo/Minecraft/gradle/wrapper/gradle-wrapper.jar b/minerl/Malmo/Minecraft/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 000000000..30d399d8d
Binary files /dev/null and b/minerl/Malmo/Minecraft/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/minerl/Malmo/Minecraft/gradle/wrapper/gradle-wrapper.properties b/minerl/Malmo/Minecraft/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 000000000..e18cba72f
--- /dev/null
+++ b/minerl/Malmo/Minecraft/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Sep 14 12:28:28 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip
diff --git a/minerl/Malmo/Minecraft/gradlew b/minerl/Malmo/Minecraft/gradlew
new file mode 100755
index 000000000..91a7e269e
--- /dev/null
+++ b/minerl/Malmo/Minecraft/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/minerl/Malmo/Minecraft/gradlew.bat b/minerl/Malmo/Minecraft/gradlew.bat
new file mode 100755
index 000000000..8a0b282aa
--- /dev/null
+++ b/minerl/Malmo/Minecraft/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/minerl/Malmo/Minecraft/launchClient.bat b/minerl/Malmo/Minecraft/launchClient.bat
new file mode 100755
index 000000000..2833d536c
--- /dev/null
+++ b/minerl/Malmo/Minecraft/launchClient.bat
@@ -0,0 +1,104 @@
+@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
+:: 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"
+
+:: 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
+)
+
+:: Now all supplied options are stored in variables whose names are the
+:: option names. Missing options have the default value, or are undefined if
+:: there is no default.
+:: To get the value of a single parameter, just remember to include the `-`
+:: eg: echo The value of -username is: !-username!
+
+REM now output the config file:
+if not exist run\config\ (
+ mkdir run\config
+)
+(
+echo # Configuration file
+echo # Autogenerated from command-line options
+echo.
+echo malmoports {
+echo I:portOverride=!-port!
+echo }
+echo malmoscore {
+echo I:policy=!-scorepolicy!
+echo }
+echo malmoperformance {
+echo I:outDir=$performanceDir
+echo }
+echo malmoseed {
+echo I:seed=$seed
+echo }
+if "!-env!"=="true" (
+ echo envtype {
+ echo B:env=!-env!
+ echo }
+ )
+if "!-replaceable!"=="true" (
+ echo runtype {
+ echo B:replaceable=!-replaceable!
+ echo }
+ )
+) > "run\config\malmomodCLIENT.cfg"
+
+:launchLoop
+REM finally run Minecraft:
+call gradlew runClient --no-daemon -PrunDir="!-runDir!"
+if "!-replaceable!"=="true" (
+ goto :launchLoop
+)
diff --git a/minerl/Malmo/Minecraft/launchClient.sh b/minerl/Malmo/Minecraft/launchClient.sh
new file mode 100755
index 000000000..d844a430f
--- /dev/null
+++ b/minerl/Malmo/Minecraft/launchClient.sh
@@ -0,0 +1,121 @@
+#!/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"
+
+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;;
+ *) 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
+
+
+configDir="$runDir/config"
+
+# Now write the configuration file
+if [ ! -d $configDir ]; then
+ mkdir $configDir
+fi
+echo "# Configuration file
+# Autogenerated from command-line options
+
+malmoports {
+ I:portOverride=$port
+}
+malmoscore {
+ I:policy=$scorepolicy
+}
+
+malmoperformance {
+ I:outDir=$performanceDir
+}
+
+malmoseed {
+ I:seed=$seed
+}
+" > $configDir/malmomodCLIENT.cfg
+
+if [ $replaceable -gt 0 ]; then
+ echo "runtype {
+ B:replaceable=true
+}
+" >> $configDir/malmomodCLIENT.cfg
+fi
+
+
+if [ $env -gt 0 ]; then
+ echo "envtype {
+ B:env=true
+}
+" >> $configDir/malmomodCLIENT.cfg
+fi
+
+cat $configDir/malmomodCLIENT.cfg
+
+echo "$runDir"
+# Finally we can launch the Mod, which will load the config file
+# ./gradlew makeStart
+
+# ./gradlew setupDecompWorkspace
+# ./gradlew build
+# gradle does not respect --gradle-user-home when it comes to where to download itself
+# rather, it is set in gradle.properties and is controlled by an env variable
+# If build/libs/MalmoMod-0.37.0-fat.jar does not exist change command to 'test'
+echo $MINERL_FORCE_BUILD
+
+if [ ! -e build/libs/MalmoMod-0.37.0-fat.jar ] || [ "$MINERL_FORCE_BUILD" == "1" ]; then
+ echo "HELLO"
+ cmd="./gradlew runClient --stacktrace -PrunDir=$runDir"
+else
+
+ export GRADLE_USER_HOME=${runDir}/gradle
+ cd $runDir
+ cmd="java -Dfml.coreMods.load=com.microsoft.Malmo.OverclockingPlugin -Xmx2G -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -jar ../build/libs/MalmoMod-0.37.0-fat.jar"
+fi
+# If build/libs/MalmoMod-0.37.0-fat.jar does not exist change command to 'test'
+
+# TODO (R): Decide if this dependency on xvfb-run is deserved. Maybe this should go externally!
+if [ $(uname) == 'Linux' ]; then
+ xvfb-run -a -s "-screen 0 1024x768x24" $cmd
+else
+ $cmd
+fi
+[ $replaceable -gt 0 ]
+
diff --git a/minerl/Malmo/Minecraft/launch_minecraft_in_background.py b/minerl/Malmo/Minecraft/launch_minecraft_in_background.py
new file mode 100644
index 000000000..10d2c929a
--- /dev/null
+++ b/minerl/Malmo/Minecraft/launch_minecraft_in_background.py
@@ -0,0 +1,99 @@
+from __future__ import print_function
+# ------------------------------------------------------------------------------------------------
+# 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.
+# ------------------------------------------------------------------------------------------------
+
+# Used for integration tests.
+
+from builtins import str
+from builtins import range
+import io
+import os
+import platform
+import socket
+import subprocess
+import sys
+import time
+
+
+def _port_has_listener(port):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ result = sock.connect_ex(('127.0.0.1', port))
+ sock.close()
+ return result == 0
+
+
+def launch_minecraft_in_background(minecraft_path, ports=None, timeout=360, replaceable=False, score=False):
+ if ports is None:
+ ports = []
+ if len(ports) == 0:
+ ports = [10000] # Default
+ processes = []
+ for port in ports:
+ if _port_has_listener(port):
+ print('Something is listening on port', port, '- will assume Minecraft is running.')
+ continue
+ replaceable_arg = " -replaceable " if replaceable else ""
+ scorepolicy_arg = " -scorepolicy " if score else ""
+ scorepolicy_value = " 2 " if score else ""
+ print('Nothing is listening on port', port, '- will attempt to launch Minecraft from a new terminal.')
+ if os.name == 'nt':
+ args = [minecraft_path + '/launchClient.bat', '-port', str(port), replaceable_arg.strip(),
+ scorepolicy_arg.strip(), scorepolicy_value.strip()]
+ p = subprocess.Popen([arg for arg in args if arg != ""],
+ creationflags=subprocess.CREATE_NEW_CONSOLE, close_fds=True)
+ elif sys.platform == 'darwin':
+ # Can't pass parameters to launchClient via Terminal.app, so create a small launch
+ # script instead.
+ # (Launching a process to run the terminal app to run a small launch script to run
+ # the launchClient script to run Minecraft... is it possible that this is not the most
+ # straightforward way to go about things?)
+ launcher_file = "/tmp/launcher_" + str(os.getpid()) + ".sh"
+ tmp_file = open(launcher_file, "w")
+ tmp_file.write(minecraft_path + '/launchClient.sh -port ' + str(port) +
+ replaceable_arg + scorepolicy_arg + scorepolicy_value)
+ tmp_file.close()
+ os.chmod(launcher_file, 0o700)
+ p = subprocess.Popen(['open', '-a', 'Terminal.app', launcher_file])
+ else:
+ p = subprocess.Popen(minecraft_path + "/launchClient.sh -port " + str(port) +
+ replaceable_arg + scorepolicy_arg + scorepolicy_value,
+ close_fds=True, shell=True,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ processes.append(p)
+ print('Giving Minecraft some time to launch... ')
+ launched = False
+ for _ in range(timeout // 3):
+ print('.', end=' ')
+ time.sleep(3)
+ if _port_has_listener(port):
+ print('ok')
+ launched = True
+ break
+ if not launched:
+ print('Minecraft not yet launched. Giving up.')
+ exit(1)
+ return processes
+
+
+if __name__ == "__main__":
+ minecraft_launch_path = os.path.dirname(os.path.abspath(__file__))
+ launch_ports = [int(port_arg) for port_arg in sys.argv[1:] if port_arg != "--replaceable" and port_arg != "--score"]
+ launch_minecraft_in_background(minecraft_launch_path, launch_ports, 300,
+ replaceable="--replaceable" in sys.argv,
+ score="--score" in sys.argv)
diff --git a/minerl/Malmo/Minecraft/run/config/forge.cfg b/minerl/Malmo/Minecraft/run/config/forge.cfg
new file mode 100644
index 000000000..03ad7307d
--- /dev/null
+++ b/minerl/Malmo/Minecraft/run/config/forge.cfg
@@ -0,0 +1,78 @@
+# Configuration file
+
+client {
+ # Replace the vanilla bucket models with Forges own dynamic bucket model. Unifies bucket visuals if a mod uses the Forge bucket model.
+ B:replaceVanillaBucketModel=false
+
+ # Toggle off to make missing model text in the gui fit inside the slot.
+ B:zoomInMissingModelTextInGui=false
+
+ # The timestamp of the last reminder to update to Java 8 in number of milliseconds since January 1, 1970, 00:00:00 GMT. Nag will show only once every 24 hours. To disable it set this to some really high number.
+ D:java8Reminder=0.0
+
+ # Disable culling of hidden faces next to stairs and slabs. Causes extra rendering, but may fix some resource packs that exploit this vanilla mechanic.
+ B:disableStairSlabCulling=false
+
+ # Enable forge to queue all chunk updates to the Chunk Update thread. May increase FPS significantly, but may also cause weird rendering lag. Not recommended for computers without a significant number of cores available.
+ B:alwaysSetupTerrainOffThread=false
+}
+
+
+general {
+ # Set to true to disable Forge's version check mechanics. Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github.
+ B:disableVersionCheck=false
+
+ # Controls the number threshold at which Packet51 is preferred over Packet52, default and minimum 64, maximum 1024
+ I:clumpingThreshold=64
+
+ # Set to true to enable the post initialization sorting of crafting recipes using Forge's sorter. May cause desyncing on conflicting recipes. MUST RESTART MINECRAFT IF CHANGED FROM THE CONFIG GUI.
+ B:sortRecipies=true
+
+ # Set this to true to remove any Entity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.
+ B:removeErroringEntities=false
+
+ # Set this to true to remove any TileEntity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.
+ B:removeErroringTileEntities=false
+
+ # Set this to true to check the entire entity's collision bounding box for ladders instead of just the block they are in. Causes noticeable differences in mechanics so default is vanilla behavior. Default: false
+ B:fullBoundingBoxLadders=false
+
+ # Control the range of sky blending for colored skies in biomes.
+ I:biomeSkyBlendRange <
+ 2
+ 4
+ 6
+ 8
+ 10
+ 12
+ 14
+ 16
+ 18
+ 20
+ 22
+ 24
+ 26
+ 28
+ 30
+ 32
+ 34
+ >
+
+ # Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic.
+ D:zombieBaseSummonChance=0.1
+
+ # Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic.
+ D:zombieBabyChance=0.05
+
+ # Enable the forge block rendering pipeline - fixes the lighting of custom models.
+ B:forgeLightPipelineEnabled=true
+ B:enableGlobalConfig=false
+}
+
+
+version_checking {
+ # Enable the entire mod update check system. This only applies to mods using the Forge system.
+ B:Global=true
+}
+
+
diff --git a/minerl/Malmo/Minecraft/run/config/forgeChunkLoading.cfg b/minerl/Malmo/Minecraft/run/config/forgeChunkLoading.cfg
new file mode 100644
index 000000000..d41b63169
--- /dev/null
+++ b/minerl/Malmo/Minecraft/run/config/forgeChunkLoading.cfg
@@ -0,0 +1,47 @@
+# Configuration file
+
+##########################################################################################################
+# defaults
+#--------------------------------------------------------------------------------------------------------#
+# Default configuration for forge chunk loading control
+##########################################################################################################
+
+defaults {
+ # Are mod overrides enabled?
+ B:enabled=true
+
+ # The default maximum number of chunks a mod can force, per ticket,
+ # for a mod without an override. This is the maximum number of chunks a single ticket can force.
+ I:maximumChunksPerTicket=25
+
+ # The default maximum ticket count for a mod which does not have an override
+ # in this file. This is the number of chunk loading requests a mod is allowed to make.
+ I:maximumTicketCount=200
+
+ # The number of tickets a player can be assigned instead of a mod. This is shared across all mods and it is up to the mods to use it.
+ I:playerTicketCount=500
+
+ # Unloaded chunks can first be kept in a dormant cache for quicker
+ # loading times. Specify the size (in chunks) of that cache here
+ I:dormantChunkCacheSize=0
+}
+
+
+##########################################################################################################
+# forge
+#--------------------------------------------------------------------------------------------------------#
+# Sample mod specific control section.
+# Copy this section and rename the with the modid for the mod you wish to override.
+# A value of zero in either entry effectively disables any chunkloading capabilities
+# for that mod
+##########################################################################################################
+
+forge {
+ # Maximum chunks per ticket for the mod.
+ I:maximumChunksPerTicket=25
+
+ # Maximum ticket count for the mod. Zero disables chunkloading capabilities.
+ I:maximumTicketCount=200
+}
+
+
diff --git a/minerl/herobraine/data/__init__.py b/minerl/Malmo/Minecraft/run/config/malmomodCLIENTPermanent.cfg
similarity index 100%
rename from minerl/herobraine/data/__init__.py
rename to minerl/Malmo/Minecraft/run/config/malmomodCLIENTPermanent.cfg
diff --git a/minerl/Malmo/Minecraft/run/options.txt b/minerl/Malmo/Minecraft/run/options.txt
new file mode 100644
index 000000000..5cf57361e
--- /dev/null
+++ b/minerl/Malmo/Minecraft/run/options.txt
@@ -0,0 +1,102 @@
+version:922
+invertYMouse:false
+mouseSensitivity:0.5
+fov:1.5
+gamma:2.0
+saturation:0.0
+renderDistance:4
+guiScale:0
+particles:2
+bobView:false
+anaglyph3d:false
+maxFps:-1
+fboEnable:true
+difficulty:3
+fancyGraphics:false
+ao:2
+renderClouds:true
+resourcePacks:[]
+incompatibleResourcePacks:[]
+lastServer:
+lang:en_us
+chatVisibility:0
+chatColors:true
+chatLinks:true
+chatLinksPrompt:true
+chatOpacity:1.0
+snooperEnabled:true
+fullscreen:false
+enableVsync:false
+useVbo:true
+hideServerAddress:false
+advancedItemTooltips:false
+pauseOnLostFocus:false
+touchscreen:false
+overrideWidth:0
+overrideHeight:0
+heldItemTooltips:false
+chatHeightFocused:1.0
+chatHeightUnfocused:0.44366196
+chatScale:1.0
+chatWidth:1.0
+showInventoryAchievementHint:true
+mipmapLevels:4
+forceUnicodeFont:false
+reducedDebugInfo:false
+useNativeTransport:true
+entityShadows:true
+mainHand:right
+attackIndicator:1
+showSubtitles:false
+realmsNotifications:true
+enableWeakAttacks:false
+autoJump:false
+key_key.attack:-100
+key_key.use:-99
+key_key.forward:17
+key_key.left:30
+key_key.back:31
+key_key.right:32
+key_key.jump:57
+key_key.sneak:42
+key_key.sprint:29
+key_key.drop:16
+key_key.inventory:18
+key_key.chat:20
+key_key.playerlist:15
+key_key.pickItem:-98
+key_key.command:53
+key_key.screenshot:60
+key_key.togglePerspective:63
+key_key.smoothCamera:0
+key_key.fullscreen:87
+key_key.spectatorOutlines:0
+key_key.swapHands:33
+key_key.hotbar.1:2
+key_key.hotbar.2:3
+key_key.hotbar.3:4
+key_key.hotbar.4:5
+key_key.hotbar.5:6
+key_key.hotbar.6:7
+key_key.hotbar.7:8
+key_key.hotbar.8:9
+key_key.hotbar.9:10
+key_key.toggleMalmo:28
+key_key.handyTestHook:22
+soundCategory_master:1.0
+soundCategory_music:0.0
+soundCategory_record:0.0
+soundCategory_weather:0.0
+soundCategory_block:0.0
+soundCategory_hostile:0.0
+soundCategory_neutral:0.0
+soundCategory_player:0.0
+soundCategory_ambient:0.0
+soundCategory_voice:0.0
+modelPart_cape:true
+modelPart_jacket:true
+modelPart_left_sleeve:true
+modelPart_right_sleeve:true
+modelPart_left_pants_leg:true
+modelPart_right_pants_leg:true
+modelPart_hat:true
diff --git a/minerl/Malmo/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientState.java b/minerl/Malmo/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientState.java
new file mode 100755
index 000000000..190dc484e
--- /dev/null
+++ b/minerl/Malmo/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientState.java
@@ -0,0 +1,59 @@
+// --------------------------------------------------------------------------------------------------
+// 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.Client;
+
+import com.microsoft.Malmo.IState;
+
+/** Set of states used by MissionStateTracker to ensure the Mod remains in a valid state.
+ * Note: these do not necessarily occur in the order presented here.
+ * If adding states here, please also add a MissionStateEpisode class to MissionStateTracker,
+ * and a line to the switch statement in MissionStateTracker.getStateEpisodeForState().
+ */
+public enum ClientState implements IState
+{
+ WAITING_FOR_MOD_READY,
+ DORMANT,
+ CREATING_HANDLERS,
+ EVALUATING_WORLD_REQUIREMENTS,
+ PAUSING_OLD_SERVER,
+ CLOSING_OLD_SERVER,
+ CREATING_NEW_WORLD,
+ WAITING_FOR_SERVER_READY,
+ RUNNING,
+ IDLING,
+ MISSION_ENDED,
+ MISSION_ABORTED,
+ WAITING_FOR_SERVER_MISSION_END,
+ // Error conditions:
+ ERROR_DUFF_HANDLERS,
+ ERROR_INTEGRATED_SERVER_UNREACHABLE,
+ ERROR_NO_WORLD,
+ ERROR_CANNOT_CREATE_WORLD,
+ ERROR_CANNOT_START_AGENT,
+ ERROR_LOST_NETWORK_CONNECTION,
+ ERROR_CANNOT_CONNECT_TO_SERVER,
+ ERROR_TIMED_OUT_WAITING_FOR_WORLD_CREATE,
+ ERROR_TIMED_OUT_WAITING_FOR_EPISODE_START,
+ ERROR_TIMED_OUT_WAITING_FOR_EPISODE_PAUSE,
+ ERROR_TIMED_OUT_WAITING_FOR_EPISODE_CLOSE,
+ ERROR_TIMED_OUT_WAITING_FOR_MISSION_END,
+ ERROR_LOST_AGENT,
+ ERROR_LOST_VIDEO
+}
diff --git a/minerl/Malmo/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientStateMachine.java b/minerl/Malmo/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientStateMachine.java
new file mode 100755
index 000000000..dc0656693
--- /dev/null
+++ b/minerl/Malmo/Minecraft/src/main/java/com/microsoft/Malmo/Client/ClientStateMachine.java
@@ -0,0 +1,2562 @@
+// --------------------------------------------------------------------------------------------------
+// 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.Client;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+
+import javax.xml.bind.JAXBException;
+import javax.xml.stream.XMLStreamException;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.client.gui.GuiDisconnected;
+import net.minecraft.client.gui.GuiIngameMenu;
+import net.minecraft.client.gui.GuiMainMenu;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.multiplayer.ThreadLanServerPing;
+import net.minecraft.client.multiplayer.WorldClient;
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.client.settings.GameSettings;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.init.Blocks;
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.crafting.FurnaceRecipes;
+import net.minecraft.launchwrapper.Launch;
+import net.minecraft.network.NetworkManager;
+import net.minecraft.server.integrated.IntegratedServer;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.text.TextComponentString;
+import net.minecraft.world.GameType;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.IChunkProvider;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent;
+import net.minecraftforge.fml.common.FMLCommonHandler;
+import net.minecraftforge.fml.common.Loader;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
+import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
+import net.minecraftforge.fml.common.gameevent.TickEvent.ServerTickEvent;
+
+import org.xml.sax.SAXException;
+
+import com.google.gson.JsonObject;
+import com.microsoft.Malmo.IState;
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.MalmoMod.IMalmoMessageListener;
+import com.microsoft.Malmo.MalmoMod.MalmoMessageType;
+import com.microsoft.Malmo.StateEpisode;
+import com.microsoft.Malmo.StateMachine;
+import com.microsoft.Malmo.Client.MalmoModClient.InputType;
+import com.microsoft.Malmo.MissionHandlerInterfaces.IVideoProducer;
+import com.microsoft.Malmo.MissionHandlerInterfaces.IWantToQuit;
+import com.microsoft.Malmo.MissionHandlers.MissionBehaviour;
+import com.microsoft.Malmo.MissionHandlers.MultidimensionalReward;
+import com.microsoft.Malmo.Schemas.AgentSection;
+import com.microsoft.Malmo.Schemas.AgentStart;
+import com.microsoft.Malmo.Schemas.ClientAgentConnection;
+import com.microsoft.Malmo.Schemas.MinecraftServerConnection;
+import com.microsoft.Malmo.Schemas.Mission;
+import com.microsoft.Malmo.Schemas.MissionDiagnostics;
+import com.microsoft.Malmo.Schemas.MissionEnded;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import com.microsoft.Malmo.Schemas.MissionResult;
+import com.microsoft.Malmo.Schemas.Reward;
+import com.microsoft.Malmo.Schemas.ServerSection.HumanInteraction;
+import com.microsoft.Malmo.Schemas.ModSettings;
+import com.microsoft.Malmo.Schemas.PosAndDirection;
+import com.microsoft.Malmo.Utils.AddressHelper;
+import com.microsoft.Malmo.Utils.AuthenticationHelper;
+import com.microsoft.Malmo.Utils.SchemaHelper;
+import com.microsoft.Malmo.Utils.ScreenHelper;
+import com.microsoft.Malmo.Utils.SeedHelper;
+import com.microsoft.Malmo.Utils.ScoreHelper;
+import com.microsoft.Malmo.Utils.TextureHelper;
+import com.microsoft.Malmo.Utils.ScreenHelper.TextCategory;
+import com.microsoft.Malmo.Utils.TCPInputPoller;
+import com.microsoft.Malmo.Utils.TCPInputPoller.CommandAndIPAddress;
+import com.microsoft.Malmo.Utils.TimeHelper.SyncTickEvent;
+import com.microsoft.Malmo.Utils.TCPSocketChannel;
+import com.microsoft.Malmo.Utils.TCPUtils;
+import com.microsoft.Malmo.Utils.TimeHelper;
+import com.mojang.authlib.properties.Property;
+
+/**
+ * Class designed to track and control the state of the mod, especially regarding mission launching/running.
+ * States are defined by the MissionState enum, and control is handled by
+ * MissionStateEpisode subclasses. The ability to set the state directly is
+ * restricted, but hooks such as onPlayerReadyForMission etc are exposed to
+ * allow subclasses to react to certain state changes.
+ * The ProjectMalmo mod app class inherits from this and uses these hooks to run missions.
+ */
+public class ClientStateMachine extends StateMachine implements IMalmoMessageListener
+{
+ private static final int WAIT_MAX_TICKS = 2000; // Over 1 minute and a half in client ticks.
+ private static final int VIDEO_MAX_WAIT = 90 * 1000; // Max wait for video in ms.
+ private static final String MISSING_MCP_PORT_ERROR = "no_mcp";
+ private static final String INFO_MCP_PORT = "info_mcp";
+ private static final String INFO_RESERVE_STATUS = "info_reservation";
+
+ private MissionInit currentMissionInit = null; // The MissionInit object for the mission currently being loaded/run.
+ private MissionBehaviour missionBehaviour = new MissionBehaviour();
+ private String missionQuitCode = ""; // The reason why this mission ended.
+ private MultidimensionalReward finalReward = new MultidimensionalReward(true); // The reward at the end of the mission, sent separately to ensure timely delivery.
+ private MissionDiagnostics missionEndedData = new MissionDiagnostics();
+ private ScreenHelper screenHelper = new ScreenHelper();
+ protected MalmoModClient inputController;
+
+ // Env service:
+ protected MalmoEnvServer envServer;
+
+ // Socket stuff:
+ protected TCPInputPoller missionPoller;
+ protected TCPInputPoller controlInputPoller;
+ protected int integratedServerPort;
+ String reservationID = ""; // empty if we are not reserved, otherwise "RESERVED" + the experiment ID we are reserved for.
+ long reservationExpirationTime = 0;
+ private TCPSocketChannel missionControlSocket;
+
+ private void reserveClient(String id)
+ {
+ synchronized(this.reservationID)
+ {
+ ClientStateMachine.this.getScreenHelper().clearFragment(INFO_RESERVE_STATUS);
+
+ // id is in the form :, where long is the length of time to keep the reservation for,
+ // and expID is the experimentationID used to ensure the client is reserved for the correct experiment.
+ int separator = id.indexOf(":");
+ if (separator == -1)
+ {
+ System.out.println("Error - malformed reservation request - client will not be reserved.");
+ this.reservationID = "";
+ }
+ else
+ {
+ long duration = Long.valueOf(id.substring(0, separator));
+ String expID = id.substring(separator + 1);
+ this.reservationExpirationTime = System.currentTimeMillis() + duration;
+ // We don't just use the id, in case users have supplied a blank string as their experiment ID.
+ this.reservationID = "RESERVED" + expID;
+ ClientStateMachine.this.getScreenHelper().addFragment("Reserved: " + expID, TextCategory.TXT_INFO, (int)duration);//INFO_RESERVE_STATUS);
+ }
+ }
+ }
+
+ private boolean isReserved()
+ {
+ synchronized(this.reservationID)
+ {
+ System.out.println("==== RES: " + this.reservationID + " - " + (this.reservationExpirationTime - System.currentTimeMillis()));
+ return !this.reservationID.isEmpty() && this.reservationExpirationTime > System.currentTimeMillis();
+ }
+ }
+
+ private boolean isAvailable(String id)
+ {
+ synchronized(this.reservationID)
+ {
+ return (this.reservationID.isEmpty() || this.reservationID.equals("RESERVED" + id) || System.currentTimeMillis() >= this.reservationExpirationTime);
+ }
+ }
+
+ private void cancelReservation()
+ {
+ synchronized(this.reservationID)
+ {
+ this.reservationID = "";
+ ClientStateMachine.this.getScreenHelper().clearFragment(INFO_RESERVE_STATUS);
+ }
+ }
+
+ protected TCPSocketChannel getMissionControlSocket() { return this.missionControlSocket; }
+
+ protected void createMissionControlSocket()
+ {
+ TCPUtils.LogSection ls = new TCPUtils.LogSection("Creating MissionControlSocket");
+ // Set up a TCP connection to the agent:
+ ClientAgentConnection cac = currentMissionInit().getClientAgentConnection();
+ if (this.missionControlSocket == null ||
+ this.missionControlSocket.getPort() != cac.getAgentMissionControlPort() ||
+ this.missionControlSocket.getAddress() == null ||
+ !this.missionControlSocket.isValid() ||
+ !this.missionControlSocket.isOpen() ||
+ !this.missionControlSocket.getAddress().equals(cac.getAgentIPAddress()))
+ {
+ if (this.missionControlSocket != null)
+ this.missionControlSocket.close();
+ this.missionControlSocket = new TCPSocketChannel(cac.getAgentIPAddress(), cac.getAgentMissionControlPort(), "mcp");
+ }
+ ls.close();
+ }
+
+ public ClientStateMachine(ClientState initialState, MalmoModClient inputController)
+ {
+ super(initialState);
+ this.inputController = inputController;
+
+ // Register ourself on the event busses, so we can harness the client tick:
+ MinecraftForge.EVENT_BUS.register(this);
+ MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_TEXT);
+ }
+
+ @Override
+ public void clearErrorDetails()
+ {
+ super.clearErrorDetails();
+ this.missionQuitCode = "";
+ }
+
+ @SubscribeEvent
+ public void onClientTick(TickEvent.ClientTickEvent ev)
+ {
+ // Use the client tick to ensure we regularly update our state (from the client thread)
+ updateState();
+ }
+
+ public ScreenHelper getScreenHelper()
+ {
+ return screenHelper;
+ }
+
+ @Override
+ public void onMessage(MalmoMessageType messageType, Map data)
+ {
+ if (messageType == MalmoMessageType.SERVER_TEXT)
+ {
+ String chat = data.get("chat");
+ if (chat != null)
+ Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessageWithOptionalDeletion(new TextComponentString(chat), 1);
+ else
+ {
+ String text = data.get("text");
+ ScreenHelper.TextCategory category = ScreenHelper.TextCategory.valueOf(data.get("category"));
+ String strtime = data.get("displayTime");
+ Integer time = (strtime != null) ? Integer.valueOf(strtime) : null;
+ this.getScreenHelper().addFragment(text, category, time);
+ }
+ }
+ }
+
+ @Override
+ protected String getName()
+ {
+ return "CLIENT";
+ }
+
+ @Override
+ protected void onPreStateChange(IState toState)
+ {
+ this.getScreenHelper().addFragment("CLIENT: " + toState, ScreenHelper.TextCategory.TXT_CLIENT_STATE, "");
+ }
+
+ /**
+ * Create the episode object for the requested state.
+ *
+ * @param state the state the mod is entering
+ * @return a MissionStateEpisode that localises all the logic required to run this state
+ */
+ @Override
+ protected StateEpisode getStateEpisodeForState(IState state)
+ {
+ if (!(state instanceof ClientState))
+ return null;
+
+ ClientState cs = (ClientState) state;
+ switch (cs) {
+ case WAITING_FOR_MOD_READY:
+ return new InitialiseClientModEpisode(this);
+ case DORMANT:
+ return new DormantEpisode(this);
+ case CREATING_HANDLERS:
+ return new CreateHandlersEpisode(this);
+ case EVALUATING_WORLD_REQUIREMENTS:
+ return new EvaluateWorldRequirementsEpisode(this);
+ case PAUSING_OLD_SERVER:
+ return new PauseOldServerEpisode(this);
+ case CLOSING_OLD_SERVER:
+ return new CloseOldServerEpisode(this);
+ case CREATING_NEW_WORLD:
+ return new CreateWorldEpisode(this);
+ case WAITING_FOR_SERVER_READY:
+ return new WaitingForServerEpisode(this);
+ case RUNNING:
+ return new MissionRunningEpisode(this);
+ case IDLING:
+ return new MissionIdlingEpisode(this);
+ case MISSION_ENDED:
+ return new MissionEndedEpisode(this, MissionResult.ENDED, false, false, true);
+ case ERROR_DUFF_HANDLERS:
+ return new MissionEndedEpisode(this, MissionResult.MOD_FAILED_TO_INSTANTIATE_HANDLERS, true, true, true);
+ case ERROR_INTEGRATED_SERVER_UNREACHABLE:
+ return new MissionEndedEpisode(this, MissionResult.MOD_SERVER_UNREACHABLE, true, true, true);
+ case ERROR_NO_WORLD:
+ return new MissionEndedEpisode(this, MissionResult.MOD_HAS_NO_WORLD_LOADED, true, true, true);
+ case ERROR_CANNOT_CREATE_WORLD:
+ return new MissionEndedEpisode(this, MissionResult.MOD_FAILED_TO_CREATE_WORLD, true, true, true);
+ case ERROR_CANNOT_START_AGENT: // run-ons deliberate
+ case ERROR_LOST_AGENT:
+ case ERROR_LOST_VIDEO:
+ return new MissionEndedEpisode(this, MissionResult.MOD_HAS_NO_AGENT_AVAILABLE, true, true, false);
+ case ERROR_LOST_NETWORK_CONNECTION: // run-on deliberate
+ case ERROR_CANNOT_CONNECT_TO_SERVER:
+ return new MissionEndedEpisode(this, MissionResult.MOD_CONNECTION_FAILED, true, false, true); // No point trying to inform the server - we can't reach it anyway!
+ case ERROR_TIMED_OUT_WAITING_FOR_EPISODE_START: // run-ons deliberate
+ case ERROR_TIMED_OUT_WAITING_FOR_EPISODE_PAUSE:
+ case ERROR_TIMED_OUT_WAITING_FOR_EPISODE_CLOSE:
+ case ERROR_TIMED_OUT_WAITING_FOR_MISSION_END:
+ case ERROR_TIMED_OUT_WAITING_FOR_WORLD_CREATE:
+ return new MissionEndedEpisode(this, MissionResult.MOD_CONNECTION_FAILED, true, true, true);
+ case MISSION_ABORTED:
+ return new MissionEndedEpisode(this, MissionResult.MOD_SERVER_ABORTED_MISSION, true, false, true); // Don't inform the server - it already knows (we're acting on its notification)
+ case WAITING_FOR_SERVER_MISSION_END:
+ return new WaitingForServerMissionEndEpisode(this);
+ default:
+ break;
+ }
+ return null;
+ }
+
+ protected MissionInit currentMissionInit()
+ {
+ return this.currentMissionInit;
+ }
+
+ protected MissionBehaviour currentMissionBehaviour()
+ {
+ return this.missionBehaviour;
+ }
+
+ protected class MissionInitResult
+ {
+ public MissionInit missionInit = null;
+ public boolean wasMissionInit = false;
+ public String error = null;
+ }
+
+ protected MissionInitResult decodeMissionInit(String command)
+ {
+ MissionInitResult result = new MissionInitResult();
+ if (command == null)
+ {
+ result.error = "Null command passed.";
+ return result;
+ }
+
+ String rootNodeName = SchemaHelper.getRootNodeName(command);
+ if (rootNodeName != null && rootNodeName.equals("MissionInit"))
+ {
+ result.wasMissionInit = true;
+ // Attempt to decode the MissionInit XML string.
+ try
+ {
+ result.missionInit = (MissionInit) SchemaHelper.deserialiseObject(command, "MissionInit.xsd", MissionInit.class);
+ }
+ catch (JAXBException e)
+ {
+ System.out.println("JAXB exception: " + e);
+ if (e.getMessage() != null)
+ result.error = e.getMessage();
+ else if (e.getLinkedException() != null && e.getLinkedException().getMessage() != null)
+ result.error = e.getLinkedException().getMessage();
+ else
+ result.error = "Unspecified problem parsing MissionInit - check your Mission xml.";
+ }
+ catch (SAXException e)
+ {
+ System.out.println("SAX exception: " + e);
+ result.error = e.getMessage();
+ }
+ catch (XMLStreamException e)
+ {
+ System.out.println("XMLStreamException: " + e);
+ result.error = e.getMessage();
+ }
+ }
+ return result;
+ }
+
+ protected boolean areMissionsEqual(Mission m1, Mission m2)
+ {
+ return true;
+ // FIX NEEDED - the following code fails because m1 may have been
+ // modified since loading - eg the MazeDecorator writes directly to the XML,
+ // and the use of some of the getters in the XSD-generated code can cause extra
+ // (empty) nodes to be added to the resulting XML.
+ // We need a more robust way of comparing two mission objects.
+ // For now, simply return true, since a false positive is less dangerous
+ // than a false negative.
+ /*
+ try {
+ String s1 = SchemaHelper.serialiseObject(m1, Mission.class);
+ String s2 = SchemaHelper.serialiseObject(m2, Mission.class);
+ return s1.compareTo(s2) == 0;
+ } catch( JAXBException e ) {
+ System.out.println("JAXB exception: " + e);
+ return false;
+ }*/
+ }
+
+ /**
+ * Set up the mission poller.
+ * This is called during the initialisation episode, but also needs to be
+ * available for other episodes in case the configuration changes, resulting
+ * in changes to the ports.
+ *
+ * @throws UnknownHostException
+ */
+ protected void initialiseComms() throws UnknownHostException
+ {
+ // Start polling for missions:
+ if (this.missionPoller != null)
+ {
+ this.missionPoller.stopServer();
+ }
+
+ this.missionPoller = new TCPInputPoller(AddressHelper.getMissionControlPortOverride(), AddressHelper.MIN_MISSION_CONTROL_PORT, AddressHelper.MAX_FREE_PORT, true, "mcp")
+ {
+ @Override
+ public void onError(String error, DataOutputStream dos)
+ {
+ System.out.println("SENDING ERROR: " + error);
+ try
+ {
+ dos.writeInt(error.length());
+ dos.writeBytes(error);
+ dos.flush();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+
+ private void reply(String reply, DataOutputStream dos)
+ {
+ System.out.println("REPLYING WITH: " + reply);
+ try
+ {
+ dos.writeInt(reply.length());
+ dos.writeBytes(reply);
+ dos.flush();
+ }
+ catch (IOException e)
+ {
+ System.out.println("Failed to reply to message!");
+ }
+ }
+
+ @Override
+ public boolean onCommand(String command, String ipFrom, DataOutputStream dos)
+ {
+ System.out.println("Received from " + ipFrom + ":" + command);
+ boolean keepProcessing = false;
+
+ // Possible commands:
+ // 1: MALMO_REQUEST_CLIENT::
+ // 2: MALMO_CANCEL_REQUEST
+ // 3: MALMO_FIND_SERVER
+ // 4: MALMO_KILL_CLIENT
+ // 5: MissionInit
+
+ String reservePrefixGeneral = "MALMO_REQUEST_CLIENT:";
+ String reservePrefix = reservePrefixGeneral + Loader.instance().activeModContainer().getVersion() + ":";
+ String findServerPrefix = "MALMO_FIND_SERVER";
+ String cancelRequestCommand = "MALMO_CANCEL_REQUEST";
+ String killClientCommand = "MALMO_KILL_CLIENT";
+
+ if (command.startsWith(reservePrefix))
+ {
+ // Reservation request.
+ // We either reply with MALMOOK, if we are free, or MALMOBUSY if not.
+ IState currentState = getStableState();
+ if (currentState != null && currentState.equals(ClientState.DORMANT) && !isReserved())
+ {
+ reserveClient(command.substring(reservePrefix.length()));
+ reply("MALMOOK", dos);
+ }
+ else
+ {
+ // We're busy - we can't be reserved.
+ reply("MALMOBUSY", dos);
+ }
+ }
+ else if (command.startsWith(reservePrefixGeneral))
+ {
+ // Reservation request, but it didn't match the request we expect, above.
+ // This happens if the agent sending the request is running a different version of Malmo -
+ // a version mismatch error.
+ reply("MALMOERRORVERSIONMISMATCH in reservation string (Got " + command + ", expected " + reservePrefix + " - check your path for old versions of MalmoPython/MalmoJava/Malmo.lib etc)", dos);
+ }
+ else if (command.equals(cancelRequestCommand))
+ {
+ // If we've been reserved, cancel the reservation.
+ if (isReserved())
+ {
+ cancelReservation();
+ reply("MALMOOK", dos);
+ }
+ else
+ {
+ // We weren't reserved in the first place - something is odd.
+ reply("MALMOERRORAttempt to cancel a reservation that was never made.", dos);
+ }
+ }
+ else if (command.startsWith(findServerPrefix))
+ {
+ // Request to find the server for the given experiment ID.
+ String expID = command.substring(findServerPrefix.length());
+ if (currentMissionInit() != null && currentMissionInit().getExperimentUID().equals(expID))
+ {
+ // Our Experiment IDs match, so we are running the same experiment.
+ // Return the port and server IP address to the caller:
+ MinecraftServerConnection msc = currentMissionInit().getMinecraftServerConnection();
+ if (msc == null)
+ reply("MALMONOSERVERYET", dos); // Mission might be starting up.
+ else
+ reply("MALMOS" + msc.getAddress().trim() + ":" + msc.getPort(), dos);
+ }
+ else
+ {
+ // We don't have a MissionInit ourselves, or we're running a different experiment,
+ // so we can't help.
+ reply("MALMONOSERVER", dos);
+ }
+ }
+ else if (command.equals(killClientCommand))
+ {
+ // Kill switch provided in case AI takes over the world...
+ // Or, more likely, in case this Minecraft instance has become unreliable (eg if it's been running for several days)
+ // and needs to be replaced with a fresh instance.
+ // If we are currently running a mission, we gracefully decline, to prevent users from wiping out
+ // other users' experiments.
+ // We also decline unless we were launched in "replaceable" mode - a command-line switch that indicates we were
+ // launched by a script which is still running, and can therefore replace us when we terminate.
+ IState currentState = getStableState();
+ if (currentState != null && currentState.equals(ClientState.DORMANT) && !isReserved())
+ {
+ Configuration config = MalmoMod.instance.getModSessionConfigFile();
+ if (config.getBoolean("replaceable", "runtype", false, "Will be replaced if killed"))
+ {
+ reply("MALMOOK", dos);
+
+ missionPoller.stopServer();
+ exitJava();
+ }
+ else
+ {
+ reply("MALMOERRORNOTKILLABLE", dos);
+ }
+ }
+ else
+ {
+ // We're too busy and important to be killed.
+ reply("MALMOBUSY", dos);
+ }
+ }
+ else
+ {
+ // See if we've been sent a MissionInit message:
+
+ MissionInitResult missionInitResult = decodeMissionInit(command);
+
+ if (missionInitResult.wasMissionInit && missionInitResult.missionInit == null)
+ {
+ // Got sent a duff MissionInit xml - pass back the JAXB/SAXB errors.
+ reply("MALMOERROR" + missionInitResult.error, dos);
+ }
+ else if (missionInitResult.wasMissionInit && missionInitResult.missionInit != null)
+ {
+ MissionInit missionInit = missionInitResult.missionInit;
+ // We've been sent a MissionInit message.
+ // First, check the version number:
+ String platformVersion = missionInit.getPlatformVersion();
+ String ourVersion = Loader.instance().activeModContainer().getVersion();
+ if (platformVersion == null || !platformVersion.equals(ourVersion))
+ {
+ reply("MALMOERRORVERSIONMISMATCH (Got " + platformVersion + ", expected " + ourVersion + " - check your path for old versions of MalmoPython/MalmoJava/Malmo.lib etc)", dos);
+ }
+ else
+ {
+ // MissionInit passed to us - this is a request to launch this mission. Can we?
+ IState currentState = getStableState();
+ if (currentState != null && currentState.equals(ClientState.DORMANT) && isAvailable(missionInit.getExperimentUID()))
+ {
+ reply("MALMOOK", dos);
+ keepProcessing = true; // State machine will now process this MissionInit and start the mission.
+ }
+ else
+ {
+ // We're busy - we can't run this mission.
+ reply("MALMOBUSY", dos);
+ }
+ }
+ }
+ }
+
+ return keepProcessing;
+ }
+ };
+
+ int mcPort = 0;
+ if (MalmoEnvServer.isEnv()) {
+ // Start up new "Env" service instead of Malmo AgentHost api.
+ System.out.println("***** Start MalmoEnvServer on port " + AddressHelper.getMissionControlPortOverride());
+ this.envServer = new MalmoEnvServer(
+ Loader.instance().activeModContainer().getVersion(),
+ AddressHelper.getMissionControlPortOverride(),
+ this.missionPoller,
+ this.inputController);
+ Thread thread = new Thread("MalmoEnvServer") {
+ public void run() {
+ try {
+ envServer.serve();
+ } catch (IOException ioe) {
+ System.out.println("MalmoEnvServer exist on " + ioe);
+ }
+ }
+ };
+ thread.start();
+ } else {
+ // "Legacy" AgentHost api.
+ this.missionPoller.start();
+ mcPort = ClientStateMachine.this.missionPoller.getPortBlocking();
+ }
+
+ // Tell the address helper what the actual port is:
+ AddressHelper.setMissionControlPort(mcPort);
+ if (AddressHelper.getMissionControlPort() == -1)
+ {
+ // Failed to create a mission control port - nothing will work!
+ System.out.println("**** NO MISSION CONTROL SOCKET CREATED - WAS THE PORT IN USE? (Check Mod GUI options) ****");
+ ClientStateMachine.this.getScreenHelper().addFragment("ERROR: Could not open a Mission Control Port - check the Mod GUI options.", TextCategory.TXT_CLIENT_WARNING, MISSING_MCP_PORT_ERROR);
+ }
+ else
+ {
+ // Clear the error string, if there was one:
+ ClientStateMachine.this.getScreenHelper().clearFragment(MISSING_MCP_PORT_ERROR);
+ }
+ // Display the port number:
+ ClientStateMachine.this.getScreenHelper().clearFragment(INFO_MCP_PORT);
+ if (AddressHelper.getMissionControlPort() != -1)
+ ClientStateMachine.this.getScreenHelper().addFragment("MCP: " + AddressHelper.getMissionControlPort(), TextCategory.TXT_INFO, INFO_MCP_PORT);
+ }
+
+ public static void exitJava() {
+ // Give non-hard exit 10 seconds to complete and force a hard exit.
+ Thread deadMansHandle = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 10; i > 0; i--) {
+ try {
+ Thread.sleep(1000);
+ System.out.println("Waiting to exit " + i + "...");
+ } catch (InterruptedException e) {
+ System.out.println("Interrupted " + i + "...");
+ }
+ }
+
+ // Kill it with fire!!!
+ System.out.println("Attempting hard exit");
+ FMLCommonHandler.instance().exitJava(0, true);
+ }
+ });
+
+ deadMansHandle.setDaemon(true);
+ deadMansHandle.start();
+
+ // Have to use FMLCommonHandler; direct calls to System.exit() are trapped and denied by the FML code.
+ FMLCommonHandler.instance().exitJava(0, false);
+ }
+
+ // ---------------------------------------------------------------------------------------------------------
+ // Episode helpers - each extends a MissionStateEpisode to encapsulate a certain state
+ // ---------------------------------------------------------------------------------------------------------
+
+ public abstract class ErrorAwareEpisode extends StateEpisode implements IMalmoMessageListener
+ {
+ protected Boolean errorFlag = false;
+ protected Map errorData = null;
+
+ public ErrorAwareEpisode(ClientStateMachine machine)
+ {
+ super(machine);
+ MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_ABORT);
+ }
+
+ protected boolean pingAgent(boolean abortIfFailed)
+ {
+ if (AddressHelper.getMissionControlPort() == 0) {
+ // MalmoEnvServer has no server to client ping.
+ return true;
+ }
+
+ boolean sentOkay = ClientStateMachine.this.getMissionControlSocket().sendTCPString("", 1);
+ if (!sentOkay)
+ {
+ // It's not available - bail.
+ ClientStateMachine.this.getScreenHelper().addFragment("ERROR: Lost contact with agent - aborting mission", TextCategory.TXT_CLIENT_WARNING, 10000);
+ if (abortIfFailed)
+ episodeHasCompletedWithErrors(ClientState.ERROR_LOST_AGENT, "Lost contact with the agent");
+ }
+ return sentOkay;
+ }
+
+ @Override
+ public void onMessage(MalmoMessageType messageType, Map data)
+ {
+ if (messageType == MalmoMod.MalmoMessageType.SERVER_ABORT)
+ {
+ synchronized (this.errorFlag)
+ {
+ this.errorFlag = true;
+ this.errorData = data;
+ // Save the error message, if there is one:
+ if (data != null)
+ {
+ String message = data.get("message");
+ String user = data.get("username");
+ String error = data.get("error");
+ String report = "";
+ if (user != null)
+ report += "From " + user + ": ";
+ if (error != null)
+ report += error;
+ if (message != null)
+ report += " (" + message + ")";
+ ClientStateMachine.this.saveErrorDetails(report);
+ }
+ onAbort(data);
+ }
+ }
+ }
+
+ @Override
+ public void cleanup()
+ {
+ super.cleanup();
+ MalmoMod.MalmoMessageHandler.deregisterForMessage(this, MalmoMessageType.SERVER_ABORT);
+ }
+
+ protected boolean inAbortState()
+ {
+ synchronized (this.errorFlag)
+ {
+ return this.errorFlag;
+ }
+ }
+
+ protected Map getErrorData()
+ {
+ synchronized (this.errorFlag)
+ {
+ return this.errorData;
+ }
+ }
+
+ protected void onAbort(Map errorData)
+ {
+ // Default does nothing, but can be overridden.
+ }
+ }
+
+ /**
+ * Helper base class that responds to the config change and updates our AddressHelper.
+ * This will also reset the mission poller. Depending on the state, more
+ * work may be needed (eg to recreate the command handler, etc) - it's up to
+ * the individual state episodes to do whatever else needs doing.
+ */
+ abstract public class ConfigAwareStateEpisode extends ErrorAwareEpisode
+ {
+ ConfigAwareStateEpisode(ClientStateMachine machine)
+ {
+ super(machine);
+ }
+
+ @Override
+ public void onConfigChanged(OnConfigChangedEvent ev)
+ {
+ if (ev.getConfigID().equals(MalmoMod.SOCKET_CONFIGS))
+ {
+ AddressHelper.update(MalmoMod.instance.getModSessionConfigFile());
+ try
+ {
+ ClientStateMachine.this.initialiseComms();
+ }
+ catch (UnknownHostException e)
+ {
+ // TODO What to do here?
+ e.printStackTrace();
+ }
+ ScreenHelper.update(MalmoMod.instance.getModPermanentConfigFile());
+ TCPUtils.update(MalmoMod.instance.getModPermanentConfigFile());
+ }
+ }
+ }
+
+ /** Initial episode - perform client setup */
+ public class InitialiseClientModEpisode extends ConfigAwareStateEpisode
+ {
+ InitialiseClientModEpisode(ClientStateMachine machine)
+ {
+ super(machine);
+ }
+
+ @Override
+ protected void execute() throws Exception
+ {
+ ClientStateMachine.this.initialiseComms();
+
+ // This is necessary in order to allow user to exit the Minecraft window without halting the experiment:
+ GameSettings settings = Minecraft.getMinecraft().gameSettings;
+ settings.pauseOnLostFocus = false;
+ // And hook the screen helper into the ingame gui (which is responsible for overlaying chat, titles etc) -
+ // this has to be done after Minecraft.init(), so we do it here.
+ ScreenHelper.hookIntoInGameGui();
+ }
+
+ @Override
+ public void onRenderTick(TickEvent.RenderTickEvent ev)
+ {
+ // We wait until we start to get render ticks, at which point we assume Minecraft has finished starting up.
+ episodeHasCompleted(ClientState.DORMANT);
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------------------
+ /** Dormant state - receptive to new missions */
+ public class DormantEpisode extends ConfigAwareStateEpisode
+ {
+ private ClientStateMachine csMachine;
+
+ protected DormantEpisode(ClientStateMachine machine)
+ {
+ super(machine);
+ this.csMachine = machine;
+ }
+
+ @Override
+ protected void execute()
+ {
+ TextureHelper.init();
+
+ // Clear our current MissionInit state:
+ csMachine.currentMissionInit = null;
+ // Clear our current error state:
+ clearErrorDetails();
+ // And clear out any stale commands left over from recent missions:
+ if (ClientStateMachine.this.controlInputPoller != null)
+ ClientStateMachine.this.controlInputPoller.clearCommands();
+ // Finally, do some Java housekeeping:
+ System.gc();
+
+ if (envServer != null) {
+ envServer.setRunning(false);
+ }
+ }
+
+ @Override
+ public void onClientTick(TickEvent.ClientTickEvent ev) throws Exception
+ {
+
+ Minecraft.getMinecraft().mcProfiler.startSection("malmoHandleMissionCommands");
+ checkForMissionCommand();
+ Minecraft.getMinecraft().mcProfiler.endSection();
+ }
+
+ private void checkForMissionCommand() throws Exception
+ {
+ if (ClientStateMachine.this.missionPoller == null)
+ return;
+
+ CommandAndIPAddress comip = missionPoller.getCommandAndIPAddress();
+ if (comip == null)
+ return;
+ String missionMessage = comip.command;
+ if (missionMessage == null || missionMessage.length() == 0)
+ return;
+ Minecraft.getMinecraft().mcProfiler.endSection();
+ Minecraft.getMinecraft().mcProfiler.startSection("malmoDecodeMissionInit");
+
+ MissionInitResult missionInitResult = decodeMissionInit(missionMessage);
+ Minecraft.getMinecraft().mcProfiler.endSection();
+
+ MissionInit missionInit = missionInitResult.missionInit;
+ if (missionInit != null)
+ {
+ missionInit.getClientAgentConnection().setAgentIPAddress(comip.ipAddress);
+ System.out.println("Mission received: " + missionInit.getMission().getAbout().getSummary());
+ csMachine.currentMissionInit = missionInit;
+ TimeHelper.SyncManager.numTicks = 0;
+ ScoreHelper.logMissionInit(missionInit);
+
+ ClientStateMachine.this.createMissionControlSocket();
+ // Move on to next state:
+ episodeHasCompleted(ClientState.CREATING_HANDLERS);
+ }
+ else
+ {
+ throw new Exception("Failed to get valid MissionInit object from SchemaHelper.");
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------------------
+ /**
+ * Now the MissionInit XML has been decoded, the client needs to create the
+ * Mission Handlers.
+ */
+ public class CreateHandlersEpisode extends ConfigAwareStateEpisode
+ {
+ protected CreateHandlersEpisode(ClientStateMachine machine)
+ {
+ super(machine);
+ }
+
+ @Override
+ protected void execute() throws Exception
+ {
+ // First, clear our reservation state, if we were reserved:
+ ClientStateMachine.this.cancelReservation();
+
+ // Now try creating the handlers:
+ try
+ {
+ if(envServer != null){
+ SeedHelper.advanceNextSeed(envServer.getSeed());
+ }
+ ClientStateMachine.this.missionBehaviour = MissionBehaviour.createAgentHandlersFromMissionInit(currentMissionInit());
+ if (envServer != null) {
+ ClientStateMachine.this.missionBehaviour.addQuitProducer(envServer);
+ }
+ }
+ catch (Exception e)
+ {
+ // TODO
+ System.err.println("ERROR: Exception caught making agent handlers" + e.toString());
+ e.printStackTrace();
+ }
+ // Set up our command input poller. This is only checked during the MissionRunning episode, but
+ // it needs to be started now, so we can report the port it's using back to the agent.
+ TCPUtils.LogSection ls = new TCPUtils.LogSection("Initialise Command Input Poller");
+ ClientAgentConnection cac = currentMissionInit().getClientAgentConnection();
+ int requestedPort = cac.getClientCommandsPort();
+ // If the requested port is 0, we dynamically allocate our own port, and feed that back to the agent.
+ // If the requested port is non-zero, we have to use it.
+ if (requestedPort != 0 && ClientStateMachine.this.controlInputPoller != null && ClientStateMachine.this.controlInputPoller.getPort() != requestedPort)
+ {
+ // A specific port has been requested, and it's not the one we are currently using,
+ // so we need to recreate our poller.
+ System.out.println("Requested command port is not the same as the input poller port; the port was not free. Stopping server.");
+ ClientStateMachine.this.controlInputPoller.stopServer();
+ ClientStateMachine.this.controlInputPoller = null;
+ }
+ if (ClientStateMachine.this.controlInputPoller == null)
+ {
+ if (requestedPort == 0)
+ ClientStateMachine.this.controlInputPoller = new TCPInputPoller(AddressHelper.MIN_FREE_PORT, AddressHelper.MAX_FREE_PORT, true, "com");
+ else
+ ClientStateMachine.this.controlInputPoller = new TCPInputPoller(requestedPort, "com");
+ System.out.println("Starting command server.");
+ ClientStateMachine.this.controlInputPoller.start();
+ }
+ // Make sure the cac is up-to-date:
+ cac.setClientCommandsPort(ClientStateMachine.this.controlInputPoller.getPortBlocking());
+ ls.close();
+
+ // Check to see whether anything has caused us to abort - if so, go to the abort state.
+ if (inAbortState())
+ episodeHasCompleted(ClientState.MISSION_ABORTED);
+
+ // Set the agent's name as the current username:
+ List agents = currentMissionInit().getMission().getAgentSection();
+ String agentName = agents.get(currentMissionInit().getClientRole()).getName();
+ AuthenticationHelper.setPlayerName(Minecraft.getMinecraft().getSession(), agentName);
+ // Handlers and poller created successfully; proceed to next stage of loading.
+ // We will either need to connect to an existing server, or to start
+ // a new integrated server ourselves, depending on our role.
+ // For now, assume that the mod with role 0 is responsible for the server.
+ if (currentMissionInit().getClientRole() == 0)
+ {
+ // We are responsible for the server - investigate what needs to happen next:
+ episodeHasCompleted(ClientState.EVALUATING_WORLD_REQUIREMENTS);
+ }
+ else
+ {
+ // We may need to connect to a server.
+ episodeHasCompleted(ClientState.WAITING_FOR_SERVER_READY);
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------------------
+ /**
+ * Attempt to connect to a server. Wait until connection is established.
+ */
+ public class WaitingForServerEpisode extends ConfigAwareStateEpisode
+ {
+ String agentName;
+ int ticksUntilNextPing = 0;
+ int totalTicks = 0;
+ boolean waitingForChunk = false;
+ boolean waitingForPlayer = true;
+
+ protected WaitingForServerEpisode(ClientStateMachine machine)
+ {
+ super(machine);
+ MalmoMod.MalmoMessageHandler.registerForMessage(this, MalmoMessageType.SERVER_ALLPLAYERSJOINED);
+ }
+
+ private boolean isChunkReady()
+ {
+ // First, find the starting position we ought to have:
+ List agents = currentMissionInit().getMission().getAgentSection();
+ if (agents == null || agents.size() <= currentMissionInit().getClientRole())
+ return true; // This should never happen.
+ AgentSection as = agents.get(currentMissionInit().getClientRole());
+ if (as.getAgentStart() != null && as.getAgentStart().getPlacement() != null)
+ {
+ PosAndDirection pos = as.getAgentStart().getPlacement();
+ int x = MathHelper.floor(pos.getX().doubleValue()) >> 4;
+ int z = MathHelper.floor(pos.getZ().doubleValue()) >> 4;
+ // Now get the chunk we should be starting in:
+ IChunkProvider chunkprov = Minecraft.getMinecraft().world.getChunkProvider();
+ EntityPlayerSP player = Minecraft.getMinecraft().player;
+ if (player.addedToChunk)
+ {
+ // Our player is already added to a chunk - is it the right one?
+ Chunk actualChunk = chunkprov.provideChunk(player.chunkCoordX, player.chunkCoordZ);
+ Chunk requestedChunk = chunkprov.provideChunk(x, z);
+ if (actualChunk == requestedChunk && actualChunk != null && !actualChunk.isEmpty())
+ {
+ // We're in the right chunk, and it's not an empty chunk.
+ // We're ready to proceed, but first set our client positions to where we ought to be.
+ // The server should be doing this too, but there's no harm (probably) in doing it ourselves.
+ player.posX = pos.getX().doubleValue();
+ player.posY = pos.getY().doubleValue();
+ player.posZ = pos.getZ().doubleValue();
+ return true;
+ }
+ }
+ return false; // Our starting position has been specified, but it's not yet ready.
+ }
+ return true; // No starting position specified, so doesn't matter where we start.
+ }
+
+ @Override
+ protected void onClientTick(ClientTickEvent ev)
+ {
+ // Check to see whether anything has caused us to abort - if so, go to the abort state.
+ if (inAbortState())
+ episodeHasCompleted(ClientState.MISSION_ABORTED);
+
+ if (this.waitingForPlayer)
+ {
+ if (Minecraft.getMinecraft().player != null)
+ {
+ this.waitingForPlayer = false;
+ handleLan();
+ }
+ else
+ return;
+ }
+
+ totalTicks++;
+
+ if (ticksUntilNextPing == 0)
+ {
+ // Tell the server what our agent name is.
+ // We do this repeatedly, because the server might not yet be listening.
+ if (Minecraft.getMinecraft().player != null && !this.waitingForChunk)
+ {
+ HashMap map = new HashMap();
+ map.put("agentname", agentName);
+ map.put("username", Minecraft.getMinecraft().player.getName());
+ currentMissionBehaviour().appendExtraServerInformation(map);
+ System.out.println("***Telling server we are ready - " + agentName);
+ MalmoMod.network.sendToServer(new MalmoMod.MalmoMessage(MalmoMessageType.CLIENT_AGENTREADY, 0, map));
+ }
+
+ // We also ping our agent, just to check it is still available:
+ pingAgent(true); // Will abort to an error state if client unavailable.
+
+ ticksUntilNextPing = 10; // Try again in ten ticks.
+ }
+ else
+ {
+ ticksUntilNextPing--;
+ }
+
+ if (this.waitingForChunk)
+ {
+ // The server is ready, we're just waiting for our chunk to appear.
+ if (isChunkReady())
+ proceed();
+ }
+
+ List agents = currentMissionInit().getMission().getAgentSection();
+ boolean completedWithErrors = false;
+
+ if (agents.size() > 1 && currentMissionInit().getClientRole() != 0)
+ {
+ // We are waiting to join an out-of-process server. Need to pay attention to what happens -
+ // if we can't join, for any reason, we should abort the mission.
+ GuiScreen screen = Minecraft.getMinecraft().currentScreen;
+ if (screen != null && screen instanceof GuiDisconnected) {
+ // Disconnected screen appears when something has gone wrong.
+ // Would be nice to grab the reason from the screen, but it's a private member.
+ // (Can always use reflection, but it's so inelegant.)
+ String msg = "Unable to connect to Minecraft server in multi-agent mission.";
+ TCPUtils.Log(Level.SEVERE, msg);
+ episodeHasCompletedWithErrors(ClientState.ERROR_CANNOT_CONNECT_TO_SERVER, msg);
+ completedWithErrors = true;
+ }
+ }
+
+ if (!completedWithErrors && totalTicks > WAIT_MAX_TICKS)
+ {
+ String msg = "Too long waiting for server episode to start.";
+ TCPUtils.Log(Level.SEVERE, msg);
+ episodeHasCompletedWithErrors(ClientState.ERROR_TIMED_OUT_WAITING_FOR_EPISODE_START, msg);
+ }
+
+ if(envServer != null) {
+ if(envServer.doIWantToQuit(currentMissionInit())){
+ episodeHasCompleted(ClientState.MISSION_ABORTED); //This could be a source of a race condition.
+ }
+ }
+ }
+
+ @Override
+ protected void execute() throws Exception
+ {
+ totalTicks = 0;
+
+ Minecraft.getMinecraft().displayGuiScreen(null); // Clear any menu screen that might confuse things.
+ // Get our name from the Mission:
+ List agents = currentMissionInit().getMission().getAgentSection();
+ //if (agents == null || agents.size() <= currentMissionInit().getClientRole())
+ // throw new Exception("No agent section for us!"); // TODO
+ this.agentName = agents.get(currentMissionInit().getClientRole()).getName();
+
+ if (agents.size() > 1 && currentMissionInit().getClientRole() != 0)
+ {
+ // Multi-agent mission, we should be joining a server.
+ // (Unless we are already on the correct server.)
+ String address = currentMissionInit().getMinecraftServerConnection().getAddress().trim();
+ int port = currentMissionInit().getMinecraftServerConnection().getPort();
+ String targetIP = address + ":" + port;
+ System.out.println("We should be joining " + targetIP);
+ EntityPlayerSP player = Minecraft.getMinecraft().player;
+ boolean namesMatch = (player == null) || Minecraft.getMinecraft().player.getName().equals(this.agentName);
+ if (!namesMatch)
+ {
+ // The name of our agent no longer matches the agent in our game profile -
+ // safest way to update is to log out and back in again.
+ // This hangs so just warn instead about the miss-match and proceed.
+ TCPUtils.Log(Level.WARNING,"Agent name does not match agent in game.");
+ // Minecraft.getMinecraft().world.sendQuittingDisconnectingPacket();
+ // Minecraft.getMinecraft().loadWorld((WorldClient)null);
+ }
+ if (Minecraft.getMinecraft().getCurrentServerData() == null || !Minecraft.getMinecraft().getCurrentServerData().serverIP.equals(targetIP))
+ {
+ net.minecraftforge.fml.client.FMLClientHandler.instance().connectToServerAtStartup(address, port);
+
+ // TODO - should we wait for a connected notification?
+ TimeHelper.SyncManager.setServerRunning();
+ TimeHelper.SyncManager.setPistolFired(true);
+ }
+ this.waitingForPlayer = false;
+ }
+ }
+
+ protected void handleLan()
+ {
+ // Get our name from the Mission:
+ final List agents = currentMissionInit().getMission().getAgentSection();
+ final HumanInteraction hc = currentMissionInit().getMission().getServerSection().getHumanInteraction();
+ //if (agents == null || agents.size() <= currentMissionInit().getClientRole())
+ // throw new Exception("No agent section for us!"); // TODO
+ this.agentName = agents.get(currentMissionInit().getClientRole()).getName();
+
+ if ((hc != null || agents.size() > 1) && currentMissionInit().getClientRole() == 0) // Multi-agent mission - make sure the server is open to the LAN:
+ {
+ final MinecraftServerConnection msc = new MinecraftServerConnection();
+ final String address = currentMissionInit().getClientAgentConnection().getClientIPAddress();
+ // Do we need to open to LAN?
+ if (Minecraft.getMinecraft().isSingleplayer() && !Minecraft.getMinecraft().getIntegratedServer().getPublic())
+ {
+ String portStr = "";
+ if (hc == null)
+ portStr = Minecraft.getMinecraft().getIntegratedServer().shareToLAN(GameType.SURVIVAL, true); // Set to true to stop spam kicks.
+ else{
+ try
+ {
+ // 1.11.2 - start our own server on a SPECIFIC port.
+ final IntegratedServer serv = Minecraft.getMinecraft().getIntegratedServer();
+ final Integer i = Integer.parseInt(hc.getPort());
+ serv.getNetworkSystem().addLanEndpoint((InetAddress)null, i);
+ serv.isPublic = true;
+ serv.lanServerPing = new ThreadLanServerPing(serv.getMOTD(), i + "");
+ serv.lanServerPing.start();
+ serv.getPlayerList().setGameType(GameType.SURVIVAL);
+ serv.getPlayerList().setCommandsAllowedForAll(true);
+ serv.mc.player.setPermissionLevel(true ? 4 : 0);
+
+ serv.getPlayerList().maxPlayers = hc.getMaxPlayers() + 1; //TODO: for multi-agent add more.
+ portStr = i + "";
+
+ } catch (final IOException var6)
+ {
+ System.out.println("[ERROR] Could not make MineRL agent server public on port" + hc.getPort() + ".");
+ synchronized(this.errorFlag){
+ this.errorFlag = true;
+ }
+ }
+ }
+ ClientStateMachine.this.integratedServerPort = Integer.valueOf(portStr);
+ }
+
+ TCPUtils.Log(Level.INFO,"Integrated server port: " + ClientStateMachine.this.integratedServerPort);
+ msc.setPort(ClientStateMachine.this.integratedServerPort);
+ msc.setAddress(address);
+
+ if (envServer != null) {
+ envServer.notifyIntegrationServerStarted(ClientStateMachine.this.integratedServerPort);
+ }
+ currentMissionInit().setMinecraftServerConnection(msc);
+ }
+ }
+
+ @Override
+ public void onMessage(MalmoMessageType messageType, Map data)
+ {
+ super.onMessage(messageType, data);
+
+ if (messageType != MalmoMessageType.SERVER_ALLPLAYERSJOINED)
+ return;
+
+ List