Compare commits
2 Commits
b0884bc88c
...
1d659d2493
Author | SHA1 | Date |
---|---|---|
|
1d659d2493 | |
|
75ae2b02c8 |
|
@ -1,4 +1,8 @@
|
|||
bin/
|
||||
.gradle/
|
||||
build/
|
||||
lwjgl-rundir/_eagstorage.p.dat
|
||||
lwjgl-rundir/_eagstorage.p.dat
|
||||
sp-server/.gradle
|
||||
sp-server/.settings
|
||||
sp-server/build
|
||||
sp-server/bin
|
|
@ -1,3 +1,6 @@
|
|||
#!/bin/sh
|
||||
chmod +x gradlew
|
||||
./gradlew generateJavascript
|
||||
cd sp-server
|
||||
chmod +x gradlew
|
||||
./gradlew generateJavascript
|
|
@ -10,7 +10,8 @@ sourceSets {
|
|||
java {
|
||||
srcDirs(
|
||||
"src/main/java",
|
||||
"src/teavm/java"
|
||||
"src/teavm/java",
|
||||
"sp-server/src/ipc/java"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +40,7 @@ def folder = "javascript"
|
|||
def name = "classes.js"
|
||||
|
||||
teavm.js {
|
||||
obfuscated = true
|
||||
obfuscated = false
|
||||
sourceMap = true
|
||||
targetFileName = "../" + name
|
||||
optimization = OptimizationLevel.AGGRESSIVE
|
||||
|
@ -58,4 +59,4 @@ tasks.register('copyDebugJar', Copy) {
|
|||
from layout.buildDirectory.file("eaglercraft.jar")
|
||||
into ("lwjgl-rundir/")
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
811314
javascript/classes.js
811314
javascript/classes.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
|||
onmessage = function(o) {
|
||||
importScripts("classes.js");
|
||||
importScripts("classes_server.js");
|
||||
eaglercraftServerOpts = o.data;
|
||||
startIntegratedServer();
|
||||
main();
|
||||
};
|
||||
|
|
|
@ -142,7 +142,7 @@ gameMode.adventure=Adventure Mode
|
|||
gameMode.hardcore=Hardcore Mode!
|
||||
gameMode.changed=Your game mode has been updated
|
||||
|
||||
selectWorld.gameMode=Game Mode
|
||||
selectWorld.gameMode=Game Mode:
|
||||
selectWorld.gameMode.survival=Survival
|
||||
selectWorld.gameMode.survival.line1=Search for resources, crafting, gain
|
||||
selectWorld.gameMode.survival.line2=levels, health and hunger
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
|
@ -0,0 +1 @@
|
|||
eaglercraft
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,51 @@
|
|||
import org.teavm.gradle.api.OptimizationLevel
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
id "eclipse"
|
||||
id "org.teavm" version "0.10.2"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs(
|
||||
"src/main/java",
|
||||
"src/ipc/java"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.warnings = false
|
||||
options.compilerArgs << "-Xmaxerrs" << "1000"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
teavm(teavm.libs.jso)
|
||||
teavm(teavm.libs.jsoApis)
|
||||
compileOnly "org.teavm:teavm-core:0.10.2" // workaround for a few hacks
|
||||
}
|
||||
|
||||
def folder = "../javascript"
|
||||
def name = "classes_server.js"
|
||||
|
||||
teavm.js {
|
||||
obfuscated = false
|
||||
sourceMap = true
|
||||
targetFileName = "../" + name
|
||||
optimization = OptimizationLevel.AGGRESSIVE
|
||||
outOfProcess = false
|
||||
fastGlobalAnalysis = false
|
||||
processMemory = 512
|
||||
entryPointName = "main"
|
||||
mainClass = "net.lax1dude.eaglercraft.sp.IntegratedServer"
|
||||
outputDir = file(folder)
|
||||
properties = [ "java.util.TimeZone.autodetect": "true" ]
|
||||
debugInformation = false
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.gradle.jvmargs=-Xmx2G -Xms2G
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# 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\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# 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
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MSYS* | MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
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" -a "$nonstop" = "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 or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# 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=`expr $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
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,89 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@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
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@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="-Xmx64m" "-Xms64m"
|
||||
|
||||
@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 execute
|
||||
|
||||
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 execute
|
||||
|
||||
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
|
||||
|
||||
: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 %*
|
||||
|
||||
: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
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* The settings file is used to specify which projects to include in your build.
|
||||
*
|
||||
* Detailed information about configuring a multi-project build in Gradle can be found
|
||||
* in the user manual at https://docs.gradle.org/6.0/userguide/multi_project_builds.html
|
||||
*/
|
||||
|
||||
rootProject.name = 'eaglercraft'
|
||||
|
||||
// you eagler
|
|
@ -0,0 +1,84 @@
|
|||
package net.lax1dude.eaglercraft;
|
||||
|
||||
public class EaglercraftRandom {
|
||||
|
||||
private static final long multiplier = 0x5DEECE66DL;
|
||||
private static final long addend = 0xBL;
|
||||
private static final long mask = (1L << 48) - 1;
|
||||
|
||||
private static final double DOUBLE_UNIT = 0x1.0p-53;
|
||||
private long seed = 69;
|
||||
|
||||
public EaglercraftRandom() {
|
||||
this(System.nanoTime());
|
||||
}
|
||||
|
||||
public EaglercraftRandom(long seed) {
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
public void setSeed(long yeed) {
|
||||
seed = yeed;
|
||||
}
|
||||
|
||||
protected int next(int bits) {
|
||||
seed = (seed * multiplier + addend) & mask;
|
||||
return (int)(seed >>> (48 - bits));
|
||||
}
|
||||
|
||||
public void nextBytes(byte[] bytes) {
|
||||
for (int i = 0, len = bytes.length; i < len; )
|
||||
for (int rnd = nextInt(),
|
||||
n = Math.min(len - i, Integer.SIZE/Byte.SIZE);
|
||||
n-- > 0; rnd >>= Byte.SIZE)
|
||||
bytes[i++] = (byte)rnd;
|
||||
}
|
||||
public int nextInt() {
|
||||
return next(32);
|
||||
}
|
||||
public int nextInt(int bound) {
|
||||
int r = next(31);
|
||||
int m = bound - 1;
|
||||
if ((bound & m) == 0) // i.e., bound is a power of 2
|
||||
r = (int)((bound * (long)r) >> 31);
|
||||
else {
|
||||
for (int u = r;
|
||||
u - (r = u % bound) + m < 0;
|
||||
u = next(31))
|
||||
;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
public long nextLong() {
|
||||
return ((long)(next(32)) << 32) + next(32);
|
||||
}
|
||||
public boolean nextBoolean() {
|
||||
return next(1) != 0;
|
||||
}
|
||||
public float nextFloat() {
|
||||
return next(24) / ((float)(1 << 24));
|
||||
}
|
||||
public double nextDouble() {
|
||||
return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
|
||||
}
|
||||
private double nextNextGaussian;
|
||||
private boolean haveNextNextGaussian = false;
|
||||
public double nextGaussian() {
|
||||
// See Knuth, ACP, Section 3.4.1 Algorithm C.
|
||||
if (haveNextNextGaussian) {
|
||||
haveNextNextGaussian = false;
|
||||
return nextNextGaussian;
|
||||
} else {
|
||||
double v1, v2, s;
|
||||
do {
|
||||
v1 = 2 * nextDouble() - 1; // between -1 and 1
|
||||
v2 = 2 * nextDouble() - 1; // between -1 and 1
|
||||
s = v1 * v1 + v2 * v2;
|
||||
} while (s >= 1 || s == 0);
|
||||
double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
|
||||
nextNextGaussian = v2 * multiplier;
|
||||
haveNextNextGaussian = true;
|
||||
return v1 * multiplier;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
package net.lax1dude.eaglercraft;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.GeneralDigest;
|
||||
|
||||
/**
|
||||
* implementation of MD5 as outlined in "Handbook of Applied Cryptography",
|
||||
* pages 346 - 347.
|
||||
*/
|
||||
public class MD5Digest extends GeneralDigest {
|
||||
private static final int DIGEST_LENGTH = 16;
|
||||
|
||||
private int H1, H2, H3, H4; // IV's
|
||||
|
||||
private int[] X = new int[16];
|
||||
private int xOff;
|
||||
|
||||
public String getAlgorithmName() {
|
||||
return "MD5";
|
||||
}
|
||||
|
||||
public int getDigestSize() {
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
protected void processWord(byte[] in, int inOff) {
|
||||
X[xOff++] = littleEndianToInt(in, inOff);
|
||||
|
||||
if (xOff == 16) {
|
||||
processBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private int littleEndianToInt(byte[] bs, int off) {
|
||||
int n = bs[off] & 0xff;
|
||||
n |= (bs[++off] & 0xff) << 8;
|
||||
n |= (bs[++off] & 0xff) << 16;
|
||||
n |= bs[++off] << 24;
|
||||
return n;
|
||||
}
|
||||
|
||||
protected void processLength(long bitLength) {
|
||||
if (xOff > 14) {
|
||||
processBlock();
|
||||
}
|
||||
|
||||
X[14] = (int) (bitLength & 0xffffffff);
|
||||
X[15] = (int) (bitLength >>> 32);
|
||||
}
|
||||
|
||||
public int doFinal(byte[] out, int outOff) {
|
||||
finish();
|
||||
|
||||
intToLittleEndian(H1, out, outOff);
|
||||
intToLittleEndian(H2, out, outOff + 4);
|
||||
intToLittleEndian(H3, out, outOff + 8);
|
||||
intToLittleEndian(H4, out, outOff + 12);
|
||||
|
||||
reset();
|
||||
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
private void intToLittleEndian(int n, byte[] bs, int off) {
|
||||
bs[off] = (byte) (n);
|
||||
bs[++off] = (byte) (n >>> 8);
|
||||
bs[++off] = (byte) (n >>> 16);
|
||||
bs[++off] = (byte) (n >>> 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the chaining variables to the IV values.
|
||||
*/
|
||||
public void reset() {
|
||||
super.reset();
|
||||
|
||||
H1 = 0x67452301;
|
||||
H2 = 0xefcdab89;
|
||||
H3 = 0x98badcfe;
|
||||
H4 = 0x10325476;
|
||||
|
||||
xOff = 0;
|
||||
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// round 1 left rotates
|
||||
//
|
||||
private static final int S11 = 7;
|
||||
private static final int S12 = 12;
|
||||
private static final int S13 = 17;
|
||||
private static final int S14 = 22;
|
||||
|
||||
//
|
||||
// round 2 left rotates
|
||||
//
|
||||
private static final int S21 = 5;
|
||||
private static final int S22 = 9;
|
||||
private static final int S23 = 14;
|
||||
private static final int S24 = 20;
|
||||
|
||||
//
|
||||
// round 3 left rotates
|
||||
//
|
||||
private static final int S31 = 4;
|
||||
private static final int S32 = 11;
|
||||
private static final int S33 = 16;
|
||||
private static final int S34 = 23;
|
||||
|
||||
//
|
||||
// round 4 left rotates
|
||||
//
|
||||
private static final int S41 = 6;
|
||||
private static final int S42 = 10;
|
||||
private static final int S43 = 15;
|
||||
private static final int S44 = 21;
|
||||
|
||||
/*
|
||||
* rotate int x left n bits.
|
||||
*/
|
||||
private int rotateLeft(int x, int n) {
|
||||
return (x << n) | (x >>> (32 - n));
|
||||
}
|
||||
|
||||
/*
|
||||
* F, G, H and I are the basic MD5 functions.
|
||||
*/
|
||||
private int F(int u, int v, int w) {
|
||||
return (u & v) | (~u & w);
|
||||
}
|
||||
|
||||
private int G(int u, int v, int w) {
|
||||
return (u & w) | (v & ~w);
|
||||
}
|
||||
|
||||
private int H(int u, int v, int w) {
|
||||
return u ^ v ^ w;
|
||||
}
|
||||
|
||||
private int K(int u, int v, int w) {
|
||||
return v ^ (u | ~w);
|
||||
}
|
||||
|
||||
protected void processBlock() {
|
||||
int a = H1;
|
||||
int b = H2;
|
||||
int c = H3;
|
||||
int d = H4;
|
||||
|
||||
//
|
||||
// Round 1 - F cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + F(b, c, d) + X[0] + 0xd76aa478, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[1] + 0xe8c7b756, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[2] + 0x242070db, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[3] + 0xc1bdceee, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[4] + 0xf57c0faf, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[5] + 0x4787c62a, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[6] + 0xa8304613, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[7] + 0xfd469501, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[8] + 0x698098d8, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[9] + 0x8b44f7af, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[10] + 0xffff5bb1, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[11] + 0x895cd7be, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[12] + 0x6b901122, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[13] + 0xfd987193, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[14] + 0xa679438e, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[15] + 0x49b40821, S14) + c;
|
||||
|
||||
//
|
||||
// Round 2 - G cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + G(b, c, d) + X[1] + 0xf61e2562, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[6] + 0xc040b340, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[11] + 0x265e5a51, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[0] + 0xe9b6c7aa, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[5] + 0xd62f105d, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[10] + 0x02441453, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[15] + 0xd8a1e681, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[4] + 0xe7d3fbc8, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[9] + 0x21e1cde6, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[14] + 0xc33707d6, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[3] + 0xf4d50d87, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[8] + 0x455a14ed, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[13] + 0xa9e3e905, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[2] + 0xfcefa3f8, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[7] + 0x676f02d9, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[12] + 0x8d2a4c8a, S24) + c;
|
||||
|
||||
//
|
||||
// Round 3 - H cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + H(b, c, d) + X[5] + 0xfffa3942, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[8] + 0x8771f681, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[11] + 0x6d9d6122, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[14] + 0xfde5380c, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[1] + 0xa4beea44, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[4] + 0x4bdecfa9, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[7] + 0xf6bb4b60, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[10] + 0xbebfbc70, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[13] + 0x289b7ec6, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[0] + 0xeaa127fa, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[3] + 0xd4ef3085, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[6] + 0x04881d05, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[9] + 0xd9d4d039, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[12] + 0xe6db99e5, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[15] + 0x1fa27cf8, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[2] + 0xc4ac5665, S34) + c;
|
||||
|
||||
//
|
||||
// Round 4 - K cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + K(b, c, d) + X[0] + 0xf4292244, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[7] + 0x432aff97, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[14] + 0xab9423a7, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[5] + 0xfc93a039, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[12] + 0x655b59c3, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[3] + 0x8f0ccc92, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[10] + 0xffeff47d, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[1] + 0x85845dd1, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[8] + 0x6fa87e4f, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[15] + 0xfe2ce6e0, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[6] + 0xa3014314, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[13] + 0x4e0811a1, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[4] + 0xf7537e82, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[11] + 0xbd3af235, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[2] + 0x2ad7d2bb, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[9] + 0xeb86d391, S44) + c;
|
||||
|
||||
H1 += a;
|
||||
H2 += b;
|
||||
H3 += c;
|
||||
H4 += d;
|
||||
|
||||
//
|
||||
// reset the offset and clean out the word buffer.
|
||||
//
|
||||
xOff = 0;
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package net.lax1dude.eaglercraft;
|
||||
|
||||
public class ProfileUUID {
|
||||
|
||||
private final long mostSigBits;
|
||||
private final long leastSigBits;
|
||||
|
||||
private ProfileUUID(byte[] data) {
|
||||
long msb = 0;
|
||||
long lsb = 0;
|
||||
assert data.length == 16 : "data must be 16 bytes in length";
|
||||
for (int i=0; i<8; i++)
|
||||
msb = (msb << 8) | (data[i] & 0xff);
|
||||
for (int i=8; i<16; i++)
|
||||
lsb = (lsb << 8) | (data[i] & 0xff);
|
||||
this.mostSigBits = msb;
|
||||
this.leastSigBits = lsb;
|
||||
}
|
||||
|
||||
public ProfileUUID(long mostSigBits, long leastSigBits) {
|
||||
this.mostSigBits = mostSigBits;
|
||||
this.leastSigBits = leastSigBits;
|
||||
}
|
||||
|
||||
private static final EaglercraftRandom random = new EaglercraftRandom();
|
||||
|
||||
public static ProfileUUID randomUUID() {
|
||||
byte[] randomBytes = new byte[16];
|
||||
random.nextBytes(randomBytes);
|
||||
randomBytes[6] &= 0x0f; /* clear version */
|
||||
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new ProfileUUID(randomBytes);
|
||||
}
|
||||
|
||||
private static final MD5Digest yee = new MD5Digest();
|
||||
|
||||
public static ProfileUUID nameUUIDFromBytes(byte[] name) {
|
||||
yee.update(name, 0, name.length);
|
||||
byte[] md5Bytes = new byte[16];
|
||||
yee.doFinal(md5Bytes, 0);
|
||||
md5Bytes[6] &= 0x0f; /* clear version */
|
||||
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new ProfileUUID(md5Bytes);
|
||||
}
|
||||
|
||||
public static ProfileUUID fromString(String name) {
|
||||
String[] components = name.split("-");
|
||||
if (components.length != 5)
|
||||
throw new IllegalArgumentException("Invalid UUID string: "+name);
|
||||
for (int i=0; i<5; i++)
|
||||
components[i] = "0x"+components[i];
|
||||
|
||||
long mostSigBits = Long.decode(components[0]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[1]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[2]).longValue();
|
||||
|
||||
long leastSigBits = Long.decode(components[3]).longValue();
|
||||
leastSigBits <<= 48;
|
||||
leastSigBits |= Long.decode(components[4]).longValue();
|
||||
|
||||
return new ProfileUUID(mostSigBits, leastSigBits);
|
||||
}
|
||||
|
||||
public long getLeastSignificantBits() {
|
||||
return leastSigBits;
|
||||
}
|
||||
|
||||
public long getMostSignificantBits() {
|
||||
return mostSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version number associated with this {@code UUID}. The version
|
||||
* number describes how this {@code UUID} was generated.
|
||||
*
|
||||
* The version number has the following meaning:
|
||||
* <ul>
|
||||
* <li>1 Time-based UUID
|
||||
* <li>2 DCE security UUID
|
||||
* <li>3 Name-based UUID
|
||||
* <li>4 Randomly generated UUID
|
||||
* </ul>
|
||||
*
|
||||
* @return The version number of this {@code UUID}
|
||||
*/
|
||||
public int version() {
|
||||
// Version is bits masked by 0x000000000000F000 in MS long
|
||||
return (int)((mostSigBits >> 12) & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* The variant number associated with this {@code UUID}. The variant
|
||||
* number describes the layout of the {@code UUID}.
|
||||
*
|
||||
* The variant number has the following meaning:
|
||||
* <ul>
|
||||
* <li>0 Reserved for NCS backward compatibility
|
||||
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>
|
||||
* (Leach-Salz), used by this class
|
||||
* <li>6 Reserved, Microsoft Corporation backward compatibility
|
||||
* <li>7 Reserved for future definition
|
||||
* </ul>
|
||||
*
|
||||
* @return The variant number of this {@code UUID}
|
||||
*/
|
||||
public int variant() {
|
||||
// This field is composed of a varying number of bits.
|
||||
// 0 - - Reserved for NCS backward compatibility
|
||||
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||
// 1 1 1 Reserved for future definition.
|
||||
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62)))
|
||||
& (leastSigBits >> 63));
|
||||
}
|
||||
|
||||
public long timestamp() {
|
||||
if (version() != 1) {
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
|
||||
return (mostSigBits & 0x0FFFL) << 48
|
||||
| ((mostSigBits >> 16) & 0x0FFFFL) << 32
|
||||
| mostSigBits >>> 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* The clock sequence value associated with this UUID.
|
||||
*
|
||||
* <p> The 14 bit clock sequence value is constructed from the clock
|
||||
* sequence field of this UUID. The clock sequence field is used to
|
||||
* guarantee temporal uniqueness in a time-based UUID.
|
||||
*
|
||||
* <p> The {@code clockSequence} value is only meaningful in a time-based
|
||||
* UUID, which has version type 1. If this UUID is not a time-based UUID
|
||||
* then this method throws UnsupportedOperationException.
|
||||
*
|
||||
* @return The clock sequence of this {@code UUID}
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
* If this UUID is not a version 1 UUID
|
||||
*/
|
||||
public int clockSequence() {
|
||||
if (version() != 1) {
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
|
||||
return (int)((leastSigBits & 0x3FFF000000000000L) >>> 48);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return (digits(mostSigBits >> 32, 8) + "-" +
|
||||
digits(mostSigBits >> 16, 4) + "-" +
|
||||
digits(mostSigBits, 4) + "-" +
|
||||
digits(leastSigBits >> 48, 4) + "-" +
|
||||
digits(leastSigBits, 12));
|
||||
}
|
||||
|
||||
private static String digits(long val, int digits) {
|
||||
long hi = 1L << (digits * 4);
|
||||
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
long hilo = mostSigBits ^ leastSigBits;
|
||||
return ((int)(hilo >> 32)) ^ (int) hilo;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ((null == obj) || !(obj instanceof ProfileUUID))
|
||||
return false;
|
||||
ProfileUUID id = (ProfileUUID)obj;
|
||||
return (mostSigBits == id.mostSigBits &&
|
||||
leastSigBits == id.leastSigBits);
|
||||
}
|
||||
|
||||
public int compareTo(ProfileUUID val) {
|
||||
return (this.mostSigBits < val.mostSigBits ? -1 :
|
||||
(this.mostSigBits > val.mostSigBits ? 1 :
|
||||
(this.leastSigBits < val.leastSigBits ? -1 :
|
||||
(this.leastSigBits > val.leastSigBits ? 1 :
|
||||
0))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class BooleanResult {
|
||||
|
||||
public static final BooleanResult TRUE = new BooleanResult(true);
|
||||
public static final BooleanResult FALSE = new BooleanResult(false);
|
||||
|
||||
public final boolean bool;
|
||||
|
||||
private BooleanResult(boolean b) {
|
||||
bool = b;
|
||||
}
|
||||
|
||||
public static BooleanResult _new(boolean b) {
|
||||
return b ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
public class CRC32 implements Checksum {
|
||||
private com.jcraft.jzlib.CRC32 impl = new com.jcraft.jzlib.CRC32();
|
||||
long tbytes;
|
||||
|
||||
@Override
|
||||
public long getValue() {
|
||||
return impl.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
impl.reset();
|
||||
tbytes = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(int val) {
|
||||
impl.update(new byte[] { (byte) val }, 0, 1);
|
||||
}
|
||||
|
||||
public void update(byte[] buf) {
|
||||
update(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] buf, int off, int nbytes) {
|
||||
// avoid int overflow, check null buf
|
||||
if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) {
|
||||
impl.update(buf, off, nbytes);
|
||||
tbytes += nbytes;
|
||||
} else {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.minecraft.src.ILogAgent;
|
||||
|
||||
public class EAGLogAgent implements ILogAgent {
|
||||
|
||||
private final Logger logger = Logger.getLogger("IntegratedServer");
|
||||
|
||||
public Logger getServerLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
public void logInfo(String par1Str) {
|
||||
this.logger.log(Level.INFO, par1Str);
|
||||
}
|
||||
|
||||
public void logWarning(String par1Str) {
|
||||
this.logger.log(Level.WARNING, par1Str);
|
||||
}
|
||||
|
||||
public void logWarningFormatted(String par1Str, Object... par2ArrayOfObj) {
|
||||
this.logger.log(Level.WARNING, par1Str, par2ArrayOfObj);
|
||||
}
|
||||
|
||||
public void logWarningException(String par1Str, Throwable par2Throwable) {
|
||||
this.logger.log(Level.WARNING, par1Str, par2Throwable);
|
||||
}
|
||||
|
||||
public void logSevere(String par1Str) {
|
||||
this.logger.log(Level.SEVERE, par1Str);
|
||||
}
|
||||
|
||||
public void logSevereException(String par1Str, Throwable par2Throwable) {
|
||||
this.logger.log(Level.SEVERE, par1Str, par2Throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logFine(String var1) {
|
||||
this.logger.log(Level.FINE, var1);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.ipc.IPCPacket14StringList;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.EnumGameType;
|
||||
import net.minecraft.src.ILogAgent;
|
||||
import net.minecraft.src.WorldSettings;
|
||||
|
||||
public class EAGMinecraftServer extends MinecraftServer {
|
||||
|
||||
protected int difficulty;
|
||||
protected EnumGameType gamemode;
|
||||
protected long lastTick;
|
||||
protected WorkerListenThread listenThreadImpl;
|
||||
protected WorldSettings newWorldSettings;
|
||||
protected boolean paused;
|
||||
private int tpsCounter = 0;
|
||||
private int tpsMeasure = 0;
|
||||
private long tpsTimer = 0l;
|
||||
|
||||
public EAGMinecraftServer(String world, String owner, WorldSettings currentWorldSettings) {
|
||||
super(world);
|
||||
this.setServerOwner(owner);
|
||||
System.out.println("server owner: " + owner);
|
||||
this.setConfigurationManager(new EAGPlayerList(this));
|
||||
this.listenThreadImpl = new WorkerListenThread(this);
|
||||
this.newWorldSettings = currentWorldSettings;
|
||||
this.paused = false;
|
||||
}
|
||||
|
||||
public void setBaseServerProperties(int difficulty, EnumGameType gamemode) {
|
||||
this.difficulty = difficulty;
|
||||
this.gamemode = gamemode;
|
||||
this.setCanSpawnAnimals(true);
|
||||
this.setCanSpawnNPCs(true);
|
||||
this.setAllowPvp(true);
|
||||
this.setAllowFlight(true);
|
||||
}
|
||||
|
||||
public void mainLoop() {
|
||||
long ctm = SysUtil.steadyTimeMillis();
|
||||
|
||||
long elapsed = ctm - tpsTimer;
|
||||
if(elapsed >= 1000l) {
|
||||
tpsTimer = ctm;
|
||||
tpsMeasure = tpsCounter;
|
||||
IntegratedServer.sendIPCPacket(new IPCPacket14StringList(IPCPacket14StringList.SERVER_TPS, getTPSAndChunkBuffer(tpsMeasure)));
|
||||
tpsCounter = 0;
|
||||
}
|
||||
|
||||
if(paused && this.playersOnline.size() <= 1) {
|
||||
lastTick = ctm;
|
||||
return;
|
||||
}
|
||||
|
||||
long delta = ctm - lastTick;
|
||||
|
||||
if (delta > 2000L && ctm - this.timeOfLastWarning >= 15000L) {
|
||||
this.getLogAgent().logWarning("Can\'t keep up! Did the system time change, or is the server overloaded? Skipping " + ((delta - 2000l) / 50l) + " ticks");
|
||||
delta = 2000L;
|
||||
this.timeOfLastWarning = ctm;
|
||||
}
|
||||
|
||||
if (delta < 0L) {
|
||||
this.getLogAgent().logWarning("Time ran backwards! Did the fucking system time change?");
|
||||
delta = 0L;
|
||||
}
|
||||
|
||||
if (this.worldServers[0].areAllPlayersAsleep()) {
|
||||
this.tick();
|
||||
++tpsCounter;
|
||||
lastTick = SysUtil.steadyTimeMillis();
|
||||
} else {
|
||||
if (delta > 50l) {
|
||||
delta -= 50L;
|
||||
lastTick += 50l;
|
||||
this.tick();
|
||||
++tpsCounter;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setPaused(boolean p) {
|
||||
paused = p;
|
||||
if(!p) {
|
||||
lastTick = SysUtil.steadyTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getPaused() {
|
||||
return paused;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean startServer() throws IOException {
|
||||
SkinsPlugin.reset();
|
||||
//VoiceChatPlugin.reset();
|
||||
this.loadAllWorlds(folderName, 0l, newWorldSettings);
|
||||
this.lastTick = SysUtil.steadyTimeMillis();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopServer() {
|
||||
super.stopServer();
|
||||
SkinsPlugin.reset();
|
||||
//VoiceChatPlugin.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canStructuresSpawn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumGameType getGameType() {
|
||||
return gamemode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDifficulty() {
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHardcore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDedicatedServer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommandBlockEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkerListenThread getNetworkThread() {
|
||||
return listenThreadImpl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shareToLAN(EnumGameType var1, boolean var2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILogAgent getLogAgent() {
|
||||
return IntegratedServer.logger;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.EntityPlayerMP;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.ServerConfigurationManager;
|
||||
|
||||
public class EAGPlayerList extends ServerConfigurationManager {
|
||||
|
||||
private NBTTagCompound hostPlayerNBT = null;
|
||||
|
||||
public EAGPlayerList(MinecraftServer par1MinecraftServer) {
|
||||
super(par1MinecraftServer);
|
||||
this.viewDistance = 4;
|
||||
}
|
||||
|
||||
protected void writePlayerData(EntityPlayerMP par1EntityPlayerMP) {
|
||||
if (par1EntityPlayerMP.getCommandSenderName().equals(this.getServerInstance().getServerOwner())) {
|
||||
this.hostPlayerNBT = new NBTTagCompound();
|
||||
par1EntityPlayerMP.writeToNBT(hostPlayerNBT);
|
||||
}
|
||||
super.writePlayerData(par1EntityPlayerMP);
|
||||
}
|
||||
|
||||
public NBTTagCompound getHostPlayerData() {
|
||||
return this.hostPlayerNBT;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import com.jcraft.jzlib.CRC32;
|
||||
|
||||
public class EPK2Compiler {
|
||||
|
||||
private final ByteArrayOutputStream os;
|
||||
private final CRC32 checkSum = new CRC32();
|
||||
private int lengthIntegerOffset = 0;
|
||||
private int totalFileCount = 0;
|
||||
|
||||
public EPK2Compiler(String name, String owner, String type) {
|
||||
os = new ByteArrayOutputStream(0x200000);
|
||||
try {
|
||||
|
||||
os.write(new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)36,(byte)36}); // EAGPKG$$
|
||||
os.write(new byte[]{(byte)6,(byte)118,(byte)101,(byte)114,(byte)50,(byte)46,(byte)48}); // 6 + ver2.0
|
||||
Date d = new Date();
|
||||
|
||||
byte[] filename = (name + ".epk").getBytes(StandardCharsets.UTF_8);
|
||||
os.write(filename.length);
|
||||
os.write(filename);
|
||||
|
||||
byte[] comment = ("\n\n # Eagler EPK v2.0 (c) " + (new SimpleDateFormat("yyyy")).format(d) + " " +
|
||||
owner + "\n # export: on " + (new SimpleDateFormat("MM/dd/yyyy")).format(d) + " at " +
|
||||
(new SimpleDateFormat("hh:mm:ss aa")).format(d) + "\n\n # world name: " + name + "\n\n")
|
||||
.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
os.write((comment.length >> 8) & 255);
|
||||
os.write(comment.length & 255);
|
||||
os.write(comment);
|
||||
|
||||
writeLong(d.getTime(), os);
|
||||
|
||||
lengthIntegerOffset = os.size();
|
||||
os.write(new byte[]{(byte)255,(byte)255,(byte)255,(byte)255}); // this will be replaced with the file count
|
||||
|
||||
os.write('0'); // compression type: none
|
||||
|
||||
os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
|
||||
os.write(new byte[]{(byte)9,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)116,(byte)121,
|
||||
(byte)112,(byte)101}); // 9 + file-type
|
||||
|
||||
byte[] typeBytes = type.getBytes(StandardCharsets.UTF_8);
|
||||
writeInt(typeBytes.length, os);
|
||||
os.write(typeBytes); // write type
|
||||
os.write('>');
|
||||
|
||||
++totalFileCount;
|
||||
|
||||
os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
|
||||
os.write(new byte[]{(byte)10,(byte)119,(byte)111,(byte)114,(byte)108,(byte)100,(byte)45,(byte)110,
|
||||
(byte)97,(byte)109,(byte)101}); // 10 + world-name
|
||||
|
||||
byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
|
||||
writeInt(nameBytes.length, os);
|
||||
os.write(nameBytes); // write name
|
||||
os.write('>');
|
||||
|
||||
++totalFileCount;
|
||||
|
||||
os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
|
||||
os.write(new byte[]{(byte)11,(byte)119,(byte)111,(byte)114,(byte)108,(byte)100,(byte)45,(byte)111,
|
||||
(byte)119,(byte)110,(byte)101,(byte)114}); // 11 + world-owner
|
||||
|
||||
byte[] ownerBytes = owner.getBytes(StandardCharsets.UTF_8);
|
||||
writeInt(ownerBytes.length, os);
|
||||
os.write(ownerBytes); // write owner
|
||||
os.write('>');
|
||||
|
||||
++totalFileCount;
|
||||
|
||||
}catch(IOException ex) {
|
||||
throw new RuntimeException("This happened somehow", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void append(String name, byte[] dat) {
|
||||
try {
|
||||
|
||||
checkSum.reset();
|
||||
checkSum.update(dat, 0, dat.length);
|
||||
long sum = checkSum.getValue();
|
||||
|
||||
os.write(new byte[]{(byte)70,(byte)73,(byte)76,(byte)69}); // FILE
|
||||
|
||||
byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
|
||||
os.write(nameBytes.length);
|
||||
os.write(nameBytes);
|
||||
|
||||
writeInt(dat.length + 5, os);
|
||||
writeInt((int)sum, os);
|
||||
|
||||
os.write(dat);
|
||||
|
||||
os.write(':');
|
||||
os.write('>');
|
||||
|
||||
++totalFileCount;
|
||||
|
||||
}catch(IOException ex) {
|
||||
throw new RuntimeException("This happened somehow", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] complete() {
|
||||
try {
|
||||
|
||||
os.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$
|
||||
os.write(new byte[]{(byte)58,(byte)58,(byte)58,(byte)89,(byte)69,(byte)69,(byte)58,(byte)62}); // :::YEE:>
|
||||
|
||||
byte[] ret = os.toByteArray();
|
||||
|
||||
ret[lengthIntegerOffset] = (byte)((totalFileCount >> 24) & 0xFF);
|
||||
ret[lengthIntegerOffset + 1] = (byte)((totalFileCount >> 16) & 0xFF);
|
||||
ret[lengthIntegerOffset + 2] = (byte)((totalFileCount >> 8) & 0xFF);
|
||||
ret[lengthIntegerOffset + 3] = (byte)(totalFileCount & 0xFF);
|
||||
|
||||
return ret;
|
||||
|
||||
}catch(IOException ex) {
|
||||
throw new RuntimeException("This happened somehow", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeInt(int i, OutputStream os) throws IOException {
|
||||
os.write((i >> 24) & 0xFF);
|
||||
os.write((i >> 16) & 0xFF);
|
||||
os.write((i >> 8) & 0xFF);
|
||||
os.write(i & 0xFF);
|
||||
}
|
||||
|
||||
public static void writeLong(long i, OutputStream os) throws IOException {
|
||||
os.write((int)((i >> 56) & 0xFF));
|
||||
os.write((int)((i >> 48) & 0xFF));
|
||||
os.write((int)((i >> 40) & 0xFF));
|
||||
os.write((int)((i >> 32) & 0xFF));
|
||||
os.write((int)((i >> 24) & 0xFF));
|
||||
os.write((int)((i >> 16) & 0xFF));
|
||||
os.write((int)((i >> 8) & 0xFF));
|
||||
os.write((int)(i & 0xFF));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.jcraft.jzlib.CRC32;
|
||||
import com.jcraft.jzlib.GZIPInputStream;
|
||||
import com.jcraft.jzlib.InflaterInputStream;
|
||||
|
||||
public class EPKDecompiler {
|
||||
|
||||
public static class FileEntry {
|
||||
public final String type;
|
||||
public final String name;
|
||||
public final byte[] data;
|
||||
protected FileEntry(String type, String name, byte[] data) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
private ByteArrayInputStream in2;
|
||||
private DataInputStream in;
|
||||
private InputStream zis;
|
||||
private SHA1Digest dg;
|
||||
private CRC32 crc32;
|
||||
private int numFiles;
|
||||
private boolean isFinished = false;
|
||||
private boolean isOldFormat = false;
|
||||
|
||||
public EPKDecompiler(byte[] data) throws IOException {
|
||||
in2 = new ByteArrayInputStream(data);
|
||||
|
||||
byte[] header = new byte[8];
|
||||
in2.read(header);
|
||||
|
||||
if(Arrays.equals(header, new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)36,(byte)36})) {
|
||||
byte[] endCode = new byte[] { (byte)':', (byte)':', (byte)':', (byte)'Y',
|
||||
(byte)'E', (byte)'E', (byte)':', (byte)'>' };
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
if(data[data.length - 8 + i] != endCode[i]) {
|
||||
throw new IOException("EPK file is missing EOF code (:::YEE:>)");
|
||||
}
|
||||
}
|
||||
in2 = new ByteArrayInputStream(data, 8, data.length - 16);
|
||||
initNew();
|
||||
}else if(Arrays.equals(header, new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)33,(byte)33})) {
|
||||
initOld();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isOld() {
|
||||
return isOldFormat;
|
||||
}
|
||||
|
||||
public FileEntry readFile() throws IOException {
|
||||
if(!isOldFormat) {
|
||||
return readFileNew();
|
||||
}else {
|
||||
return readFileOld();
|
||||
}
|
||||
}
|
||||
|
||||
private void initNew() throws IOException {
|
||||
InputStream is = in2;
|
||||
|
||||
String vers = readASCII(is);
|
||||
if(!vers.startsWith("ver2.")) {
|
||||
throw new IOException("Unknown or invalid EPK version: " + vers);
|
||||
}
|
||||
|
||||
is.skip(is.read()); // skip filename
|
||||
is.skip(loadShort(is)); // skip comment
|
||||
is.skip(8); // skip millis date
|
||||
|
||||
numFiles = loadInt(is);
|
||||
|
||||
char compressionType = (char)is.read();
|
||||
|
||||
switch(compressionType) {
|
||||
case 'G':
|
||||
zis = new GZIPInputStream(is);
|
||||
break;
|
||||
case 'Z':
|
||||
zis = new InflaterInputStream(is);
|
||||
break;
|
||||
case '0':
|
||||
zis = is;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Invalid or unsupported EPK compression: " + compressionType);
|
||||
}
|
||||
|
||||
crc32 = new CRC32();
|
||||
|
||||
}
|
||||
|
||||
private FileEntry readFileNew() throws IOException {
|
||||
if(isFinished) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] typeBytes = new byte[4];
|
||||
zis.read(typeBytes);
|
||||
String type = readASCII(typeBytes);
|
||||
|
||||
if(numFiles == 0) {
|
||||
if(!"END$".equals(type)) {
|
||||
throw new IOException("EPK file is missing END code (END$)");
|
||||
}
|
||||
isFinished = true;
|
||||
return null;
|
||||
}else {
|
||||
if("END$".equals(type)) {
|
||||
throw new IOException("Unexpected END when there are still " + numFiles + " files remaining");
|
||||
}else {
|
||||
String name = readASCII(zis);
|
||||
int len = loadInt(zis);
|
||||
byte[] data;
|
||||
|
||||
if("FILE".equals(type)) {
|
||||
if(len < 5) {
|
||||
throw new IOException("File '" + name + "' is incomplete (no crc)");
|
||||
}
|
||||
|
||||
int loadedCrc = loadInt(zis);
|
||||
|
||||
data = new byte[len - 5];
|
||||
zis.read(data);
|
||||
|
||||
crc32.reset();
|
||||
crc32.update(data, 0, data.length);
|
||||
if((int)crc32.getValue() != loadedCrc) {
|
||||
throw new IOException("File '" + name + "' has an invalid checksum");
|
||||
}
|
||||
|
||||
if(zis.read() != ':') {
|
||||
throw new IOException("File '" + name + "' is incomplete");
|
||||
}
|
||||
}else {
|
||||
data = new byte[len];
|
||||
zis.read(data);
|
||||
}
|
||||
|
||||
if(zis.read() != '>') {
|
||||
throw new IOException("Object '" + name + "' is incomplete");
|
||||
}
|
||||
|
||||
--numFiles;
|
||||
return new FileEntry(type, name, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int loadShort(InputStream is) throws IOException {
|
||||
return (is.read() << 8) | is.read();
|
||||
}
|
||||
|
||||
private static final int loadInt(InputStream is) throws IOException {
|
||||
return (is.read() << 24) | (is.read() << 16) | (is.read() << 8) | is.read();
|
||||
}
|
||||
|
||||
public static final String readASCII(byte[] bytesIn) throws IOException {
|
||||
char[] charIn = new char[bytesIn.length];
|
||||
for(int i = 0; i < bytesIn.length; ++i) {
|
||||
charIn[i] = (char)((int)bytesIn[i] & 0xFF);
|
||||
}
|
||||
return new String(charIn);
|
||||
}
|
||||
|
||||
private static final String readASCII(InputStream bytesIn) throws IOException {
|
||||
int len = bytesIn.read();
|
||||
char[] charIn = new char[len];
|
||||
for(int i = 0; i < len; ++i) {
|
||||
charIn[i] = (char)(bytesIn.read() & 0xFF);
|
||||
}
|
||||
return new String(charIn);
|
||||
}
|
||||
|
||||
private void initOld() throws IOException {
|
||||
isOldFormat = true;
|
||||
dg = new SHA1Digest();
|
||||
in = new DataInputStream(in2);
|
||||
in.readUTF();
|
||||
in = new DataInputStream(new InflaterInputStream(in2));
|
||||
}
|
||||
|
||||
private FileEntry readFileOld() throws IOException {
|
||||
if(isFinished) {
|
||||
return null;
|
||||
}
|
||||
String s = in.readUTF();
|
||||
if(s.equals(" end")) {
|
||||
isFinished = true;
|
||||
return null;
|
||||
}else if(!s.equals("<file>")) {
|
||||
throw new IOException("invalid epk file");
|
||||
}
|
||||
String path = in.readUTF();
|
||||
byte[] digest = new byte[20];
|
||||
byte[] digest2 = new byte[20];
|
||||
in.read(digest);
|
||||
int len = in.readInt();
|
||||
byte[] file = new byte[len];
|
||||
in.read(file);
|
||||
dg.update(file, 0, len); dg.doFinal(digest2, 0);
|
||||
if(!Arrays.equals(digest, digest2)) throw new IOException("invalid file hash for "+path);
|
||||
if(!"</file>".equals(in.readUTF())) throw new IOException("invalid epk file");
|
||||
return new FileEntry("FILE", path, file);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class EaglerUUID {
|
||||
|
||||
private final long mostSigBits;
|
||||
private final long leastSigBits;
|
||||
|
||||
private EaglerUUID(byte[] data) {
|
||||
long msb = 0;
|
||||
long lsb = 0;
|
||||
assert data.length == 16 : "data must be 16 bytes in length";
|
||||
for (int i=0; i<8; i++)
|
||||
msb = (msb << 8) | (data[i] & 0xff);
|
||||
for (int i=8; i<16; i++)
|
||||
lsb = (lsb << 8) | (data[i] & 0xff);
|
||||
this.mostSigBits = msb;
|
||||
this.leastSigBits = lsb;
|
||||
}
|
||||
|
||||
public EaglerUUID(long mostSigBits, long leastSigBits) {
|
||||
this.mostSigBits = mostSigBits;
|
||||
this.leastSigBits = leastSigBits;
|
||||
}
|
||||
|
||||
private static final EaglercraftRandom random = new EaglercraftRandom();
|
||||
|
||||
public static EaglerUUID randomUUID() {
|
||||
byte[] randomBytes = new byte[16];
|
||||
random.nextBytes(randomBytes);
|
||||
randomBytes[6] &= 0x0f; /* clear version */
|
||||
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new EaglerUUID(randomBytes);
|
||||
}
|
||||
|
||||
private static final MD5Digest yee = new MD5Digest();
|
||||
|
||||
public static EaglerUUID nameUUIDFromBytes(byte[] name) {
|
||||
yee.update(name, 0, name.length);
|
||||
byte[] md5Bytes = new byte[16];
|
||||
yee.doFinal(md5Bytes, 0);
|
||||
md5Bytes[6] &= 0x0f; /* clear version */
|
||||
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new EaglerUUID(md5Bytes);
|
||||
}
|
||||
|
||||
public static EaglerUUID fromString(String name) {
|
||||
String[] components = name.split("-");
|
||||
if (components.length != 5)
|
||||
throw new IllegalArgumentException("Invalid UUID string: "+name);
|
||||
for (int i=0; i<5; i++)
|
||||
components[i] = "0x"+components[i];
|
||||
|
||||
long mostSigBits = Long.decode(components[0]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[1]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[2]).longValue();
|
||||
|
||||
long leastSigBits = Long.decode(components[3]).longValue();
|
||||
leastSigBits <<= 48;
|
||||
leastSigBits |= Long.decode(components[4]).longValue();
|
||||
|
||||
return new EaglerUUID(mostSigBits, leastSigBits);
|
||||
}
|
||||
|
||||
public long getLeastSignificantBits() {
|
||||
return leastSigBits;
|
||||
}
|
||||
|
||||
public long getMostSignificantBits() {
|
||||
return mostSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version number associated with this {@code UUID}. The version
|
||||
* number describes how this {@code UUID} was generated.
|
||||
*
|
||||
* The version number has the following meaning:
|
||||
* <ul>
|
||||
* <li>1 Time-based UUID
|
||||
* <li>2 DCE security UUID
|
||||
* <li>3 Name-based UUID
|
||||
* <li>4 Randomly generated UUID
|
||||
* </ul>
|
||||
*
|
||||
* @return The version number of this {@code UUID}
|
||||
*/
|
||||
public int version() {
|
||||
// Version is bits masked by 0x000000000000F000 in MS long
|
||||
return (int)((mostSigBits >> 12) & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* The variant number associated with this {@code UUID}. The variant
|
||||
* number describes the layout of the {@code UUID}.
|
||||
*
|
||||
* The variant number has the following meaning:
|
||||
* <ul>
|
||||
* <li>0 Reserved for NCS backward compatibility
|
||||
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>
|
||||
* (Leach-Salz), used by this class
|
||||
* <li>6 Reserved, Microsoft Corporation backward compatibility
|
||||
* <li>7 Reserved for future definition
|
||||
* </ul>
|
||||
*
|
||||
* @return The variant number of this {@code UUID}
|
||||
*/
|
||||
public int variant() {
|
||||
// This field is composed of a varying number of bits.
|
||||
// 0 - - Reserved for NCS backward compatibility
|
||||
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||
// 1 1 1 Reserved for future definition.
|
||||
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62)))
|
||||
& (leastSigBits >> 63));
|
||||
}
|
||||
|
||||
public long timestamp() {
|
||||
if (version() != 1) {
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
|
||||
return (mostSigBits & 0x0FFFL) << 48
|
||||
| ((mostSigBits >> 16) & 0x0FFFFL) << 32
|
||||
| mostSigBits >>> 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* The clock sequence value associated with this UUID.
|
||||
*
|
||||
* <p> The 14 bit clock sequence value is constructed from the clock
|
||||
* sequence field of this UUID. The clock sequence field is used to
|
||||
* guarantee temporal uniqueness in a time-based UUID.
|
||||
*
|
||||
* <p> The {@code clockSequence} value is only meaningful in a time-based
|
||||
* UUID, which has version type 1. If this UUID is not a time-based UUID
|
||||
* then this method throws UnsupportedOperationException.
|
||||
*
|
||||
* @return The clock sequence of this {@code UUID}
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
* If this UUID is not a version 1 UUID
|
||||
*/
|
||||
public int clockSequence() {
|
||||
if (version() != 1) {
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
|
||||
return (int)((leastSigBits & 0x3FFF000000000000L) >>> 48);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return (digits(mostSigBits >> 32, 8) + "-" +
|
||||
digits(mostSigBits >> 16, 4) + "-" +
|
||||
digits(mostSigBits, 4) + "-" +
|
||||
digits(leastSigBits >> 48, 4) + "-" +
|
||||
digits(leastSigBits, 12));
|
||||
}
|
||||
|
||||
private static String digits(long val, int digits) {
|
||||
long hi = 1L << (digits * 4);
|
||||
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
long hilo = mostSigBits ^ leastSigBits;
|
||||
return ((int)(hilo >> 32)) ^ (int) hilo;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ((null == obj) || !(obj instanceof EaglerUUID))
|
||||
return false;
|
||||
EaglerUUID id = (EaglerUUID)obj;
|
||||
return (mostSigBits == id.mostSigBits &&
|
||||
leastSigBits == id.leastSigBits);
|
||||
}
|
||||
|
||||
public int compareTo(EaglerUUID val) {
|
||||
return (this.mostSigBits < val.mostSigBits ? -1 :
|
||||
(this.mostSigBits > val.mostSigBits ? 1 :
|
||||
(this.leastSigBits < val.leastSigBits ? -1 :
|
||||
(this.leastSigBits > val.leastSigBits ? 1 :
|
||||
0))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class EaglercraftRandom {
|
||||
|
||||
private static final long multiplier = 0x5DEECE66DL;
|
||||
private static final long addend = 0xBL;
|
||||
private static final long mask = (1L << 48) - 1;
|
||||
|
||||
private static final double DOUBLE_UNIT = 0x1.0p-53;
|
||||
private long seed = 69;
|
||||
|
||||
public EaglercraftRandom() {
|
||||
this(System.nanoTime());
|
||||
}
|
||||
|
||||
public EaglercraftRandom(long seed) {
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
public void setSeed(long yeed) {
|
||||
seed = yeed;
|
||||
}
|
||||
|
||||
protected int next(int bits) {
|
||||
seed = (seed * multiplier + addend) & mask;
|
||||
return (int)(seed >>> (48 - bits));
|
||||
}
|
||||
|
||||
public void nextBytes(byte[] bytes) {
|
||||
for (int i = 0, len = bytes.length; i < len; )
|
||||
for (int rnd = nextInt(),
|
||||
n = Math.min(len - i, Integer.SIZE/Byte.SIZE);
|
||||
n-- > 0; rnd >>= Byte.SIZE)
|
||||
bytes[i++] = (byte)rnd;
|
||||
}
|
||||
public int nextInt() {
|
||||
return next(32);
|
||||
}
|
||||
public int nextInt(int bound) {
|
||||
int r = next(31);
|
||||
int m = bound - 1;
|
||||
if ((bound & m) == 0) // i.e., bound is a power of 2
|
||||
r = (int)((bound * (long)r) >> 31);
|
||||
else {
|
||||
for (int u = r;
|
||||
u - (r = u % bound) + m < 0;
|
||||
u = next(31))
|
||||
;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
public long nextLong() {
|
||||
return ((long)(next(32)) << 32) + next(32);
|
||||
}
|
||||
public boolean nextBoolean() {
|
||||
return next(1) != 0;
|
||||
}
|
||||
public float nextFloat() {
|
||||
return next(24) / ((float)(1 << 24));
|
||||
}
|
||||
public double nextDouble() {
|
||||
return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
|
||||
}
|
||||
private double nextNextGaussian;
|
||||
private boolean haveNextNextGaussian = false;
|
||||
public double nextGaussian() {
|
||||
// See Knuth, ACP, Section 3.4.1 Algorithm C.
|
||||
if (haveNextNextGaussian) {
|
||||
haveNextNextGaussian = false;
|
||||
return nextNextGaussian;
|
||||
} else {
|
||||
double v1, v2, s;
|
||||
do {
|
||||
v1 = 2 * nextDouble() - 1; // between -1 and 1
|
||||
v2 = 2 * nextDouble() - 1; // between -1 and 1
|
||||
s = v1 * v1 + v2 * v2;
|
||||
} while (s >= 1 || s == 0);
|
||||
double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
|
||||
nextNextGaussian = v2 * multiplier;
|
||||
haveNextNextGaussian = true;
|
||||
return v1 * multiplier;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
// note that there's a few things not implemented, but I don't care.
|
||||
|
||||
public class ExpiringSet<T> extends HashSet<T> {
|
||||
private final long expiration;
|
||||
private final ExpiringEvent<T> event;
|
||||
|
||||
private final Map<T, Long> timestamps = new HashMap<>();
|
||||
|
||||
public ExpiringSet(long expiration) {
|
||||
this.expiration = expiration;
|
||||
this.event = null;
|
||||
}
|
||||
|
||||
public ExpiringSet(long expiration, ExpiringEvent<T> event) {
|
||||
this.expiration = expiration;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public interface ExpiringEvent<T> {
|
||||
void onExpiration(T item);
|
||||
}
|
||||
|
||||
public void checkForExpirations() {
|
||||
Iterator<T> iterator = this.timestamps.keySet().iterator();
|
||||
long now = SysUtil.steadyTimeMillis();
|
||||
while (iterator.hasNext()) {
|
||||
T element = iterator.next();
|
||||
if (super.contains(element)) {
|
||||
if (this.timestamps.get(element) + this.expiration < now) {
|
||||
if (this.event != null)
|
||||
this.event.onExpiration(element);
|
||||
iterator.remove();
|
||||
super.remove(element);
|
||||
}
|
||||
} else {
|
||||
iterator.remove();
|
||||
super.remove(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(T o) {
|
||||
checkForExpirations();
|
||||
boolean success = super.add(o);
|
||||
if (success)
|
||||
timestamps.put(o, SysUtil.steadyTimeMillis());
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean remove(Object o) {
|
||||
checkForExpirations();
|
||||
boolean success = super.remove(o);
|
||||
if (success)
|
||||
timestamps.remove(o);
|
||||
return success;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.timestamps.clear();
|
||||
super.clear();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
checkForExpirations();
|
||||
return super.contains(o);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
/**
|
||||
* base implementation of MD4 family style digest as outlined in
|
||||
* "Handbook of Applied Cryptography", pages 344 - 347.
|
||||
*/
|
||||
public abstract class GeneralDigest {
|
||||
private byte[] xBuf;
|
||||
private int xBufOff;
|
||||
|
||||
private long byteCount;
|
||||
|
||||
/**
|
||||
* Standard constructor
|
||||
*/
|
||||
protected GeneralDigest()
|
||||
{
|
||||
xBuf = new byte[4];
|
||||
xBufOff = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor. We are using copy constructors in place
|
||||
* of the Object.clone() interface as this interface is not
|
||||
* supported by J2ME.
|
||||
*/
|
||||
protected GeneralDigest(GeneralDigest t)
|
||||
{
|
||||
xBuf = new byte[t.xBuf.length];
|
||||
System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
|
||||
|
||||
xBufOff = t.xBufOff;
|
||||
byteCount = t.byteCount;
|
||||
}
|
||||
|
||||
public void update(
|
||||
byte in)
|
||||
{
|
||||
xBuf[xBufOff++] = in;
|
||||
|
||||
if (xBufOff == xBuf.length)
|
||||
{
|
||||
processWord(xBuf, 0);
|
||||
xBufOff = 0;
|
||||
}
|
||||
|
||||
byteCount++;
|
||||
}
|
||||
|
||||
public void update(
|
||||
byte[] in,
|
||||
int inOff,
|
||||
int len)
|
||||
{
|
||||
//
|
||||
// fill the current word
|
||||
//
|
||||
while ((xBufOff != 0) && (len > 0))
|
||||
{
|
||||
update(in[inOff]);
|
||||
|
||||
inOff++;
|
||||
len--;
|
||||
}
|
||||
|
||||
//
|
||||
// process whole words.
|
||||
//
|
||||
while (len > xBuf.length)
|
||||
{
|
||||
processWord(in, inOff);
|
||||
|
||||
inOff += xBuf.length;
|
||||
len -= xBuf.length;
|
||||
byteCount += xBuf.length;
|
||||
}
|
||||
|
||||
//
|
||||
// load in the remainder.
|
||||
//
|
||||
while (len > 0)
|
||||
{
|
||||
update(in[inOff]);
|
||||
|
||||
inOff++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
public void finish()
|
||||
{
|
||||
long bitLength = (byteCount << 3);
|
||||
|
||||
//
|
||||
// add the pad bytes.
|
||||
//
|
||||
update((byte)128);
|
||||
|
||||
while (xBufOff != 0)
|
||||
{
|
||||
update((byte)0);
|
||||
}
|
||||
|
||||
processLength(bitLength);
|
||||
|
||||
processBlock();
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
byteCount = 0;
|
||||
|
||||
xBufOff = 0;
|
||||
for ( int i = 0; i < xBuf.length; i++ ) {
|
||||
xBuf[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void processWord(byte[] in, int inOff);
|
||||
|
||||
protected abstract void processLength(long bitLength);
|
||||
|
||||
protected abstract void processBlock();
|
||||
}
|
|
@ -0,0 +1,548 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSFunctor;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.ipc.*;
|
||||
import net.minecraft.src.AchievementList;
|
||||
import net.minecraft.src.AchievementMap;
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.EnumGameType;
|
||||
import net.minecraft.src.ILogAgent;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.StringTranslate;
|
||||
import net.minecraft.src.WorldSettings;
|
||||
import net.minecraft.src.WorldType;
|
||||
|
||||
public class IntegratedServer {
|
||||
|
||||
private static final LinkedList<PKT> messageQueue = new LinkedList<>();
|
||||
|
||||
protected static class PKT {
|
||||
protected final String channel;
|
||||
protected final byte[] data;
|
||||
protected PKT(String channel, byte[] data) {
|
||||
this.channel = channel;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
private static EAGMinecraftServer currentProcess = null;
|
||||
private static WorldSettings newWorldSettings = null;
|
||||
|
||||
public static EAGMinecraftServer getServer() {
|
||||
return currentProcess;
|
||||
}
|
||||
|
||||
public static final ILogAgent logger = new EAGLogAgent();
|
||||
|
||||
@JSFunctor
|
||||
private static interface WorkerBinaryPacketHandler extends JSObject {
|
||||
public void onMessage(String channel, ArrayBuffer buf);
|
||||
}
|
||||
|
||||
private static class WorkerBinaryPacketHandlerImpl implements WorkerBinaryPacketHandler {
|
||||
|
||||
public void onMessage(String channel, ArrayBuffer buf) {
|
||||
if(channel == null) {
|
||||
System.err.println("Recieved IPC packet with null channel");
|
||||
return;
|
||||
}
|
||||
|
||||
if(buf == null) {
|
||||
System.err.println("Recieved IPC packet with null buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized(messageQueue) {
|
||||
messageQueue.add(new PKT(channel, TeaVMUtils.wrapByteArrayBuffer(buf)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void tryStopServer() {
|
||||
if(currentProcess != null) {
|
||||
try {
|
||||
currentProcess.stopServer();
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Failed to stop server!");
|
||||
throwExceptionToClient("Failed to stop server!", t);
|
||||
}
|
||||
currentProcess = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateStatusString(String stat, float prog) {
|
||||
sendIPCPacket(new IPCPacket0DProgressUpdate(stat, prog));
|
||||
}
|
||||
|
||||
private static boolean isServerStopped() {
|
||||
return currentProcess == null || !currentProcess.isServerRunning();
|
||||
}
|
||||
|
||||
public static void throwExceptionToClient(String msg, Throwable t) {
|
||||
String str = t.toString();
|
||||
System.err.println("Exception was raised to client: " + str);
|
||||
t.printStackTrace();
|
||||
List<String> arr = new LinkedList<>();
|
||||
for(StackTraceElement e : t.getStackTrace()) {
|
||||
String st = e.toString();
|
||||
arr.add(st);
|
||||
}
|
||||
sendIPCPacket(new IPCPacket15ThrowException(str, arr));
|
||||
}
|
||||
|
||||
public static void sendTaskFailed() {
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacketFFProcessKeepAlive.FAILURE));
|
||||
}
|
||||
|
||||
private static void processAsyncMessageQueue() {
|
||||
ArrayList<PKT> cur;
|
||||
synchronized(messageQueue) {
|
||||
if(messageQueue.size() <= 0) {
|
||||
return;
|
||||
}
|
||||
cur = new ArrayList<PKT>(messageQueue);
|
||||
messageQueue.clear();
|
||||
}
|
||||
Iterator<PKT> itr = cur.iterator();
|
||||
while(itr.hasNext()) {
|
||||
PKT msg = itr.next();
|
||||
if(msg.channel.equals("IPC")) {
|
||||
|
||||
IPCPacketBase packet;
|
||||
try {
|
||||
packet = IPCPacketManager.IPCDeserialize(msg.data);
|
||||
}catch(IOException e) {
|
||||
System.err.print("Failed to deserialize IPC packet: ");
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
int id = packet.id();
|
||||
|
||||
try {
|
||||
switch(id) {
|
||||
case IPCPacket00StartServer.ID: {
|
||||
IPCPacket00StartServer pkt = (IPCPacket00StartServer)packet;
|
||||
|
||||
if(!isServerStopped()) {
|
||||
currentProcess.stopServer();
|
||||
}
|
||||
|
||||
currentProcess = new EAGMinecraftServer(pkt.worldName, pkt.ownerName, newWorldSettings);
|
||||
currentProcess.setBaseServerProperties(pkt.initialDifficulty, newWorldSettings == null ? EnumGameType.SURVIVAL : newWorldSettings.getGameType());
|
||||
currentProcess.startServer();
|
||||
|
||||
String[] worlds = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
if(worlds == null || (worlds.length == 1 && worlds[0].trim().length() <= 0)) {
|
||||
worlds = null;
|
||||
}
|
||||
if(worlds == null) {
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(pkt.worldName);
|
||||
}else {
|
||||
boolean found = false;
|
||||
for(String s : worlds) {
|
||||
if(s.equals(pkt.worldName)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
String[] s = new String[worlds.length + 1];
|
||||
s[0] = pkt.worldName;
|
||||
System.arraycopy(worlds, 0, s, 1, worlds.length);
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", s));
|
||||
}
|
||||
}
|
||||
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket00StartServer.ID));
|
||||
}
|
||||
break;
|
||||
case IPCPacket01StopServer.ID: {
|
||||
if(!isServerStopped()) {
|
||||
try {
|
||||
currentProcess.stopServer();
|
||||
currentProcess = null;
|
||||
}catch(Throwable t) {
|
||||
throwExceptionToClient("Failed to stop server!", t);
|
||||
}
|
||||
}else {
|
||||
System.err.println("Client tried to stop server while it wasn't running for some reason");
|
||||
}
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket01StopServer.ID));
|
||||
}
|
||||
break;
|
||||
case IPCPacket02InitWorld.ID: {
|
||||
tryStopServer();
|
||||
IPCPacket02InitWorld pkt = (IPCPacket02InitWorld)packet;
|
||||
newWorldSettings = new WorldSettings(pkt.seed, pkt.gamemode == 1 ? EnumGameType.CREATIVE : EnumGameType.SURVIVAL, pkt.structures,
|
||||
pkt.gamemode == 2, pkt.worldType == 1 ? WorldType.FLAT : (pkt.worldType == 2 ? WorldType.LARGE_BIOMES : WorldType.DEFAULT_1_1));
|
||||
newWorldSettings.func_82750_a(pkt.worldArgs);
|
||||
if(pkt.bonusChest) {
|
||||
newWorldSettings.enableBonusChest();
|
||||
}
|
||||
if(pkt.cheats) {
|
||||
newWorldSettings.enableCommands();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket03DeleteWorld.ID: {
|
||||
tryStopServer();
|
||||
IPCPacket03DeleteWorld pkt = (IPCPacket03DeleteWorld)packet;
|
||||
if(SYS.VFS.deleteFiles("worlds/" + pkt.worldName + "/") <= 0) {
|
||||
throwExceptionToClient("Failed to delete world!", new RuntimeException("VFS did not delete directory 'worlds/" + pkt.worldName + "' correctly"));
|
||||
sendTaskFailed();
|
||||
break;
|
||||
}
|
||||
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
if(worldsTxt != null) {
|
||||
LinkedList<String> newWorlds = new LinkedList<>();
|
||||
for(String str : worldsTxt) {
|
||||
if(!str.equalsIgnoreCase(pkt.worldName)) {
|
||||
newWorlds.add(str);
|
||||
}
|
||||
}
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", newWorlds));
|
||||
}
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket03DeleteWorld.ID));
|
||||
}
|
||||
break;
|
||||
case IPCPacket04RenameWorld.ID: {
|
||||
tryStopServer();
|
||||
IPCPacket04RenameWorld pkt = (IPCPacket04RenameWorld)packet;
|
||||
if(SYS.VFS.renameFiles("worlds/" + pkt.worldOldName + "/", "worlds/" + pkt.worldNewName + "/", pkt.copy) <= 0) {
|
||||
throwExceptionToClient("Failed to copy/rename server!", new RuntimeException("VFS did not copy/rename directory 'worlds/" + pkt.worldOldName + "' correctly"));
|
||||
sendTaskFailed();
|
||||
break;
|
||||
}else {
|
||||
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
LinkedList<String> newWorlds = new LinkedList<>();
|
||||
if(worldsTxt != null) {
|
||||
for(String str : worldsTxt) {
|
||||
if(pkt.copy || !str.equalsIgnoreCase(pkt.worldOldName)) {
|
||||
newWorlds.add(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
newWorlds.add(pkt.worldNewName);
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", newWorlds));
|
||||
VFile worldDat = new VFile("worlds", pkt.worldNewName, "level.dat");
|
||||
if(worldDat.canRead()) {
|
||||
NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(worldDat.getAllBytes());
|
||||
worldDatNBT.getCompoundTag("Data").setString("LevelName", pkt.displayName);
|
||||
worldDat.setAllBytes(CompressedStreamTools.compress(worldDatNBT));
|
||||
}else {
|
||||
throwExceptionToClient("Failed to copy/rename world!", new RuntimeException("Failed to change level.dat world '" + pkt.worldNewName + "' display name to '" + pkt.displayName + "' because level.dat was missing"));
|
||||
sendTaskFailed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket04RenameWorld.ID));
|
||||
}
|
||||
break;
|
||||
case IPCPacket05RequestData.ID: {
|
||||
IPCPacket05RequestData pkt = (IPCPacket05RequestData)packet;
|
||||
if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_EAG) {
|
||||
try {
|
||||
sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterEPK.exportWorld(pkt.worldName)));
|
||||
} catch (Throwable t) {
|
||||
String realWorldName = pkt.worldName;
|
||||
int i = realWorldName.lastIndexOf(new String(new char[] { (char)253, (char)233, (char)233 }));
|
||||
if(i != -1) {
|
||||
realWorldName = realWorldName.substring(0, i);
|
||||
}
|
||||
throwExceptionToClient("Failed to export world '" + realWorldName+ "' as EPK", t);
|
||||
sendTaskFailed();
|
||||
}
|
||||
}else if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_MCA) {
|
||||
try {
|
||||
sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterMCA.exportWorld(pkt.worldName)));
|
||||
} catch (Throwable t) {
|
||||
throwExceptionToClient("Failed to export world '" + pkt.worldName+ "' as MCA", t);
|
||||
sendTaskFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket06RenameWorldNBT.ID: {
|
||||
IPCPacket06RenameWorldNBT pkt = (IPCPacket06RenameWorldNBT)packet;
|
||||
if(isServerStopped()) {
|
||||
VFile worldDat = new VFile("worlds", pkt.worldName, "level.dat");
|
||||
if(worldDat.canRead()) {
|
||||
NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(worldDat.getAllBytes());
|
||||
worldDatNBT.getCompoundTag("Data").setString("LevelName", pkt.displayName);
|
||||
worldDat.setAllBytes(CompressedStreamTools.compress(worldDatNBT));
|
||||
}else {
|
||||
throwExceptionToClient("Failed to rename world!", new RuntimeException("Failed to change level.dat world '" + pkt.worldName + "' display name to '" + pkt.displayName + "' because level.dat was missing"));
|
||||
}
|
||||
}else {
|
||||
System.err.println("Client tried to rename a world '" + pkt.worldName + "' to have name '" + pkt.displayName + "' while the server is running");
|
||||
sendTaskFailed();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket07ImportWorld.ID: {
|
||||
IPCPacket07ImportWorld pkt = (IPCPacket07ImportWorld)packet;
|
||||
if(isServerStopped()) {
|
||||
if(pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_EAG) {
|
||||
try {
|
||||
WorldConverterEPK.importWorld(pkt.worldData, pkt.worldName);
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket07ImportWorld.ID));
|
||||
}catch(Throwable t) {
|
||||
SYS.VFS.deleteFiles("worlds/" + VFSSaveHandler.worldNameToFolderName(pkt.worldName) + "/");
|
||||
throwExceptionToClient("Failed to import world '" + pkt.worldName + "' as EPK", t);
|
||||
sendTaskFailed();
|
||||
}
|
||||
}else if(pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_MCA) {
|
||||
try {
|
||||
WorldConverterMCA.importWorld(pkt.worldData, pkt.worldName);
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket07ImportWorld.ID));
|
||||
}catch(Throwable t) {
|
||||
SYS.VFS.deleteFiles("worlds/" + VFSSaveHandler.worldNameToFolderName(pkt.worldName) + "/");
|
||||
throwExceptionToClient("Failed to import world '" + pkt.worldName + "' as MCA", t);
|
||||
sendTaskFailed();
|
||||
}
|
||||
}else {
|
||||
System.err.println("Client tried to import a world in an unknown format: 0x" + Integer.toHexString(pkt.worldFormat));
|
||||
sendTaskFailed();
|
||||
}
|
||||
}else {
|
||||
System.err.println("Client tried to import a world '" + pkt.worldName + "' while the server is running");
|
||||
sendTaskFailed();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket09RequestResponse.ID:
|
||||
|
||||
break;
|
||||
case IPCPacket0ASetWorldDifficulty.ID: {
|
||||
IPCPacket0ASetWorldDifficulty pkt = (IPCPacket0ASetWorldDifficulty)packet;
|
||||
if(!isServerStopped()) {
|
||||
currentProcess.setDifficultyForAllWorlds(pkt.difficulty);
|
||||
}else {
|
||||
System.err.println("Client tried to set difficulty '" + pkt.difficulty + "' while server was stopped");
|
||||
sendTaskFailed();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket0BPause.ID: {
|
||||
IPCPacket0BPause pkt = (IPCPacket0BPause)packet;
|
||||
if(!isServerStopped()) {
|
||||
if(!pkt.pause && !currentProcess.getPaused()) {
|
||||
currentProcess.saveAllWorlds(true);
|
||||
}else {
|
||||
currentProcess.setPaused(pkt.pause);
|
||||
if(pkt.pause) {
|
||||
currentProcess.saveAllWorlds(true);
|
||||
}
|
||||
}
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket0BPause.ID));
|
||||
}else {
|
||||
System.err.println("Client tried to " + (pkt.pause ? "pause" : "unpause") + " while server was stopped");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket0CPlayerChannel.ID: {
|
||||
IPCPacket0CPlayerChannel pkt = (IPCPacket0CPlayerChannel)packet;
|
||||
if(!isServerStopped()) {
|
||||
if(pkt.open) {
|
||||
if(!currentProcess.getNetworkThread().openChannel(pkt.channel)) {
|
||||
System.err.println("Client tried to open a duplicate channel '" + pkt.channel + "'");
|
||||
}
|
||||
}else {
|
||||
if(!currentProcess.getNetworkThread().closeChannel(pkt.channel)) {
|
||||
System.err.println("Client tried to close a null channel '" + pkt.channel + "'");
|
||||
}
|
||||
}
|
||||
}else {
|
||||
System.err.println("Client tried to " + (pkt.open ? "open" : "close") + " channel '" + pkt.channel + "' while server was stopped");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket0EListWorlds.ID: {
|
||||
if(isServerStopped()) {
|
||||
String[] worlds = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
if(worlds == null || (worlds.length == 1 && worlds[0].trim().length() <= 0)) {
|
||||
worlds = null;
|
||||
}
|
||||
if(worlds == null) {
|
||||
sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, new LinkedList<NBTTagCompound>()));
|
||||
break;
|
||||
}
|
||||
LinkedList<String> updatedList = new LinkedList<>();
|
||||
LinkedList<NBTTagCompound> sendListNBT = new LinkedList<>();
|
||||
boolean rewrite = false;
|
||||
for(String w : worlds) {
|
||||
byte[] dat = (new VFile("worlds", w, "level.dat")).getAllBytes();
|
||||
if(dat != null) {
|
||||
NBTTagCompound worldDatNBT;
|
||||
try {
|
||||
worldDatNBT = CompressedStreamTools.decompress(dat);
|
||||
worldDatNBT.setString("folderName", w);
|
||||
sendListNBT.add(worldDatNBT);
|
||||
updatedList.add(w);
|
||||
continue;
|
||||
}catch(IOException e) {
|
||||
// shit fuck
|
||||
}
|
||||
|
||||
}
|
||||
rewrite = true;
|
||||
System.err.println("World level.dat for '" + w + "' was not found, attempting to delete 'worlds/" + w + "/*'");
|
||||
if(SYS.VFS.deleteFiles("worlds/" + w) <= 0) {
|
||||
System.err.println("No files were deleted in 'worlds/" + w + "/*', this may be corruption but '" + w + "' will still be removed from worlds.txt");
|
||||
}
|
||||
}
|
||||
if(rewrite) {
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", updatedList));
|
||||
}
|
||||
sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, sendListNBT));
|
||||
}else {
|
||||
System.err.println("Client tried to list worlds while server was running");
|
||||
sendTaskFailed();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket0FListFiles.ID:
|
||||
|
||||
break;
|
||||
case IPCPacket10FileRead.ID:
|
||||
|
||||
break;
|
||||
case IPCPacket12FileWrite.ID:
|
||||
|
||||
break;
|
||||
case IPCPacket13FileCopyMove.ID:
|
||||
|
||||
break;
|
||||
case IPCPacket14StringList.ID: {
|
||||
IPCPacket14StringList pkt = (IPCPacket14StringList)packet;
|
||||
switch(pkt.opCode) {
|
||||
case IPCPacket14StringList.LOCALE:
|
||||
StringTranslate.init(pkt.stringList);
|
||||
break;
|
||||
case IPCPacket14StringList.STAT_GUID:
|
||||
AchievementMap.init(pkt.stringList);
|
||||
AchievementList.init();
|
||||
break;
|
||||
default:
|
||||
System.err.println("Strange string list 0x" + Integer.toHexString(pkt.opCode) + " with length " + pkt.stringList.size() + " recieved");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IPCPacket17ConfigureLAN.ID: {
|
||||
IPCPacket17ConfigureLAN pkt = (IPCPacket17ConfigureLAN)packet;
|
||||
//currentProcess.getConfigurationManager().configureLAN(pkt.gamemode, pkt.cheats, pkt.iceServers); // FIX THIS SHIT
|
||||
}
|
||||
break;
|
||||
case IPCPacket18ClearPlayers.ID: {
|
||||
SYS.VFS.deleteFiles("worlds/" + ((IPCPacket18ClearPlayers)packet).worldName + "/player");
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket18ClearPlayers.ID));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.err.println("IPC packet type 0x" + Integer.toHexString(id) + " class '" + packet.getClass().getSimpleName() + "' was not handled");
|
||||
sendTaskFailed();
|
||||
break;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
String str = "IPC packet 0x" + Integer.toHexString(id) + " class '" + packet.getClass().getSimpleName() + "' was not processed correctly";
|
||||
System.err.println(str);
|
||||
throwExceptionToClient(str, t);
|
||||
sendTaskFailed();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
long watchDog = SysUtil.steadyTimeMillis();
|
||||
itr = cur.iterator();
|
||||
int overflow = 0;
|
||||
while(itr.hasNext()) {
|
||||
PKT msg = itr.next();
|
||||
if(!msg.channel.equals("IPC")) {
|
||||
if(SysUtil.steadyTimeMillis() - watchDog > 500l) {
|
||||
++overflow;
|
||||
continue;
|
||||
}
|
||||
if(!msg.channel.startsWith("NET|") || currentProcess == null) {
|
||||
//System.err.println("Unknown ICP channel: '" + msg.channel + "' passed " + msg.data.length + " bytes");
|
||||
continue;
|
||||
}
|
||||
String u = msg.channel.substring(4);
|
||||
currentProcess.getNetworkThread().recievePacket(u, msg.data);
|
||||
}
|
||||
}
|
||||
if(overflow > 0) {
|
||||
System.err.println("Async ICP queue is overloaded, server dropped " + overflow + " player packets");
|
||||
}
|
||||
}
|
||||
|
||||
@JSBody(params = { "ch", "dat" }, script = "postMessage({ ch: ch, dat : dat });")
|
||||
private static native void sendWorkerPacket(String channel, ArrayBuffer arr);
|
||||
|
||||
public static void sendIPCPacket(IPCPacketBase pkt) {
|
||||
byte[] serialized;
|
||||
|
||||
try {
|
||||
serialized = IPCPacketManager.IPCSerialize(pkt);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Could not serialize IPC packet 0x" + Integer.toHexString(pkt.id()) + " class '" + pkt.getClass().getSimpleName() + "'");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
sendWorkerPacket("IPC", TeaVMUtils.unwrapArrayBuffer(serialized));
|
||||
}
|
||||
|
||||
public static void sendPlayerPacket(String channel, byte[] buf) {
|
||||
//System.out.println("[Server][SEND][" + channel + "]: " + buf.length);
|
||||
sendWorkerPacket("NET|" + channel, TeaVMUtils.unwrapArrayBuffer(buf));
|
||||
}
|
||||
|
||||
private static boolean isRunning = false;
|
||||
|
||||
public static void halt() {
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
private static void mainLoop() {
|
||||
processAsyncMessageQueue();
|
||||
|
||||
if(currentProcess != null) {
|
||||
currentProcess.mainLoop();
|
||||
if(currentProcess.isServerStopped()) {
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket01StopServer.ID));
|
||||
currentProcess = null;
|
||||
}
|
||||
}else {
|
||||
SysUtil.sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
@JSBody(params = { "wb" }, script = "onmessage = function(o) { wb(o.data.ch, o.data.dat); };")
|
||||
private static native void registerPacketHandler(WorkerBinaryPacketHandler wb);
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Starting integrated server (kinda)");
|
||||
registerPacketHandler(new WorkerBinaryPacketHandlerImpl());
|
||||
isRunning = true;
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(0xFF));
|
||||
|
||||
while(isRunning) {
|
||||
mainLoop();
|
||||
SysUtil.immediateContinue();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
/**
|
||||
* implementation of MD5 as outlined in "Handbook of Applied Cryptography",
|
||||
* pages 346 - 347.
|
||||
*/
|
||||
public class MD5Digest extends GeneralDigest {
|
||||
private static final int DIGEST_LENGTH = 16;
|
||||
|
||||
private int H1, H2, H3, H4; // IV's
|
||||
|
||||
private int[] X = new int[16];
|
||||
private int xOff;
|
||||
|
||||
public String getAlgorithmName() {
|
||||
return "MD5";
|
||||
}
|
||||
|
||||
public int getDigestSize() {
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
protected void processWord(byte[] in, int inOff) {
|
||||
X[xOff++] = littleEndianToInt(in, inOff);
|
||||
|
||||
if (xOff == 16) {
|
||||
processBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private int littleEndianToInt(byte[] bs, int off) {
|
||||
int n = bs[off] & 0xff;
|
||||
n |= (bs[++off] & 0xff) << 8;
|
||||
n |= (bs[++off] & 0xff) << 16;
|
||||
n |= bs[++off] << 24;
|
||||
return n;
|
||||
}
|
||||
|
||||
protected void processLength(long bitLength) {
|
||||
if (xOff > 14) {
|
||||
processBlock();
|
||||
}
|
||||
|
||||
X[14] = (int) (bitLength & 0xffffffff);
|
||||
X[15] = (int) (bitLength >>> 32);
|
||||
}
|
||||
|
||||
public int doFinal(byte[] out, int outOff) {
|
||||
finish();
|
||||
|
||||
intToLittleEndian(H1, out, outOff);
|
||||
intToLittleEndian(H2, out, outOff + 4);
|
||||
intToLittleEndian(H3, out, outOff + 8);
|
||||
intToLittleEndian(H4, out, outOff + 12);
|
||||
|
||||
reset();
|
||||
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
private void intToLittleEndian(int n, byte[] bs, int off) {
|
||||
bs[off] = (byte) (n);
|
||||
bs[++off] = (byte) (n >>> 8);
|
||||
bs[++off] = (byte) (n >>> 16);
|
||||
bs[++off] = (byte) (n >>> 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the chaining variables to the IV values.
|
||||
*/
|
||||
public void reset() {
|
||||
super.reset();
|
||||
|
||||
H1 = 0x67452301;
|
||||
H2 = 0xefcdab89;
|
||||
H3 = 0x98badcfe;
|
||||
H4 = 0x10325476;
|
||||
|
||||
xOff = 0;
|
||||
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// round 1 left rotates
|
||||
//
|
||||
private static final int S11 = 7;
|
||||
private static final int S12 = 12;
|
||||
private static final int S13 = 17;
|
||||
private static final int S14 = 22;
|
||||
|
||||
//
|
||||
// round 2 left rotates
|
||||
//
|
||||
private static final int S21 = 5;
|
||||
private static final int S22 = 9;
|
||||
private static final int S23 = 14;
|
||||
private static final int S24 = 20;
|
||||
|
||||
//
|
||||
// round 3 left rotates
|
||||
//
|
||||
private static final int S31 = 4;
|
||||
private static final int S32 = 11;
|
||||
private static final int S33 = 16;
|
||||
private static final int S34 = 23;
|
||||
|
||||
//
|
||||
// round 4 left rotates
|
||||
//
|
||||
private static final int S41 = 6;
|
||||
private static final int S42 = 10;
|
||||
private static final int S43 = 15;
|
||||
private static final int S44 = 21;
|
||||
|
||||
/*
|
||||
* rotate int x left n bits.
|
||||
*/
|
||||
private int rotateLeft(int x, int n) {
|
||||
return (x << n) | (x >>> (32 - n));
|
||||
}
|
||||
|
||||
/*
|
||||
* F, G, H and I are the basic MD5 functions.
|
||||
*/
|
||||
private int F(int u, int v, int w) {
|
||||
return (u & v) | (~u & w);
|
||||
}
|
||||
|
||||
private int G(int u, int v, int w) {
|
||||
return (u & w) | (v & ~w);
|
||||
}
|
||||
|
||||
private int H(int u, int v, int w) {
|
||||
return u ^ v ^ w;
|
||||
}
|
||||
|
||||
private int K(int u, int v, int w) {
|
||||
return v ^ (u | ~w);
|
||||
}
|
||||
|
||||
protected void processBlock() {
|
||||
int a = H1;
|
||||
int b = H2;
|
||||
int c = H3;
|
||||
int d = H4;
|
||||
|
||||
//
|
||||
// Round 1 - F cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + F(b, c, d) + X[0] + 0xd76aa478, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[1] + 0xe8c7b756, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[2] + 0x242070db, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[3] + 0xc1bdceee, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[4] + 0xf57c0faf, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[5] + 0x4787c62a, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[6] + 0xa8304613, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[7] + 0xfd469501, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[8] + 0x698098d8, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[9] + 0x8b44f7af, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[10] + 0xffff5bb1, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[11] + 0x895cd7be, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[12] + 0x6b901122, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[13] + 0xfd987193, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[14] + 0xa679438e, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[15] + 0x49b40821, S14) + c;
|
||||
|
||||
//
|
||||
// Round 2 - G cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + G(b, c, d) + X[1] + 0xf61e2562, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[6] + 0xc040b340, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[11] + 0x265e5a51, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[0] + 0xe9b6c7aa, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[5] + 0xd62f105d, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[10] + 0x02441453, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[15] + 0xd8a1e681, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[4] + 0xe7d3fbc8, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[9] + 0x21e1cde6, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[14] + 0xc33707d6, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[3] + 0xf4d50d87, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[8] + 0x455a14ed, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[13] + 0xa9e3e905, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[2] + 0xfcefa3f8, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[7] + 0x676f02d9, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[12] + 0x8d2a4c8a, S24) + c;
|
||||
|
||||
//
|
||||
// Round 3 - H cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + H(b, c, d) + X[5] + 0xfffa3942, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[8] + 0x8771f681, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[11] + 0x6d9d6122, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[14] + 0xfde5380c, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[1] + 0xa4beea44, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[4] + 0x4bdecfa9, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[7] + 0xf6bb4b60, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[10] + 0xbebfbc70, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[13] + 0x289b7ec6, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[0] + 0xeaa127fa, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[3] + 0xd4ef3085, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[6] + 0x04881d05, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[9] + 0xd9d4d039, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[12] + 0xe6db99e5, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[15] + 0x1fa27cf8, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[2] + 0xc4ac5665, S34) + c;
|
||||
|
||||
//
|
||||
// Round 4 - K cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + K(b, c, d) + X[0] + 0xf4292244, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[7] + 0x432aff97, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[14] + 0xab9423a7, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[5] + 0xfc93a039, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[12] + 0x655b59c3, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[3] + 0x8f0ccc92, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[10] + 0xffeff47d, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[1] + 0x85845dd1, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[8] + 0x6fa87e4f, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[15] + 0xfe2ce6e0, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[6] + 0xa3014314, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[13] + 0x4e0811a1, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[4] + 0xf7537e82, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[11] + 0xbd3af235, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[2] + 0x2ad7d2bb, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[9] + 0xeb86d391, S44) + c;
|
||||
|
||||
H1 += a;
|
||||
H2 += b;
|
||||
H3 += c;
|
||||
H4 += d;
|
||||
|
||||
//
|
||||
// reset the offset and clean out the word buffer.
|
||||
//
|
||||
xOff = 0;
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSClass;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
import org.teavm.jso.workers.MessagePort;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
@JSClass
|
||||
public class MessageChannel implements JSObject {
|
||||
|
||||
@JSBody(params = { }, script = "return (typeof MessageChannel !== \"undefined\");")
|
||||
public static native boolean supported();
|
||||
|
||||
@JSProperty
|
||||
public native MessagePort getPort1();
|
||||
|
||||
@JSProperty
|
||||
public native MessagePort getPort2();
|
||||
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class NoCatchParse {
|
||||
|
||||
public static final int INT_EXCEPTION = Integer.MIN_VALUE;
|
||||
public static final float FLOAT_EXCEPTION = Float.NaN;
|
||||
public static final double DOUBLE_EXCEPTION = Double.NaN;
|
||||
|
||||
public static int parseInt(String s) {
|
||||
return parseInt(s, 10, false, INT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int radix) {
|
||||
return parseInt(s, radix, false, INT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int radix, boolean log) {
|
||||
return parseInt(s, radix, log, INT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int radix, boolean log, int exceptionResult) {
|
||||
if (s == null) {
|
||||
if (log) {
|
||||
System.err.println("parseInt: string was null");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
if (s.isEmpty()) {
|
||||
if (log) {
|
||||
System.err.println("parseInt: string was empty");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
|
||||
if (log) {
|
||||
System.err.println("parseInt: invalid radix '" + radix + "'");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
tryFail: {
|
||||
int result = 0;
|
||||
boolean negative = false;
|
||||
int i = 0, len = s.length();
|
||||
int limit = -Integer.MAX_VALUE;
|
||||
int multmin;
|
||||
int digit;
|
||||
|
||||
if (len > 0) {
|
||||
char firstChar = s.charAt(0);
|
||||
if (firstChar < '0') { // Possible leading "+" or "-"
|
||||
if (firstChar == '-') {
|
||||
negative = true;
|
||||
limit = Integer.MIN_VALUE;
|
||||
} else if (firstChar != '+')
|
||||
break tryFail;
|
||||
|
||||
if (len == 1)
|
||||
break tryFail;
|
||||
i++;
|
||||
}
|
||||
multmin = limit / radix;
|
||||
while (i < len) {
|
||||
// Accumulating negatively avoids surprises near MAX_VALUE
|
||||
digit = Character.digit(s.charAt(i++), radix);
|
||||
if (digit < 0 || result < multmin) {
|
||||
|
||||
break tryFail;
|
||||
}
|
||||
result *= radix;
|
||||
if (result < limit + digit) {
|
||||
break tryFail;
|
||||
}
|
||||
result -= digit;
|
||||
}
|
||||
} else {
|
||||
break tryFail;
|
||||
}
|
||||
int ret = negative ? result : -result;
|
||||
if (ret == exceptionResult) {
|
||||
System.err.println(
|
||||
"parseInt: number '" + s + "' was parsed successfully but it is equal to exceptionResult");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
if (log) {
|
||||
System.err.println("parseInt: cannot parse '" + s + "'");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
public static double parseDouble(String s) {
|
||||
return parseDouble(s, false, DOUBLE_EXCEPTION);
|
||||
}
|
||||
|
||||
public static double parseDouble(String s, boolean log) {
|
||||
return parseDouble(s, log, DOUBLE_EXCEPTION);
|
||||
}
|
||||
|
||||
public static double parseDouble(String s, boolean log, double exceptionResult) {
|
||||
if (s == null) {
|
||||
if (log) {
|
||||
System.err.println("parseDouble: string was null");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
if (s.isEmpty()) {
|
||||
if (log) {
|
||||
System.err.println("parseDouble: string was empty");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
tryFail: {
|
||||
int start = 0;
|
||||
int end = s.length();
|
||||
while (s.charAt(start) <= ' ') {
|
||||
if (++start == end) {
|
||||
break tryFail;
|
||||
}
|
||||
}
|
||||
while (s.charAt(end - 1) <= ' ') {
|
||||
--end;
|
||||
}
|
||||
|
||||
boolean negative = false;
|
||||
int index = start;
|
||||
if (s.charAt(index) == '-') {
|
||||
++index;
|
||||
negative = true;
|
||||
} else if (s.charAt(index) == '+') {
|
||||
++index;
|
||||
}
|
||||
if (index == end) {
|
||||
break tryFail;
|
||||
}
|
||||
char c = s.charAt(index);
|
||||
|
||||
long mantissa = 0;
|
||||
int exp = 0;
|
||||
boolean hasOneDigit = false;
|
||||
if (c != '.') {
|
||||
hasOneDigit = true;
|
||||
if (c < '0' || c > '9') {
|
||||
break tryFail;
|
||||
}
|
||||
while (index < end && s.charAt(index) == '0') {
|
||||
++index;
|
||||
}
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < Long.MAX_VALUE / 10 - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
} else {
|
||||
++exp;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
if (index < end && s.charAt(index) == '.') {
|
||||
++index;
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < Long.MAX_VALUE / 10 - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
--exp;
|
||||
}
|
||||
++index;
|
||||
hasOneDigit = true;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
break tryFail;
|
||||
}
|
||||
}
|
||||
if (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c != 'e' && c != 'E') {
|
||||
break tryFail;
|
||||
}
|
||||
++index;
|
||||
boolean negativeExp = false;
|
||||
if (index == end) {
|
||||
break tryFail;
|
||||
}
|
||||
if (s.charAt(index) == '-') {
|
||||
++index;
|
||||
negativeExp = true;
|
||||
} else if (s.charAt(index) == '+') {
|
||||
++index;
|
||||
}
|
||||
int numExp = 0;
|
||||
hasOneDigit = false;
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
numExp = 10 * numExp + (c - '0');
|
||||
hasOneDigit = true;
|
||||
++index;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
break tryFail;
|
||||
}
|
||||
if (negativeExp) {
|
||||
numExp = -numExp;
|
||||
}
|
||||
exp += numExp;
|
||||
}
|
||||
if (exp > 308 || exp == 308 && mantissa > 17976931348623157L) {
|
||||
return !negative ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
if (negative) {
|
||||
mantissa = -mantissa;
|
||||
}
|
||||
return mantissa * doubleDecimalExponent(exp);
|
||||
}
|
||||
if (log) {
|
||||
System.err.println("parseDouble: cannot parse '" + s + "'");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
public static double doubleDecimalExponent(int n) {
|
||||
double d;
|
||||
if (n < 0) {
|
||||
d = 0.1;
|
||||
n = -n;
|
||||
} else {
|
||||
d = 10;
|
||||
}
|
||||
double result = 1;
|
||||
while (n != 0) {
|
||||
if (n % 2 != 0) {
|
||||
result *= d;
|
||||
}
|
||||
d *= d;
|
||||
n /= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float parseFloat(String s) {
|
||||
return parseFloat(s, false, FLOAT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static float parseFloat(String s, boolean log) {
|
||||
return parseFloat(s, log, FLOAT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static float parseFloat(String s, boolean log, float exceptionResult) {
|
||||
if (s == null) {
|
||||
if (log) {
|
||||
System.err.println("parseFloat: string was null");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
if (s.isEmpty()) {
|
||||
if (log) {
|
||||
System.err.println("parseFloat: string was empty");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
tryFail: {
|
||||
int start = 0;
|
||||
int end = s.length();
|
||||
while (s.charAt(start) <= ' ') {
|
||||
if (++start == end) {
|
||||
break tryFail;
|
||||
}
|
||||
}
|
||||
while (s.charAt(end - 1) <= ' ') {
|
||||
--end;
|
||||
}
|
||||
|
||||
boolean negative = false;
|
||||
int index = start;
|
||||
if (s.charAt(index) == '-') {
|
||||
++index;
|
||||
negative = true;
|
||||
} else if (s.charAt(index) == '+') {
|
||||
++index;
|
||||
}
|
||||
if (index == end) {
|
||||
break tryFail;
|
||||
}
|
||||
char c = s.charAt(index);
|
||||
|
||||
int mantissa = 0;
|
||||
int exp = 0;
|
||||
|
||||
boolean hasOneDigit = false;
|
||||
if (c != '.') {
|
||||
hasOneDigit = true;
|
||||
if (c < '0' || c > '9') {
|
||||
break tryFail;
|
||||
}
|
||||
|
||||
while (index < end && s.charAt(index) == '0') {
|
||||
++index;
|
||||
}
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < (Integer.MAX_VALUE / 10) - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
} else {
|
||||
++exp;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < end && s.charAt(index) == '.') {
|
||||
++index;
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < (Integer.MAX_VALUE / 10) - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
--exp;
|
||||
}
|
||||
++index;
|
||||
hasOneDigit = true;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
break tryFail;
|
||||
}
|
||||
}
|
||||
if (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c != 'e' && c != 'E') {
|
||||
break tryFail;
|
||||
}
|
||||
++index;
|
||||
boolean negativeExp = false;
|
||||
if (index == end) {
|
||||
break tryFail;
|
||||
}
|
||||
if (s.charAt(index) == '-') {
|
||||
++index;
|
||||
negativeExp = true;
|
||||
} else if (s.charAt(index) == '+') {
|
||||
++index;
|
||||
}
|
||||
int numExp = 0;
|
||||
hasOneDigit = false;
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
numExp = 10 * numExp + (c - '0');
|
||||
hasOneDigit = true;
|
||||
++index;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
break tryFail;
|
||||
}
|
||||
if (negativeExp) {
|
||||
numExp = -numExp;
|
||||
}
|
||||
exp += numExp;
|
||||
}
|
||||
if (exp > 38 || exp == 38 && mantissa > 34028234) {
|
||||
return !negative ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
|
||||
}
|
||||
if (negative) {
|
||||
mantissa = -mantissa;
|
||||
}
|
||||
return mantissa * floatDecimalExponent(exp);
|
||||
}
|
||||
if (log) {
|
||||
System.err.println("parseFloat: cannot parse '" + s + "'");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
private static float floatDecimalExponent(int n) {
|
||||
double d;
|
||||
if (n < 0) {
|
||||
d = 0.1;
|
||||
n = -n;
|
||||
} else {
|
||||
d = 10;
|
||||
}
|
||||
double result = 1;
|
||||
while (n != 0) {
|
||||
if (n % 2 != 0) {
|
||||
result *= d;
|
||||
}
|
||||
d *= d;
|
||||
n /= 2;
|
||||
}
|
||||
return (float) result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2023-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class RandomAccessMemoryFile implements DataInput, DataOutput {
|
||||
|
||||
private byte[] buffer;
|
||||
private int length;
|
||||
private int pos;
|
||||
|
||||
public RandomAccessMemoryFile(byte[] initialBuffer, int initialLength) {
|
||||
this.buffer = initialBuffer;
|
||||
this.length = initialLength;
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
private void grow(int newMaxSize) {
|
||||
if (length < newMaxSize) {
|
||||
if (buffer.length < newMaxSize) {
|
||||
byte[] newBuffer = new byte[newMaxSize | 0x7FFFF];
|
||||
System.arraycopy(buffer, 0, newBuffer, 0, length);
|
||||
buffer = newBuffer;
|
||||
}
|
||||
length = newMaxSize;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getByteArray() {
|
||||
byte[] b = new byte[length];
|
||||
System.arraycopy(buffer, 0, b, 0, length);
|
||||
return b;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return (pos < length) ? (buffer[pos++] & 0xff) : -1;
|
||||
}
|
||||
|
||||
private int readBytes(byte b[], int off, int len) throws IOException {
|
||||
if (pos >= length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int avail = length - pos;
|
||||
if (len > avail) {
|
||||
len = avail;
|
||||
}
|
||||
if (len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
System.arraycopy(buffer, pos, b, off, len);
|
||||
pos += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
return readBytes(b, off, len);
|
||||
}
|
||||
|
||||
public int read(byte b[]) throws IOException {
|
||||
return readBytes(b, 0, b.length);
|
||||
}
|
||||
|
||||
public final void readFully(byte b[]) throws IOException {
|
||||
readFully(b, 0, b.length);
|
||||
}
|
||||
|
||||
public final void readFully(byte b[], int off, int len) throws IOException {
|
||||
int n = 0;
|
||||
do {
|
||||
int count = this.read(b, off + n, len - n);
|
||||
if (count < 0)
|
||||
throw new EOFException();
|
||||
n += count;
|
||||
} while (n < len);
|
||||
}
|
||||
|
||||
public int skipBytes(int n) throws IOException {
|
||||
int newpos;
|
||||
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
newpos = pos + n;
|
||||
if (newpos > length) {
|
||||
newpos = length;
|
||||
}
|
||||
seek(newpos);
|
||||
|
||||
return (int) (newpos - pos);
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
grow(pos + 1);
|
||||
buffer[pos] = (byte) b;
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
private void writeBytes(byte b[], int off, int len) throws IOException {
|
||||
grow(pos + len);
|
||||
System.arraycopy(b, off, buffer, pos, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
public void write(byte b[]) throws IOException {
|
||||
writeBytes(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
writeBytes(b, off, len);
|
||||
}
|
||||
|
||||
public void seek(int pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(int newLength) {
|
||||
grow(newLength);
|
||||
}
|
||||
|
||||
public final boolean readBoolean() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0)
|
||||
throw new EOFException();
|
||||
return (ch != 0);
|
||||
}
|
||||
|
||||
public final byte readByte() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0)
|
||||
throw new EOFException();
|
||||
return (byte) (ch);
|
||||
}
|
||||
|
||||
public final int readUnsignedByte() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0)
|
||||
throw new EOFException();
|
||||
return ch;
|
||||
}
|
||||
|
||||
public final short readShort() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new EOFException();
|
||||
return (short) ((ch1 << 8) + (ch2 << 0));
|
||||
}
|
||||
|
||||
public final int readUnsignedShort() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new EOFException();
|
||||
return (ch1 << 8) + (ch2 << 0);
|
||||
}
|
||||
|
||||
public final char readChar() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new EOFException();
|
||||
return (char) ((ch1 << 8) + (ch2 << 0));
|
||||
}
|
||||
|
||||
public final int readInt() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
int ch3 = this.read();
|
||||
int ch4 = this.read();
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0)
|
||||
throw new EOFException();
|
||||
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
|
||||
}
|
||||
|
||||
public final long readLong() throws IOException {
|
||||
return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
public final float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
public final double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
public final String readLine() throws IOException {
|
||||
StringBuilder input = new StringBuilder();
|
||||
int c = -1;
|
||||
boolean eol = false;
|
||||
|
||||
while (!eol) {
|
||||
switch (c = read()) {
|
||||
case -1:
|
||||
case '\n':
|
||||
eol = true;
|
||||
break;
|
||||
case '\r':
|
||||
eol = true;
|
||||
int cur = pos;
|
||||
if ((read()) != '\n') {
|
||||
seek(cur);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
input.append((char) c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((c == -1) && (input.length() == 0)) {
|
||||
return null;
|
||||
}
|
||||
return input.toString();
|
||||
}
|
||||
|
||||
public final String readUTF() throws IOException {
|
||||
throw new IOException("TODO");
|
||||
}
|
||||
|
||||
public final void writeBoolean(boolean v) throws IOException {
|
||||
write(v ? 1 : 0);
|
||||
}
|
||||
|
||||
public final void writeByte(int v) throws IOException {
|
||||
write(v);
|
||||
}
|
||||
|
||||
public final void writeShort(int v) throws IOException {
|
||||
write((v >>> 8) & 0xFF);
|
||||
write((v >>> 0) & 0xFF);
|
||||
}
|
||||
|
||||
public final void writeChar(int v) throws IOException {
|
||||
write((v >>> 8) & 0xFF);
|
||||
write((v >>> 0) & 0xFF);
|
||||
}
|
||||
|
||||
public final void writeInt(int v) throws IOException {
|
||||
write((v >>> 24) & 0xFF);
|
||||
write((v >>> 16) & 0xFF);
|
||||
write((v >>> 8) & 0xFF);
|
||||
write((v >>> 0) & 0xFF);
|
||||
}
|
||||
|
||||
public final void writeLong(long v) throws IOException {
|
||||
write((int) (v >>> 56) & 0xFF);
|
||||
write((int) (v >>> 48) & 0xFF);
|
||||
write((int) (v >>> 40) & 0xFF);
|
||||
write((int) (v >>> 32) & 0xFF);
|
||||
write((int) (v >>> 24) & 0xFF);
|
||||
write((int) (v >>> 16) & 0xFF);
|
||||
write((int) (v >>> 8) & 0xFF);
|
||||
write((int) (v >>> 0) & 0xFF);
|
||||
}
|
||||
|
||||
public final void writeFloat(float v) throws IOException {
|
||||
writeInt(Float.floatToIntBits(v));
|
||||
}
|
||||
|
||||
public final void writeDouble(double v) throws IOException {
|
||||
writeLong(Double.doubleToLongBits(v));
|
||||
}
|
||||
|
||||
public final void writeBytes(String s) throws IOException {
|
||||
int len = s.length();
|
||||
byte[] b = new byte[len];
|
||||
s.getBytes(0, len, b, 0);
|
||||
writeBytes(b, 0, len);
|
||||
}
|
||||
|
||||
public final void writeChars(String s) throws IOException {
|
||||
int clen = s.length();
|
||||
int blen = 2 * clen;
|
||||
byte[] b = new byte[blen];
|
||||
char[] c = new char[clen];
|
||||
s.getChars(0, clen, c, 0);
|
||||
for (int i = 0, j = 0; i < clen; i++) {
|
||||
b[j++] = (byte) (c[i] >>> 8);
|
||||
b[j++] = (byte) (c[i] >>> 0);
|
||||
}
|
||||
writeBytes(b, 0, blen);
|
||||
}
|
||||
|
||||
public final void writeUTF(String str) throws IOException {
|
||||
throw new IOException("TODO");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
/**
|
||||
* implementation of SHA-1 as outlined in "Handbook of Applied Cryptography",
|
||||
* pages 346 - 349.
|
||||
*
|
||||
* It is interesting to ponder why the, apart from the extra IV, the other
|
||||
* difference here from MD5 is the "endienness" of the word processing!
|
||||
*/
|
||||
public class SHA1Digest extends GeneralDigest {
|
||||
private static final int DIGEST_LENGTH = 20;
|
||||
|
||||
private int H1, H2, H3, H4, H5;
|
||||
|
||||
private int[] X = new int[80];
|
||||
private int xOff;
|
||||
|
||||
/**
|
||||
* Standard constructor
|
||||
*/
|
||||
public SHA1Digest() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor. This will copy the state of the provided message digest.
|
||||
*/
|
||||
public SHA1Digest(SHA1Digest t) {
|
||||
super(t);
|
||||
|
||||
H1 = t.H1;
|
||||
H2 = t.H2;
|
||||
H3 = t.H3;
|
||||
H4 = t.H4;
|
||||
H5 = t.H5;
|
||||
|
||||
System.arraycopy(t.X, 0, X, 0, t.X.length);
|
||||
xOff = t.xOff;
|
||||
}
|
||||
|
||||
public String getAlgorithmName() {
|
||||
return "SHA-1";
|
||||
}
|
||||
|
||||
public int getDigestSize() {
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
protected void processWord(byte[] in, int inOff) {
|
||||
X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16) | ((in[inOff + 2] & 0xff) << 8)
|
||||
| ((in[inOff + 3] & 0xff));
|
||||
|
||||
if (xOff == 16) {
|
||||
processBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void unpackWord(int word, byte[] out, int outOff) {
|
||||
out[outOff] = (byte) (word >>> 24);
|
||||
out[outOff + 1] = (byte) (word >>> 16);
|
||||
out[outOff + 2] = (byte) (word >>> 8);
|
||||
out[outOff + 3] = (byte) word;
|
||||
}
|
||||
|
||||
protected void processLength(long bitLength) {
|
||||
if (xOff > 14) {
|
||||
processBlock();
|
||||
}
|
||||
|
||||
X[14] = (int) (bitLength >>> 32);
|
||||
X[15] = (int) (bitLength & 0xffffffff);
|
||||
}
|
||||
|
||||
public int doFinal(byte[] out, int outOff) {
|
||||
finish();
|
||||
|
||||
unpackWord(H1, out, outOff);
|
||||
unpackWord(H2, out, outOff + 4);
|
||||
unpackWord(H3, out, outOff + 8);
|
||||
unpackWord(H4, out, outOff + 12);
|
||||
unpackWord(H5, out, outOff + 16);
|
||||
|
||||
reset();
|
||||
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the chaining variables
|
||||
*/
|
||||
public void reset() {
|
||||
super.reset();
|
||||
|
||||
H1 = 0x67452301;
|
||||
H2 = 0xefcdab89;
|
||||
H3 = 0x98badcfe;
|
||||
H4 = 0x10325476;
|
||||
H5 = 0xc3d2e1f0;
|
||||
|
||||
xOff = 0;
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Additive constants
|
||||
//
|
||||
private static final int Y1 = 0x5a827999;
|
||||
private static final int Y2 = 0x6ed9eba1;
|
||||
private static final int Y3 = 0x8f1bbcdc;
|
||||
private static final int Y4 = 0xca62c1d6;
|
||||
|
||||
private int f(int u, int v, int w) {
|
||||
return ((u & v) | ((~u) & w));
|
||||
}
|
||||
|
||||
private int h(int u, int v, int w) {
|
||||
return (u ^ v ^ w);
|
||||
}
|
||||
|
||||
private int g(int u, int v, int w) {
|
||||
return ((u & v) | (u & w) | (v & w));
|
||||
}
|
||||
|
||||
private int rotateLeft(int x, int n) {
|
||||
return (x << n) | (x >>> (32 - n));
|
||||
}
|
||||
|
||||
protected void processBlock() {
|
||||
//
|
||||
// expand 16 word block into 80 word block.
|
||||
//
|
||||
for (int i = 16; i <= 79; i++) {
|
||||
X[i] = rotateLeft((X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]), 1);
|
||||
}
|
||||
|
||||
//
|
||||
// set up working variables.
|
||||
//
|
||||
int A = H1;
|
||||
int B = H2;
|
||||
int C = H3;
|
||||
int D = H4;
|
||||
int E = H5;
|
||||
|
||||
//
|
||||
// round 1
|
||||
//
|
||||
for (int j = 0; j <= 19; j++) {
|
||||
int t = rotateLeft(A, 5) + f(B, C, D) + E + X[j] + Y1;
|
||||
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotateLeft(B, 30);
|
||||
B = A;
|
||||
A = t;
|
||||
}
|
||||
|
||||
//
|
||||
// round 2
|
||||
//
|
||||
for (int j = 20; j <= 39; j++) {
|
||||
int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y2;
|
||||
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotateLeft(B, 30);
|
||||
B = A;
|
||||
A = t;
|
||||
}
|
||||
|
||||
//
|
||||
// round 3
|
||||
//
|
||||
for (int j = 40; j <= 59; j++) {
|
||||
int t = rotateLeft(A, 5) + g(B, C, D) + E + X[j] + Y3;
|
||||
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotateLeft(B, 30);
|
||||
B = A;
|
||||
A = t;
|
||||
}
|
||||
|
||||
//
|
||||
// round 4
|
||||
//
|
||||
for (int j = 60; j <= 79; j++) {
|
||||
int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y4;
|
||||
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotateLeft(B, 30);
|
||||
B = A;
|
||||
A = t;
|
||||
}
|
||||
|
||||
H1 += A;
|
||||
H2 += B;
|
||||
H3 += C;
|
||||
H4 += D;
|
||||
H5 += E;
|
||||
|
||||
//
|
||||
// reset the offset and clean out the word buffer.
|
||||
//
|
||||
xOff = 0;
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.VirtualFilesystem.VFSHandle;
|
||||
|
||||
public class SYS {
|
||||
|
||||
public static final VirtualFilesystem VFS;
|
||||
|
||||
@JSBody(params = { }, script = "return eaglercraftServerOpts.worldDatabaseName;")
|
||||
private static native String getWorldDatabaseName();
|
||||
|
||||
static {
|
||||
|
||||
VFSHandle vh = VirtualFilesystem.openVFS("_net_lax1dude_eaglercraft_sp_VirtualFilesystem_1_5_2_" + getWorldDatabaseName());
|
||||
|
||||
if(vh.vfs == null) {
|
||||
System.err.println("Could not init filesystem!");
|
||||
IntegratedServer.throwExceptionToClient("Could not init filesystem!", new RuntimeException("VFSHandle.vfs was null"));
|
||||
}
|
||||
|
||||
VFS = vh.vfs;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.minecraft.src.EntityPlayerMP;
|
||||
import net.minecraft.src.Packet250CustomPayload;
|
||||
|
||||
public class SkinsPlugin {
|
||||
|
||||
private static final HashMap<String,byte[]> skinCollection = new HashMap<>();
|
||||
private static final HashMap<String,byte[]> capeCollection = new HashMap<>();
|
||||
private static final HashMap<String,Long> lastSkinLayerUpdate = new HashMap<>();
|
||||
|
||||
private static final int[] SKIN_DATA_SIZE = new int[] { 64*32*4, 64*64*4, -9, -9, 1, 64*64*4, -9 }; // 128 pixel skins crash clients
|
||||
private static final int[] CAPE_DATA_SIZE = new int[] { 32*32*4, -9, 1 };
|
||||
|
||||
public static boolean handleMessage(EntityPlayerMP player, Packet250CustomPayload payload) {
|
||||
if(payload.data.length > 0) {
|
||||
String user = player.username;
|
||||
byte[] msg = payload.data;
|
||||
try {
|
||||
if("EAG|MySkin".equals(payload.channel)) {
|
||||
if(!skinCollection.containsKey(user)) {
|
||||
int t = (int)msg[0] & 0xFF;
|
||||
if(t < SKIN_DATA_SIZE.length && msg.length == (SKIN_DATA_SIZE[t] + 1)) {
|
||||
skinCollection.put(user, msg);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if("EAG|MyCape".equals(payload.channel)) {
|
||||
if(!capeCollection.containsKey(user)) {
|
||||
int t = (int)msg[0] & 0xFF;
|
||||
if(t < CAPE_DATA_SIZE.length && msg.length == (CAPE_DATA_SIZE[t] + 2)) {
|
||||
capeCollection.put(user, msg);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if("EAG|FetchSkin".equals(payload.channel)) {
|
||||
if(msg.length > 2) {
|
||||
String fetch = new String(msg, 2, msg.length - 2, StandardCharsets.UTF_8);
|
||||
byte[] data;
|
||||
if((data = skinCollection.get(fetch)) != null) {
|
||||
byte[] conc = new byte[data.length + 2];
|
||||
conc[0] = msg[0]; conc[1] = msg[1]; //synchronization cookie
|
||||
System.arraycopy(data, 0, conc, 2, data.length);
|
||||
if((data = capeCollection.get(fetch)) != null) {
|
||||
byte[] conc2 = new byte[conc.length + data.length];
|
||||
System.arraycopy(conc, 0, conc2, 0, conc.length);
|
||||
System.arraycopy(data, 0, conc2, conc.length, data.length);
|
||||
conc = conc2;
|
||||
}
|
||||
player.playerNetServerHandler.sendPacketToPlayer(new Packet250CustomPayload("EAG|UserSkin", conc));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if("EAG|SkinLayers".equals(payload.channel)) {
|
||||
long millis = SysUtil.steadyTimeMillis();
|
||||
Long lsu = lastSkinLayerUpdate.get(user);
|
||||
if(lsu != null && millis - lsu < 700L) { // DoS protection
|
||||
return true;
|
||||
}
|
||||
lastSkinLayerUpdate.put(user, millis);
|
||||
byte[] data;
|
||||
if((data = capeCollection.get(user)) != null) {
|
||||
data[1] = msg[0];
|
||||
}else {
|
||||
data = new byte[] { (byte)2, msg[0], (byte)0 };
|
||||
capeCollection.put(user, data);
|
||||
}
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
DataOutputStream dd = new DataOutputStream(bao);
|
||||
dd.write(msg[0]);
|
||||
dd.writeUTF(user);
|
||||
byte[] bpacket = bao.toByteArray();
|
||||
for(Object o : player.mcServer.getConfigurationManager().playerEntityList) {
|
||||
EntityPlayerMP pl = (EntityPlayerMP) o;
|
||||
if(!pl.username.equals(user)) {
|
||||
pl.playerNetServerHandler.sendPacketToPlayer(new Packet250CustomPayload("EAG|SkinLayers", bpacket));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
// hacker
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void handleDisconnect(EntityPlayerMP player) {
|
||||
skinCollection.remove(player.username);
|
||||
capeCollection.remove(player.username);
|
||||
lastSkinLayerUpdate.remove(player.username);
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
skinCollection.clear();
|
||||
capeCollection.clear();
|
||||
lastSkinLayerUpdate.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.browser.Window;
|
||||
import org.teavm.jso.core.JSString;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.dom.events.MessageEvent;
|
||||
import org.teavm.platform.Platform;
|
||||
import org.teavm.platform.PlatformRunnable;
|
||||
|
||||
public class SysUtil {
|
||||
|
||||
private static final JSObject steadyTimeFunc = getSteadyTimeFunc();
|
||||
|
||||
@JSBody(params = { }, script = "return ((typeof performance !== \"undefined\") && (typeof performance.now === \"function\"))"
|
||||
+ "? performance.now.bind(performance)"
|
||||
+ ": (function(epochStart){ return function() { return Date.now() - epochStart; }; })(Date.now());")
|
||||
private static native JSObject getSteadyTimeFunc();
|
||||
|
||||
@JSBody(params = { "steadyTimeFunc" }, script = "return steadyTimeFunc();")
|
||||
private static native double steadyTimeMillis0(JSObject steadyTimeFunc);
|
||||
|
||||
public static long steadyTimeMillis() {
|
||||
return (long)steadyTimeMillis0(steadyTimeFunc);
|
||||
}
|
||||
|
||||
public static long nanoTime() {
|
||||
return (long)(steadyTimeMillis0(steadyTimeFunc) * 1000000.0);
|
||||
}
|
||||
|
||||
@Async
|
||||
public static native void sleep(int millis);
|
||||
|
||||
private static void sleep(int millis, final AsyncCallback<Void> callback) {
|
||||
Platform.schedule(new DumbSleepHandler(callback), millis);
|
||||
}
|
||||
|
||||
private static class DumbSleepHandler implements PlatformRunnable {
|
||||
private final AsyncCallback<Void> callback;
|
||||
private DumbSleepHandler(AsyncCallback<Void> callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
callback.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasCheckedImmediateContinue = false;
|
||||
private static MessageChannel immediateContinueChannel = null;
|
||||
private static Runnable currentContinueHack = null;
|
||||
private static final JSString emptyJSString = JSString.valueOf("");
|
||||
|
||||
public static void immediateContinue() {
|
||||
if(!hasCheckedImmediateContinue) {
|
||||
hasCheckedImmediateContinue = true;
|
||||
checkImmediateContinueSupport();
|
||||
}
|
||||
if(immediateContinueChannel != null) {
|
||||
immediateContinueTeaVM();
|
||||
}else {
|
||||
sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Async
|
||||
private static native void immediateContinueTeaVM();
|
||||
|
||||
private static void immediateContinueTeaVM(final AsyncCallback<Void> cb) {
|
||||
if(currentContinueHack != null) {
|
||||
cb.error(new IllegalStateException("Worker thread is already waiting for an immediate continue callback!"));
|
||||
return;
|
||||
}
|
||||
currentContinueHack = () -> {
|
||||
cb.complete(null);
|
||||
};
|
||||
try {
|
||||
immediateContinueChannel.getPort2().postMessage(emptyJSString);
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Caught error posting immediate continue, using setTimeout instead");
|
||||
Window.setTimeout(() -> cb.complete(null), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkImmediateContinueSupport() {
|
||||
try {
|
||||
immediateContinueChannel = null;
|
||||
if(!MessageChannel.supported()) {
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to MessageChannel being unsupported");
|
||||
return;
|
||||
}
|
||||
immediateContinueChannel = new MessageChannel();
|
||||
immediateContinueChannel.getPort1().addEventListener("message", new EventListener<MessageEvent>() {
|
||||
@Override
|
||||
public void handleEvent(MessageEvent evt) {
|
||||
Runnable toRun = currentContinueHack;
|
||||
currentContinueHack = null;
|
||||
if(toRun != null) {
|
||||
toRun.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
immediateContinueChannel.getPort1().start();
|
||||
immediateContinueChannel.getPort2().start();
|
||||
final boolean[] checkMe = new boolean[1];
|
||||
checkMe[0] = false;
|
||||
currentContinueHack = () -> {
|
||||
checkMe[0] = true;
|
||||
};
|
||||
immediateContinueChannel.getPort2().postMessage(emptyJSString);
|
||||
if(checkMe[0]) {
|
||||
currentContinueHack = null;
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to actually continuing immediately");
|
||||
return;
|
||||
}
|
||||
sleep(10);
|
||||
currentContinueHack = null;
|
||||
if(!checkMe[0]) {
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to startup check failing");
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to exceptions");
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void safeShutdownChannel(MessageChannel chan) {
|
||||
try {
|
||||
chan.getPort1().close();
|
||||
}catch(Throwable tt) {
|
||||
}
|
||||
try {
|
||||
chan.getPort2().close();
|
||||
}catch(Throwable tt) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.typedarrays.ArrayBufferView;
|
||||
import org.teavm.jso.typedarrays.Float32Array;
|
||||
import org.teavm.jso.typedarrays.Int16Array;
|
||||
import org.teavm.jso.typedarrays.Int32Array;
|
||||
import org.teavm.jso.typedarrays.Int8Array;
|
||||
import org.teavm.jso.typedarrays.Uint8Array;
|
||||
|
||||
public class TeaVMUtils {
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native Int8Array unwrapByteArray(byte[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class)
|
||||
public static native ArrayBuffer unwrapArrayBuffer(byte[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native ArrayBufferView unwrapArrayBufferView(byte[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class)
|
||||
public static native byte[] wrapByteArray(Int8Array buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class)
|
||||
public static native byte[] wrapByteArrayBuffer(ArrayBuffer buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native byte[] wrapByteArrayBufferView(ArrayBufferView buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapUnsignedTypedArray.class)
|
||||
public static native Uint8Array unwrapUnsignedByteArray(byte[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native byte[] wrapUnsignedByteArray(Uint8Array buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native Int32Array unwrapIntArray(int[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class)
|
||||
public static native ArrayBuffer unwrapArrayBuffer(int[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native ArrayBufferView unwrapArrayBufferView(int[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class)
|
||||
public static native int[] wrapIntArray(Int32Array buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class)
|
||||
public static native int[] wrapIntArrayBuffer(ArrayBuffer buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native int[] wrapIntArrayBufferView(ArrayBufferView buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native Float32Array unwrapFloatArray(float[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class)
|
||||
public static native ArrayBuffer unwrapArrayBuffer(float[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native ArrayBufferView unwrapArrayBufferView(float[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class)
|
||||
public static native float[] wrapFloatArray(Float32Array buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class)
|
||||
public static native float[] wrapFloatArrayBuffer(ArrayBuffer buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native float[] wrapFloatArrayBufferView(ArrayBufferView buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native Int16Array unwrapShortArray(short[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class)
|
||||
public static native ArrayBuffer unwrapArrayBuffer(short[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native ArrayBufferView unwrapArrayBufferView(short[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class)
|
||||
public static native short[] wrapShortArray(Int16Array buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class)
|
||||
public static native short[] wrapShortArrayBuffer(ArrayBuffer buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native short[] wrapShortArrayBuffer(ArrayBufferView buf);
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.spi.Generator;
|
||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||
import org.teavm.backend.javascript.spi.Injector;
|
||||
import org.teavm.backend.javascript.spi.InjectorContext;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class TeaVMUtilsUnwrapGenerator {
|
||||
|
||||
// WARNING: This code uses internal TeaVM APIs that may not have
|
||||
// been intended for end users of the compiler to program with
|
||||
|
||||
public static class UnwrapArrayBuffer implements Injector {
|
||||
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) {
|
||||
context.writeExpr(context.getArgument(0));
|
||||
context.getWriter().append(".data.buffer");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UnwrapTypedArray implements Injector {
|
||||
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) {
|
||||
context.writeExpr(context.getArgument(0));
|
||||
context.getWriter().append(".data");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WrapArrayBuffer implements Generator {
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) {
|
||||
String parName = context.getParameterName(1);
|
||||
switch (methodRef.getName()) {
|
||||
case "wrapByteArrayBuffer":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_bytecls").append(',').ws();
|
||||
writer.append("new Int8Array(").append(parName).append("))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapIntArrayBuffer":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_intcls").append(',').ws();
|
||||
writer.append("new Int32Array(").append(parName).append("))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapFloatArrayBuffer":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_floatcls").append(',').ws();
|
||||
writer.append("new Float32Array(").append(parName).append("))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapShortArrayBuffer":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_shortcls").append(',').ws();
|
||||
writer.append("new Int16Array(").append(parName).append("))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WrapArrayBufferView implements Generator {
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) {
|
||||
String parName = context.getParameterName(1);
|
||||
switch (methodRef.getName()) {
|
||||
case "wrapByteArrayBufferView":
|
||||
case "wrapUnsignedByteArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_bytecls").append(',').ws();
|
||||
writer.append("new Int8Array(").append(parName).append(".buffer))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapIntArrayBufferView":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_intcls").append(',').ws();
|
||||
writer.append("new Int32Array(").append(parName).append(".buffer))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapFloatArrayBufferView":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_floatcls").append(',').ws();
|
||||
writer.append("new Float32Array(").append(parName).append(".buffer))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapShortArrayBufferView":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_shortcls").append(',').ws();
|
||||
writer.append("new Int16Array(").append(parName).append(".buffer))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WrapTypedArray implements Generator {
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) {
|
||||
String parName = context.getParameterName(1);
|
||||
switch (methodRef.getName()) {
|
||||
case "wrapByteArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_shortcls").append(',').ws();
|
||||
writer.append(parName).append(")").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapIntArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_intcls").append(',').ws();
|
||||
writer.append(parName).append(")").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapFloatArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_floatcls").append(',').ws();
|
||||
writer.append(parName).append(")").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapShortArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_shortcls").append(',').ws();
|
||||
writer.append(parName).append(")").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UnwrapUnsignedTypedArray implements Injector {
|
||||
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) {
|
||||
context.getWriter().append("new Uint8Array(");
|
||||
context.writeExpr(context.getArgument(0));
|
||||
context.getWriter().append(".data.buffer)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.src.Chunk;
|
||||
import net.minecraft.src.ChunkCoordIntPair;
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.Entity;
|
||||
import net.minecraft.src.EntityList;
|
||||
import net.minecraft.src.ExtendedBlockStorage;
|
||||
import net.minecraft.src.IChunkLoader;
|
||||
import net.minecraft.src.MinecraftException;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.NBTTagList;
|
||||
import net.minecraft.src.NextTickListEntry;
|
||||
import net.minecraft.src.NibbleArray;
|
||||
import net.minecraft.src.TileEntity;
|
||||
import net.minecraft.src.World;
|
||||
|
||||
public class VFSChunkLoader implements IChunkLoader {
|
||||
|
||||
public final VFile chunkDirectory;
|
||||
|
||||
private static final String hex = "0123456789ABCDEF";
|
||||
|
||||
public static String getChunkPath(int x, int z) {
|
||||
int unsignedX = x + 1900000;
|
||||
int unsignedZ = z + 1900000;
|
||||
|
||||
char[] path = new char[12];
|
||||
for(int i = 5; i >= 0; --i) {
|
||||
path[i] = hex.charAt((unsignedX >> (i * 4)) & 0xF);
|
||||
path[i + 6] = hex.charAt((unsignedZ >> (i * 4)) & 0xF);
|
||||
}
|
||||
|
||||
return new String(path);
|
||||
}
|
||||
|
||||
public static ChunkCoordIntPair getChunkCoords(String filename) {
|
||||
String strX = filename.substring(0, 6);
|
||||
String strZ = filename.substring(6);
|
||||
|
||||
int retX = 0;
|
||||
int retZ = 0;
|
||||
|
||||
for(int i = 0; i < 6; ++i) {
|
||||
retX |= hex.indexOf(strX.charAt(i)) << (i << 2);
|
||||
retZ |= hex.indexOf(strZ.charAt(i)) << (i << 2);
|
||||
}
|
||||
|
||||
return new ChunkCoordIntPair(retX - 1900000, retZ - 1900000);
|
||||
}
|
||||
|
||||
public VFSChunkLoader(VFile chunkDirectory) {
|
||||
this.chunkDirectory = chunkDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk loadChunk(World var1, int var2, int var3) throws IOException {
|
||||
VFile file = new VFile(chunkDirectory, getChunkPath(var2, var3) + ".dat");
|
||||
byte[] bytes = file.getAllBytes();
|
||||
|
||||
if(bytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
NBTTagCompound nbt = CompressedStreamTools.decompress(bytes);
|
||||
nbt = nbt.getCompoundTag("Level");
|
||||
return readChunkFromNBT(var1, nbt, var2, var3);
|
||||
}catch(Throwable t) {
|
||||
file.delete();
|
||||
System.err.println("Corrupted chunk has been deleted: [" + var2 + ", " + var3 + "]");
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChunk(World var1, Chunk var2) throws MinecraftException, IOException {
|
||||
|
||||
NBTTagCompound chunkFile = new NBTTagCompound();
|
||||
this.writeChunkToNBT(var2, var1, chunkFile);
|
||||
|
||||
byte[] save;
|
||||
|
||||
try {
|
||||
NBTTagCompound chunkFileSave = new NBTTagCompound();
|
||||
chunkFileSave.setCompoundTag("Level", chunkFile);
|
||||
save = CompressedStreamTools.compress(chunkFileSave);
|
||||
}catch(IOException e) {
|
||||
System.err.println("Corrupted chunk could not be serialized: [" + var2.xPosition + ", " + var2.zPosition + "]");
|
||||
return;
|
||||
}
|
||||
|
||||
VFile file = new VFile(chunkDirectory, getChunkPath(var2.xPosition, var2.zPosition) + ".dat");
|
||||
|
||||
if(!file.setAllBytes(save)) {
|
||||
System.err.println("Corrupted chunk could not be written: [" + var2.xPosition + ", " + var2.zPosition + "] to file \"" + file.toString() + "\")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveExtraChunkData(World var1, Chunk var2) {
|
||||
// ?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkTick() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveExtraData() {
|
||||
// unused
|
||||
}
|
||||
|
||||
private Chunk readChunkFromNBT(World par1World, NBTTagCompound par2NBTTagCompound, int x, int z) {
|
||||
int var3 = x; //par2NBTTagCompound.getInteger("xPos");
|
||||
int var4 = z; //par2NBTTagCompound.getInteger("zPos");
|
||||
Chunk var5 = new Chunk(par1World, var3, var4);
|
||||
var5.heightMap = par2NBTTagCompound.getIntArray("HeightMap");
|
||||
var5.isTerrainPopulated = par2NBTTagCompound.getBoolean("TerrainPopulated");
|
||||
NBTTagList var6 = par2NBTTagCompound.getTagList("Sections");
|
||||
byte var7 = 16;
|
||||
ExtendedBlockStorage[] var8 = new ExtendedBlockStorage[var7];
|
||||
boolean var9 = !par1World.provider.hasNoSky;
|
||||
|
||||
for (int var10 = 0; var10 < var6.tagCount(); ++var10) {
|
||||
NBTTagCompound var11 = (NBTTagCompound) var6.tagAt(var10);
|
||||
byte var12 = var11.getByte("Y");
|
||||
ExtendedBlockStorage var13 = new ExtendedBlockStorage(var12 << 4, var9);
|
||||
var13.setBlockLSBArray(var11.getByteArray("Blocks"));
|
||||
|
||||
if (var11.hasKey("Add")) {
|
||||
var13.setBlockMSBArray(new NibbleArray(var11.getByteArray("Add"), 4));
|
||||
}
|
||||
|
||||
var13.setBlockMetadataArray(new NibbleArray(var11.getByteArray("Data"), 4));
|
||||
var13.setBlocklightArray(new NibbleArray(var11.getByteArray("BlockLight"), 4));
|
||||
|
||||
if (var9) {
|
||||
var13.setSkylightArray(new NibbleArray(var11.getByteArray("SkyLight"), 4));
|
||||
}
|
||||
|
||||
var13.removeInvalidBlocks();
|
||||
var8[var12] = var13;
|
||||
}
|
||||
|
||||
var5.setStorageArrays(var8);
|
||||
|
||||
if (par2NBTTagCompound.hasKey("Biomes")) {
|
||||
var5.setBiomeArray(par2NBTTagCompound.getByteArray("Biomes"));
|
||||
}
|
||||
|
||||
NBTTagList var17 = par2NBTTagCompound.getTagList("Entities");
|
||||
|
||||
if (var17 != null) {
|
||||
for (int var18 = 0; var18 < var17.tagCount(); ++var18) {
|
||||
NBTTagCompound var20 = (NBTTagCompound) var17.tagAt(var18);
|
||||
Entity var22 = EntityList.createEntityFromNBT(var20, par1World);
|
||||
var5.hasEntities = true;
|
||||
|
||||
if (var22 != null) {
|
||||
var5.addEntity(var22);
|
||||
Entity var14 = var22;
|
||||
|
||||
for (NBTTagCompound var15 = var20; var15.hasKey("Riding"); var15 = var15.getCompoundTag("Riding")) {
|
||||
Entity var16 = EntityList.createEntityFromNBT(var15.getCompoundTag("Riding"), par1World);
|
||||
|
||||
if (var16 != null) {
|
||||
var5.addEntity(var16);
|
||||
var14.mountEntity(var16);
|
||||
}
|
||||
|
||||
var14 = var16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NBTTagList var19 = par2NBTTagCompound.getTagList("TileEntities");
|
||||
|
||||
if (var19 != null) {
|
||||
for (int var21 = 0; var21 < var19.tagCount(); ++var21) {
|
||||
NBTTagCompound var24 = (NBTTagCompound) var19.tagAt(var21);
|
||||
TileEntity var26 = TileEntity.createAndLoadEntity(var24);
|
||||
|
||||
if (var26 != null) {
|
||||
var5.addTileEntity(var26);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (par2NBTTagCompound.hasKey("TileTicks")) {
|
||||
NBTTagList var23 = par2NBTTagCompound.getTagList("TileTicks");
|
||||
|
||||
if (var23 != null) {
|
||||
for (int var25 = 0; var25 < var23.tagCount(); ++var25) {
|
||||
NBTTagCompound var27 = (NBTTagCompound) var23.tagAt(var25);
|
||||
par1World.scheduleBlockUpdateFromLoad(var27.getInteger("x"), var27.getInteger("y"),
|
||||
var27.getInteger("z"), var27.getInteger("i"), var27.getInteger("t"), var27.getInteger("p"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
|
||||
private void writeChunkToNBT(Chunk par1Chunk, World par2World, NBTTagCompound par3NBTTagCompound) {
|
||||
par3NBTTagCompound.setInteger("xPos", par1Chunk.xPosition);
|
||||
par3NBTTagCompound.setInteger("zPos", par1Chunk.zPosition);
|
||||
par3NBTTagCompound.setLong("LastUpdate", par2World.getTotalWorldTime());
|
||||
par3NBTTagCompound.setIntArray("HeightMap", par1Chunk.heightMap);
|
||||
par3NBTTagCompound.setBoolean("TerrainPopulated", par1Chunk.isTerrainPopulated);
|
||||
ExtendedBlockStorage[] var4 = par1Chunk.getBlockStorageArray();
|
||||
NBTTagList var5 = new NBTTagList("Sections");
|
||||
boolean var6 = !par2World.provider.hasNoSky;
|
||||
ExtendedBlockStorage[] var7 = var4;
|
||||
int var8 = var4.length;
|
||||
NBTTagCompound var11;
|
||||
|
||||
for (int var9 = 0; var9 < var8; ++var9) {
|
||||
ExtendedBlockStorage var10 = var7[var9];
|
||||
|
||||
if (var10 != null) {
|
||||
var11 = new NBTTagCompound();
|
||||
var11.setByte("Y", (byte) (var10.getYLocation() >> 4 & 255));
|
||||
var11.setByteArray("Blocks", var10.getBlockLSBArray());
|
||||
|
||||
if (var10.getBlockMSBArray() != null) {
|
||||
var11.setByteArray("Add", var10.getBlockMSBArray().data);
|
||||
}
|
||||
|
||||
var11.setByteArray("Data", var10.getMetadataArray().data);
|
||||
var11.setByteArray("BlockLight", var10.getBlocklightArray().data);
|
||||
|
||||
if (var6) {
|
||||
var11.setByteArray("SkyLight", var10.getSkylightArray().data);
|
||||
} else {
|
||||
var11.setByteArray("SkyLight", new byte[var10.getBlocklightArray().data.length]);
|
||||
}
|
||||
|
||||
var5.appendTag(var11);
|
||||
}
|
||||
}
|
||||
|
||||
par3NBTTagCompound.setTag("Sections", var5);
|
||||
par3NBTTagCompound.setByteArray("Biomes", par1Chunk.getBiomeArray());
|
||||
par1Chunk.hasEntities = false;
|
||||
NBTTagList var16 = new NBTTagList();
|
||||
Iterator var18;
|
||||
|
||||
for (var8 = 0; var8 < par1Chunk.entityLists.length; ++var8) {
|
||||
var18 = par1Chunk.entityLists[var8].iterator();
|
||||
|
||||
while (var18.hasNext()) {
|
||||
Entity var20 = (Entity) var18.next();
|
||||
var11 = new NBTTagCompound();
|
||||
|
||||
if (var20.writeToNBTOptional(var11)) {
|
||||
par1Chunk.hasEntities = true;
|
||||
var16.appendTag(var11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
par3NBTTagCompound.setTag("Entities", var16);
|
||||
NBTTagList var17 = new NBTTagList();
|
||||
var18 = par1Chunk.chunkTileEntityMap.values().iterator();
|
||||
|
||||
while (var18.hasNext()) {
|
||||
TileEntity var21 = (TileEntity) var18.next();
|
||||
var11 = new NBTTagCompound();
|
||||
var21.writeToNBT(var11);
|
||||
var17.appendTag(var11);
|
||||
}
|
||||
|
||||
par3NBTTagCompound.setTag("TileEntities", var17);
|
||||
List var19 = par2World.getPendingBlockUpdates(par1Chunk, false);
|
||||
|
||||
if (var19 != null) {
|
||||
long var22 = par2World.getTotalWorldTime();
|
||||
NBTTagList var12 = new NBTTagList();
|
||||
Iterator var13 = var19.iterator();
|
||||
|
||||
while (var13.hasNext()) {
|
||||
NextTickListEntry var14 = (NextTickListEntry) var13.next();
|
||||
NBTTagCompound var15 = new NBTTagCompound();
|
||||
var15.setInteger("i", var14.blockID);
|
||||
var15.setInteger("x", var14.xCoord);
|
||||
var15.setInteger("y", var14.yCoord);
|
||||
var15.setInteger("z", var14.zCoord);
|
||||
var15.setInteger("t", (int) (var14.scheduledTime - var22));
|
||||
var15.setInteger("p", var14.priority);
|
||||
var12.appendTag(var15);
|
||||
}
|
||||
|
||||
par3NBTTagCompound.setTag("TileTicks", var12);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public interface VFSIterator {
|
||||
|
||||
public static class BreakLoop extends RuntimeException {
|
||||
public BreakLoop() {
|
||||
super("iterator loop break request");
|
||||
}
|
||||
}
|
||||
|
||||
public default void end() {
|
||||
throw new BreakLoop();
|
||||
}
|
||||
|
||||
public void next(VIteratorFile entry);
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import net.minecraft.src.IProgressUpdate;
|
||||
import net.minecraft.src.ISaveFormat;
|
||||
import net.minecraft.src.ISaveHandler;
|
||||
|
||||
public class VFSSaveFormat implements ISaveFormat {
|
||||
|
||||
private VFSSaveHandler folder;
|
||||
|
||||
public VFSSaveFormat(VFSSaveHandler dir) {
|
||||
folder = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISaveHandler getSaveLoader(String var1, boolean var2) {
|
||||
return folder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushCache() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteWorldDirectory(String var1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOldMapFormat(String var1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean convertMapFormat(String var1, IProgressUpdate var2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.EntityPlayer;
|
||||
import net.minecraft.src.IChunkLoader;
|
||||
import net.minecraft.src.IPlayerFileData;
|
||||
import net.minecraft.src.ISaveHandler;
|
||||
import net.minecraft.src.MinecraftException;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.WorldInfo;
|
||||
import net.minecraft.src.WorldProvider;
|
||||
|
||||
public class VFSSaveHandler implements ISaveHandler, IPlayerFileData {
|
||||
|
||||
public final VFile worldDirectory;
|
||||
|
||||
private final HashMap<Integer, VFSChunkLoader> chunkLoaders = new HashMap<>();
|
||||
|
||||
public VFSSaveHandler(VFile worldDirectory) {
|
||||
this.worldDirectory = worldDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldInfo loadWorldInfo() {
|
||||
|
||||
byte[] level_dat_bin = (new VFile(worldDirectory, "level.dat")).getAllBytes();
|
||||
|
||||
if(level_dat_bin == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
NBTTagCompound level_dat = CompressedStreamTools.decompress(level_dat_bin);
|
||||
return new WorldInfo(level_dat.getCompoundTag("Data"));
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Could not parse level.dat!");
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSessionLock() throws MinecraftException {
|
||||
// no
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkLoader getChunkLoader(WorldProvider var1) {
|
||||
VFSChunkLoader loader = chunkLoaders.get(var1.dimensionId);
|
||||
|
||||
if(loader == null) {
|
||||
loader = new VFSChunkLoader(new VFile(worldDirectory, "level" + var1.dimensionId));
|
||||
chunkLoaders.put(var1.dimensionId, loader);
|
||||
}
|
||||
|
||||
return loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveWorldInfoWithPlayer(WorldInfo var1, NBTTagCompound var2) {
|
||||
NBTTagCompound var3 = var2 != null ? var1.cloneNBTCompound(var2) : var1.getNBTTagCompound();
|
||||
NBTTagCompound var4 = new NBTTagCompound();
|
||||
var4.setTag("Data", var3);
|
||||
|
||||
VFile level_dat = new VFile(worldDirectory, "level.dat");
|
||||
|
||||
byte[] compressed;
|
||||
|
||||
try {
|
||||
compressed = CompressedStreamTools.compress(var4);
|
||||
}catch(IOException e) {
|
||||
System.err.println("Could not serialize \"" + level_dat + "\"");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!level_dat.setAllBytes(compressed)) {
|
||||
System.err.println("Could not save \"" + level_dat + "\" to filesystem");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveWorldInfo(WorldInfo var1) {
|
||||
saveWorldInfoWithPlayer(var1, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPlayerFileData getSaveHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public VFile getMapFileFromName(String var1) {
|
||||
return new VFile(worldDirectory, "data", var1 + ".dat");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorldDirectoryName() {
|
||||
return worldDirectory.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writePlayerData(EntityPlayer var1) {
|
||||
NBTTagCompound var2 = new NBTTagCompound();
|
||||
var1.writeToNBT(var2);
|
||||
|
||||
byte[] bin;
|
||||
|
||||
try {
|
||||
bin = CompressedStreamTools.compress(var2);
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Could not serialize player data for \"" + var1.username + "\"");
|
||||
t.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
VFile playerData = new VFile(worldDirectory, "player", var1.username.toLowerCase() + ".dat");
|
||||
|
||||
if(!playerData.setAllBytes(bin)) {
|
||||
System.err.println("Could not write player data for \"" + var1.username + "\" to file \"" + playerData.toString() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound readPlayerData(EntityPlayer var1) {
|
||||
VFile playerData = new VFile(worldDirectory, "player", var1.username.toLowerCase() + ".dat");
|
||||
|
||||
NBTTagCompound ret = null;
|
||||
|
||||
byte[] playerBin = playerData.getAllBytes();
|
||||
if(playerBin != null) {
|
||||
try {
|
||||
ret = CompressedStreamTools.decompress(playerBin);
|
||||
var1.readFromNBT(ret);
|
||||
}catch(IOException e) {
|
||||
System.err.println("Could not deserialize player data for \"" + var1.username + "\"");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAvailablePlayerDat() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String worldNameToFolderName(String par1Str) {
|
||||
par1Str = par1Str.replaceAll("[\\./\"]", "_");
|
||||
|
||||
boolean shit = true;
|
||||
while(shit) {
|
||||
shit = (new VFile("worlds", par1Str, "level.dat")).exists();
|
||||
if(shit) {
|
||||
par1Str = par1Str + "_";
|
||||
}
|
||||
}
|
||||
|
||||
return par1Str;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class VFSTestClass {
|
||||
|
||||
public static void test(VirtualFilesystem vfs) {
|
||||
/*
|
||||
System.out.println("'test1' exists: " + vfs.getFile("test1").exists());
|
||||
System.out.println("'test1' chars: " + vfs.getFile("test1").getAllChars());
|
||||
System.out.println("'test2' chars: " + vfs.getFile("test2").getAllChars());
|
||||
System.out.println("'test3' chars: " + vfs.getFile("test3").getAllChars());
|
||||
System.out.println("'test2' exists: " + vfs.getFile("test2").exists());
|
||||
System.out.println("'test3' exists: " + vfs.getFile("test3").exists());
|
||||
|
||||
System.out.println("'test1' set chars 'test string 1': " + vfs.getFile("test1").setAllChars("test string 1"));
|
||||
System.out.println("'test2' set chars 'test string 2': " + vfs.getFile("test2").setAllChars("test string 2"));
|
||||
System.out.println("'test3' set chars 'test string 3': " + vfs.getFile("test3").setAllChars("test string 3"));
|
||||
|
||||
System.out.println("'test1' exists: " + vfs.getFile("test1").exists());
|
||||
System.out.println("'test2' exists: " + vfs.getFile("test2").exists());
|
||||
System.out.println("'test3' exists: " + vfs.getFile("test3").exists());
|
||||
|
||||
System.out.println("'test1' chars: " + vfs.getFile("test1").getAllChars());
|
||||
System.out.println("'test2' chars: " + vfs.getFile("test2").getAllChars());
|
||||
System.out.println("'test3' chars: " + vfs.getFile("test3").getAllChars());
|
||||
|
||||
System.out.println("'test3' delete: " + vfs.getFile("test3").delete());
|
||||
System.out.println("'test3' exists: " + vfs.getFile("test3").exists());
|
||||
|
||||
System.out.println("'test2' delete: " + vfs.getFile("test2").delete());
|
||||
System.out.println("'test2' chars: " + vfs.getFile("test2").getAllChars());
|
||||
|
||||
System.out.println("'test4' exists: " + vfs.getFile("test4").exists());
|
||||
System.out.println("'test1' to 'test4' rename: " + vfs.getFile("test1").rename("test4"));
|
||||
System.out.println("'test4' exists: " + vfs.getFile("test4").exists());
|
||||
System.out.println("'test4' chars: " + vfs.getFile("test4").getAllChars());
|
||||
System.out.println("'test1' exists: " + vfs.getFile("test1").exists());
|
||||
System.out.println("'test4' to 'test1' rename: " + vfs.getFile("test4").rename("test1"));
|
||||
System.out.println("'test4' exists: " + vfs.getFile("test4").exists());
|
||||
System.out.println("'test4' chars: " + vfs.getFile("test4").getAllChars());
|
||||
System.out.println("'test1' exists: " + vfs.getFile("test1").exists());
|
||||
System.out.println("'test1' chars: " + vfs.getFile("test1").getAllChars());
|
||||
|
||||
System.out.println("'test1' cache get chars: " + vfs.getFile("test1", true).getAllChars());
|
||||
System.out.println("'test1' cache exists: " + vfs.getFile("test1", true).exists());
|
||||
System.out.println("'test1' cache delete: " + vfs.getFile("test1", true).delete());
|
||||
System.out.println("'test1' cache exists: " + vfs.getFile("test1", true).exists());
|
||||
System.out.println("'test1' cache get chars: " + vfs.getFile("test1", true).getAllChars());
|
||||
|
||||
System.out.println("'test1' cache set chars 'test cache string 1': " + vfs.getFile("test1", true).setAllChars("test cache string 1"));
|
||||
System.out.println("'test2' cache set chars 'test cache string 2': " + vfs.getFile("test2", true).setAllChars("test cache string 2"));
|
||||
System.out.println("'test3' cache set chars 'test cache string 3': " + vfs.getFile("test3", true).setAllChars("test cache string 3"));
|
||||
|
||||
System.out.println("'test1' cache chars: " + vfs.getFile("test1").getAllChars());
|
||||
System.out.println("'test2' cache chars: " + vfs.getFile("test2").getAllChars());
|
||||
System.out.println("'test3' cache chars: " + vfs.getFile("test3").getAllChars());
|
||||
|
||||
System.out.println("'test1' cache copy chars: " + VirtualFilesystem.utf8(vfs.getFile("test1").getAllBytes(true)));
|
||||
System.out.println("'test2' cache copy chars: " + VirtualFilesystem.utf8(vfs.getFile("test2").getAllBytes(true)));
|
||||
System.out.println("'test3' cache copy chars: " + VirtualFilesystem.utf8(vfs.getFile("test3").getAllBytes(true)));
|
||||
*/
|
||||
|
||||
VFile f = new VFile("test1");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("/test1");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("/test2/");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("test2/");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("test2/teste");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("\\test2\\teste");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("\\test2\\teste\\..\\eag");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("test2", "teste", "eag");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile(f, "../", "test2", "teste", "eag");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile(f, "../../", "test2", ".", "eag");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("you/eag", f);
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile(" you/ eag ", f);
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("\\yee\\", f);
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("\\yee\\", "yeeler", f, new VFile("yee"));
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile(f, new VFile("yee2"));
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("yee/deevler/", new VFile("yee2"));
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("yee/../../../../", new VFile("yee2"));
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("yee/../../deevler../../", new VFile("yee2"));
|
||||
System.out.println(f);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class VFile {
|
||||
|
||||
public static final String pathSeperator = "/";
|
||||
public static final String[] altPathSeperator = new String[] { "\\" };
|
||||
|
||||
public static String normalizePath(String p) {
|
||||
for(int i = 0; i < altPathSeperator.length; ++i) {
|
||||
p = p.replace(altPathSeperator[i], pathSeperator);
|
||||
}
|
||||
if(p.startsWith(pathSeperator)) {
|
||||
p = p.substring(1);
|
||||
}
|
||||
if(p.endsWith(pathSeperator)) {
|
||||
p = p.substring(0, p.length() - pathSeperator.length());
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public static String[] splitPath(String p) {
|
||||
String[] pth = normalizePath(p).split(pathSeperator);
|
||||
for(int i = 0; i < pth.length; ++i) {
|
||||
pth[i] = pth[i].trim();
|
||||
}
|
||||
return pth;
|
||||
}
|
||||
|
||||
protected String path;
|
||||
|
||||
public static String createPath(Object... p) {
|
||||
ArrayList<String> r = new ArrayList<>();
|
||||
for(int i = 0; i < p.length; ++i) {
|
||||
if(p[i] == null) {
|
||||
continue;
|
||||
}
|
||||
String gg = p[i].toString();
|
||||
if(gg == null) {
|
||||
continue;
|
||||
}
|
||||
String[] parts = splitPath(gg);
|
||||
for(int j = 0; j < parts.length; ++j) {
|
||||
if(parts[j] == null || parts[j].equals(".")) {
|
||||
continue;
|
||||
}else if(parts[j].equals("..") && r.size() > 0) {
|
||||
int k = r.size() - 1;
|
||||
if(!r.get(k).equals("..")) {
|
||||
r.remove(k);
|
||||
}else {
|
||||
r.add("..");
|
||||
}
|
||||
}else {
|
||||
r.add(parts[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(r.size() > 0) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for(int i = 0; i < r.size(); ++i) {
|
||||
if(i > 0) {
|
||||
s.append(pathSeperator);
|
||||
}
|
||||
s.append(r.get(i));
|
||||
}
|
||||
return s.toString();
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public VFile(Object... p) {
|
||||
this.path = createPath(p);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return isRelative() ? null : SYS.VFS.getFile(path).getInputStream();
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return isRelative() ? null : SYS.VFS.getFile(path).getOutputStream();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isRelative() {
|
||||
return path == null || path.contains("..");
|
||||
}
|
||||
|
||||
public boolean canRead() {
|
||||
return !isRelative() && SYS.VFS.fileExists(path);
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path.equals("unnamed") ? null : path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.lastIndexOf(pathSeperator);
|
||||
return i == -1 ? path : path.substring(i + 1);
|
||||
}
|
||||
|
||||
public boolean canWrite() {
|
||||
return !isRelative();
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? ".." : path.substring(0, i);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return path == null ? 0 : path.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return path != null && o != null && (o instanceof VFile) && path.equals(((VFile)o).path);
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return !isRelative() && SYS.VFS.fileExists(path);
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return !isRelative() && SYS.VFS.deleteFile(path);
|
||||
}
|
||||
|
||||
public boolean renameTo(String p, boolean copy) {
|
||||
if(!isRelative() && SYS.VFS.renameFile(path, p, copy)) {
|
||||
path = p;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return isRelative() ? -1 : SYS.VFS.getFile(path).getSize();
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
if(isRelative()) {
|
||||
throw new ArrayIndexOutOfBoundsException("File is relative");
|
||||
}
|
||||
SYS.VFS.getFile(path).getBytes(fileOffset, array, offset, length);
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
if(isRelative()) {
|
||||
throw new RuntimeException("File is relative");
|
||||
}
|
||||
SYS.VFS.getFile(path).setCacheEnabled();
|
||||
}
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllBytes();
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllChars();
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllLines();
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllBytes(copy);
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllChars(bytes);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllBytes(bytes);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllBytes(bytes, copy);
|
||||
}
|
||||
|
||||
public List<String> list() {
|
||||
if(isRelative()) {
|
||||
return Arrays.asList(path);
|
||||
}
|
||||
return SYS.VFS.listFiles(path);
|
||||
}
|
||||
|
||||
public int deleteAll() {
|
||||
return isRelative() ? 0 : SYS.VFS.deleteFiles(path);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.dom.events.Event;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.indexeddb.IDBCursor;
|
||||
import org.teavm.jso.indexeddb.IDBRequest;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.typedarrays.Uint8Array;
|
||||
|
||||
/**
|
||||
* Do not use an instance of this class outside of the VFSIterator.next() method
|
||||
*/
|
||||
public class VIteratorFile extends VFile {
|
||||
|
||||
static final VIteratorFile instance = new VIteratorFile();
|
||||
|
||||
private VIteratorFile() {
|
||||
super("");
|
||||
this.idx = -1;
|
||||
this.cur = null;
|
||||
this.vfs = null;
|
||||
}
|
||||
|
||||
private static class VirtualIteratorOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
private final VIteratorFile itr;
|
||||
|
||||
protected VirtualIteratorOutputStream(VIteratorFile itr) {
|
||||
this.itr = itr;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if(!itr.setAllBytes(super.toByteArray(), false)) {
|
||||
throw new IOException("Could not close stream and write to \"" + itr.path + "\" on VFS \"" + itr.vfs.database + "\" (the file was probably deleted)");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int idx;
|
||||
private IDBCursor cur;
|
||||
private VirtualFilesystem vfs;
|
||||
private boolean wasDeleted;
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));")
|
||||
private static native String readKey(JSObject k);
|
||||
|
||||
static VIteratorFile create(int idx, VirtualFilesystem vfs, IDBCursor cur) {
|
||||
String k = readKey(cur.getKey());
|
||||
if(k == null) {
|
||||
return null;
|
||||
}
|
||||
instance.update(idx, k, vfs, cur);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public VFile makeVFile() {
|
||||
return new VFile(path);
|
||||
}
|
||||
|
||||
private void update(int idx, String path, VirtualFilesystem vfs, IDBCursor cur) {
|
||||
this.idx = idx;
|
||||
this.path = path;
|
||||
this.vfs = vfs;
|
||||
this.cur = cur;
|
||||
this.wasDeleted = false;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return !wasDeleted ? new ByteArrayInputStream(getAllBytes()) : null;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return !wasDeleted ? new VirtualIteratorOutputStream(this) : null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isRelative() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canRead() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? path : path.substring(i + 1);
|
||||
}
|
||||
|
||||
public boolean canWrite() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? ".." : path.substring(0, i);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return path == null ? 0 : path.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return path != null && o != null && (o instanceof VFile) && path.equals(((VFile)o).path);
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return wasDeleted = AsyncHandlers.awaitRequest(cur.delete()).bool;
|
||||
}
|
||||
|
||||
public boolean renameTo(String p) {
|
||||
byte[] data = getAllBytes();
|
||||
String op = path;
|
||||
path = p;
|
||||
if(!setAllBytes(data)) {
|
||||
path = op;
|
||||
return false;
|
||||
}
|
||||
path = op;
|
||||
if(!delete()) {
|
||||
return false;
|
||||
}
|
||||
path = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
throw new RuntimeException("Value of entry is missing");
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
throw new RuntimeException("Value of the fucking value of the entry is missing");
|
||||
}
|
||||
|
||||
return arr.getByteLength();
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
throw new ArrayIndexOutOfBoundsException("Value of entry is missing");
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
throw new ArrayIndexOutOfBoundsException("Value of the fucking value of the entry is missing");
|
||||
}
|
||||
|
||||
Uint8Array a = new Uint8Array(arr);
|
||||
|
||||
if(a.getLength() < fileOffset + length) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + path + "' size was "+a.getLength()+" but user tried to read index "+(fileOffset + length - 1));
|
||||
}
|
||||
|
||||
for(int i = 0; i < length; ++i) {
|
||||
array[i + offset] = (byte)a.get(i + fileOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
// no
|
||||
}
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);")
|
||||
private static native ArrayBuffer readRow(JSObject obj);
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Uint8Array a = new Uint8Array(arr);
|
||||
int ii = a.getByteLength();
|
||||
|
||||
byte[] array = new byte[ii];
|
||||
for(int i = 0; i < ii; ++i) {
|
||||
array[i] = (byte)a.get(i);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
return VirtualFilesystem.utf8(getAllBytes());
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
return VirtualFilesystem.lines(VirtualFilesystem.utf8(getAllBytes()));
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
return getAllBytes();
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
return setAllBytes(VirtualFilesystem.utf8(bytes));
|
||||
}
|
||||
|
||||
public List<String> list() {
|
||||
throw new RuntimeException("Cannot perform list all in VFS callback");
|
||||
}
|
||||
|
||||
public int deleteAll() {
|
||||
throw new RuntimeException("Cannot perform delete all in VFS callback");
|
||||
}
|
||||
|
||||
@JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };")
|
||||
private static native JSObject writeRow(String name, ArrayBuffer data);
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
ArrayBuffer a = new ArrayBuffer(bytes.length);
|
||||
Uint8Array ar = new Uint8Array(a);
|
||||
ar.set(bytes);
|
||||
JSObject obj = writeRow(path, a);
|
||||
BooleanResult r = AsyncHandlers.awaitRequest(cur.update(obj));
|
||||
return r.bool;
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
return setAllBytes(bytes);
|
||||
}
|
||||
|
||||
public static class AsyncHandlers {
|
||||
|
||||
@Async
|
||||
public static native BooleanResult awaitRequest(IDBRequest r);
|
||||
|
||||
private static void awaitRequest(IDBRequest r, final AsyncCallback<BooleanResult> cb) {
|
||||
r.addEventListener("success", new EventListener<Event>() {
|
||||
@Override
|
||||
public void handleEvent(Event evt) {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.addEventListener("error", new EventListener<Event>() {
|
||||
@Override
|
||||
public void handleEvent(Event evt) {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,688 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.indexeddb.EventHandler;
|
||||
import org.teavm.jso.indexeddb.IDBCountRequest;
|
||||
import org.teavm.jso.indexeddb.IDBCursor;
|
||||
import org.teavm.jso.indexeddb.IDBCursorRequest;
|
||||
import org.teavm.jso.indexeddb.IDBDatabase;
|
||||
import org.teavm.jso.indexeddb.IDBFactory;
|
||||
import org.teavm.jso.indexeddb.IDBGetRequest;
|
||||
import org.teavm.jso.indexeddb.IDBObjectStoreParameters;
|
||||
import org.teavm.jso.indexeddb.IDBOpenDBRequest;
|
||||
import org.teavm.jso.indexeddb.IDBRequest;
|
||||
import org.teavm.jso.indexeddb.IDBTransaction;
|
||||
import org.teavm.jso.indexeddb.IDBVersionChangeEvent;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.typedarrays.Int8Array;
|
||||
import org.teavm.jso.typedarrays.Uint8Array;
|
||||
|
||||
public class VirtualFilesystem {
|
||||
|
||||
protected static class VirtualOutputStream extends ByteArrayOutputStream {
|
||||
private final VFSFile file;
|
||||
|
||||
protected VirtualOutputStream(VFSFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if(!file.setAllBytes(super.toByteArray(), false)) {
|
||||
throw new IOException("Could not close stream and write to \"" + file.filePath + "\" on VFS \"" + file.virtualFilesystem.database + "\" (the file was probably deleted)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class VFSFile {
|
||||
|
||||
public final VirtualFilesystem virtualFilesystem;
|
||||
protected boolean cacheEnabled;
|
||||
protected String filePath;
|
||||
protected int fileSize = -1;
|
||||
protected boolean hasBeenDeleted = false;
|
||||
protected boolean hasBeenAccessed = false;
|
||||
protected boolean exists = false;
|
||||
|
||||
protected byte[] cache = null;
|
||||
protected long cacheHit;
|
||||
|
||||
protected VFSFile(VirtualFilesystem vfs, String filePath, boolean cacheEnabled) {
|
||||
this.virtualFilesystem = vfs;
|
||||
this.filePath = filePath;
|
||||
this.cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(cacheEnabled) {
|
||||
setCacheEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof VFSFile) && ((VFSFile)o).filePath.equals(filePath);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return filePath.hashCode();
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(fileSize < 0) {
|
||||
if(cacheEnabled) {
|
||||
byte[] b = getAllBytes(false);
|
||||
if(b != null) {
|
||||
fileSize = b.length;
|
||||
}
|
||||
}else {
|
||||
ArrayBuffer dat = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
if(dat != null) {
|
||||
fileSize = dat.getByteLength();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
byte[] dat = getAllBytes(false);
|
||||
if(dat == null) {
|
||||
return null;
|
||||
}
|
||||
return new ByteArrayInputStream(dat);
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return new VirtualOutputStream(this);
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
if(hasBeenDeleted) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' has been deleted");
|
||||
}else if(hasBeenAccessed && !exists) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' does not exist");
|
||||
}
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(cacheEnabled && cache != null) {
|
||||
System.arraycopy(cache, fileOffset, array, offset, length);
|
||||
}else {
|
||||
ArrayBuffer aa = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
hasBeenAccessed = true;
|
||||
if(aa != null) {
|
||||
exists = true;
|
||||
}else {
|
||||
exists = false;
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' does not exist");
|
||||
}
|
||||
this.fileSize = aa.getByteLength();
|
||||
if(cacheEnabled) {
|
||||
cache = TeaVMUtils.wrapByteArrayBuffer(aa);
|
||||
}
|
||||
if(fileSize < fileOffset + length) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' size was "+fileSize+" but user tried to read index "+(fileOffset + length - 1));
|
||||
}
|
||||
TeaVMUtils.unwrapByteArray(array).set(new Int8Array(aa, fileOffset, length), offset);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
if(!cacheEnabled && !hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
cache = getAllBytes(false);
|
||||
cacheEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
return getAllBytes(false);
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
return utf8(getAllBytes(false));
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
return lines(getAllChars());
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
if(hasBeenDeleted || (hasBeenAccessed && !exists)) {
|
||||
return null;
|
||||
}
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(cacheEnabled && cache != null) {
|
||||
byte[] b = cache;
|
||||
if(copy) {
|
||||
b = new byte[cache.length];
|
||||
System.arraycopy(cache, 0, b, 0, cache.length);
|
||||
}
|
||||
return b;
|
||||
}else {
|
||||
hasBeenAccessed = true;
|
||||
ArrayBuffer b = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
if(b != null) {
|
||||
exists = true;
|
||||
}else {
|
||||
exists = false;
|
||||
return null;
|
||||
}
|
||||
this.fileSize = b.getByteLength();
|
||||
if(cacheEnabled) {
|
||||
if(copy) {
|
||||
cache = new byte[fileSize];
|
||||
TeaVMUtils.unwrapByteArray(cache).set(new Int8Array(b));
|
||||
}else {
|
||||
cache = TeaVMUtils.wrapByteArrayBuffer(b);
|
||||
}
|
||||
}
|
||||
return TeaVMUtils.wrapByteArrayBuffer(b);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
return setAllBytes(utf8(bytes), true);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
return setAllBytes(bytes, true);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
if(hasBeenDeleted || bytes == null) {
|
||||
return false;
|
||||
}
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
this.fileSize = bytes.length;
|
||||
if(cacheEnabled) {
|
||||
byte[] copz = bytes;
|
||||
if(copy) {
|
||||
copz = new byte[bytes.length];
|
||||
System.arraycopy(bytes, 0, copz, 0, bytes.length);
|
||||
}
|
||||
cache = copz;
|
||||
return sync();
|
||||
}else {
|
||||
boolean s = AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, filePath,
|
||||
TeaVMUtils.unwrapArrayBuffer(bytes)).bool;
|
||||
hasBeenAccessed = true;
|
||||
exists = exists || s;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean sync() {
|
||||
if(cacheEnabled && cache != null && !hasBeenDeleted) {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
boolean tryWrite = AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, filePath,
|
||||
TeaVMUtils.unwrapArrayBuffer(cache)).bool;
|
||||
hasBeenAccessed = true;
|
||||
exists = exists || tryWrite;
|
||||
return tryWrite;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
if(!hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(!AsyncHandlers.deleteFile(virtualFilesystem.indexeddb, filePath).bool) {
|
||||
hasBeenAccessed = true;
|
||||
return false;
|
||||
}
|
||||
virtualFilesystem.fileMap.remove(filePath);
|
||||
hasBeenDeleted = true;
|
||||
hasBeenAccessed = true;
|
||||
exists = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean rename(String newName, boolean copy) {
|
||||
if(!hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
ArrayBuffer arr = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
hasBeenAccessed = true;
|
||||
if(arr != null) {
|
||||
exists = true;
|
||||
if(!AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, newName, arr).bool) {
|
||||
return false;
|
||||
}
|
||||
if(!copy && !AsyncHandlers.deleteFile(virtualFilesystem.indexeddb, filePath).bool) {
|
||||
return false;
|
||||
}
|
||||
}else {
|
||||
exists = false;
|
||||
}
|
||||
if(!copy) {
|
||||
virtualFilesystem.fileMap.remove(filePath);
|
||||
filePath = newName;
|
||||
virtualFilesystem.fileMap.put(newName, this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
if(hasBeenDeleted) {
|
||||
return false;
|
||||
}
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(hasBeenAccessed) {
|
||||
return exists;
|
||||
}
|
||||
exists = AsyncHandlers.fileExists(virtualFilesystem.indexeddb, filePath).bool;
|
||||
hasBeenAccessed = true;
|
||||
return exists;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final HashMap<String, VFSFile> fileMap = new HashMap<>();
|
||||
|
||||
public final String database;
|
||||
private final IDBDatabase indexeddb;
|
||||
|
||||
public static class VFSHandle {
|
||||
|
||||
public final boolean failedInit;
|
||||
public final boolean failedLocked;
|
||||
public final String failedError;
|
||||
public final VirtualFilesystem vfs;
|
||||
|
||||
public VFSHandle(boolean init, boolean locked, String error, VirtualFilesystem db) {
|
||||
failedInit = init;
|
||||
failedLocked = locked;
|
||||
failedError = error;
|
||||
vfs = db;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if(failedInit) {
|
||||
return "IDBFactory threw an exception, IndexedDB is most likely not supported in this browser." + (failedError == null ? "" : "\n\n" + failedError);
|
||||
}
|
||||
if(failedLocked) {
|
||||
return "The filesystem requested is already in use on a different tab.";
|
||||
}
|
||||
if(failedError != null) {
|
||||
return "The IDBFactory.open() request failed, reason: " + failedError;
|
||||
}
|
||||
return "Virtual Filesystem Object: " + vfs.database;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static VFSHandle openVFS(String db) {
|
||||
DatabaseOpen evt = AsyncHandlers.openDB(db);
|
||||
if(evt.failedInit) {
|
||||
return new VFSHandle(true, false, evt.failedError, null);
|
||||
}
|
||||
if(evt.failedLocked) {
|
||||
return new VFSHandle(false, true, null, null);
|
||||
}
|
||||
if(evt.failedError != null) {
|
||||
return new VFSHandle(false, false, evt.failedError, null);
|
||||
}
|
||||
return new VFSHandle(false, false, null, new VirtualFilesystem(db, evt.database));
|
||||
}
|
||||
|
||||
private VirtualFilesystem(String db, IDBDatabase idb) {
|
||||
database = db;
|
||||
indexeddb = idb;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
indexeddb.close();
|
||||
}
|
||||
|
||||
public VFSFile getFile(String path) {
|
||||
return getFile(path, false);
|
||||
}
|
||||
|
||||
public VFSFile getFile(String path, boolean cache) {
|
||||
VFSFile f = fileMap.get(path);
|
||||
if(f == null) {
|
||||
fileMap.put(path, f = new VFSFile(this, path, cache));
|
||||
}else {
|
||||
if(cache) {
|
||||
f.setCacheEnabled();
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
public boolean renameFile(String oldName, String newName, boolean copy) {
|
||||
return getFile(oldName).rename(newName, copy);
|
||||
}
|
||||
|
||||
public boolean deleteFile(String path) {
|
||||
return getFile(path).delete();
|
||||
}
|
||||
|
||||
public boolean fileExists(String path) {
|
||||
return getFile(path).exists();
|
||||
}
|
||||
|
||||
public List<String> listFiles(String prefix) {
|
||||
final ArrayList<String> list = new ArrayList<>();
|
||||
AsyncHandlers.iterateFiles(indexeddb, this, prefix, false, (v) -> {
|
||||
list.add(v.getPath());
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<VFile> listVFiles(String prefix) {
|
||||
final ArrayList<VFile> list = new ArrayList<>();
|
||||
AsyncHandlers.iterateFiles(indexeddb, this, prefix, false, (v) -> {
|
||||
list.add(new VFile(v.getPath()));
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
public int deleteFiles(String prefix) {
|
||||
return AsyncHandlers.deleteFiles(indexeddb, prefix);
|
||||
}
|
||||
|
||||
public int iterateFiles(String prefix, boolean rw, VFSIterator itr) {
|
||||
return AsyncHandlers.iterateFiles(indexeddb, this, prefix, rw, itr);
|
||||
}
|
||||
|
||||
public int renameFiles(String oldPrefix, String newPrefix, boolean copy) {
|
||||
List<String> filesToCopy = listFiles(oldPrefix);
|
||||
int i = 0;
|
||||
for(String str : filesToCopy) {
|
||||
String f = VFile.createPath(newPrefix, str.substring(oldPrefix.length()));
|
||||
if(!renameFile(str, f, copy)) {
|
||||
System.err.println("Could not " + (copy ? "copy" : "rename") + " file \"" + str + "\" to \"" + f + "\" for some reason");
|
||||
}else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public void flushCache(long age) {
|
||||
long curr = SysUtil.steadyTimeMillis();
|
||||
Iterator<VFSFile> files = fileMap.values().iterator();
|
||||
while(files.hasNext()) {
|
||||
if(curr - files.next().cacheHit > age) {
|
||||
files.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DatabaseOpen {
|
||||
|
||||
protected final boolean failedInit;
|
||||
protected final boolean failedLocked;
|
||||
protected final String failedError;
|
||||
|
||||
protected final IDBDatabase database;
|
||||
|
||||
protected DatabaseOpen(boolean init, boolean locked, String error, IDBDatabase db) {
|
||||
failedInit = init;
|
||||
failedLocked = locked;
|
||||
failedError = error;
|
||||
database = db;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JSBody(script = "return ((typeof indexedDB) !== 'undefined') ? indexedDB : null;")
|
||||
protected static native IDBFactory createIDBFactory();
|
||||
|
||||
protected static class AsyncHandlers {
|
||||
|
||||
@Async
|
||||
protected static native DatabaseOpen openDB(String name);
|
||||
|
||||
private static void openDB(String name, final AsyncCallback<DatabaseOpen> cb) {
|
||||
IDBFactory i = createIDBFactory();
|
||||
if(i == null) {
|
||||
cb.complete(new DatabaseOpen(false, false, "window.indexedDB was null or undefined", null));
|
||||
return;
|
||||
}
|
||||
final IDBOpenDBRequest f = i.open(name, 1);
|
||||
f.setOnBlocked(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, true, null, null));
|
||||
}
|
||||
});
|
||||
f.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, false, null, f.getResult()));
|
||||
}
|
||||
});
|
||||
f.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, false, "open error", null));
|
||||
}
|
||||
});
|
||||
f.setOnUpgradeNeeded(new EventListener<IDBVersionChangeEvent>() {
|
||||
@Override
|
||||
public void handleEvent(IDBVersionChangeEvent evt) {
|
||||
f.getResult().createObjectStore("filesystem", IDBObjectStoreParameters.create().keyPath("path"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult deleteFile(IDBDatabase db, String name);
|
||||
|
||||
private static void deleteFile(IDBDatabase db, String name, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBRequest r = tx.objectStore("filesystem").delete(makeTheFuckingKeyWork(name));
|
||||
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);")
|
||||
protected static native ArrayBuffer readRow(JSObject obj);
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return [obj];")
|
||||
private static native JSObject makeTheFuckingKeyWork(String k);
|
||||
|
||||
@Async
|
||||
protected static native ArrayBuffer readWholeFile(IDBDatabase db, String name);
|
||||
|
||||
private static void readWholeFile(IDBDatabase db, String name, final AsyncCallback<ArrayBuffer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readonly");
|
||||
final IDBGetRequest r = tx.objectStore("filesystem").get(makeTheFuckingKeyWork(name));
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(readRow(r.getResult()));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(null);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));")
|
||||
private static native String readKey(JSObject k);
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"undefined\") ? null : (((typeof k.path) === \"undefined\") ? null : (((typeof k.path) === \"string\") ? k[0] : null));")
|
||||
private static native String readRowKey(JSObject r);
|
||||
|
||||
@Async
|
||||
protected static native Integer iterateFiles(IDBDatabase db, final VirtualFilesystem vfs, final String prefix, boolean rw, final VFSIterator itr);
|
||||
|
||||
private static void iterateFiles(IDBDatabase db, final VirtualFilesystem vfs, final String prefix, boolean rw, final VFSIterator itr, final AsyncCallback<Integer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", rw ? "readwrite" : "readonly");
|
||||
final IDBCursorRequest r = tx.objectStore("filesystem").openCursor();
|
||||
final int[] res = new int[1];
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
IDBCursor c = r.getResult();
|
||||
if(c == null || c.getKey() == null || c.getValue() == null) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
String k = readKey(c.getKey());
|
||||
if(k != null) {
|
||||
if(k.startsWith(prefix)) {
|
||||
int ci = res[0]++;
|
||||
try {
|
||||
itr.next(VIteratorFile.create(ci, vfs, c));
|
||||
}catch(VFSIterator.BreakLoop ex) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
c.doContinue();
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(res[0] > 0 ? res[0] : -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native Integer deleteFiles(IDBDatabase db, final String prefix);
|
||||
|
||||
private static void deleteFiles(IDBDatabase db, final String prefix, final AsyncCallback<Integer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBCursorRequest r = tx.objectStore("filesystem").openCursor();
|
||||
final int[] res = new int[1];
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
IDBCursor c = r.getResult();
|
||||
if(c == null || c.getKey() == null || c.getValue() == null) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
String k = readKey(c.getKey());
|
||||
if(k != null) {
|
||||
if(k.startsWith(prefix)) {
|
||||
c.delete();
|
||||
++res[0];
|
||||
}
|
||||
}
|
||||
c.doContinue();
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(res[0] > 0 ? res[0] : -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult fileExists(IDBDatabase db, String name);
|
||||
|
||||
private static void fileExists(IDBDatabase db, String name, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readonly");
|
||||
final IDBCountRequest r = tx.objectStore("filesystem").count(makeTheFuckingKeyWork(name));
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(r.getResult() > 0));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };")
|
||||
protected static native JSObject writeRow(String name, ArrayBuffer data);
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult writeWholeFile(IDBDatabase db, String name, ArrayBuffer data);
|
||||
|
||||
private static void writeWholeFile(IDBDatabase db, String name, ArrayBuffer data, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBRequest r = tx.objectStore("filesystem").put(writeRow(name, data));
|
||||
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static byte[] utf8(String str) {
|
||||
if(str == null) return null;
|
||||
return str.getBytes(Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
public static String utf8(byte[] str) {
|
||||
if(str == null) return null;
|
||||
return new String(str, Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
public static String CRLFtoLF(String str) {
|
||||
if(str == null) return null;
|
||||
str = str.indexOf('\r') != -1 ? str.replace("\r", "") : str;
|
||||
str = str.trim();
|
||||
if(str.endsWith("\n")) {
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
if(str.startsWith("\n")) {
|
||||
str = str.substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String[] lines(String str) {
|
||||
if(str == null) return null;
|
||||
return CRLFtoLF(str).split("\n");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.NetHandler;
|
||||
|
||||
public class WorkerListenThread {
|
||||
/** Reference to the MinecraftServer object. */
|
||||
private final MinecraftServer mcServer;
|
||||
private final HashSet<NetHandler> connections = new HashSet<>();
|
||||
private final HashMap<String, WorkerNetworkManager> channels = new HashMap<>();
|
||||
|
||||
/** Whether the network listener object is listening. */
|
||||
public volatile boolean isListening = false;
|
||||
|
||||
public WorkerListenThread(MinecraftServer par1MinecraftServer) {
|
||||
this.mcServer = par1MinecraftServer;
|
||||
this.isListening = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds this connection to the list of currently connected players
|
||||
*/
|
||||
public void addPlayer(NetHandler par1NetServerHandler) {
|
||||
System.out.println("[Server][ADDPLAYER][" + par1NetServerHandler.getClass().getSimpleName() + "]");
|
||||
this.connections.add(par1NetServerHandler);
|
||||
}
|
||||
|
||||
public void stopListening() {
|
||||
this.isListening = false;
|
||||
List<String> names = new ArrayList<>(channels.keySet());
|
||||
for(int i = 0, l = names.size(); i < l; ++i) {
|
||||
closeChannel(names.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openChannel(String player) {
|
||||
System.out.println("[Server][OPENCHANNEL][" + player + "]");
|
||||
return channels.put(player, new WorkerNetworkManager(player, mcServer, this)) == null;
|
||||
}
|
||||
|
||||
public void recievePacket(String player, byte[] data) {
|
||||
WorkerNetworkManager channel = channels.get(player);
|
||||
if(channel == null) {
|
||||
return;
|
||||
}
|
||||
channel.addToRecieveQueue(data);
|
||||
}
|
||||
|
||||
public boolean closeChannel(String player) {
|
||||
System.out.println("[Server][CLOSECHANNEL][" + player + "]");
|
||||
WorkerNetworkManager channel = channels.get(player);
|
||||
if(channel == null) {
|
||||
return false;
|
||||
}
|
||||
channels.remove(player);
|
||||
channel.networkShutdown(null, null, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteDeadConnections() {
|
||||
Iterator<NetHandler> itr = this.connections.iterator();
|
||||
while (itr.hasNext()) {
|
||||
NetHandler handler = itr.next();
|
||||
if (handler.shouldBeRemoved()) {
|
||||
itr.remove();
|
||||
//System.out.println("[Client][REMOVEDEAD]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all incoming connections and packets
|
||||
*/
|
||||
public void handleNetworkListenThread() {
|
||||
|
||||
deleteDeadConnections();
|
||||
|
||||
List<NetHandler> conns = new ArrayList<>(this.connections);
|
||||
for (NetHandler var2 : conns) {
|
||||
var2.handlePackets();
|
||||
}
|
||||
|
||||
deleteDeadConnections();
|
||||
|
||||
}
|
||||
|
||||
public MinecraftServer getServer() {
|
||||
return this.mcServer;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import net.lax1dude.eaglercraft.sp.ipc.IPCPacket0CPlayerChannel;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.INetworkManager;
|
||||
import net.minecraft.src.NetHandler;
|
||||
import net.minecraft.src.NetLoginHandler;
|
||||
import net.minecraft.src.NetServerHandler;
|
||||
import net.minecraft.src.Packet;
|
||||
|
||||
public class WorkerNetworkManager implements INetworkManager {
|
||||
|
||||
private NetHandler theNetHandler;
|
||||
private MinecraftServer minecraftServer;
|
||||
private String ipcChannel;
|
||||
private boolean isAlive;
|
||||
private WorkerListenThread listenThread;
|
||||
|
||||
private LinkedList<byte[]> frags = new LinkedList<>();
|
||||
|
||||
public WorkerNetworkManager(String ipcChannel, MinecraftServer srv, WorkerListenThread th) {
|
||||
this.ipcChannel = ipcChannel;
|
||||
this.theNetHandler = new NetLoginHandler(srv, this);
|
||||
th.addPlayer(theNetHandler);
|
||||
this.minecraftServer = srv;
|
||||
this.isAlive = true;
|
||||
this.listenThread = th;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetHandler(NetHandler var1) {
|
||||
theNetHandler = var1;
|
||||
listenThread.addPlayer(theNetHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToSendQueue(Packet var1) {
|
||||
if(!isAlive) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream(var1.getPacketSize() + 1);
|
||||
Packet.writePacket(var1, new DataOutputStream(bao));
|
||||
IntegratedServer.sendPlayerPacket(ipcChannel, bao.toByteArray());
|
||||
}catch(IOException e) {
|
||||
System.err.println("Failed to serialize minecraft packet '" + var1.getPacketId() + "' for IPC channel 'NET|" + ipcChannel + "'");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void addToRecieveQueue(byte[] fragment) {
|
||||
//System.out.println("[Server][READ][QUEUE][" + ipcChannel + "]: " + fragment.length);
|
||||
if(!isAlive) {
|
||||
return;
|
||||
}
|
||||
frags.add(fragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wakeThreads() {
|
||||
// no
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processReadPackets() {
|
||||
while(frags.size() > 0) {
|
||||
byte[] pktBytes = frags.remove(0);
|
||||
try {
|
||||
ByteArrayInputStream bai = new ByteArrayInputStream(pktBytes);
|
||||
int pktId = bai.read();
|
||||
|
||||
if(pktId == -1) {
|
||||
System.err.println("Recieved invalid '-1' packet");
|
||||
continue;
|
||||
}
|
||||
|
||||
Packet pkt = Packet.getNewPacket(minecraftServer.getLogAgent(), pktId);
|
||||
|
||||
if(pkt == null) {
|
||||
System.err.println("Recieved invalid '" + pktId + "' packet");
|
||||
continue;
|
||||
}
|
||||
|
||||
pkt.readPacketData(new DataInputStream(bai));
|
||||
|
||||
//System.out.println("[Server][" + ipcChannel + "]: packet '" + pkt.getClass().getSimpleName() + "' recieved");
|
||||
|
||||
try {
|
||||
pkt.processPacket(theNetHandler);
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Could not process minecraft packet 0x" + Integer.toHexString(pkt.getPacketId()) + " class '" + pkt.getClass().getSimpleName() + "' on channel 'NET|" + ipcChannel + "'");
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
}catch(IOException ex) {
|
||||
System.err.println("Could not deserialize a " + pktBytes.length + " byte long minecraft packet of type '" + (pktBytes.length <= 0 ? -1 : (int)(pktBytes[0] & 0xFF)) + "' on channel 'NET|" + ipcChannel + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serverShutdown() {
|
||||
if(isAlive) {
|
||||
listenThread.closeChannel(ipcChannel);
|
||||
IntegratedServer.sendIPCPacket(new IPCPacket0CPlayerChannel(ipcChannel, false));
|
||||
}
|
||||
if(theNetHandler != null && (theNetHandler instanceof NetServerHandler)) {
|
||||
((NetServerHandler)theNetHandler).kickPlayerFromServer(null);
|
||||
}
|
||||
isAlive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int packetSize() { // why is this a thing
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void networkShutdown(String var1, Object... var2) {
|
||||
if(isAlive) {
|
||||
listenThread.closeChannel(ipcChannel);
|
||||
IntegratedServer.sendIPCPacket(new IPCPacket0CPlayerChannel(ipcChannel, false));
|
||||
}
|
||||
if(theNetHandler != null && (theNetHandler instanceof NetServerHandler)) {
|
||||
((NetServerHandler)theNetHandler).kickPlayerFromServer(null);
|
||||
}
|
||||
isAlive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnections() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerURI() {
|
||||
return "None? I dont fucking know what a URI is";
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof WorkerNetworkManager) && ((WorkerNetworkManager)o).ipcChannel.equals(ipcChannel);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return ipcChannel.hashCode();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
|
||||
public class WorldConverterEPK {
|
||||
|
||||
public static void importWorld(byte[] archiveContents, String newName) throws IOException {
|
||||
String folder = VFSSaveHandler.worldNameToFolderName(newName);
|
||||
VFile dir = new VFile("worlds", folder);
|
||||
EPKDecompiler dc = new EPKDecompiler(archiveContents);
|
||||
EPKDecompiler.FileEntry f = null;
|
||||
int lastProgUpdate = 0;
|
||||
int prog = 0;
|
||||
boolean hasReadType = dc.isOld();
|
||||
while((f = dc.readFile()) != null) {
|
||||
byte[] b = f.data;
|
||||
if(!hasReadType) {
|
||||
if(f.type.equals("HEAD") && f.name.equals("file-type") && EPKDecompiler.readASCII(f.data).equals("epk/world152")) {
|
||||
hasReadType = true;
|
||||
continue;
|
||||
}else {
|
||||
throw new IOException("file does not contain a singleplayer 1.5.2 world!");
|
||||
}
|
||||
}
|
||||
if(f.type.equals("FILE")) {
|
||||
if(f.name.equals("level.dat")) {
|
||||
NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(b);
|
||||
worldDatNBT.getCompoundTag("Data").setString("LevelName", newName);
|
||||
worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis());
|
||||
b = CompressedStreamTools.compress(worldDatNBT);
|
||||
}
|
||||
VFile ff = new VFile(dir, f.name);
|
||||
ff.setAllBytes(b);
|
||||
prog += b.length;
|
||||
if(prog - lastProgUpdate > 10000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.importing.0", prog);
|
||||
}
|
||||
}
|
||||
}
|
||||
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
if(worldsTxt == null || worldsTxt.length <= 0) {
|
||||
worldsTxt = new String[] { folder };
|
||||
}else {
|
||||
String[] tmp = worldsTxt;
|
||||
worldsTxt = new String[worldsTxt.length + 1];
|
||||
System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length);
|
||||
worldsTxt[worldsTxt.length - 1] = folder;
|
||||
}
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", worldsTxt));
|
||||
}
|
||||
|
||||
public static byte[] exportWorld(String worldName) {
|
||||
String realWorldName = worldName;
|
||||
String worldOwner = "UNKNOWN";
|
||||
int j = realWorldName.lastIndexOf(new String(new char[] { (char)253, (char)233, (char)233 }));
|
||||
if(j != -1) {
|
||||
worldOwner = realWorldName.substring(j + 3);
|
||||
realWorldName = realWorldName.substring(0, j);
|
||||
}
|
||||
final int[] bytesWritten = new int[1];
|
||||
final int[] lastUpdate = new int[1];
|
||||
String pfx = "worlds/" + realWorldName + "/";
|
||||
EPK2Compiler c = new EPK2Compiler(realWorldName, worldOwner, "epk/world152");
|
||||
SYS.VFS.iterateFiles(pfx, false, (i) -> {
|
||||
byte[] b = i.getAllBytes();
|
||||
c.append(i.path.substring(pfx.length()), b);
|
||||
bytesWritten[0] += b.length;
|
||||
if (bytesWritten[0] - lastUpdate[0] > 10000) {
|
||||
lastUpdate[0] = bytesWritten[0];
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.1", bytesWritten[0]);
|
||||
}
|
||||
});
|
||||
return c.complete();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.RegionFile;
|
||||
|
||||
public class WorldConverterMCA {
|
||||
|
||||
public static void importWorld(byte[] archiveContents, String newName) throws IOException {
|
||||
String folderName = newName.replaceAll("[\\./\"]", "_");
|
||||
VFile worldDir = new VFile("worlds", folderName);
|
||||
while((new VFile(worldDir, "level.dat")).exists() || (new VFile(worldDir, "level.dat_old")).exists()) {
|
||||
folderName += "_";
|
||||
worldDir = new VFile("worlds", folderName);
|
||||
}
|
||||
List<char[]> fileNames = new ArrayList<>();
|
||||
try(ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(archiveContents))) {
|
||||
ZipEntry folderNameFile = null;
|
||||
while((folderNameFile = zis.getNextEntry()) != null) {
|
||||
if (folderNameFile.getName().contains("__MACOSX/")) continue;
|
||||
if (folderNameFile.isDirectory()) continue;
|
||||
String lowerName = folderNameFile.getName().toLowerCase();
|
||||
if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr"))) continue;
|
||||
fileNames.add(folderNameFile.getName().toCharArray());
|
||||
}
|
||||
}
|
||||
final int[] i = new int[] { 0 };
|
||||
while(fileNames.get(0).length > i[0] && fileNames.stream().allMatch(w -> w[i[0]] == fileNames.get(0)[i[0]])) i[0]++;
|
||||
int folderPrefixOffset = i[0];
|
||||
try(ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(archiveContents))) {
|
||||
ZipEntry f = null;
|
||||
int lastProgUpdate = 0;
|
||||
int prog = 0;
|
||||
while ((f = zis.getNextEntry()) != null) {
|
||||
if (f.getName().contains("__MACOSX/")) continue;
|
||||
if (f.isDirectory()) continue;
|
||||
String lowerName = f.getName().toLowerCase();
|
||||
if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr") || lowerName.endsWith(".bmp"))) continue;
|
||||
byte[] b;
|
||||
int sz = (int)f.getSize();
|
||||
if(sz >= 0) {
|
||||
b = new byte[sz];
|
||||
int j = 0, k;
|
||||
while(j < b.length && (k = zis.read(b, j, b.length - j)) != -1) {
|
||||
j += k;
|
||||
}
|
||||
}else {
|
||||
b = inputStreamToBytesNoClose(zis);
|
||||
}
|
||||
String fileName = f.getName().substring(folderPrefixOffset);
|
||||
if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) {
|
||||
NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new ByteArrayInputStream(b));
|
||||
worldDatNBT.getCompoundTag("Data").setString("LevelName", newName);
|
||||
worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis());
|
||||
ByteArrayOutputStream bo = new ByteArrayOutputStream();
|
||||
CompressedStreamTools.writeCompressed(worldDatNBT, bo);
|
||||
b = bo.toByteArray();
|
||||
VFile ff = new VFile(worldDir, fileName);
|
||||
ff.setAllBytes(b);
|
||||
prog += b.length;
|
||||
} else if ((fileName.endsWith(".mcr") || fileName.endsWith(".mca")) && (fileName.startsWith("region/") || fileName.startsWith("DIM1/region/") || fileName.startsWith("DIM-1/region/"))) {
|
||||
VFile chunkFolder = new VFile(worldDir, fileName.startsWith("DIM1") ? "level1" : (fileName.startsWith("DIM-1") ? "level-1" : "level0"));
|
||||
RegionFile mca = new RegionFile(new RandomAccessMemoryFile(b, b.length));
|
||||
for(int j = 0; j < 32; ++j) {
|
||||
for(int k = 0; k < 32; ++k) {
|
||||
if(mca.isChunkSaved(j, k)) {
|
||||
NBTTagCompound chunkNBT;
|
||||
NBTTagCompound chunkLevel;
|
||||
try {
|
||||
chunkNBT = CompressedStreamTools.read(mca.getChunkDataInputStream(j, k));
|
||||
if(!chunkNBT.hasKey("Level")) {
|
||||
throw new IOException("Chunk is missing level data!");
|
||||
}
|
||||
chunkLevel = chunkNBT.getCompoundTag("Level");
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Could not read chunk: " + j + ", " + k);
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
int chunkX = chunkLevel.getInteger("xPos");
|
||||
int chunkZ = chunkLevel.getInteger("zPos");
|
||||
VFile chunkOut = new VFile(chunkFolder, VFSChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat");
|
||||
if(chunkOut.exists()) {
|
||||
System.err.println("Chunk already exists: " + chunkOut.getPath());
|
||||
continue;
|
||||
}
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
CompressedStreamTools.writeCompressed(chunkNBT, bao);
|
||||
b = bao.toByteArray();
|
||||
chunkOut.setAllBytes(b);
|
||||
prog += b.length;
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.importing.1", prog);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (fileName.startsWith("data/") || fileName.startsWith("players/")) {
|
||||
VFile ff = new VFile(worldDir, fileName);
|
||||
ff.setAllBytes(b);
|
||||
prog += b.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
if(worldsTxt == null || worldsTxt.length <= 0 || (worldsTxt.length == 1 && worldsTxt[0].trim().length() <= 0)) {
|
||||
worldsTxt = new String[] { folderName };
|
||||
}else {
|
||||
String[] tmp = worldsTxt;
|
||||
worldsTxt = new String[worldsTxt.length + 1];
|
||||
System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length);
|
||||
worldsTxt[worldsTxt.length - 1] = folderName;
|
||||
}
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", worldsTxt));
|
||||
}
|
||||
|
||||
public static byte[] exportWorld(String folderName) throws IOException {
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
VFile worldFolder;
|
||||
try(ZipOutputStream zos = new ZipOutputStream(bao)) {
|
||||
zos.setComment("contains backup of world '" + folderName + "'");
|
||||
worldFolder = new VFile("worlds", folderName);
|
||||
VFile vf = new VFile(worldFolder, "level.dat");
|
||||
byte[] b;
|
||||
int lastProgUpdate = 0;
|
||||
int prog = 0;
|
||||
boolean safe = false;
|
||||
if(vf.exists()) {
|
||||
zos.putNextEntry(new ZipEntry(folderName + "/level.dat"));
|
||||
b = vf.getAllBytes();
|
||||
zos.write(b);
|
||||
prog += b.length;
|
||||
safe = true;
|
||||
}
|
||||
vf = new VFile(worldFolder, "level.dat_old");
|
||||
if(vf.exists()) {
|
||||
zos.putNextEntry(new ZipEntry(folderName + "/level.dat_old"));
|
||||
b = vf.getAllBytes();
|
||||
zos.write(b);
|
||||
prog += b.length;
|
||||
safe = true;
|
||||
}
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog);
|
||||
}
|
||||
String[] srcFolderNames = new String[] { "level0", "level-1", "level1" };
|
||||
String[] dstFolderNames = new String[] { "/region/", "/DIM-1/region/", "/DIM1/region/" };
|
||||
List<VFile> fileList;
|
||||
for(int i = 0; i < 3; ++i) {
|
||||
vf = new VFile(worldFolder, srcFolderNames[i]);
|
||||
fileList = SYS.VFS.listVFiles(vf.getPath());
|
||||
String regionFolder = folderName + dstFolderNames[i];
|
||||
Map<String,RegionFile> regionFiles = new HashMap<>();
|
||||
for(int k = 0, l = fileList.size(); k < l; ++k) {
|
||||
VFile chunkFile = fileList.get(k);
|
||||
NBTTagCompound chunkNBT;
|
||||
NBTTagCompound chunkLevel;
|
||||
try {
|
||||
b = chunkFile.getAllBytes();
|
||||
chunkNBT = CompressedStreamTools.readCompressed(new ByteArrayInputStream(b));
|
||||
if(!chunkNBT.hasKey("Level")) {
|
||||
throw new IOException("Chunk is missing level data!");
|
||||
}
|
||||
chunkLevel = chunkNBT.getCompoundTag("Level");
|
||||
}catch(IOException t) {
|
||||
System.err.println("Could not read chunk: " + chunkFile.getPath());
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
int chunkX = chunkLevel.getInteger("xPos");
|
||||
int chunkZ = chunkLevel.getInteger("zPos");
|
||||
String regionFileName = "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca";
|
||||
RegionFile rf = regionFiles.get(regionFileName);
|
||||
if(rf == null) {
|
||||
rf = new RegionFile(new RandomAccessMemoryFile(new byte[65536], 0));
|
||||
regionFiles.put(regionFileName, rf);
|
||||
}
|
||||
try(DataOutputStream dos = rf.getChunkDataOutputStream(chunkX & 31, chunkZ & 31)) {
|
||||
CompressedStreamTools.write(chunkNBT, dos);
|
||||
}catch(IOException t) {
|
||||
System.err.println("Could not write chunk to " + regionFileName + ": " + chunkFile.getPath());
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
prog += b.length;
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog);
|
||||
}
|
||||
}
|
||||
if(regionFiles.isEmpty()) {
|
||||
System.err.println("No region files were generated");
|
||||
continue;
|
||||
}
|
||||
for(Entry<String,RegionFile> etr : regionFiles.entrySet()) {
|
||||
String regionPath = regionFolder + etr.getKey();
|
||||
zos.putNextEntry(new ZipEntry(regionPath));
|
||||
zos.write(etr.getValue().getFile().getByteArray());
|
||||
}
|
||||
}
|
||||
fileList = SYS.VFS.listVFiles((new VFile(worldFolder, "data")).getPath());
|
||||
for(int k = 0, l = fileList.size(); k < l; ++k) {
|
||||
VFile dataFile = fileList.get(k);
|
||||
zos.putNextEntry(new ZipEntry(folderName + "/data/" + dataFile.getName()));
|
||||
b = dataFile.getAllBytes();
|
||||
zos.write(b);
|
||||
prog += b.length;
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog);
|
||||
}
|
||||
}
|
||||
fileList = SYS.VFS.listVFiles((new VFile(worldFolder, "players")).getPath());
|
||||
for(int k = 0, l = fileList.size(); k < l; ++k) {
|
||||
VFile dataFile = fileList.get(k);
|
||||
zos.putNextEntry(new ZipEntry(folderName + "/players/" + dataFile.getName()));
|
||||
b = dataFile.getAllBytes();
|
||||
zos.write(b);
|
||||
prog += b.length;
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bao.toByteArray();
|
||||
}
|
||||
|
||||
private static byte[] inputStreamToBytesNoClose(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
|
||||
byte[] buf = new byte[1024];
|
||||
int i;
|
||||
while ((i = is.read(buf)) != -1) {
|
||||
os.write(buf, 0, i);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,975 @@
|
|||
package net.minecraft.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.IntegratedServer;
|
||||
import net.lax1dude.eaglercraft.sp.SYS;
|
||||
import net.lax1dude.eaglercraft.sp.SysUtil;
|
||||
import net.lax1dude.eaglercraft.sp.VFSSaveHandler;
|
||||
import net.lax1dude.eaglercraft.sp.VFile;
|
||||
import net.lax1dude.eaglercraft.sp.WorkerListenThread;
|
||||
import net.lax1dude.eaglercraft.sp.ipc.IPCPacket0DProgressUpdate;
|
||||
import net.lax1dude.eaglercraft.sp.ipc.IPCPacket14StringList;
|
||||
import net.minecraft.src.AxisAlignedBB;
|
||||
import net.minecraft.src.ChunkCoordinates;
|
||||
import net.minecraft.src.CommandBase;
|
||||
import net.minecraft.src.DispenserBehaviors;
|
||||
import net.minecraft.src.EntityPlayer;
|
||||
import net.minecraft.src.EntityPlayerMP;
|
||||
import net.minecraft.src.EnumGameType;
|
||||
import net.minecraft.src.ICommandManager;
|
||||
import net.minecraft.src.ICommandSender;
|
||||
import net.minecraft.src.ILogAgent;
|
||||
import net.minecraft.src.IProgressUpdate;
|
||||
import net.minecraft.src.ISaveHandler;
|
||||
import net.minecraft.src.IUpdatePlayerListBox;
|
||||
import net.minecraft.src.MinecraftException;
|
||||
import net.minecraft.src.Packet;
|
||||
import net.minecraft.src.Packet4UpdateTime;
|
||||
import net.minecraft.src.ServerCommandManager;
|
||||
import net.minecraft.src.ServerConfigurationManager;
|
||||
import net.minecraft.src.StringTranslate;
|
||||
import net.minecraft.src.StringUtils;
|
||||
import net.minecraft.src.World;
|
||||
import net.minecraft.src.WorldInfo;
|
||||
import net.minecraft.src.WorldManager;
|
||||
import net.minecraft.src.WorldServer;
|
||||
import net.minecraft.src.WorldServerMulti;
|
||||
import net.minecraft.src.WorldSettings;
|
||||
|
||||
public abstract class MinecraftServer implements ICommandSender, Runnable {
|
||||
/** Instance of Minecraft Server. */
|
||||
protected static MinecraftServer mcServer = null;
|
||||
|
||||
/** List of names of players who are online. */
|
||||
protected final List playersOnline = new ArrayList();
|
||||
protected final ICommandManager commandManager;
|
||||
|
||||
/** The server world instances. */
|
||||
public WorldServer[] worldServers;
|
||||
|
||||
/** The ServerConfigurationManager instance. */
|
||||
protected ServerConfigurationManager serverConfigManager;
|
||||
|
||||
/**
|
||||
* Indicates whether the server is running or not. Set to false to initiate a
|
||||
* shutdown.
|
||||
*/
|
||||
protected boolean serverRunning = true;
|
||||
|
||||
/** Indicates to other classes that the server is safely stopped. */
|
||||
protected boolean serverStopped = false;
|
||||
|
||||
/** Incremented every tick. */
|
||||
protected int tickCounter = 0;
|
||||
|
||||
/**
|
||||
* The task the server is currently working on(and will output on
|
||||
* outputPercentRemaining).
|
||||
*/
|
||||
protected String currentTask;
|
||||
|
||||
/** The percentage of the current task finished so far. */
|
||||
protected int percentDone;
|
||||
|
||||
/** True if the server has animals turned on. */
|
||||
protected boolean canSpawnAnimals;
|
||||
protected boolean canSpawnNPCs;
|
||||
|
||||
/** Indicates whether PvP is active on the server or not. */
|
||||
protected boolean pvpEnabled;
|
||||
|
||||
/** Determines if flight is allowed or not. */
|
||||
protected boolean allowFlight;
|
||||
|
||||
/** The server MOTD string. */
|
||||
protected String motd;
|
||||
|
||||
/** Maximum build height. */
|
||||
protected int buildLimit;
|
||||
protected long lastSentPacketID;
|
||||
protected long lastSentPacketSize;
|
||||
protected long lastReceivedID;
|
||||
protected long lastReceivedSize;
|
||||
public final long[] sentPacketCountArray = new long[100];
|
||||
public final long[] sentPacketSizeArray = new long[100];
|
||||
public final long[] receivedPacketCountArray = new long[100];
|
||||
public final long[] receivedPacketSizeArray = new long[100];
|
||||
public final long[] tickTimeArray = new long[100];
|
||||
|
||||
/** Stats are [dimension][tick%100] system.nanoTime is stored. */
|
||||
public long[][] timeOfLastDimensionTick;
|
||||
|
||||
/** Username of the server owner (for integrated servers) */
|
||||
protected String serverOwner;
|
||||
protected String folderName;
|
||||
|
||||
/**
|
||||
* If true, there is no need to save chunks or stop the server, because that is
|
||||
* already being done.
|
||||
*/
|
||||
protected boolean worldIsBeingDeleted;
|
||||
protected String texturePack = "";
|
||||
protected boolean serverIsRunning = false;
|
||||
|
||||
/**
|
||||
* Set when warned for "Can't keep up", which triggers again after 15 seconds.
|
||||
*/
|
||||
protected long timeOfLastWarning;
|
||||
protected String userMessage;
|
||||
protected boolean field_104057_T = false;
|
||||
|
||||
public MinecraftServer(String folder) {
|
||||
mcServer = this;
|
||||
this.folderName = folder;
|
||||
this.commandManager = new ServerCommandManager();
|
||||
this.registerDispenseBehaviors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all dispense behaviors.
|
||||
*/
|
||||
private void registerDispenseBehaviors() {
|
||||
DispenserBehaviors.registerDispenserBehaviours();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the server and starts it.
|
||||
*/
|
||||
protected abstract boolean startServer() throws IOException;
|
||||
|
||||
/**
|
||||
* Typically "menu.convertingLevel", "menu.loadingLevel" or others.
|
||||
*/
|
||||
protected void setUserMessage(String par1Str) {
|
||||
IntegratedServer.sendIPCPacket(new IPCPacket0DProgressUpdate(par1Str, 0.0f));
|
||||
this.logInfo(par1Str);
|
||||
this.userMessage = par1Str;
|
||||
}
|
||||
|
||||
protected void setUserMessage(String par1Str, float prog) {
|
||||
IntegratedServer.sendIPCPacket(new IPCPacket0DProgressUpdate(par1Str, prog));
|
||||
this.logInfo(par1Str + ": " + (prog > 1.0f ? "" + (int)prog : "" + (int)(prog * 100.0f) + "%"));
|
||||
this.userMessage = par1Str;
|
||||
}
|
||||
|
||||
protected void loadAllWorlds(String par1Str, long par3, WorldSettings par5WorldType) {
|
||||
this.setUserMessage("menu.loadingLevel");
|
||||
this.worldServers = new WorldServer[3];
|
||||
this.timeOfLastDimensionTick = new long[this.worldServers.length][100];
|
||||
ISaveHandler var7 = new VFSSaveHandler(new VFile("worlds", par1Str));
|
||||
WorldInfo var9 = var7.loadWorldInfo();
|
||||
WorldSettings var8;
|
||||
|
||||
if (var9 == null) {
|
||||
if(par5WorldType == null) {
|
||||
throw new IllegalArgumentException("World '" + par1Str + "' does not exist and WorldSettings is null");
|
||||
}
|
||||
var8 = par5WorldType;
|
||||
} else {
|
||||
var8 = new WorldSettings(var9);
|
||||
}
|
||||
|
||||
for (int var10 = 0; var10 < this.worldServers.length; ++var10) {
|
||||
byte var11 = 0;
|
||||
|
||||
if (var10 == 1) {
|
||||
var11 = -1;
|
||||
}
|
||||
|
||||
if (var10 == 2) {
|
||||
var11 = 1;
|
||||
}
|
||||
|
||||
if (var10 == 0) {
|
||||
this.worldServers[var10] = new WorldServer(this, var7, par1Str, var11, var8, this.getLogAgent());
|
||||
} else {
|
||||
this.worldServers[var10] = new WorldServerMulti(this, var7, par1Str, var11, var8, this.worldServers[0], this.getLogAgent());
|
||||
}
|
||||
|
||||
this.worldServers[var10].addWorldAccess(new WorldManager(this, this.worldServers[var10]));
|
||||
this.worldServers[var10].getWorldInfo().setGameType(this.getGameType());
|
||||
|
||||
this.serverConfigManager.setPlayerManager(this.worldServers);
|
||||
}
|
||||
|
||||
this.setDifficultyForAllWorlds(this.getDifficulty());
|
||||
this.setGameType(var8.getGameType());
|
||||
this.initialWorldChunkLoad();
|
||||
}
|
||||
|
||||
protected void initialWorldChunkLoad() {
|
||||
int var5 = 0;
|
||||
//this.setUserMessage("menu.generatingTerrain");
|
||||
byte var6 = 0;
|
||||
this.setUserMessage("Preparing start region for level " + var6);
|
||||
|
||||
// Removed 'spawn chunks' for performance, they are unnecessary
|
||||
|
||||
/*
|
||||
WorldServer var7 = this.worldServers[var6];
|
||||
ChunkCoordinates var8 = var7.getSpawnPoint();
|
||||
long var9 = System.currentTimeMillis();
|
||||
|
||||
int prepareRadius = 64;
|
||||
|
||||
for (int var11 = -prepareRadius; var11 <= prepareRadius && this.isServerRunning(); var11 += 16) {
|
||||
for (int var12 = -prepareRadius; var12 <= prepareRadius && this.isServerRunning(); var12 += 16) {
|
||||
long var13 = System.currentTimeMillis();
|
||||
|
||||
if (var13 - var9 > 1000L) {
|
||||
setUserMessage("Preparing spawn area", Math.min(var5 / 64.0f, 0.99f));
|
||||
var9 = var13;
|
||||
}
|
||||
|
||||
++var5;
|
||||
var7.theChunkProviderServer.loadChunk(var8.posX + var11 >> 4, var8.posZ + var12 >> 4);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
this.clearCurrentTask();
|
||||
}
|
||||
|
||||
public abstract boolean canStructuresSpawn();
|
||||
|
||||
public abstract EnumGameType getGameType();
|
||||
|
||||
/**
|
||||
* Defaults to "1" (Easy) for the dedicated server, defaults to "2" (Normal) on
|
||||
* the client.
|
||||
*/
|
||||
public abstract int getDifficulty();
|
||||
|
||||
/**
|
||||
* Defaults to false.
|
||||
*/
|
||||
public abstract boolean isHardcore();
|
||||
|
||||
/**
|
||||
* Used to display a percent remaining given text and the percentage.
|
||||
*/
|
||||
protected void outputPercentRemaining(String par1Str, int par2) {
|
||||
this.currentTask = par1Str;
|
||||
this.percentDone = par2;
|
||||
setUserMessage(par1Str, (par2 / 100.0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current task to null and set its percentage to 0.
|
||||
*/
|
||||
protected void clearCurrentTask() {
|
||||
this.currentTask = null;
|
||||
this.percentDone = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* par1 indicates if a log message should be output.
|
||||
*/
|
||||
public void saveAllWorlds(boolean par1) {
|
||||
if (!this.worldIsBeingDeleted) {
|
||||
WorldServer[] var2 = this.worldServers;
|
||||
int var3 = var2.length;
|
||||
|
||||
for (int var4 = 0; var4 < var3; ++var4) {
|
||||
WorldServer var5 = var2[var4];
|
||||
|
||||
if (var5 != null) {
|
||||
setUserMessage("Saving chunks for level \'" + var5.getWorldInfo().getWorldName() + "\'/" + var5.provider.getDimensionName());
|
||||
|
||||
try {
|
||||
var5.saveAllChunks(true, (IProgressUpdate) null);
|
||||
} catch (MinecraftException var7) {
|
||||
this.getLogAgent().logWarning(var7.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all necessary data as preparation for stopping the server.
|
||||
*/
|
||||
public void stopServer() {
|
||||
if (!this.worldIsBeingDeleted) {
|
||||
setUserMessage("Stopping server");
|
||||
|
||||
if (this.getNetworkThread() != null) {
|
||||
this.getNetworkThread().stopListening();
|
||||
}
|
||||
|
||||
if (this.serverConfigManager != null) {
|
||||
this.getLogAgent().logInfo("Saving players");
|
||||
this.serverConfigManager.saveAllPlayerData();
|
||||
this.serverConfigManager.removeAllPlayers();
|
||||
}
|
||||
|
||||
setUserMessage("Saving worlds");
|
||||
this.saveAllWorlds(false);
|
||||
|
||||
for (int var1 = 0; var1 < this.worldServers.length; ++var1) {
|
||||
WorldServer var2 = this.worldServers[var1];
|
||||
var2.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "getHostname" is already taken, but both return the hostname.
|
||||
*/
|
||||
public String getServerHostname() {
|
||||
return "127.1.1.1";
|
||||
}
|
||||
|
||||
public void setHostname(String par1Str) {
|
||||
throw new IllegalArgumentException("variable removed");
|
||||
}
|
||||
|
||||
public boolean isServerRunning() {
|
||||
return this.serverRunning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the serverRunning variable to false, in order to get the server to shut
|
||||
* down.
|
||||
*/
|
||||
public void initiateShutdown() {
|
||||
this.serverRunning = false;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
if (this.startServer()) {
|
||||
long var1 = SysUtil.steadyTimeMillis();
|
||||
|
||||
for (long var50 = 0L; this.serverRunning; this.serverIsRunning = true) {
|
||||
long var5 = SysUtil.steadyTimeMillis();
|
||||
long var7 = var5 - var1;
|
||||
|
||||
if (var7 > 2000L && var1 - this.timeOfLastWarning >= 15000L) {
|
||||
this.getLogAgent().logWarning(
|
||||
"Can\'t keep up! Did the system time change, or is the server overloaded?");
|
||||
var7 = 2000L;
|
||||
this.timeOfLastWarning = var1;
|
||||
}
|
||||
|
||||
if (var7 < 0L) {
|
||||
this.getLogAgent().logWarning("Time ran backwards! Did the system time change?");
|
||||
var7 = 0L;
|
||||
}
|
||||
|
||||
var50 += var7;
|
||||
var1 = var5;
|
||||
|
||||
if (this.worldServers[0].areAllPlayersAsleep()) {
|
||||
this.tick();
|
||||
var50 = 0L;
|
||||
} else {
|
||||
while (var50 > 50L) {
|
||||
var50 -= 50L;
|
||||
this.tick();
|
||||
}
|
||||
}
|
||||
|
||||
SysUtil.sleep(1);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Server did not init correctly");
|
||||
}
|
||||
} catch (Throwable var48) {
|
||||
this.getLogAgent().logSevereException(
|
||||
"Encountered an unexpected exception " + var48.getClass().getSimpleName(), var48);
|
||||
var48.printStackTrace();
|
||||
IntegratedServer.throwExceptionToClient("Encountered an unexpected exception", var48);
|
||||
} finally {
|
||||
try {
|
||||
this.stopServer();
|
||||
this.serverStopped = true;
|
||||
} catch (Throwable var46) {
|
||||
var46.printStackTrace();
|
||||
} finally {
|
||||
this.systemExitNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected VFile getDataDirectory() {
|
||||
return new VFile(".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly calls System.exit(0), instantly killing the program.
|
||||
*/
|
||||
protected void systemExitNow() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function called by run() every loop.
|
||||
*/
|
||||
protected void tick() {
|
||||
long var1 = System.nanoTime();
|
||||
AxisAlignedBB.getAABBPool().cleanPool();
|
||||
++this.tickCounter;
|
||||
|
||||
this.updateTimeLightAndEntities();
|
||||
|
||||
if (this.tickCounter % 900 == 0) {
|
||||
this.serverConfigManager.saveAllPlayerData();
|
||||
this.saveAllWorlds(true);
|
||||
}
|
||||
|
||||
this.tickTimeArray[this.tickCounter % 100] = System.nanoTime() - var1;
|
||||
this.sentPacketCountArray[this.tickCounter % 100] = Packet.sentID - this.lastSentPacketID;
|
||||
this.lastSentPacketID = Packet.sentID;
|
||||
this.sentPacketSizeArray[this.tickCounter % 100] = Packet.sentSize - this.lastSentPacketSize;
|
||||
this.lastSentPacketSize = Packet.sentSize;
|
||||
this.receivedPacketCountArray[this.tickCounter % 100] = Packet.receivedID - this.lastReceivedID;
|
||||
this.lastReceivedID = Packet.receivedID;
|
||||
this.receivedPacketSizeArray[this.tickCounter % 100] = Packet.receivedSize - this.lastReceivedSize;
|
||||
this.lastReceivedSize = Packet.receivedSize;
|
||||
}
|
||||
|
||||
public List<String> getTPSAndChunkBuffer(int tpsCounter) {
|
||||
ArrayList<String> strs = new ArrayList();
|
||||
strs.add("Ticks/Second: " + tpsCounter + "/20");
|
||||
|
||||
int c = 0;
|
||||
int oc = 0;
|
||||
int e = 0;
|
||||
int te = 0;
|
||||
int r = 0;
|
||||
int w = 0;
|
||||
int g = 0;
|
||||
int tu = 0;
|
||||
int lu = 0;
|
||||
for(int i = 0; i < worldServers.length; ++i) {
|
||||
c += worldServers[i].getChunkProvider().getLoadedChunkCount();
|
||||
e += worldServers[i].loadedEntityList.size();
|
||||
te += worldServers[i].loadedTileEntityList.size();
|
||||
r += worldServers[i].getR();
|
||||
w += worldServers[i].getW();
|
||||
g += worldServers[i].getG();
|
||||
lu += worldServers[i].getLU();
|
||||
tu += worldServers[i].getTU();
|
||||
}
|
||||
for(EntityPlayerMP p : (List<EntityPlayerMP>)this.playersOnline) {
|
||||
oc += p.loadedChunks.size();
|
||||
}
|
||||
|
||||
strs.add("Chunks: " + c + "/" + (c + oc));
|
||||
strs.add("Entities: " + e + "+" + te);
|
||||
strs.add("R: " + r + ", G: " + g + ", W: " + w);
|
||||
strs.add("TU: " + tu + " LU: " + lu);
|
||||
int pp = this.playersOnline.size();
|
||||
if(pp > 1) {
|
||||
strs.add("Players: " + pp);
|
||||
}
|
||||
return strs;
|
||||
}
|
||||
|
||||
public void updateTimeLightAndEntities() {
|
||||
int var1;
|
||||
|
||||
for (var1 = 0; var1 < this.worldServers.length; ++var1) {
|
||||
long var2 = System.nanoTime();
|
||||
|
||||
if (var1 == 0 || this.getAllowNether()) {
|
||||
WorldServer var4 = this.worldServers[var1];
|
||||
var4.getWorldVec3Pool().clear();
|
||||
|
||||
if (this.tickCounter % 20 == 0) {
|
||||
this.serverConfigManager.sendPacketToAllPlayersInDimension(
|
||||
new Packet4UpdateTime(var4.getTotalWorldTime(), var4.getWorldTime(), this.worldServers[var1].getGameRules().getGameRuleBooleanValue("doDaylightCycle")),
|
||||
var4.provider.dimensionId);
|
||||
}
|
||||
|
||||
var4.tick();
|
||||
var4.updateEntities();
|
||||
|
||||
var4.getEntityTracker().updateTrackedEntities();
|
||||
}
|
||||
|
||||
this.timeOfLastDimensionTick[var1][this.tickCounter % 100] = System.nanoTime() - var2;
|
||||
}
|
||||
|
||||
this.getNetworkThread().handleNetworkListenThread();
|
||||
this.serverConfigManager.sendPlayerInfoToAllPlayers();
|
||||
|
||||
for (var1 = 0; var1 < this.playersOnline.size(); ++var1) {
|
||||
((IUpdatePlayerListBox) this.playersOnline.get(var1)).update();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getAllowNether() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void func_82010_a(IUpdatePlayerListBox par1IUpdatePlayerListBox) {
|
||||
this.playersOnline.add(par1IUpdatePlayerListBox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a File object from the specified string.
|
||||
*/
|
||||
public VFile getFile(String par1Str) {
|
||||
return new VFile(folderName, par1Str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the message with a level of INFO.
|
||||
*/
|
||||
public void logInfo(String par1Str) {
|
||||
this.getLogAgent().logInfo(par1Str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the message with a level of WARN.
|
||||
*/
|
||||
public void logWarning(String par1Str) {
|
||||
this.getLogAgent().logWarning(par1Str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the worldServer by the given dimension.
|
||||
*/
|
||||
public WorldServer worldServerForDimension(int par1) {
|
||||
return par1 == -1 ? this.worldServers[1] : (par1 == 1 ? this.worldServers[2] : this.worldServers[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server's hostname.
|
||||
*/
|
||||
public String getHostname() {
|
||||
return this.getServerHostname();
|
||||
}
|
||||
|
||||
/**
|
||||
* Never used, but "getServerPort" is already taken.
|
||||
*/
|
||||
public int getPort() {
|
||||
return this.getServerPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server message of the day
|
||||
*/
|
||||
public String getMotd() {
|
||||
return this.motd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server's Minecraft version as string.
|
||||
*/
|
||||
public String getMinecraftVersion() {
|
||||
return "1.5.2";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of players currently on the server.
|
||||
*/
|
||||
public int getCurrentPlayerCount() {
|
||||
return this.serverConfigManager.getCurrentPlayerCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of players allowed on the server.
|
||||
*/
|
||||
public int getMaxPlayers() {
|
||||
return this.serverConfigManager.getMaxPlayers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the usernames of all the connected players.
|
||||
*/
|
||||
public String[] getAllUsernames() {
|
||||
return this.serverConfigManager.getAllUsernames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by RCon's Query in the form of "MajorServerMod 1.2.3: MyPlugin 1.3;
|
||||
* AnotherPlugin 2.1; AndSoForth 1.0".
|
||||
*/
|
||||
public String getPlugins() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a command received by an RCon instance
|
||||
*/
|
||||
public String handleRConCommand(String par1Str) {
|
||||
return "fuck off";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if debugging is enabled, false otherwise.
|
||||
*/
|
||||
public boolean isDebuggingEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the error message with a level of SEVERE.
|
||||
*/
|
||||
public void logSevere(String par1Str) {
|
||||
this.getLogAgent().logSevere(par1Str);
|
||||
}
|
||||
|
||||
/**
|
||||
* If isDebuggingEnabled(), logs the message with a level of INFO.
|
||||
*/
|
||||
public void logDebug(String par1Str) {
|
||||
if (this.isDebuggingEnabled()) {
|
||||
this.getLogAgent().logInfo(par1Str);
|
||||
}
|
||||
}
|
||||
|
||||
public String getServerModName() {
|
||||
return "eaglercraft";
|
||||
}
|
||||
|
||||
/**
|
||||
* If par2Str begins with /, then it searches for commands, otherwise it returns
|
||||
* players.
|
||||
*/
|
||||
public List getPossibleCompletions(ICommandSender par1ICommandSender, String par2Str) {
|
||||
ArrayList var3 = new ArrayList();
|
||||
|
||||
if (par2Str.startsWith("/")) {
|
||||
par2Str = par2Str.substring(1);
|
||||
boolean var10 = !par2Str.contains(" ");
|
||||
List var11 = this.commandManager.getPossibleCommands(par1ICommandSender, par2Str);
|
||||
|
||||
if (var11 != null) {
|
||||
Iterator var12 = var11.iterator();
|
||||
|
||||
while (var12.hasNext()) {
|
||||
String var13 = (String) var12.next();
|
||||
|
||||
if (var10) {
|
||||
var3.add("/" + var13);
|
||||
} else {
|
||||
var3.add(var13);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return var3;
|
||||
} else {
|
||||
String[] var4 = par2Str.split(" ", -1);
|
||||
String var5 = var4[var4.length - 1];
|
||||
String[] var6 = this.serverConfigManager.getAllUsernames();
|
||||
int var7 = var6.length;
|
||||
|
||||
for (int var8 = 0; var8 < var7; ++var8) {
|
||||
String var9 = var6[var8];
|
||||
|
||||
if (CommandBase.doesStringStartWith(var5, var9)) {
|
||||
var3.add(var9);
|
||||
}
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets mcServer.
|
||||
*/
|
||||
public static MinecraftServer getServer() {
|
||||
return mcServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this command sender (usually username, but possibly "Rcon")
|
||||
*/
|
||||
public String getCommandSenderName() {
|
||||
return "Host";
|
||||
}
|
||||
|
||||
public void sendChatToPlayer(String par1Str) {
|
||||
this.getLogAgent().logInfo(StringUtils.stripControlCodes(par1Str));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the command sender is allowed to use the given command.
|
||||
*/
|
||||
public boolean canCommandSenderUseCommand(int par1, String par2Str) {
|
||||
return par2Str.equals(this.getServerOwner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates and formats the given string key with the given arguments.
|
||||
*/
|
||||
public String translateString(String par1Str, Object... par2ArrayOfObj) {
|
||||
return StringTranslate.getInstance().translateKeyFormat(par1Str, par2ArrayOfObj);
|
||||
}
|
||||
|
||||
public ICommandManager getCommandManager() {
|
||||
return this.commandManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets serverPort.
|
||||
*/
|
||||
public int getServerPort() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void setServerPort(int par1) {
|
||||
throw new IllegalArgumentException("variable removed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username of the server owner (for integrated servers)
|
||||
*/
|
||||
public String getServerOwner() {
|
||||
return this.serverOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the username of the owner of this server (in the case of an integrated
|
||||
* server)
|
||||
*/
|
||||
public void setServerOwner(String par1Str) {
|
||||
this.serverOwner = par1Str;
|
||||
}
|
||||
|
||||
public boolean isSinglePlayer() {
|
||||
return this.serverOwner != null;
|
||||
}
|
||||
|
||||
public String getFolderName() {
|
||||
return this.folderName;
|
||||
}
|
||||
|
||||
public void setFolderName(String par1Str) {
|
||||
this.folderName = par1Str;
|
||||
}
|
||||
|
||||
public void setDifficultyForAllWorlds(int par1) {
|
||||
for (int var2 = 0; var2 < this.worldServers.length; ++var2) {
|
||||
WorldServer var3 = this.worldServers[var2];
|
||||
|
||||
if (var3 != null) {
|
||||
if (var3.getWorldInfo().isHardcoreModeEnabled()) {
|
||||
var3.difficultySetting = 3;
|
||||
var3.setAllowedSpawnTypes(true, true);
|
||||
} else if (this.isSinglePlayer()) {
|
||||
var3.difficultySetting = par1;
|
||||
var3.setAllowedSpawnTypes(var3.difficultySetting > 0, true);
|
||||
} else {
|
||||
var3.difficultySetting = par1;
|
||||
var3.setAllowedSpawnTypes(this.allowSpawnMonsters(), this.canSpawnAnimals);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean allowSpawnMonsters() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this is a demo or not.
|
||||
*/
|
||||
public boolean isDemo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this is a demo or not.
|
||||
*/
|
||||
public void setDemo(boolean par1) {
|
||||
throw new IllegalArgumentException("variable removed");
|
||||
}
|
||||
|
||||
public void canCreateBonusChest(boolean par1) {
|
||||
throw new IllegalArgumentException("variable removed");
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING : directly calls
|
||||
* getActiveAnvilConverter().deleteWorldDirectory(theWorldServer[0].getSaveHandler().getWorldDirectoryName());
|
||||
*/
|
||||
public void deleteWorldAndStopServer() {
|
||||
this.worldIsBeingDeleted = true;
|
||||
|
||||
for (int var1 = 0; var1 < this.worldServers.length; ++var1) {
|
||||
WorldServer var2 = this.worldServers[var1];
|
||||
|
||||
if (var2 != null) {
|
||||
var2.flush();
|
||||
}
|
||||
}
|
||||
|
||||
String dir = this.worldServers[0].getSaveHandler().getWorldDirectoryName();
|
||||
SYS.VFS.deleteFiles(dir);
|
||||
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
if(worldsTxt != null) {
|
||||
LinkedList<String> newWorlds = new LinkedList();
|
||||
for(String str : worldsTxt) {
|
||||
if(!str.equalsIgnoreCase(dir)) {
|
||||
newWorlds.add(str);
|
||||
}
|
||||
}
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", newWorlds));
|
||||
}
|
||||
|
||||
this.initiateShutdown();
|
||||
}
|
||||
|
||||
public String getTexturePack() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setTexturePack(String par1Str) {
|
||||
throw new IllegalArgumentException("variable removed");
|
||||
}
|
||||
|
||||
/**
|
||||
* This is checked to be 16 upon receiving the packet, otherwise the packet is
|
||||
* ignored.
|
||||
*/
|
||||
public int textureSize() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
public abstract boolean isDedicatedServer();
|
||||
|
||||
public boolean isServerInOnlineMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setOnlineMode(boolean par1) {
|
||||
throw new IllegalArgumentException("variable removed");
|
||||
}
|
||||
|
||||
public boolean getCanSpawnAnimals() {
|
||||
return this.canSpawnAnimals;
|
||||
}
|
||||
|
||||
public void setCanSpawnAnimals(boolean par1) {
|
||||
this.canSpawnAnimals = par1;
|
||||
}
|
||||
|
||||
public boolean getCanSpawnNPCs() {
|
||||
return this.canSpawnNPCs;
|
||||
}
|
||||
|
||||
public void setCanSpawnNPCs(boolean par1) {
|
||||
this.canSpawnNPCs = par1;
|
||||
}
|
||||
|
||||
public boolean isPVPEnabled() {
|
||||
return this.pvpEnabled;
|
||||
}
|
||||
|
||||
public void setAllowPvp(boolean par1) {
|
||||
this.pvpEnabled = par1;
|
||||
}
|
||||
|
||||
public boolean isFlightAllowed() {
|
||||
return this.allowFlight;
|
||||
}
|
||||
|
||||
public void setAllowFlight(boolean par1) {
|
||||
this.allowFlight = par1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether command blocks are enabled.
|
||||
*/
|
||||
public abstract boolean isCommandBlockEnabled();
|
||||
|
||||
public String getMOTD() {
|
||||
return this.motd;
|
||||
}
|
||||
|
||||
public void setMOTD(String par1Str) {
|
||||
this.motd = par1Str;
|
||||
}
|
||||
|
||||
public int getBuildLimit() {
|
||||
return 256;
|
||||
}
|
||||
|
||||
public void setBuildLimit(int par1) {
|
||||
throw new IllegalArgumentException("variable removed");
|
||||
}
|
||||
|
||||
public boolean isServerStopped() {
|
||||
return this.serverStopped;
|
||||
}
|
||||
|
||||
public ServerConfigurationManager getConfigurationManager() {
|
||||
return this.serverConfigManager;
|
||||
}
|
||||
|
||||
public void setConfigurationManager(ServerConfigurationManager par1ServerConfigurationManager) {
|
||||
this.serverConfigManager = par1ServerConfigurationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the game type for all worlds.
|
||||
*/
|
||||
public void setGameType(EnumGameType par1EnumGameType) {
|
||||
for (int var2 = 0; var2 < this.worldServers.length; ++var2) {
|
||||
getServer().worldServers[var2].getWorldInfo().setGameType(par1EnumGameType);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract WorkerListenThread getNetworkThread();
|
||||
|
||||
public boolean getGuiEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* On dedicated does nothing. On integrated, sets commandsAllowedForAll,
|
||||
* gameType and allows external connections.
|
||||
*/
|
||||
public abstract String shareToLAN(EnumGameType var1, boolean var2);
|
||||
|
||||
public int getTickCounter() {
|
||||
return this.tickCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the position for this command sender.
|
||||
*/
|
||||
public ChunkCoordinates getPlayerCoordinates() {
|
||||
return new ChunkCoordinates(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the spawn protection area's size.
|
||||
*/
|
||||
public int getSpawnProtectionSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isBlockProtected(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract ILogAgent getLogAgent();
|
||||
|
||||
public void func_104055_i(boolean par1) {
|
||||
this.field_104057_T = par1;
|
||||
}
|
||||
|
||||
public boolean func_104056_am() {
|
||||
return this.field_104057_T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current player count, maximum player count, and player entity list.
|
||||
*/
|
||||
public static ServerConfigurationManager getServerConfigurationManager(MinecraftServer par0MinecraftServer) {
|
||||
return par0MinecraftServer.serverConfigManager;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
final class AABBLocalPool extends ThreadLocal
|
||||
{
|
||||
protected AABBPool createNewDefaultPool()
|
||||
{
|
||||
return new AABBPool(300, 2000);
|
||||
}
|
||||
|
||||
protected Object initialValue()
|
||||
{
|
||||
return this.createNewDefaultPool();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AABBPool
|
||||
{
|
||||
/**
|
||||
* Maximum number of times the pool can be "cleaned" before the list is shrunk
|
||||
*/
|
||||
private final int maxNumCleans;
|
||||
|
||||
/**
|
||||
* Number of Pool entries to remove when cleanPool is called maxNumCleans times.
|
||||
*/
|
||||
private final int numEntriesToRemove;
|
||||
|
||||
/** List of AABB stored in this Pool */
|
||||
private final List listAABB = new ArrayList();
|
||||
|
||||
/** Next index to use when adding a Pool Entry. */
|
||||
private int nextPoolIndex;
|
||||
|
||||
/**
|
||||
* Largest index reached by this Pool (can be reset to 0 upon calling cleanPool)
|
||||
*/
|
||||
private int maxPoolIndex;
|
||||
|
||||
/** Number of times this Pool has been cleaned */
|
||||
private int numCleans;
|
||||
|
||||
public AABBPool(int par1, int par2)
|
||||
{
|
||||
this.maxNumCleans = par1;
|
||||
this.numEntriesToRemove = par2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new AABB, or reuses one that's no longer in use. Parameters: minX, minY, minZ, maxX, maxY, maxZ. AABBs
|
||||
* returned from this function should only be used for one frame or tick, as after that they will be reused.
|
||||
*/
|
||||
public AxisAlignedBB getAABB(double par1, double par3, double par5, double par7, double par9, double par11)
|
||||
{
|
||||
AxisAlignedBB var13;
|
||||
|
||||
if (this.nextPoolIndex >= this.listAABB.size())
|
||||
{
|
||||
var13 = new AxisAlignedBB(par1, par3, par5, par7, par9, par11);
|
||||
this.listAABB.add(var13);
|
||||
}
|
||||
else
|
||||
{
|
||||
var13 = (AxisAlignedBB)this.listAABB.get(this.nextPoolIndex);
|
||||
var13.setBounds(par1, par3, par5, par7, par9, par11);
|
||||
}
|
||||
|
||||
++this.nextPoolIndex;
|
||||
return var13;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the pool as "empty", starting over when adding new entries. If this is called maxNumCleans times, the list
|
||||
* size is reduced
|
||||
*/
|
||||
public void cleanPool()
|
||||
{
|
||||
if (this.nextPoolIndex > this.maxPoolIndex)
|
||||
{
|
||||
this.maxPoolIndex = this.nextPoolIndex;
|
||||
}
|
||||
|
||||
if (this.numCleans++ == this.maxNumCleans)
|
||||
{
|
||||
int var1 = Math.max(this.maxPoolIndex, this.listAABB.size() - this.numEntriesToRemove);
|
||||
|
||||
while (this.listAABB.size() > var1)
|
||||
{
|
||||
this.listAABB.remove(var1);
|
||||
}
|
||||
|
||||
this.maxPoolIndex = 0;
|
||||
this.numCleans = 0;
|
||||
}
|
||||
|
||||
this.nextPoolIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the AABBPool
|
||||
*/
|
||||
public void clearPool()
|
||||
{
|
||||
this.nextPoolIndex = 0;
|
||||
this.listAABB.clear();
|
||||
}
|
||||
|
||||
public int getlistAABBsize()
|
||||
{
|
||||
return this.listAABB.size();
|
||||
}
|
||||
|
||||
public int getnextPoolIndex()
|
||||
{
|
||||
return this.nextPoolIndex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class Achievement extends StatBase
|
||||
{
|
||||
/**
|
||||
* Is the column (related to center of achievement gui, in 24 pixels unit) that the achievement will be displayed.
|
||||
*/
|
||||
public final int displayColumn;
|
||||
|
||||
/**
|
||||
* Is the row (related to center of achievement gui, in 24 pixels unit) that the achievement will be displayed.
|
||||
*/
|
||||
public final int displayRow;
|
||||
|
||||
/**
|
||||
* Holds the parent achievement, that must be taken before this achievement is avaiable.
|
||||
*/
|
||||
public final Achievement parentAchievement;
|
||||
|
||||
/**
|
||||
* Holds the description of the achievement, ready to be formatted and/or displayed.
|
||||
*/
|
||||
private final String achievementDescription;
|
||||
|
||||
|
||||
/**
|
||||
* Holds the ItemStack that will be used to draw the achievement into the GUI.
|
||||
*/
|
||||
public final ItemStack theItemStack;
|
||||
|
||||
/**
|
||||
* Special achievements have a 'spiked' (on normal texture pack) frame, special achievements are the hardest ones to
|
||||
* achieve.
|
||||
*/
|
||||
private boolean isSpecial;
|
||||
|
||||
public Achievement(int par1, String par2Str, int par3, int par4, Item par5Item, Achievement par6Achievement)
|
||||
{
|
||||
this(par1, par2Str, par3, par4, new ItemStack(par5Item), par6Achievement);
|
||||
}
|
||||
|
||||
public Achievement(int par1, String par2Str, int par3, int par4, Block par5Block, Achievement par6Achievement)
|
||||
{
|
||||
this(par1, par2Str, par3, par4, new ItemStack(par5Block), par6Achievement);
|
||||
}
|
||||
|
||||
public Achievement(int par1, String par2Str, int par3, int par4, ItemStack par5ItemStack, Achievement par6Achievement)
|
||||
{
|
||||
super(5242880 + par1, "achievement." + par2Str);
|
||||
this.theItemStack = par5ItemStack;
|
||||
this.achievementDescription = "achievement." + par2Str + ".desc";
|
||||
this.displayColumn = par3;
|
||||
this.displayRow = par4;
|
||||
|
||||
if (par3 < AchievementList.minDisplayColumn)
|
||||
{
|
||||
AchievementList.minDisplayColumn = par3;
|
||||
}
|
||||
|
||||
if (par4 < AchievementList.minDisplayRow)
|
||||
{
|
||||
AchievementList.minDisplayRow = par4;
|
||||
}
|
||||
|
||||
if (par3 > AchievementList.maxDisplayColumn)
|
||||
{
|
||||
AchievementList.maxDisplayColumn = par3;
|
||||
}
|
||||
|
||||
if (par4 > AchievementList.maxDisplayRow)
|
||||
{
|
||||
AchievementList.maxDisplayRow = par4;
|
||||
}
|
||||
|
||||
this.parentAchievement = par6Achievement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not the given achievement or statistic is independent (i.e., lacks prerequisites for being
|
||||
* update).
|
||||
*/
|
||||
public Achievement setIndependent()
|
||||
{
|
||||
this.isIndependent = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special achievements have a 'spiked' (on normal texture pack) frame, special achievements are the hardest ones to
|
||||
* achieve.
|
||||
*/
|
||||
public Achievement setSpecial()
|
||||
{
|
||||
this.isSpecial = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the achievement on the internal list of registered achievements, also, it's check for duplicated id's.
|
||||
*/
|
||||
public Achievement registerAchievement()
|
||||
{
|
||||
super.registerStat();
|
||||
AchievementList.achievementList.add(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the StatBase-derived class is a statistic (running counter) or an achievement (one-shot).
|
||||
*/
|
||||
public boolean isAchievement()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully description of the achievement - ready to be displayed on screen.
|
||||
*/
|
||||
// public String getDescription()
|
||||
// {
|
||||
// return this.statStringFormatter != null ? this.statStringFormatter.formatString(StatCollector.translateToLocal(this.achievementDescription)) : StatCollector.translateToLocal(this.achievementDescription);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Defines a string formatter for the achievement.
|
||||
// */
|
||||
// public Achievement setStatStringFormatter(IStatStringFormat par1IStatStringFormat)
|
||||
// {
|
||||
// this.statStringFormatter = par1IStatStringFormat;
|
||||
// return this;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Special achievements have a 'spiked' (on normal texture pack) frame, special achievements are the hardest ones to
|
||||
* achieve.
|
||||
*/
|
||||
public boolean getSpecial()
|
||||
{
|
||||
return this.isSpecial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the stat into StatList.
|
||||
*/
|
||||
public StatBase registerStat()
|
||||
{
|
||||
return this.registerAchievement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the current stat as independent (i.e., lacking prerequisites for being updated) and returns the
|
||||
* current instance.
|
||||
*/
|
||||
public StatBase initIndependentStat()
|
||||
{
|
||||
return this.setIndependent();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AchievementList
|
||||
{
|
||||
/** Is the smallest column used to display a achievement on the GUI. */
|
||||
public static int minDisplayColumn;
|
||||
|
||||
/** Is the smallest row used to display a achievement on the GUI. */
|
||||
public static int minDisplayRow;
|
||||
|
||||
/** Is the biggest column used to display a achievement on the GUI. */
|
||||
public static int maxDisplayColumn;
|
||||
|
||||
/** Is the biggest row used to display a achievement on the GUI. */
|
||||
public static int maxDisplayRow;
|
||||
|
||||
/** Holds a list of all registered achievements. */
|
||||
public static List achievementList = new ArrayList();
|
||||
|
||||
/** Is the 'open inventory' achievement. */
|
||||
public static Achievement openInventory = (new Achievement(0, "openInventory", 0, 0, Item.book, (Achievement)null)).setIndependent().registerAchievement();
|
||||
|
||||
/** Is the 'getting wood' achievement. */
|
||||
public static Achievement mineWood = (new Achievement(1, "mineWood", 2, 1, Block.wood, openInventory)).registerAchievement();
|
||||
|
||||
/** Is the 'benchmarking' achievement. */
|
||||
public static Achievement buildWorkBench = (new Achievement(2, "buildWorkBench", 4, -1, Block.workbench, mineWood)).registerAchievement();
|
||||
|
||||
/** Is the 'time to mine' achievement. */
|
||||
public static Achievement buildPickaxe = (new Achievement(3, "buildPickaxe", 4, 2, Item.pickaxeWood, buildWorkBench)).registerAchievement();
|
||||
|
||||
/** Is the 'hot topic' achievement. */
|
||||
public static Achievement buildFurnace = (new Achievement(4, "buildFurnace", 3, 4, Block.furnaceIdle, buildPickaxe)).registerAchievement();
|
||||
|
||||
/** Is the 'acquire hardware' achievement. */
|
||||
public static Achievement acquireIron = (new Achievement(5, "acquireIron", 1, 4, Item.ingotIron, buildFurnace)).registerAchievement();
|
||||
|
||||
/** Is the 'time to farm' achievement. */
|
||||
public static Achievement buildHoe = (new Achievement(6, "buildHoe", 2, -3, Item.hoeWood, buildWorkBench)).registerAchievement();
|
||||
|
||||
/** Is the 'bake bread' achievement. */
|
||||
public static Achievement makeBread = (new Achievement(7, "makeBread", -1, -3, Item.bread, buildHoe)).registerAchievement();
|
||||
|
||||
/** Is the 'the lie' achievement. */
|
||||
public static Achievement bakeCake = (new Achievement(8, "bakeCake", 0, -5, Item.cake, buildHoe)).registerAchievement();
|
||||
|
||||
/** Is the 'getting a upgrade' achievement. */
|
||||
public static Achievement buildBetterPickaxe = (new Achievement(9, "buildBetterPickaxe", 6, 2, Item.pickaxeStone, buildPickaxe)).registerAchievement();
|
||||
|
||||
/** Is the 'delicious fish' achievement. */
|
||||
public static Achievement cookFish = (new Achievement(10, "cookFish", 2, 6, Item.fishCooked, buildFurnace)).registerAchievement();
|
||||
|
||||
/** Is the 'on a rail' achievement */
|
||||
public static Achievement onARail = (new Achievement(11, "onARail", 2, 3, Block.rail, acquireIron)).setSpecial().registerAchievement();
|
||||
|
||||
/** Is the 'time to strike' achievement. */
|
||||
public static Achievement buildSword = (new Achievement(12, "buildSword", 6, -1, Item.swordWood, buildWorkBench)).registerAchievement();
|
||||
|
||||
/** Is the 'monster hunter' achievement. */
|
||||
public static Achievement killEnemy = (new Achievement(13, "killEnemy", 8, -1, Item.bone, buildSword)).registerAchievement();
|
||||
|
||||
/** is the 'cow tipper' achievement. */
|
||||
public static Achievement killCow = (new Achievement(14, "killCow", 7, -3, Item.leather, buildSword)).registerAchievement();
|
||||
|
||||
/** Is the 'when pig fly' achievement. */
|
||||
public static Achievement flyPig = (new Achievement(15, "flyPig", 8, -4, Item.saddle, killCow)).setSpecial().registerAchievement();
|
||||
|
||||
/** The achievement for killing a Skeleton from 50 meters aways. */
|
||||
public static Achievement snipeSkeleton = (new Achievement(16, "snipeSkeleton", 7, 0, Item.bow, killEnemy)).setSpecial().registerAchievement();
|
||||
|
||||
/** Is the 'DIAMONDS!' achievement */
|
||||
public static Achievement diamonds = (new Achievement(17, "diamonds", -1, 5, Item.diamond, acquireIron)).registerAchievement();
|
||||
|
||||
/** Is the 'We Need to Go Deeper' achievement */
|
||||
public static Achievement portal = (new Achievement(18, "portal", -1, 7, Block.obsidian, diamonds)).registerAchievement();
|
||||
|
||||
/** Is the 'Return to Sender' achievement */
|
||||
public static Achievement ghast = (new Achievement(19, "ghast", -4, 8, Item.ghastTear, portal)).setSpecial().registerAchievement();
|
||||
|
||||
/** Is the 'Into Fire' achievement */
|
||||
public static Achievement blazeRod = (new Achievement(20, "blazeRod", 0, 9, Item.blazeRod, portal)).registerAchievement();
|
||||
|
||||
/** Is the 'Local Brewery' achievement */
|
||||
public static Achievement potion = (new Achievement(21, "potion", 2, 8, Item.potion, blazeRod)).registerAchievement();
|
||||
|
||||
/** Is the 'The End?' achievement */
|
||||
public static Achievement theEnd = (new Achievement(22, "theEnd", 3, 10, Item.eyeOfEnder, blazeRod)).setSpecial().registerAchievement();
|
||||
|
||||
/** Is the 'The End.' achievement */
|
||||
public static Achievement theEnd2 = (new Achievement(23, "theEnd2", 4, 13, Block.dragonEgg, theEnd)).setSpecial().registerAchievement();
|
||||
|
||||
/** Is the 'Enchanter' achievement */
|
||||
public static Achievement enchantments = (new Achievement(24, "enchantments", -4, 4, Block.enchantmentTable, diamonds)).registerAchievement();
|
||||
public static Achievement overkill = (new Achievement(25, "overkill", -4, 1, Item.swordDiamond, enchantments)).setSpecial().registerAchievement();
|
||||
|
||||
/** Is the 'Librarian' achievement */
|
||||
public static Achievement bookcase = (new Achievement(26, "bookcase", -3, 6, Block.bookShelf, enchantments)).registerAchievement();
|
||||
|
||||
/**
|
||||
* A stub functions called to make the static initializer for this class run.
|
||||
*/
|
||||
public static void init() {}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AchievementMap {
|
||||
/** Holds the singleton instance of AchievementMap. */
|
||||
public static AchievementMap instance = null;
|
||||
|
||||
public static void init(List<String> guid) {
|
||||
instance = new AchievementMap(guid);
|
||||
//StatList.initAll(); // FIX THIS SHIT
|
||||
}
|
||||
|
||||
/** Maps a achievement id with it's unique GUID. */
|
||||
private Map guidMap = new HashMap();
|
||||
|
||||
private AchievementMap(List<String> guid) {
|
||||
for (String var2 : guid) {
|
||||
String[] var3 = var2.split(",");
|
||||
int var4 = Integer.parseInt(var3[0]);
|
||||
this.guidMap.put(Integer.valueOf(var4), var3[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique GUID of a achievement id.
|
||||
*/
|
||||
public static String getGuid(int par0) {
|
||||
return (String) instance.guidMap.get(Integer.valueOf(par0));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class AnimalChest extends InventoryBasic
|
||||
{
|
||||
public AnimalChest(String par1Str, int par2)
|
||||
{
|
||||
super(par1Str, false, par2);
|
||||
}
|
||||
|
||||
public AnimalChest(String par1Str, boolean par2, int par3)
|
||||
{
|
||||
super(par1Str, par2, par3);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class AnimationFrame
|
||||
{
|
||||
private final int frameIndex;
|
||||
private final int frameTime;
|
||||
|
||||
public AnimationFrame(int par1)
|
||||
{
|
||||
this(par1, -1);
|
||||
}
|
||||
|
||||
public AnimationFrame(int par1, int par2)
|
||||
{
|
||||
this.frameIndex = par1;
|
||||
this.frameTime = par2;
|
||||
}
|
||||
|
||||
public boolean hasNoTime()
|
||||
{
|
||||
return this.frameTime == -1;
|
||||
}
|
||||
|
||||
public int getFrameTime()
|
||||
{
|
||||
return this.frameTime;
|
||||
}
|
||||
|
||||
public int getFrameIndex()
|
||||
{
|
||||
return this.frameIndex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class AnimationMetadataSection
|
||||
{
|
||||
private final List animationFrames;
|
||||
private final int frameWidth;
|
||||
private final int frameHeight;
|
||||
private final int frameTime;
|
||||
|
||||
public AnimationMetadataSection(List par1List, int par2, int par3, int par4)
|
||||
{
|
||||
this.animationFrames = par1List;
|
||||
this.frameWidth = par2;
|
||||
this.frameHeight = par3;
|
||||
this.frameTime = par4;
|
||||
}
|
||||
|
||||
public int getFrameHeight()
|
||||
{
|
||||
return this.frameHeight;
|
||||
}
|
||||
|
||||
public int getFrameWidth()
|
||||
{
|
||||
return this.frameWidth;
|
||||
}
|
||||
|
||||
public int getFrameCount()
|
||||
{
|
||||
return this.animationFrames.size();
|
||||
}
|
||||
|
||||
public int getFrameTime()
|
||||
{
|
||||
return this.frameTime;
|
||||
}
|
||||
|
||||
private AnimationFrame getAnimationFrame(int par1)
|
||||
{
|
||||
return (AnimationFrame)this.animationFrames.get(par1);
|
||||
}
|
||||
|
||||
public int getFrameTimeSingle(int par1)
|
||||
{
|
||||
AnimationFrame var2 = this.getAnimationFrame(par1);
|
||||
return var2.hasNoTime() ? this.frameTime : var2.getFrameTime();
|
||||
}
|
||||
|
||||
public boolean frameHasTime(int par1)
|
||||
{
|
||||
return !((AnimationFrame)this.animationFrames.get(par1)).hasNoTime();
|
||||
}
|
||||
|
||||
public int getFrameIndex(int par1)
|
||||
{
|
||||
return ((AnimationFrame)this.animationFrames.get(par1)).getFrameIndex();
|
||||
}
|
||||
|
||||
public Set getFrameIndexSet()
|
||||
{
|
||||
HashSet var1 = new HashSet<>();
|
||||
Iterator var2 = this.animationFrames.iterator();
|
||||
|
||||
while (var2.hasNext())
|
||||
{
|
||||
AnimationFrame var3 = (AnimationFrame)var2.next();
|
||||
var1.add(Integer.valueOf(var3.getFrameIndex()));
|
||||
}
|
||||
|
||||
return var1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public interface Attribute
|
||||
{
|
||||
String getAttributeUnlocalizedName();
|
||||
|
||||
double clampValue(double var1);
|
||||
|
||||
double getDefaultValue();
|
||||
|
||||
boolean getShouldWatch();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue