sp-server
This commit is contained in:
parent
353c5d7165
commit
f4e3cbe2fe
|
@ -8,8 +8,11 @@ plugins {
|
|||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'src/main/java'
|
||||
srcDir 'src/teavm/java'
|
||||
srcDirs(
|
||||
"src/main/java",
|
||||
"src/lwjgl/java",
|
||||
"sp-server/src/ipc/java"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +27,7 @@ tasks.withType(JavaCompile) {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
//implementation fileTree(dir: './lwjgl-rundir/', include: '*.jar')
|
||||
implementation fileTree(dir: './lwjgl-rundir/', include: '*.jar')
|
||||
|
||||
teavm(teavm.libs.jso)
|
||||
teavm(teavm.libs.jsoApis)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="bin/main" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test,teavm"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/main" path="src/ipc/java">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test,teavm"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>eaglercraft-sp-server-service-pack-2</name>
|
||||
<comment>Project sp-server created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,46 @@
|
|||
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()
|
||||
}
|
||||
|
||||
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 = true
|
||||
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-sp-server-service-pack-2'
|
||||
|
||||
// you eagler
|
|
@ -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,40 @@
|
|||
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 func_98233_a(String par1Str) {
|
||||
this.logger.log(Level.INFO, par1Str);
|
||||
}
|
||||
|
||||
public void func_98236_b(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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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().func_98236_b("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().func_98236_b("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,555 @@
|
|||
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.enableCheats();
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
|
||||
registerPacketHandler(new WorkerBinaryPacketHandlerImpl());
|
||||
|
||||
isRunning = true;
|
||||
|
||||
sendIPCPacket(new IPCPacketFFProcessKeepAlive(0xFF));
|
||||
|
||||
while(isRunning) {
|
||||
|
||||
mainLoop();
|
||||
|
||||
SysUtil.immediateContinue();
|
||||
}
|
||||
|
||||
// yee
|
||||
}
|
||||
|
||||
}
|
|
@ -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.sendPacket(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.sendPacket(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.compressChunk(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.addEntityID(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.field_82754_f);
|
||||
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 getPlayerNBTManager() {
|
||||
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,289 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.EntityPlayerMP;
|
||||
import net.minecraft.src.Packet250CustomPayload;
|
||||
|
||||
public class VoiceChatPlugin {
|
||||
|
||||
private static final Map<String, EntityPlayerMP> voicePlayers = new HashMap<>();
|
||||
private static final Map<String, ExpiringSet<String>> voiceRequests = new HashMap<>();
|
||||
private static final Set<String[]> voicePairs = new HashSet<>();
|
||||
|
||||
private static final List<String> iceServers = new ArrayList<>();
|
||||
|
||||
private static final int VOICE_SIGNAL_ALLOWED = 0;
|
||||
private static final int VOICE_SIGNAL_REQUEST = 0;
|
||||
private static final int VOICE_SIGNAL_CONNECT = 1;
|
||||
private static final int VOICE_SIGNAL_DISCONNECT = 2;
|
||||
private static final int VOICE_SIGNAL_ICE = 3;
|
||||
private static final int VOICE_SIGNAL_DESC = 4;
|
||||
private static final int VOICE_SIGNAL_GLOBAL = 5;
|
||||
|
||||
public static boolean handleMessage(EntityPlayerMP player, Packet250CustomPayload payload) {
|
||||
if ("EAG|Voice".equals(payload.channel) && payload.data.length > 0) {
|
||||
deev: {
|
||||
String user = player.username;
|
||||
byte[] msg = payload.data;
|
||||
try {
|
||||
DataInputStream streamIn = new DataInputStream(new ByteArrayInputStream(msg));
|
||||
int sig = streamIn.read();
|
||||
switch (sig) {
|
||||
case VOICE_SIGNAL_CONNECT:
|
||||
if (voicePlayers.containsKey(user))
|
||||
break deev; // user is already using voice chat
|
||||
// send out packet for player joined voice
|
||||
// notice: everyone on the server can see this packet!! however, it doesn't do
|
||||
// anything but let clients know that the player has turned on voice chat
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
dos.write(VOICE_SIGNAL_CONNECT);
|
||||
dos.writeUTF(user);
|
||||
byte[] out = baos.toByteArray();
|
||||
for (EntityPlayerMP conn : voicePlayers.values())
|
||||
conn.playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|Voice", out));
|
||||
voicePlayers.put(user, player);
|
||||
for (String username : voicePlayers.keySet())
|
||||
sendVoicePlayers(username);
|
||||
break;
|
||||
case VOICE_SIGNAL_DISCONNECT:
|
||||
if (!voicePlayers.containsKey(user))
|
||||
break deev; // user is not using voice chat
|
||||
try {
|
||||
String user2 = streamIn.readUTF();
|
||||
if (!voicePlayers.containsKey(user2))
|
||||
break deev;
|
||||
if (removeIf(voicePairs, pair -> (pair[0].equals(user) && pair[1].equals(user2))
|
||||
|| (pair[0].equals(user2) && pair[1].equals(user)))) {
|
||||
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
|
||||
DataOutputStream dos2 = new DataOutputStream(baos2);
|
||||
dos2.write(VOICE_SIGNAL_DISCONNECT);
|
||||
dos2.writeUTF(user);
|
||||
voicePlayers.get(user2).playerNetServerHandler.sendPacket(
|
||||
new Packet250CustomPayload("EAG|Voice", baos2.toByteArray()));
|
||||
baos2 = new ByteArrayOutputStream();
|
||||
dos2 = new DataOutputStream(baos2);
|
||||
dos2.write(VOICE_SIGNAL_DISCONNECT);
|
||||
dos2.writeUTF(user2);
|
||||
player.playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|Voice", baos2.toByteArray()));
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
removeUser(user);
|
||||
}
|
||||
break;
|
||||
case VOICE_SIGNAL_REQUEST:
|
||||
if (!voicePlayers.containsKey(user))
|
||||
break deev; // user is not using voice chat
|
||||
String targetUser = streamIn.readUTF();
|
||||
if (user.equals(targetUser))
|
||||
break deev; // prevent duplicates
|
||||
if (checkVoicePair(user, targetUser))
|
||||
break deev; // already paired
|
||||
if (!voicePlayers.containsKey(targetUser))
|
||||
break deev; // target user is not using voice chat
|
||||
if (!voiceRequests.containsKey(user))
|
||||
voiceRequests.put(user, new ExpiringSet<>(2000));
|
||||
if (voiceRequests.get(user).contains(targetUser))
|
||||
break deev;
|
||||
voiceRequests.get(user).add(targetUser);
|
||||
|
||||
// check if other has requested earlier
|
||||
if (voiceRequests.containsKey(targetUser) && voiceRequests.get(targetUser).contains(user)) {
|
||||
if (voiceRequests.containsKey(targetUser)) {
|
||||
voiceRequests.get(targetUser).remove(user);
|
||||
if (voiceRequests.get(targetUser).isEmpty())
|
||||
voiceRequests.remove(targetUser);
|
||||
}
|
||||
if (voiceRequests.containsKey(user)) {
|
||||
voiceRequests.get(user).remove(targetUser);
|
||||
if (voiceRequests.get(user).isEmpty())
|
||||
voiceRequests.remove(user);
|
||||
}
|
||||
// send each other add data
|
||||
voicePairs.add(new String[] { user, targetUser });
|
||||
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
|
||||
DataOutputStream dos2 = new DataOutputStream(baos2);
|
||||
dos2.write(VOICE_SIGNAL_CONNECT);
|
||||
dos2.writeUTF(user);
|
||||
dos2.writeBoolean(false);
|
||||
voicePlayers.get(targetUser).playerNetServerHandler.sendPacket(
|
||||
new Packet250CustomPayload("EAG|Voice", baos2.toByteArray()));
|
||||
baos2 = new ByteArrayOutputStream();
|
||||
dos2 = new DataOutputStream(baos2);
|
||||
dos2.write(VOICE_SIGNAL_CONNECT);
|
||||
dos2.writeUTF(targetUser);
|
||||
dos2.writeBoolean(true);
|
||||
player.playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|Voice", baos2.toByteArray()));
|
||||
}
|
||||
break;
|
||||
case VOICE_SIGNAL_ICE:
|
||||
case VOICE_SIGNAL_DESC:
|
||||
if (!voicePlayers.containsKey(user))
|
||||
break deev; // user is not using voice chat
|
||||
String targetUser2 = streamIn.readUTF();
|
||||
if (checkVoicePair(user, targetUser2)) {
|
||||
String data = streamIn.readUTF();
|
||||
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
|
||||
DataOutputStream dos2 = new DataOutputStream(baos2);
|
||||
dos2.write(sig);
|
||||
dos2.writeUTF(user);
|
||||
dos2.writeUTF(data);
|
||||
voicePlayers.get(targetUser2).playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|Voice", baos2.toByteArray()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// hacker
|
||||
// t.printStackTrace(); // todo: remove in production
|
||||
removeUser(user);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void activate(List<String> ice) {
|
||||
if(iceServers.size() == 0) {
|
||||
iceServers.addAll(ice);
|
||||
for(Object o : MinecraftServer.getServer().getConfigurationManager().playerEntityList) {
|
||||
handleConnect((EntityPlayerMP) o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleConnect(EntityPlayerMP player) {
|
||||
if(iceServers.size() > 0) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
dos.write(VOICE_SIGNAL_ALLOWED);
|
||||
dos.writeBoolean(true);
|
||||
dos.write(iceServers.size());
|
||||
for (String str : iceServers) {
|
||||
dos.writeUTF(str);
|
||||
}
|
||||
player.playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|Voice", baos.toByteArray()));
|
||||
sendVoicePlayers(player.username);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleDisconnect(EntityPlayerMP player) {
|
||||
removeUser(player.username);
|
||||
}
|
||||
|
||||
private static void removeUser(String name) {
|
||||
voicePlayers.remove(name);
|
||||
for (String username : voicePlayers.keySet()) {
|
||||
if (!name.equals(username))
|
||||
sendVoicePlayers(username);
|
||||
}
|
||||
for (String[] voicePair : voicePairs) {
|
||||
String target = null;
|
||||
if (voicePair[0].equals(name)) {
|
||||
target = voicePair[1];
|
||||
} else if (voicePair[1].equals(name)) {
|
||||
target = voicePair[0];
|
||||
}
|
||||
if (target != null && voicePlayers.containsKey(target)) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
dos.write(VOICE_SIGNAL_DISCONNECT);
|
||||
dos.writeUTF(name);
|
||||
voicePlayers.get(target).playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|Voice", baos.toByteArray()));
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
removeIf(voicePairs, pair -> pair[0].equals(name) || pair[1].equals(name));
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
voicePlayers.clear();
|
||||
voiceRequests.clear();
|
||||
voicePairs.clear();
|
||||
iceServers.clear();
|
||||
}
|
||||
|
||||
private static void sendVoicePlayers(String name) {
|
||||
if (!voicePlayers.containsKey(name))
|
||||
return;
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
dos.write(VOICE_SIGNAL_GLOBAL);
|
||||
Set<String> mostlyGlobalPlayers = new HashSet<>();
|
||||
for (String username : voicePlayers.keySet()) {
|
||||
if (username.equals(name))
|
||||
continue;
|
||||
if (anyMatch(voicePairs, pair -> (pair[0].equals(name) && pair[1].equals(username))
|
||||
|| (pair[0].equals(username) && pair[1].equals(name))))
|
||||
continue;
|
||||
mostlyGlobalPlayers.add(username);
|
||||
}
|
||||
if (mostlyGlobalPlayers.size() > 0) {
|
||||
dos.writeInt(mostlyGlobalPlayers.size());
|
||||
for (String username : mostlyGlobalPlayers)
|
||||
dos.writeUTF(username);
|
||||
voicePlayers.get(name).playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|Voice", baos.toByteArray()));
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkVoicePair(String user1, String user2) {
|
||||
return anyMatch(voicePairs, pair -> (pair[0].equals(user1) && pair[1].equals(user2))
|
||||
|| (pair[0].equals(user2) && pair[1].equals(user1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* JDK 8 function not available in TeaVM
|
||||
*/
|
||||
private static <T> boolean removeIf(Collection<T> collection, Predicate<T> pre) {
|
||||
boolean ret = false;
|
||||
Iterator<T> itr = collection.iterator();
|
||||
while(itr.hasNext()) {
|
||||
if(pre.test(itr.next())) {
|
||||
itr.remove();
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* JDK 8 function not available in TeaVM
|
||||
*/
|
||||
private static <T> boolean anyMatch(Collection<T> collection, Predicate<T> pre) {
|
||||
boolean ret = false;
|
||||
Iterator<T> itr = collection.iterator();
|
||||
while(itr.hasNext()) {
|
||||
if(pre.test(itr.next())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -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,146 @@
|
|||
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).kickPlayer(null);
|
||||
}
|
||||
isAlive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumChunkDataPackets() { // 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).kickPlayer(null);
|
||||
}
|
||||
isAlive = false;
|
||||
}
|
||||
|
||||
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,979 @@
|
|||
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.func_96467_a();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the server and starts it.
|
||||
*/
|
||||
protected abstract boolean startServer() throws IOException;
|
||||
|
||||
protected void convertMapIfNeeded(String par1Str) {
|
||||
// no
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().func_98236_b(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().func_98233_a("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().func_98236_b(
|
||||
"Can\'t keep up! Did the system time change, or is the server overloaded?");
|
||||
var7 = 2000L;
|
||||
this.timeOfLastWarning = var1;
|
||||
}
|
||||
|
||||
if (var7 < 0L) {
|
||||
this.getLogAgent().func_98236_b("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()),
|
||||
var4.provider.dimensionId);
|
||||
}
|
||||
|
||||
var4.tick();
|
||||
var4.updateEntities();
|
||||
|
||||
var4.getEntityTracker().updateTrackedEntities();
|
||||
}
|
||||
|
||||
this.timeOfLastDimensionTick[var1][this.tickCounter % 100] = System.nanoTime() - var2;
|
||||
}
|
||||
|
||||
this.getNetworkThread().handleNetworkListenThread();
|
||||
this.serverConfigManager.onTick();
|
||||
|
||||
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().func_98233_a(par1Str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the message with a level of WARN.
|
||||
*/
|
||||
public void logWarning(String par1Str) {
|
||||
this.getLogAgent().func_98236_b(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().func_98233_a(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().func_98233_a(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 getCommandSenderPosition() {
|
||||
return new ChunkCoordinates(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the spawn protection area's size.
|
||||
*/
|
||||
public int getSpawnProtectionSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean func_96290_a(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,86 @@
|
|||
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 = 0;
|
||||
|
||||
/**
|
||||
* Largest index reached by this Pool (can be reset to 0 upon calling cleanPool)
|
||||
*/
|
||||
private int maxPoolIndex = 0;
|
||||
|
||||
/** Number of times this Pool has been cleaned */
|
||||
private int numCleans = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public int getlistAABBsize() {
|
||||
return this.listAABB.size();
|
||||
}
|
||||
|
||||
public int getnextPoolIndex() {
|
||||
return this.nextPoolIndex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,187 @@
|
|||
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;
|
||||
|
||||
/** The list holding all achievements */
|
||||
public static List achievementList = new ArrayList();
|
||||
|
||||
/** Is the 'open inventory' achievement. */
|
||||
public static Achievement openInventory = null;
|
||||
|
||||
/** Is the 'getting wood' achievement. */
|
||||
public static Achievement mineWood = null;
|
||||
|
||||
/** Is the 'benchmarking' achievement. */
|
||||
public static Achievement buildWorkBench = null;
|
||||
|
||||
/** Is the 'time to mine' achievement. */
|
||||
public static Achievement buildPickaxe = null;
|
||||
|
||||
/** Is the 'hot topic' achievement. */
|
||||
public static Achievement buildFurnace = null;
|
||||
|
||||
/** Is the 'acquire hardware' achievement. */
|
||||
public static Achievement acquireIron = null;
|
||||
|
||||
/** Is the 'time to farm' achievement. */
|
||||
public static Achievement buildHoe = null;
|
||||
|
||||
/** Is the 'bake bread' achievement. */
|
||||
public static Achievement makeBread = null;
|
||||
|
||||
/** Is the 'the lie' achievement. */
|
||||
public static Achievement bakeCake = null;
|
||||
|
||||
/** Is the 'getting a upgrade' achievement. */
|
||||
public static Achievement buildBetterPickaxe = null;
|
||||
|
||||
/** Is the 'delicious fish' achievement. */
|
||||
public static Achievement cookFish = null;
|
||||
|
||||
/** Is the 'on a rail' achievement */
|
||||
public static Achievement onARail = null;
|
||||
|
||||
/** Is the 'time to strike' achievement. */
|
||||
public static Achievement buildSword = null;
|
||||
|
||||
/** Is the 'monster hunter' achievement. */
|
||||
public static Achievement killEnemy = null;
|
||||
|
||||
/** is the 'cow tipper' achievement. */
|
||||
public static Achievement killCow = null;
|
||||
|
||||
/** Is the 'when pig fly' achievement. */
|
||||
public static Achievement flyPig = null;
|
||||
|
||||
/** The achievement for killing a Skeleton from 50 meters aways. */
|
||||
public static Achievement snipeSkeleton = null;
|
||||
|
||||
/** Is the 'DIAMONDS!' achievement */
|
||||
public static Achievement diamonds = null;
|
||||
|
||||
/** Is the 'We Need to Go Deeper' achievement */
|
||||
public static Achievement portal = null;
|
||||
|
||||
/** Is the 'Return to Sender' achievement */
|
||||
public static Achievement ghast = null;
|
||||
|
||||
/** Is the 'Into Fire' achievement */
|
||||
public static Achievement blazeRod = null;
|
||||
|
||||
/** Is the 'Local Brewery' achievement */
|
||||
public static Achievement potion = null;
|
||||
|
||||
/** Is the 'The End?' achievement */
|
||||
public static Achievement theEnd = null;
|
||||
|
||||
/** Is the 'The End.' achievement */
|
||||
public static Achievement theEnd2 = null;
|
||||
|
||||
/** Is the 'Enchanter' achievement */
|
||||
public static Achievement enchantments = null;
|
||||
|
||||
public static Achievement overkill = null;
|
||||
|
||||
/** Is the 'Librarian' achievement */
|
||||
public static Achievement bookcase = null;
|
||||
|
||||
public static void init() {
|
||||
achievementList = new ArrayList();
|
||||
|
||||
openInventory = (new Achievement(0, "openInventory", 0, 0, Item.book, (Achievement) null))
|
||||
.setIndependent().registerAchievement();
|
||||
|
||||
mineWood = (new Achievement(1, "mineWood", 2, 1, Block.wood, openInventory))
|
||||
.registerAchievement();
|
||||
|
||||
buildWorkBench = (new Achievement(2, "buildWorkBench", 4, -1, Block.workbench, mineWood))
|
||||
.registerAchievement();
|
||||
|
||||
buildPickaxe = (new Achievement(3, "buildPickaxe", 4, 2, Item.pickaxeWood,
|
||||
buildWorkBench)).registerAchievement();
|
||||
|
||||
buildFurnace = (new Achievement(4, "buildFurnace", 3, 4, Block.furnaceIdle, buildPickaxe))
|
||||
.registerAchievement();
|
||||
|
||||
acquireIron = (new Achievement(5, "acquireIron", 1, 4, Item.ingotIron, buildFurnace))
|
||||
.registerAchievement();
|
||||
|
||||
buildHoe = (new Achievement(6, "buildHoe", 2, -3, Item.hoeWood, buildWorkBench))
|
||||
.registerAchievement();
|
||||
|
||||
makeBread = (new Achievement(7, "makeBread", -1, -3, Item.bread, buildHoe))
|
||||
.registerAchievement();
|
||||
|
||||
bakeCake = (new Achievement(8, "bakeCake", 0, -5, Item.cake, buildHoe))
|
||||
.registerAchievement();
|
||||
|
||||
buildBetterPickaxe = (new Achievement(9, "buildBetterPickaxe", 6, 2, Item.pickaxeStone,
|
||||
buildPickaxe)).registerAchievement();
|
||||
|
||||
cookFish = (new Achievement(10, "cookFish", 2, 6, Item.fishCooked, buildFurnace))
|
||||
.registerAchievement();
|
||||
|
||||
onARail = (new Achievement(11, "onARail", 2, 3, Block.rail, acquireIron)).setSpecial()
|
||||
.registerAchievement();
|
||||
|
||||
buildSword = (new Achievement(12, "buildSword", 6, -1, Item.swordWood, buildWorkBench))
|
||||
.registerAchievement();
|
||||
|
||||
killEnemy = (new Achievement(13, "killEnemy", 8, -1, Item.bone, buildSword))
|
||||
.registerAchievement();
|
||||
|
||||
killCow = (new Achievement(14, "killCow", 7, -3, Item.leather, buildSword))
|
||||
.registerAchievement();
|
||||
|
||||
flyPig = (new Achievement(15, "flyPig", 8, -4, Item.saddle, killCow)).setSpecial()
|
||||
.registerAchievement();
|
||||
|
||||
snipeSkeleton = (new Achievement(16, "snipeSkeleton", 7, 0, Item.bow, killEnemy))
|
||||
.setSpecial().registerAchievement();
|
||||
|
||||
diamonds = (new Achievement(17, "diamonds", -1, 5, Item.diamond, acquireIron))
|
||||
.registerAchievement();
|
||||
|
||||
portal = (new Achievement(18, "portal", -1, 7, Block.obsidian, diamonds))
|
||||
.registerAchievement();
|
||||
|
||||
ghast = (new Achievement(19, "ghast", -4, 8, Item.ghastTear, portal)).setSpecial()
|
||||
.registerAchievement();
|
||||
|
||||
blazeRod = (new Achievement(20, "blazeRod", 0, 9, Item.blazeRod, portal))
|
||||
.registerAchievement();
|
||||
|
||||
potion = (new Achievement(21, "potion", 2, 8, Item.potion, blazeRod))
|
||||
.registerAchievement();
|
||||
|
||||
theEnd = (new Achievement(22, "theEnd", 3, 10, Item.eyeOfEnder, blazeRod)).setSpecial()
|
||||
.registerAchievement();
|
||||
|
||||
theEnd2 = (new Achievement(23, "theEnd2", 4, 13, Block.dragonEgg, theEnd)).setSpecial()
|
||||
.registerAchievement();
|
||||
|
||||
enchantments = (new Achievement(24, "enchantments", -4, 4, Block.enchantmentTable,
|
||||
diamonds)).registerAchievement();
|
||||
|
||||
overkill = (new Achievement(25, "overkill", -4, 1, Item.swordDiamond, enchantments))
|
||||
.setSpecial().registerAchievement();
|
||||
|
||||
bookcase = (new Achievement(26, "bookcase", -3, 6, Block.bookShelf, enchantments))
|
||||
.registerAchievement();
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
/** 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,425 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class AxisAlignedBB {
|
||||
/** ThreadLocal AABBPool */
|
||||
private static final AABBPool theAABBLocalPool = new AABBPool(300, 2000);
|
||||
public double minX;
|
||||
public double minY;
|
||||
public double minZ;
|
||||
public double maxX;
|
||||
public double maxY;
|
||||
public double maxZ;
|
||||
|
||||
/**
|
||||
* Returns a bounding box with the specified bounds. Args: minX, minY, minZ,
|
||||
* maxX, maxY, maxZ
|
||||
*/
|
||||
public static AxisAlignedBB getBoundingBox(double par0, double par2, double par4, double par6, double par8,
|
||||
double par10) {
|
||||
return new AxisAlignedBB(par0, par2, par4, par6, par8, par10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ThreadLocal AABBPool
|
||||
*/
|
||||
public static AABBPool getAABBPool() {
|
||||
return theAABBLocalPool;
|
||||
}
|
||||
|
||||
protected AxisAlignedBB(double par1, double par3, double par5, double par7, double par9, double par11) {
|
||||
this.minX = par1;
|
||||
this.minY = par3;
|
||||
this.minZ = par5;
|
||||
this.maxX = par7;
|
||||
this.maxY = par9;
|
||||
this.maxZ = par11;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bounds of the bounding box. Args: minX, minY, minZ, maxX, maxY, maxZ
|
||||
*/
|
||||
public AxisAlignedBB setBounds(double par1, double par3, double par5, double par7, double par9, double par11) {
|
||||
this.minX = par1;
|
||||
this.minY = par3;
|
||||
this.minZ = par5;
|
||||
this.maxX = par7;
|
||||
this.maxY = par9;
|
||||
this.maxZ = par11;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the coordinates to the bounding box extending it if the point lies
|
||||
* outside the current ranges. Args: x, y, z
|
||||
*/
|
||||
public AxisAlignedBB addCoord(double par1, double par3, double par5) {
|
||||
double var7 = this.minX;
|
||||
double var9 = this.minY;
|
||||
double var11 = this.minZ;
|
||||
double var13 = this.maxX;
|
||||
double var15 = this.maxY;
|
||||
double var17 = this.maxZ;
|
||||
|
||||
if (par1 < 0.0D) {
|
||||
var7 += par1;
|
||||
}
|
||||
|
||||
if (par1 > 0.0D) {
|
||||
var13 += par1;
|
||||
}
|
||||
|
||||
if (par3 < 0.0D) {
|
||||
var9 += par3;
|
||||
}
|
||||
|
||||
if (par3 > 0.0D) {
|
||||
var15 += par3;
|
||||
}
|
||||
|
||||
if (par5 < 0.0D) {
|
||||
var11 += par5;
|
||||
}
|
||||
|
||||
if (par5 > 0.0D) {
|
||||
var17 += par5;
|
||||
}
|
||||
|
||||
return getAABBPool().getAABB(var7, var9, var11, var13, var15, var17);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bounding box expanded by the specified vector (if negative numbers
|
||||
* are given it will shrink). Args: x, y, z
|
||||
*/
|
||||
public AxisAlignedBB expand(double par1, double par3, double par5) {
|
||||
double var7 = this.minX - par1;
|
||||
double var9 = this.minY - par3;
|
||||
double var11 = this.minZ - par5;
|
||||
double var13 = this.maxX + par1;
|
||||
double var15 = this.maxY + par3;
|
||||
double var17 = this.maxZ + par5;
|
||||
return getAABBPool().getAABB(var7, var9, var11, var13, var15, var17);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bounding box offseted by the specified vector (if negative numbers
|
||||
* are given it will shrink). Args: x, y, z
|
||||
*/
|
||||
public AxisAlignedBB getOffsetBoundingBox(double par1, double par3, double par5) {
|
||||
return getAABBPool().getAABB(this.minX + par1, this.minY + par3, this.minZ + par5, this.maxX + par1,
|
||||
this.maxY + par3, this.maxZ + par5);
|
||||
}
|
||||
|
||||
/**
|
||||
* if instance and the argument bounding boxes overlap in the Y and Z
|
||||
* dimensions, calculate the offset between them in the X dimension. return var2
|
||||
* if the bounding boxes do not overlap or if var2 is closer to 0 then the
|
||||
* calculated offset. Otherwise return the calculated offset.
|
||||
*/
|
||||
public double calculateXOffset(AxisAlignedBB par1AxisAlignedBB, double par2) {
|
||||
if (par1AxisAlignedBB.maxY > this.minY && par1AxisAlignedBB.minY < this.maxY) {
|
||||
if (par1AxisAlignedBB.maxZ > this.minZ && par1AxisAlignedBB.minZ < this.maxZ) {
|
||||
double var4;
|
||||
|
||||
if (par2 > 0.0D && par1AxisAlignedBB.maxX <= this.minX) {
|
||||
var4 = this.minX - par1AxisAlignedBB.maxX;
|
||||
|
||||
if (var4 < par2) {
|
||||
par2 = var4;
|
||||
}
|
||||
}
|
||||
|
||||
if (par2 < 0.0D && par1AxisAlignedBB.minX >= this.maxX) {
|
||||
var4 = this.maxX - par1AxisAlignedBB.minX;
|
||||
|
||||
if (var4 > par2) {
|
||||
par2 = var4;
|
||||
}
|
||||
}
|
||||
|
||||
return par2;
|
||||
} else {
|
||||
return par2;
|
||||
}
|
||||
} else {
|
||||
return par2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* if instance and the argument bounding boxes overlap in the X and Z
|
||||
* dimensions, calculate the offset between them in the Y dimension. return var2
|
||||
* if the bounding boxes do not overlap or if var2 is closer to 0 then the
|
||||
* calculated offset. Otherwise return the calculated offset.
|
||||
*/
|
||||
public double calculateYOffset(AxisAlignedBB par1AxisAlignedBB, double par2) {
|
||||
if (par1AxisAlignedBB.maxX > this.minX && par1AxisAlignedBB.minX < this.maxX) {
|
||||
if (par1AxisAlignedBB.maxZ > this.minZ && par1AxisAlignedBB.minZ < this.maxZ) {
|
||||
double var4;
|
||||
|
||||
if (par2 > 0.0D && par1AxisAlignedBB.maxY <= this.minY) {
|
||||
var4 = this.minY - par1AxisAlignedBB.maxY;
|
||||
|
||||
if (var4 < par2) {
|
||||
par2 = var4;
|
||||
}
|
||||
}
|
||||
|
||||
if (par2 < 0.0D && par1AxisAlignedBB.minY >= this.maxY) {
|
||||
var4 = this.maxY - par1AxisAlignedBB.minY;
|
||||
|
||||
if (var4 > par2) {
|
||||
par2 = var4;
|
||||
}
|
||||
}
|
||||
|
||||
return par2;
|
||||
} else {
|
||||
return par2;
|
||||
}
|
||||
} else {
|
||||
return par2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* if instance and the argument bounding boxes overlap in the Y and X
|
||||
* dimensions, calculate the offset between them in the Z dimension. return var2
|
||||
* if the bounding boxes do not overlap or if var2 is closer to 0 then the
|
||||
* calculated offset. Otherwise return the calculated offset.
|
||||
*/
|
||||
public double calculateZOffset(AxisAlignedBB par1AxisAlignedBB, double par2) {
|
||||
if (par1AxisAlignedBB.maxX > this.minX && par1AxisAlignedBB.minX < this.maxX) {
|
||||
if (par1AxisAlignedBB.maxY > this.minY && par1AxisAlignedBB.minY < this.maxY) {
|
||||
double var4;
|
||||
|
||||
if (par2 > 0.0D && par1AxisAlignedBB.maxZ <= this.minZ) {
|
||||
var4 = this.minZ - par1AxisAlignedBB.maxZ;
|
||||
|
||||
if (var4 < par2) {
|
||||
par2 = var4;
|
||||
}
|
||||
}
|
||||
|
||||
if (par2 < 0.0D && par1AxisAlignedBB.minZ >= this.maxZ) {
|
||||
var4 = this.maxZ - par1AxisAlignedBB.minZ;
|
||||
|
||||
if (var4 > par2) {
|
||||
par2 = var4;
|
||||
}
|
||||
}
|
||||
|
||||
return par2;
|
||||
} else {
|
||||
return par2;
|
||||
}
|
||||
} else {
|
||||
return par2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given bounding box intersects with this one. Args:
|
||||
* axisAlignedBB
|
||||
*/
|
||||
public boolean intersectsWith(AxisAlignedBB par1AxisAlignedBB) {
|
||||
return par1AxisAlignedBB.maxX > this.minX && par1AxisAlignedBB.minX < this.maxX
|
||||
? (par1AxisAlignedBB.maxY > this.minY && par1AxisAlignedBB.minY < this.maxY
|
||||
? par1AxisAlignedBB.maxZ > this.minZ && par1AxisAlignedBB.minZ < this.maxZ
|
||||
: false)
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offsets the current bounding box by the specified coordinates. Args: x, y, z
|
||||
*/
|
||||
public AxisAlignedBB offset(double par1, double par3, double par5) {
|
||||
this.minX += par1;
|
||||
this.minY += par3;
|
||||
this.minZ += par5;
|
||||
this.maxX += par1;
|
||||
this.maxY += par3;
|
||||
this.maxZ += par5;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the supplied Vec3D is completely inside the bounding box
|
||||
*/
|
||||
public boolean isVecInside(Vec3 par1Vec3) {
|
||||
return par1Vec3.xCoord > this.minX && par1Vec3.xCoord < this.maxX
|
||||
? (par1Vec3.yCoord > this.minY && par1Vec3.yCoord < this.maxY
|
||||
? par1Vec3.zCoord > this.minZ && par1Vec3.zCoord < this.maxZ
|
||||
: false)
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the average length of the edges of the bounding box.
|
||||
*/
|
||||
public double getAverageEdgeLength() {
|
||||
double var1 = this.maxX - this.minX;
|
||||
double var3 = this.maxY - this.minY;
|
||||
double var5 = this.maxZ - this.minZ;
|
||||
return (var1 + var3 + var5) / 3.0D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bounding box that is inset by the specified amounts
|
||||
*/
|
||||
public AxisAlignedBB contract(double par1, double par3, double par5) {
|
||||
double var7 = this.minX + par1;
|
||||
double var9 = this.minY + par3;
|
||||
double var11 = this.minZ + par5;
|
||||
double var13 = this.maxX - par1;
|
||||
double var15 = this.maxY - par3;
|
||||
double var17 = this.maxZ - par5;
|
||||
return getAABBPool().getAABB(var7, var9, var11, var13, var15, var17);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the bounding box.
|
||||
*/
|
||||
public AxisAlignedBB copy() {
|
||||
return getAABBPool().getAABB(this.minX, this.minY, this.minZ, this.maxX, this.maxY, this.maxZ);
|
||||
}
|
||||
|
||||
public MovingObjectPosition calculateIntercept(Vec3 par1Vec3, Vec3 par2Vec3) {
|
||||
Vec3 var3 = par1Vec3.getIntermediateWithXValue(par2Vec3, this.minX);
|
||||
Vec3 var4 = par1Vec3.getIntermediateWithXValue(par2Vec3, this.maxX);
|
||||
Vec3 var5 = par1Vec3.getIntermediateWithYValue(par2Vec3, this.minY);
|
||||
Vec3 var6 = par1Vec3.getIntermediateWithYValue(par2Vec3, this.maxY);
|
||||
Vec3 var7 = par1Vec3.getIntermediateWithZValue(par2Vec3, this.minZ);
|
||||
Vec3 var8 = par1Vec3.getIntermediateWithZValue(par2Vec3, this.maxZ);
|
||||
|
||||
if (!this.isVecInYZ(var3)) {
|
||||
var3 = null;
|
||||
}
|
||||
|
||||
if (!this.isVecInYZ(var4)) {
|
||||
var4 = null;
|
||||
}
|
||||
|
||||
if (!this.isVecInXZ(var5)) {
|
||||
var5 = null;
|
||||
}
|
||||
|
||||
if (!this.isVecInXZ(var6)) {
|
||||
var6 = null;
|
||||
}
|
||||
|
||||
if (!this.isVecInXY(var7)) {
|
||||
var7 = null;
|
||||
}
|
||||
|
||||
if (!this.isVecInXY(var8)) {
|
||||
var8 = null;
|
||||
}
|
||||
|
||||
Vec3 var9 = null;
|
||||
|
||||
if (var3 != null && (var9 == null || par1Vec3.squareDistanceTo(var3) < par1Vec3.squareDistanceTo(var9))) {
|
||||
var9 = var3;
|
||||
}
|
||||
|
||||
if (var4 != null && (var9 == null || par1Vec3.squareDistanceTo(var4) < par1Vec3.squareDistanceTo(var9))) {
|
||||
var9 = var4;
|
||||
}
|
||||
|
||||
if (var5 != null && (var9 == null || par1Vec3.squareDistanceTo(var5) < par1Vec3.squareDistanceTo(var9))) {
|
||||
var9 = var5;
|
||||
}
|
||||
|
||||
if (var6 != null && (var9 == null || par1Vec3.squareDistanceTo(var6) < par1Vec3.squareDistanceTo(var9))) {
|
||||
var9 = var6;
|
||||
}
|
||||
|
||||
if (var7 != null && (var9 == null || par1Vec3.squareDistanceTo(var7) < par1Vec3.squareDistanceTo(var9))) {
|
||||
var9 = var7;
|
||||
}
|
||||
|
||||
if (var8 != null && (var9 == null || par1Vec3.squareDistanceTo(var8) < par1Vec3.squareDistanceTo(var9))) {
|
||||
var9 = var8;
|
||||
}
|
||||
|
||||
if (var9 == null) {
|
||||
return null;
|
||||
} else {
|
||||
byte var10 = -1;
|
||||
|
||||
if (var9 == var3) {
|
||||
var10 = 4;
|
||||
}
|
||||
|
||||
if (var9 == var4) {
|
||||
var10 = 5;
|
||||
}
|
||||
|
||||
if (var9 == var5) {
|
||||
var10 = 0;
|
||||
}
|
||||
|
||||
if (var9 == var6) {
|
||||
var10 = 1;
|
||||
}
|
||||
|
||||
if (var9 == var7) {
|
||||
var10 = 2;
|
||||
}
|
||||
|
||||
if (var9 == var8) {
|
||||
var10 = 3;
|
||||
}
|
||||
|
||||
return new MovingObjectPosition(0, 0, 0, var10, var9);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified vector is within the YZ dimensions of the bounding
|
||||
* box. Args: Vec3D
|
||||
*/
|
||||
private boolean isVecInYZ(Vec3 par1Vec3) {
|
||||
return par1Vec3 == null ? false
|
||||
: par1Vec3.yCoord >= this.minY && par1Vec3.yCoord <= this.maxY && par1Vec3.zCoord >= this.minZ
|
||||
&& par1Vec3.zCoord <= this.maxZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified vector is within the XZ dimensions of the bounding
|
||||
* box. Args: Vec3D
|
||||
*/
|
||||
private boolean isVecInXZ(Vec3 par1Vec3) {
|
||||
return par1Vec3 == null ? false
|
||||
: par1Vec3.xCoord >= this.minX && par1Vec3.xCoord <= this.maxX && par1Vec3.zCoord >= this.minZ
|
||||
&& par1Vec3.zCoord <= this.maxZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified vector is within the XY dimensions of the bounding
|
||||
* box. Args: Vec3D
|
||||
*/
|
||||
private boolean isVecInXY(Vec3 par1Vec3) {
|
||||
return par1Vec3 == null ? false
|
||||
: par1Vec3.xCoord >= this.minX && par1Vec3.xCoord <= this.maxX && par1Vec3.yCoord >= this.minY
|
||||
&& par1Vec3.yCoord <= this.maxY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bounding box to the same bounds as the bounding box passed in. Args:
|
||||
* axisAlignedBB
|
||||
*/
|
||||
public void setBB(AxisAlignedBB par1AxisAlignedBB) {
|
||||
this.minX = par1AxisAlignedBB.minX;
|
||||
this.minY = par1AxisAlignedBB.minY;
|
||||
this.minZ = par1AxisAlignedBB.minZ;
|
||||
this.maxX = par1AxisAlignedBB.maxX;
|
||||
this.maxY = par1AxisAlignedBB.maxY;
|
||||
this.maxZ = par1AxisAlignedBB.maxZ;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "box[" + this.minX + ", " + this.minY + ", " + this.minZ + " -> " + this.maxX + ", " + this.maxY + ", "
|
||||
+ this.maxZ + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BehaviorDefaultDispenseItem implements IBehaviorDispenseItem {
|
||||
/**
|
||||
* Dispenses the specified ItemStack from a dispenser.
|
||||
*/
|
||||
public final ItemStack dispense(IBlockSource par1IBlockSource, ItemStack par2ItemStack) {
|
||||
ItemStack var3 = this.dispenseStack(par1IBlockSource, par2ItemStack);
|
||||
this.playDispenseSound(par1IBlockSource);
|
||||
this.spawnDispenseParticles(par1IBlockSource, BlockDispenser.getFacing(par1IBlockSource.getBlockMetadata()));
|
||||
return var3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispense the specified stack, play the dispense sound and spawn particles.
|
||||
*/
|
||||
protected ItemStack dispenseStack(IBlockSource par1IBlockSource, ItemStack par2ItemStack) {
|
||||
EnumFacing var3 = BlockDispenser.getFacing(par1IBlockSource.getBlockMetadata());
|
||||
IPosition var4 = BlockDispenser.getIPositionFromBlockSource(par1IBlockSource);
|
||||
ItemStack var5 = par2ItemStack.splitStack(1);
|
||||
doDispense(par1IBlockSource.getWorld(), var5, 6, var3, var4);
|
||||
return par2ItemStack;
|
||||
}
|
||||
|
||||
public static void doDispense(World par0World, ItemStack par1ItemStack, int par2, EnumFacing par3EnumFacing,
|
||||
IPosition par4IPosition) {
|
||||
double var5 = par4IPosition.getX();
|
||||
double var7 = par4IPosition.getY();
|
||||
double var9 = par4IPosition.getZ();
|
||||
EntityItem var11 = new EntityItem(par0World, var5, var7 - 0.3D, var9, par1ItemStack);
|
||||
double var12 = par0World.rand.nextDouble() * 0.1D + 0.2D;
|
||||
var11.motionX = (double) par3EnumFacing.getFrontOffsetX() * var12;
|
||||
var11.motionY = 0.20000000298023224D;
|
||||
var11.motionZ = (double) par3EnumFacing.getFrontOffsetZ() * var12;
|
||||
var11.motionX += par0World.rand.nextGaussian() * 0.007499999832361937D * (double) par2;
|
||||
var11.motionY += par0World.rand.nextGaussian() * 0.007499999832361937D * (double) par2;
|
||||
var11.motionZ += par0World.rand.nextGaussian() * 0.007499999832361937D * (double) par2;
|
||||
par0World.spawnEntityInWorld(var11);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the dispense sound from the specified block.
|
||||
*/
|
||||
protected void playDispenseSound(IBlockSource par1IBlockSource) {
|
||||
par1IBlockSource.getWorld().playAuxSFX(1000, par1IBlockSource.getXInt(), par1IBlockSource.getYInt(),
|
||||
par1IBlockSource.getZInt(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Order clients to display dispense particles from the specified block and
|
||||
* facing.
|
||||
*/
|
||||
protected void spawnDispenseParticles(IBlockSource par1IBlockSource, EnumFacing par2EnumFacing) {
|
||||
par1IBlockSource.getWorld().playAuxSFX(2000, par1IBlockSource.getXInt(), par1IBlockSource.getYInt(),
|
||||
par1IBlockSource.getZInt(), this.func_82488_a(par2EnumFacing));
|
||||
}
|
||||
|
||||
private int func_82488_a(EnumFacing par1EnumFacing) {
|
||||
return par1EnumFacing.getFrontOffsetX() + 1 + (par1EnumFacing.getFrontOffsetZ() + 1) * 3;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
final class BehaviorDispenseArmor extends BehaviorDefaultDispenseItem {
|
||||
/**
|
||||
* Dispense the specified stack, play the dispense sound and spawn particles.
|
||||
*/
|
||||
protected ItemStack dispenseStack(IBlockSource par1IBlockSource, ItemStack par2ItemStack) {
|
||||
EnumFacing var3 = BlockDispenser.getFacing(par1IBlockSource.getBlockMetadata());
|
||||
int var4 = par1IBlockSource.getXInt() + var3.getFrontOffsetX();
|
||||
int var5 = par1IBlockSource.getYInt() + var3.getFrontOffsetY();
|
||||
int var6 = par1IBlockSource.getZInt() + var3.getFrontOffsetZ();
|
||||
AxisAlignedBB var7 = AxisAlignedBB.getAABBPool().getAABB((double) var4, (double) var5, (double) var6,
|
||||
(double) (var4 + 1), (double) (var5 + 1), (double) (var6 + 1));
|
||||
List var8 = par1IBlockSource.getWorld().selectEntitiesWithinAABB(EntityLiving.class, var7,
|
||||
new EntitySelectorArmoredMob(par2ItemStack));
|
||||
|
||||
if (var8.size() > 0) {
|
||||
EntityLiving var9 = (EntityLiving) var8.get(0);
|
||||
int var10 = var9 instanceof EntityPlayer ? 1 : 0;
|
||||
int var11 = EntityLiving.getArmorPosition(par2ItemStack);
|
||||
ItemStack var12 = par2ItemStack.copy();
|
||||
var12.stackSize = 1;
|
||||
var9.setCurrentItemOrArmor(var11 - var10, var12);
|
||||
var9.func_96120_a(var11, 2.0F);
|
||||
--par2ItemStack.stackSize;
|
||||
return par2ItemStack;
|
||||
} else {
|
||||
return super.dispenseStack(par1IBlockSource, par2ItemStack);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
final class BehaviorDispenseItemProvider implements IBehaviorDispenseItem {
|
||||
/**
|
||||
* Dispenses the specified ItemStack from a dispenser.
|
||||
*/
|
||||
public ItemStack dispense(IBlockSource par1IBlockSource, ItemStack par2ItemStack) {
|
||||
return par2ItemStack;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
final class BehaviorDispenseMinecart extends BehaviorDefaultDispenseItem {
|
||||
private final BehaviorDefaultDispenseItem field_96465_b = new BehaviorDefaultDispenseItem();
|
||||
|
||||
/**
|
||||
* Dispense the specified stack, play the dispense sound and spawn particles.
|
||||
*/
|
||||
public ItemStack dispenseStack(IBlockSource par1IBlockSource, ItemStack par2ItemStack) {
|
||||
EnumFacing var3 = BlockDispenser.getFacing(par1IBlockSource.getBlockMetadata());
|
||||
World var4 = par1IBlockSource.getWorld();
|
||||
double var5 = par1IBlockSource.getX() + (double) ((float) var3.getFrontOffsetX() * 1.125F);
|
||||
double var7 = par1IBlockSource.getY() + (double) ((float) var3.getFrontOffsetY() * 1.125F);
|
||||
double var9 = par1IBlockSource.getZ() + (double) ((float) var3.getFrontOffsetZ() * 1.125F);
|
||||
int var11 = par1IBlockSource.getXInt() + var3.getFrontOffsetX();
|
||||
int var12 = par1IBlockSource.getYInt() + var3.getFrontOffsetY();
|
||||
int var13 = par1IBlockSource.getZInt() + var3.getFrontOffsetZ();
|
||||
int var14 = var4.getBlockId(var11, var12, var13);
|
||||
double var15;
|
||||
|
||||
if (BlockRailBase.isRailBlock(var14)) {
|
||||
var15 = 0.0D;
|
||||
} else {
|
||||
if (var14 != 0 || !BlockRailBase.isRailBlock(var4.getBlockId(var11, var12 - 1, var13))) {
|
||||
return this.field_96465_b.dispense(par1IBlockSource, par2ItemStack);
|
||||
}
|
||||
|
||||
var15 = -1.0D;
|
||||
}
|
||||
|
||||
EntityMinecart var17 = EntityMinecart.createMinecart(var4, var5, var7 + var15, var9,
|
||||
((ItemMinecart) par2ItemStack.getItem()).minecartType);
|
||||
var4.spawnEntityInWorld(var17);
|
||||
par2ItemStack.splitStack(1);
|
||||
return par2ItemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the dispense sound from the specified block.
|
||||
*/
|
||||
protected void playDispenseSound(IBlockSource par1IBlockSource) {
|
||||
par1IBlockSource.getWorld().playAuxSFX(1000, par1IBlockSource.getXInt(), par1IBlockSource.getYInt(),
|
||||
par1IBlockSource.getZInt(), 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public abstract class BehaviorProjectileDispense extends BehaviorDefaultDispenseItem {
|
||||
/**
|
||||
* Dispense the specified stack, play the dispense sound and spawn particles.
|
||||
*/
|
||||
public ItemStack dispenseStack(IBlockSource par1IBlockSource, ItemStack par2ItemStack) {
|
||||
World var3 = par1IBlockSource.getWorld();
|
||||
IPosition var4 = BlockDispenser.getIPositionFromBlockSource(par1IBlockSource);
|
||||
EnumFacing var5 = BlockDispenser.getFacing(par1IBlockSource.getBlockMetadata());
|
||||
IProjectile var6 = this.getProjectileEntity(var3, var4);
|
||||
var6.setThrowableHeading((double) var5.getFrontOffsetX(), (double) ((float) var5.getFrontOffsetY() + 0.1F),
|
||||
(double) var5.getFrontOffsetZ(), this.func_82500_b(), this.func_82498_a());
|
||||
var3.spawnEntityInWorld((Entity) var6);
|
||||
par2ItemStack.splitStack(1);
|
||||
return par2ItemStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the dispense sound from the specified block.
|
||||
*/
|
||||
protected void playDispenseSound(IBlockSource par1IBlockSource) {
|
||||
par1IBlockSource.getWorld().playAuxSFX(1002, par1IBlockSource.getXInt(), par1IBlockSource.getYInt(),
|
||||
par1IBlockSource.getZInt(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the projectile entity spawned by this dispense behavior.
|
||||
*/
|
||||
protected abstract IProjectile getProjectileEntity(World var1, IPosition var2);
|
||||
|
||||
protected float func_82498_a() {
|
||||
return 6.0F;
|
||||
}
|
||||
|
||||
protected float func_82500_b() {
|
||||
return 1.1F;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.SysUtil;
|
||||
|
||||
public class BiomeCache {
|
||||
/** Reference to the WorldChunkManager */
|
||||
private final WorldChunkManager chunkManager;
|
||||
|
||||
/** The last time this BiomeCache was cleaned, in milliseconds. */
|
||||
private long lastCleanupTime = 0L;
|
||||
|
||||
/**
|
||||
* The map of keys to BiomeCacheBlocks. Keys are based on the chunk x, z
|
||||
* coordinates as (x | z << 32).
|
||||
*/
|
||||
private LongHashMap cacheMap = new LongHashMap();
|
||||
|
||||
/** The list of cached BiomeCacheBlocks */
|
||||
private List cache = new ArrayList();
|
||||
|
||||
public BiomeCache(WorldChunkManager par1WorldChunkManager) {
|
||||
this.chunkManager = par1WorldChunkManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a biome cache block at location specified.
|
||||
*/
|
||||
public BiomeCacheBlock getBiomeCacheBlock(int par1, int par2) {
|
||||
par1 >>= 4;
|
||||
par2 >>= 4;
|
||||
long var3 = (long) par1 & 4294967295L | ((long) par2 & 4294967295L) << 32;
|
||||
BiomeCacheBlock var5 = (BiomeCacheBlock) this.cacheMap.getValueByKey(var3);
|
||||
|
||||
if (var5 == null) {
|
||||
var5 = new BiomeCacheBlock(this, par1, par2);
|
||||
this.cacheMap.add(var3, var5);
|
||||
this.cache.add(var5);
|
||||
}
|
||||
|
||||
var5.lastAccessTime = SysUtil.steadyTimeMillis();
|
||||
return var5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BiomeGenBase related to the x, z position from the cache.
|
||||
*/
|
||||
public BiomeGenBase getBiomeGenAt(int par1, int par2) {
|
||||
return this.getBiomeCacheBlock(par1, par2).getBiomeGenAt(par1, par2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes BiomeCacheBlocks from this cache that haven't been accessed in at
|
||||
* least 30 seconds.
|
||||
*/
|
||||
public void cleanupCache() {
|
||||
long var1 = SysUtil.steadyTimeMillis();
|
||||
long var3 = var1 - this.lastCleanupTime;
|
||||
|
||||
if (var3 > 7500L || var3 < 0L) {
|
||||
this.lastCleanupTime = var1;
|
||||
|
||||
for (int var5 = 0; var5 < this.cache.size(); ++var5) {
|
||||
BiomeCacheBlock var6 = (BiomeCacheBlock) this.cache.get(var5);
|
||||
long var7 = var1 - var6.lastAccessTime;
|
||||
|
||||
if (var7 > 30000L || var7 < 0L) {
|
||||
this.cache.remove(var5--);
|
||||
long var9 = (long) var6.xPosition & 4294967295L | ((long) var6.zPosition & 4294967295L) << 32;
|
||||
this.cacheMap.remove(var9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of cached biome types in the BiomeCacheBlock at the given
|
||||
* location.
|
||||
*/
|
||||
public BiomeGenBase[] getCachedBiomes(int par1, int par2) {
|
||||
return this.getBiomeCacheBlock(par1, par2).biomes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world chunk manager object for a biome list.
|
||||
*/
|
||||
static WorldChunkManager getChunkManager(BiomeCache par0BiomeCache) {
|
||||
return par0BiomeCache.chunkManager;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeCacheBlock {
|
||||
/** An array of chunk temperatures saved by this cache. */
|
||||
public float[] temperatureValues;
|
||||
|
||||
/** An array of chunk rainfall values saved by this cache. */
|
||||
public float[] rainfallValues;
|
||||
|
||||
/** The array of biome types stored in this BiomeCacheBlock. */
|
||||
public BiomeGenBase[] biomes;
|
||||
|
||||
/** The x coordinate of the BiomeCacheBlock. */
|
||||
public int xPosition;
|
||||
|
||||
/** The z coordinate of the BiomeCacheBlock. */
|
||||
public int zPosition;
|
||||
|
||||
/** The last time this BiomeCacheBlock was accessed, in milliseconds. */
|
||||
public long lastAccessTime;
|
||||
|
||||
/** The BiomeCache object that contains this BiomeCacheBlock */
|
||||
final BiomeCache theBiomeCache;
|
||||
|
||||
public BiomeCacheBlock(BiomeCache par1BiomeCache, int par2, int par3) {
|
||||
this.theBiomeCache = par1BiomeCache;
|
||||
this.temperatureValues = new float[256];
|
||||
this.rainfallValues = new float[256];
|
||||
this.biomes = new BiomeGenBase[256];
|
||||
this.xPosition = par2;
|
||||
this.zPosition = par3;
|
||||
BiomeCache.getChunkManager(par1BiomeCache).getTemperatures(this.temperatureValues, par2 << 4, par3 << 4, 16,
|
||||
16);
|
||||
BiomeCache.getChunkManager(par1BiomeCache).getRainfall(this.rainfallValues, par2 << 4, par3 << 4, 16, 16);
|
||||
BiomeCache.getChunkManager(par1BiomeCache).getBiomeGenAt(this.biomes, par2 << 4, par3 << 4, 16, 16, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BiomeGenBase related to the x, z position from the cache block.
|
||||
*/
|
||||
public BiomeGenBase getBiomeGenAt(int par1, int par2) {
|
||||
return this.biomes[par1 & 15 | (par2 & 15) << 4];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,405 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.EaglercraftRandom;
|
||||
|
||||
public class BiomeDecorator {
|
||||
/** The world the BiomeDecorator is currently decorating */
|
||||
protected World currentWorld;
|
||||
|
||||
/** The Biome Decorator's random number generator. */
|
||||
protected EaglercraftRandom randomGenerator;
|
||||
|
||||
/** The X-coordinate of the chunk currently being decorated */
|
||||
protected int chunk_X;
|
||||
|
||||
/** The Z-coordinate of the chunk currently being decorated */
|
||||
protected int chunk_Z;
|
||||
|
||||
/** The biome generator object. */
|
||||
protected BiomeGenBase biome;
|
||||
|
||||
/** The clay generator. */
|
||||
protected WorldGenerator clayGen = new WorldGenClay(4);
|
||||
|
||||
/** The sand generator. */
|
||||
protected WorldGenerator sandGen;
|
||||
|
||||
/** The gravel generator. */
|
||||
protected WorldGenerator gravelAsSandGen;
|
||||
|
||||
/** The dirt generator. */
|
||||
protected WorldGenerator dirtGen;
|
||||
protected WorldGenerator gravelGen;
|
||||
protected WorldGenerator coalGen;
|
||||
protected WorldGenerator ironGen;
|
||||
|
||||
/** Field that holds gold WorldGenMinable */
|
||||
protected WorldGenerator goldGen;
|
||||
|
||||
/** Field that holds redstone WorldGenMinable */
|
||||
protected WorldGenerator redstoneGen;
|
||||
|
||||
/** Field that holds diamond WorldGenMinable */
|
||||
protected WorldGenerator diamondGen;
|
||||
|
||||
/** Field that holds Lapis WorldGenMinable */
|
||||
protected WorldGenerator lapisGen;
|
||||
|
||||
/** Field that holds one of the plantYellow WorldGenFlowers */
|
||||
protected WorldGenerator plantYellowGen;
|
||||
|
||||
/** Field that holds one of the plantRed WorldGenFlowers */
|
||||
protected WorldGenerator plantRedGen;
|
||||
|
||||
/** Field that holds mushroomBrown WorldGenFlowers */
|
||||
protected WorldGenerator mushroomBrownGen;
|
||||
|
||||
/** Field that holds mushroomRed WorldGenFlowers */
|
||||
protected WorldGenerator mushroomRedGen;
|
||||
|
||||
/** Field that holds big mushroom generator */
|
||||
protected WorldGenerator bigMushroomGen;
|
||||
|
||||
/** Field that holds WorldGenReed */
|
||||
protected WorldGenerator reedGen;
|
||||
|
||||
/** Field that holds WorldGenCactus */
|
||||
protected WorldGenerator cactusGen;
|
||||
|
||||
/** The water lily generation! */
|
||||
protected WorldGenerator waterlilyGen;
|
||||
|
||||
/** Amount of waterlilys per chunk. */
|
||||
protected int waterlilyPerChunk;
|
||||
|
||||
/**
|
||||
* The number of trees to attempt to generate per chunk. Up to 10 in forests,
|
||||
* none in deserts.
|
||||
*/
|
||||
protected int treesPerChunk;
|
||||
|
||||
/**
|
||||
* The number of yellow flower patches to generate per chunk. The game generates
|
||||
* much less than this number, since it attempts to generate them at a random
|
||||
* altitude.
|
||||
*/
|
||||
protected int flowersPerChunk;
|
||||
|
||||
/** The amount of tall grass to generate per chunk. */
|
||||
protected int grassPerChunk;
|
||||
|
||||
/**
|
||||
* The number of dead bushes to generate per chunk. Used in deserts and swamps.
|
||||
*/
|
||||
protected int deadBushPerChunk;
|
||||
|
||||
/**
|
||||
* The number of extra mushroom patches per chunk. It generates 1/4 this number
|
||||
* in brown mushroom patches, and 1/8 this number in red mushroom patches. These
|
||||
* mushrooms go beyond the default base number of mushrooms.
|
||||
*/
|
||||
protected int mushroomsPerChunk;
|
||||
|
||||
/**
|
||||
* The number of reeds to generate per chunk. Reeds won't generate if the
|
||||
* randomly selected placement is unsuitable.
|
||||
*/
|
||||
protected int reedsPerChunk;
|
||||
|
||||
/**
|
||||
* The number of cactus plants to generate per chunk. Cacti only work on sand.
|
||||
*/
|
||||
protected int cactiPerChunk;
|
||||
|
||||
/**
|
||||
* The number of sand patches to generate per chunk. Sand patches only generate
|
||||
* when part of it is underwater.
|
||||
*/
|
||||
protected int sandPerChunk;
|
||||
|
||||
/**
|
||||
* The number of sand patches to generate per chunk. Sand patches only generate
|
||||
* when part of it is underwater. There appear to be two separate fields for
|
||||
* this.
|
||||
*/
|
||||
protected int sandPerChunk2;
|
||||
|
||||
/**
|
||||
* The number of clay patches to generate per chunk. Only generates when part of
|
||||
* it is underwater.
|
||||
*/
|
||||
protected int clayPerChunk;
|
||||
|
||||
/** Amount of big mushrooms per chunk */
|
||||
protected int bigMushroomsPerChunk;
|
||||
|
||||
/** True if decorator should generate surface lava & water */
|
||||
public boolean generateLakes;
|
||||
|
||||
public BiomeDecorator(BiomeGenBase par1BiomeGenBase) {
|
||||
this.sandGen = new WorldGenSand(7, Block.sand.blockID);
|
||||
this.gravelAsSandGen = new WorldGenSand(6, Block.gravel.blockID);
|
||||
this.dirtGen = new WorldGenMinable(Block.dirt.blockID, 32);
|
||||
this.gravelGen = new WorldGenMinable(Block.gravel.blockID, 32);
|
||||
this.coalGen = new WorldGenMinable(Block.oreCoal.blockID, 16);
|
||||
this.ironGen = new WorldGenMinable(Block.oreIron.blockID, 8);
|
||||
this.goldGen = new WorldGenMinable(Block.oreGold.blockID, 8);
|
||||
this.redstoneGen = new WorldGenMinable(Block.oreRedstone.blockID, 7);
|
||||
this.diamondGen = new WorldGenMinable(Block.oreDiamond.blockID, 7);
|
||||
this.lapisGen = new WorldGenMinable(Block.oreLapis.blockID, 6);
|
||||
this.plantYellowGen = new WorldGenFlowers(Block.plantYellow.blockID);
|
||||
this.plantRedGen = new WorldGenFlowers(Block.plantRed.blockID);
|
||||
this.mushroomBrownGen = new WorldGenFlowers(Block.mushroomBrown.blockID);
|
||||
this.mushroomRedGen = new WorldGenFlowers(Block.mushroomRed.blockID);
|
||||
this.bigMushroomGen = new WorldGenBigMushroom();
|
||||
this.reedGen = new WorldGenReed();
|
||||
this.cactusGen = new WorldGenCactus();
|
||||
this.waterlilyGen = new WorldGenWaterlily();
|
||||
this.waterlilyPerChunk = 0;
|
||||
this.treesPerChunk = 0;
|
||||
this.flowersPerChunk = 2;
|
||||
this.grassPerChunk = 1;
|
||||
this.deadBushPerChunk = 0;
|
||||
this.mushroomsPerChunk = 0;
|
||||
this.reedsPerChunk = 0;
|
||||
this.cactiPerChunk = 0;
|
||||
this.sandPerChunk = 1;
|
||||
this.sandPerChunk2 = 3;
|
||||
this.clayPerChunk = 1;
|
||||
this.bigMushroomsPerChunk = 0;
|
||||
this.generateLakes = true;
|
||||
this.biome = par1BiomeGenBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorates the world. Calls code that was formerly (pre-1.8) in
|
||||
* ChunkProviderGenerate.populate
|
||||
*/
|
||||
public void decorate(World par1World, EaglercraftRandom par2Random, int par3, int par4) {
|
||||
if (this.currentWorld != null) {
|
||||
throw new RuntimeException("Already decorating!!");
|
||||
} else {
|
||||
this.currentWorld = par1World;
|
||||
this.randomGenerator = par2Random;
|
||||
this.chunk_X = par3;
|
||||
this.chunk_Z = par4;
|
||||
this.decorate();
|
||||
this.currentWorld = null;
|
||||
this.randomGenerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method that does the work of actually decorating chunks
|
||||
*/
|
||||
protected void decorate() {
|
||||
this.generateOres();
|
||||
int var1;
|
||||
int var2;
|
||||
int var3;
|
||||
|
||||
for (var1 = 0; var1 < this.sandPerChunk2; ++var1) {
|
||||
var2 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var3 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.sandGen.generate(this.currentWorld, this.randomGenerator, var2,
|
||||
this.currentWorld.getTopSolidOrLiquidBlock(var2, var3), var3);
|
||||
}
|
||||
|
||||
for (var1 = 0; var1 < this.clayPerChunk; ++var1) {
|
||||
var2 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var3 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.clayGen.generate(this.currentWorld, this.randomGenerator, var2,
|
||||
this.currentWorld.getTopSolidOrLiquidBlock(var2, var3), var3);
|
||||
}
|
||||
|
||||
for (var1 = 0; var1 < this.sandPerChunk; ++var1) {
|
||||
var2 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var3 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.sandGen.generate(this.currentWorld, this.randomGenerator, var2,
|
||||
this.currentWorld.getTopSolidOrLiquidBlock(var2, var3), var3);
|
||||
}
|
||||
|
||||
var1 = this.treesPerChunk;
|
||||
|
||||
if (this.randomGenerator.nextInt(10) == 0) {
|
||||
++var1;
|
||||
}
|
||||
|
||||
int var4;
|
||||
|
||||
for (var2 = 0; var2 < var1; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
WorldGenerator var5 = this.biome.getRandomWorldGenForTrees(this.randomGenerator);
|
||||
var5.setScale(1.0D, 1.0D, 1.0D);
|
||||
var5.generate(this.currentWorld, this.randomGenerator, var3, this.currentWorld.getHeightValue(var3, var4),
|
||||
var4);
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < this.bigMushroomsPerChunk; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.bigMushroomGen.generate(this.currentWorld, this.randomGenerator, var3,
|
||||
this.currentWorld.getHeightValue(var3, var4), var4);
|
||||
}
|
||||
|
||||
int var7;
|
||||
|
||||
for (var2 = 0; var2 < this.flowersPerChunk; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.randomGenerator.nextInt(128);
|
||||
var7 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.plantYellowGen.generate(this.currentWorld, this.randomGenerator, var3, var4, var7);
|
||||
|
||||
if (this.randomGenerator.nextInt(4) == 0) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.randomGenerator.nextInt(128);
|
||||
var7 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.plantRedGen.generate(this.currentWorld, this.randomGenerator, var3, var4, var7);
|
||||
}
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < this.grassPerChunk; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.randomGenerator.nextInt(128);
|
||||
var7 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
WorldGenerator var6 = this.biome.getRandomWorldGenForGrass(this.randomGenerator);
|
||||
var6.generate(this.currentWorld, this.randomGenerator, var3, var4, var7);
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < this.deadBushPerChunk; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.randomGenerator.nextInt(128);
|
||||
var7 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
(new WorldGenDeadBush(Block.deadBush.blockID)).generate(this.currentWorld, this.randomGenerator, var3, var4,
|
||||
var7);
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < this.waterlilyPerChunk; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
|
||||
for (var7 = this.randomGenerator.nextInt(128); var7 > 0
|
||||
&& this.currentWorld.getBlockId(var3, var7 - 1, var4) == 0; --var7) {
|
||||
;
|
||||
}
|
||||
|
||||
this.waterlilyGen.generate(this.currentWorld, this.randomGenerator, var3, var7, var4);
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < this.mushroomsPerChunk; ++var2) {
|
||||
if (this.randomGenerator.nextInt(4) == 0) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
var7 = this.currentWorld.getHeightValue(var3, var4);
|
||||
this.mushroomBrownGen.generate(this.currentWorld, this.randomGenerator, var3, var7, var4);
|
||||
}
|
||||
|
||||
if (this.randomGenerator.nextInt(8) == 0) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
var7 = this.randomGenerator.nextInt(128);
|
||||
this.mushroomRedGen.generate(this.currentWorld, this.randomGenerator, var3, var7, var4);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.randomGenerator.nextInt(4) == 0) {
|
||||
var2 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var3 = this.randomGenerator.nextInt(128);
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.mushroomBrownGen.generate(this.currentWorld, this.randomGenerator, var2, var3, var4);
|
||||
}
|
||||
|
||||
if (this.randomGenerator.nextInt(8) == 0) {
|
||||
var2 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var3 = this.randomGenerator.nextInt(128);
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.mushroomRedGen.generate(this.currentWorld, this.randomGenerator, var2, var3, var4);
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < this.reedsPerChunk; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
var7 = this.randomGenerator.nextInt(128);
|
||||
this.reedGen.generate(this.currentWorld, this.randomGenerator, var3, var7, var4);
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < 10; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.randomGenerator.nextInt(128);
|
||||
var7 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.reedGen.generate(this.currentWorld, this.randomGenerator, var3, var4, var7);
|
||||
}
|
||||
|
||||
if (this.randomGenerator.nextInt(32) == 0) {
|
||||
var2 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var3 = this.randomGenerator.nextInt(128);
|
||||
var4 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
(new WorldGenPumpkin()).generate(this.currentWorld, this.randomGenerator, var2, var3, var4);
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < this.cactiPerChunk; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.randomGenerator.nextInt(128);
|
||||
var7 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
this.cactusGen.generate(this.currentWorld, this.randomGenerator, var3, var4, var7);
|
||||
}
|
||||
|
||||
if (this.generateLakes) {
|
||||
for (var2 = 0; var2 < 50; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.randomGenerator.nextInt(this.randomGenerator.nextInt(120) + 8);
|
||||
var7 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
(new WorldGenLiquids(Block.waterMoving.blockID)).generate(this.currentWorld, this.randomGenerator, var3,
|
||||
var4, var7);
|
||||
}
|
||||
|
||||
for (var2 = 0; var2 < 20; ++var2) {
|
||||
var3 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
var4 = this.randomGenerator
|
||||
.nextInt(this.randomGenerator.nextInt(this.randomGenerator.nextInt(112) + 8) + 8);
|
||||
var7 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
(new WorldGenLiquids(Block.lavaMoving.blockID)).generate(this.currentWorld, this.randomGenerator, var3,
|
||||
var4, var7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard ore generation helper. Generates most ores.
|
||||
*/
|
||||
protected void genStandardOre1(int par1, WorldGenerator par2WorldGenerator, int par3, int par4) {
|
||||
for (int var5 = 0; var5 < par1; ++var5) {
|
||||
int var6 = this.chunk_X + this.randomGenerator.nextInt(16);
|
||||
int var7 = this.randomGenerator.nextInt(par4 - par3) + par3;
|
||||
int var8 = this.chunk_Z + this.randomGenerator.nextInt(16);
|
||||
par2WorldGenerator.generate(this.currentWorld, this.randomGenerator, var6, var7, var8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard ore generation helper. Generates Lapis Lazuli.
|
||||
*/
|
||||
protected void genStandardOre2(int par1, WorldGenerator par2WorldGenerator, int par3, int par4) {
|
||||
for (int var5 = 0; var5 < par1; ++var5) {
|
||||
int var6 = this.chunk_X + this.randomGenerator.nextInt(16);
|
||||
int var7 = this.randomGenerator.nextInt(par4) + this.randomGenerator.nextInt(par4) + (par3 - par4);
|
||||
int var8 = this.chunk_Z + this.randomGenerator.nextInt(16);
|
||||
par2WorldGenerator.generate(this.currentWorld, this.randomGenerator, var6, var7, var8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates ores in the current chunk
|
||||
*/
|
||||
protected void generateOres() {
|
||||
this.genStandardOre1(20, this.dirtGen, 0, 128);
|
||||
this.genStandardOre1(10, this.gravelGen, 0, 128);
|
||||
this.genStandardOre1(20, this.coalGen, 0, 128);
|
||||
this.genStandardOre1(20, this.ironGen, 0, 64);
|
||||
this.genStandardOre1(2, this.goldGen, 0, 32);
|
||||
this.genStandardOre1(8, this.redstoneGen, 0, 16);
|
||||
this.genStandardOre1(1, this.diamondGen, 0, 16);
|
||||
this.genStandardOre2(1, this.lapisGen, 16, 16);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeEndDecorator extends BiomeDecorator {
|
||||
protected WorldGenerator spikeGen;
|
||||
|
||||
public BiomeEndDecorator(BiomeGenBase par1BiomeGenBase) {
|
||||
super(par1BiomeGenBase);
|
||||
this.spikeGen = new WorldGenSpikes(Block.whiteStone.blockID);
|
||||
}
|
||||
|
||||
/**
|
||||
* The method that does the work of actually decorating chunks
|
||||
*/
|
||||
protected void decorate() {
|
||||
this.generateOres();
|
||||
|
||||
if (this.randomGenerator.nextInt(5) == 0) {
|
||||
int var1 = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
|
||||
int var2 = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
|
||||
int var3 = this.currentWorld.getTopSolidOrLiquidBlock(var1, var2);
|
||||
|
||||
if (var3 > 0) {
|
||||
;
|
||||
}
|
||||
|
||||
this.spikeGen.generate(this.currentWorld, this.randomGenerator, var1, var3, var2);
|
||||
}
|
||||
|
||||
if (this.chunk_X == 0 && this.chunk_Z == 0) {
|
||||
EntityDragon var4 = new EntityDragon(this.currentWorld);
|
||||
var4.setLocationAndAngles(0.0D, 128.0D, 0.0D, this.randomGenerator.nextFloat() * 360.0F, 0.0F);
|
||||
this.currentWorld.spawnEntityInWorld(var4);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.EaglercraftRandom;
|
||||
|
||||
public abstract class BiomeGenBase {
|
||||
/** An array of all the biomes, indexed by biome id. */
|
||||
public static final BiomeGenBase[] biomeList = new BiomeGenBase[256];
|
||||
public static final BiomeGenBase ocean = (new BiomeGenOcean(0)).setColor(112).setBiomeName("Ocean")
|
||||
.setMinMaxHeight(-1.0F, 0.4F);
|
||||
public static final BiomeGenBase plains = (new BiomeGenPlains(1)).setColor(9286496).setBiomeName("Plains")
|
||||
.setTemperatureRainfall(0.8F, 0.4F);
|
||||
public static final BiomeGenBase desert = (new BiomeGenDesert(2)).setColor(16421912).setBiomeName("Desert")
|
||||
.setDisableRain().setTemperatureRainfall(2.0F, 0.0F).setMinMaxHeight(0.1F, 0.2F);
|
||||
public static final BiomeGenBase extremeHills = (new BiomeGenHills(3)).setColor(6316128)
|
||||
.setBiomeName("Extreme Hills").setMinMaxHeight(0.3F, 1.5F).setTemperatureRainfall(0.2F, 0.3F);
|
||||
public static final BiomeGenBase forest = (new BiomeGenForest(4)).setColor(353825).setBiomeName("Forest")
|
||||
.func_76733_a(5159473).setTemperatureRainfall(0.7F, 0.8F);
|
||||
public static final BiomeGenBase taiga = (new BiomeGenTaiga(5)).setColor(747097).setBiomeName("Taiga")
|
||||
.func_76733_a(5159473).setEnableSnow().setTemperatureRainfall(0.05F, 0.8F).setMinMaxHeight(0.1F, 0.4F);
|
||||
public static final BiomeGenBase swampland = (new BiomeGenSwamp(6)).setColor(522674).setBiomeName("Swampland")
|
||||
.func_76733_a(9154376).setMinMaxHeight(-0.2F, 0.1F).setTemperatureRainfall(0.8F, 0.9F);
|
||||
public static final BiomeGenBase river = (new BiomeGenRiver(7)).setColor(255).setBiomeName("River")
|
||||
.setMinMaxHeight(-0.5F, 0.0F);
|
||||
public static final BiomeGenBase hell = (new BiomeGenHell(8)).setColor(16711680).setBiomeName("Hell")
|
||||
.setDisableRain().setTemperatureRainfall(2.0F, 0.0F);
|
||||
|
||||
/** Is the biome used for sky world. */
|
||||
public static final BiomeGenBase sky = (new BiomeGenEnd(9)).setColor(8421631).setBiomeName("Sky").setDisableRain();
|
||||
public static final BiomeGenBase frozenOcean = (new BiomeGenOcean(10)).setColor(9474208).setBiomeName("FrozenOcean")
|
||||
.setEnableSnow().setMinMaxHeight(-1.0F, 0.5F).setTemperatureRainfall(0.0F, 0.5F);
|
||||
public static final BiomeGenBase frozenRiver = (new BiomeGenRiver(11)).setColor(10526975)
|
||||
.setBiomeName("FrozenRiver").setEnableSnow().setMinMaxHeight(-0.5F, 0.0F)
|
||||
.setTemperatureRainfall(0.0F, 0.5F);
|
||||
public static final BiomeGenBase icePlains = (new BiomeGenSnow(12)).setColor(16777215).setBiomeName("Ice Plains")
|
||||
.setEnableSnow().setTemperatureRainfall(0.0F, 0.5F);
|
||||
public static final BiomeGenBase iceMountains = (new BiomeGenSnow(13)).setColor(10526880)
|
||||
.setBiomeName("Ice Mountains").setEnableSnow().setMinMaxHeight(0.3F, 1.3F)
|
||||
.setTemperatureRainfall(0.0F, 0.5F);
|
||||
public static final BiomeGenBase mushroomIsland = (new BiomeGenMushroomIsland(14)).setColor(16711935)
|
||||
.setBiomeName("MushroomIsland").setTemperatureRainfall(0.9F, 1.0F).setMinMaxHeight(0.2F, 1.0F);
|
||||
public static final BiomeGenBase mushroomIslandShore = (new BiomeGenMushroomIsland(15)).setColor(10486015)
|
||||
.setBiomeName("MushroomIslandShore").setTemperatureRainfall(0.9F, 1.0F).setMinMaxHeight(-1.0F, 0.1F);
|
||||
|
||||
/** Beach biome. */
|
||||
public static final BiomeGenBase beach = (new BiomeGenBeach(16)).setColor(16440917).setBiomeName("Beach")
|
||||
.setTemperatureRainfall(0.8F, 0.4F).setMinMaxHeight(0.0F, 0.1F);
|
||||
|
||||
/** Desert Hills biome. */
|
||||
public static final BiomeGenBase desertHills = (new BiomeGenDesert(17)).setColor(13786898)
|
||||
.setBiomeName("DesertHills").setDisableRain().setTemperatureRainfall(2.0F, 0.0F)
|
||||
.setMinMaxHeight(0.3F, 0.8F);
|
||||
|
||||
/** Forest Hills biome. */
|
||||
public static final BiomeGenBase forestHills = (new BiomeGenForest(18)).setColor(2250012)
|
||||
.setBiomeName("ForestHills").func_76733_a(5159473).setTemperatureRainfall(0.7F, 0.8F)
|
||||
.setMinMaxHeight(0.3F, 0.7F);
|
||||
|
||||
/** Taiga Hills biome. */
|
||||
public static final BiomeGenBase taigaHills = (new BiomeGenTaiga(19)).setColor(1456435).setBiomeName("TaigaHills")
|
||||
.setEnableSnow().func_76733_a(5159473).setTemperatureRainfall(0.05F, 0.8F).setMinMaxHeight(0.3F, 0.8F);
|
||||
|
||||
/** Extreme Hills Edge biome. */
|
||||
public static final BiomeGenBase extremeHillsEdge = (new BiomeGenHills(20)).setColor(7501978)
|
||||
.setBiomeName("Extreme Hills Edge").setMinMaxHeight(0.2F, 0.8F).setTemperatureRainfall(0.2F, 0.3F);
|
||||
|
||||
/** Jungle biome identifier */
|
||||
public static final BiomeGenBase jungle = (new BiomeGenJungle(21)).setColor(5470985).setBiomeName("Jungle")
|
||||
.func_76733_a(5470985).setTemperatureRainfall(1.2F, 0.9F).setMinMaxHeight(0.2F, 0.4F);
|
||||
public static final BiomeGenBase jungleHills = (new BiomeGenJungle(22)).setColor(2900485)
|
||||
.setBiomeName("JungleHills").func_76733_a(5470985).setTemperatureRainfall(1.2F, 0.9F)
|
||||
.setMinMaxHeight(1.8F, 0.5F);
|
||||
public String biomeName;
|
||||
public int color;
|
||||
|
||||
/** The block expected to be on the top of this biome */
|
||||
public byte topBlock;
|
||||
|
||||
/** The block to fill spots in when not on the top */
|
||||
public byte fillerBlock;
|
||||
public int field_76754_C;
|
||||
|
||||
/** The minimum height of this biome. Default 0.1. */
|
||||
public float minHeight;
|
||||
|
||||
/** The maximum height of this biome. Default 0.3. */
|
||||
public float maxHeight;
|
||||
|
||||
/** The temperature of this biome. */
|
||||
public float temperature;
|
||||
|
||||
/** The rainfall in this biome. */
|
||||
public float rainfall;
|
||||
|
||||
/** Color tint applied to water depending on biome */
|
||||
public int waterColorMultiplier;
|
||||
|
||||
/** The biome decorator. */
|
||||
public BiomeDecorator theBiomeDecorator;
|
||||
|
||||
/**
|
||||
* Holds the classes of IMobs (hostile mobs) that can be spawned in the biome.
|
||||
*/
|
||||
protected List spawnableMonsterList;
|
||||
|
||||
/**
|
||||
* Holds the classes of any creature that can be spawned in the biome as
|
||||
* friendly creature.
|
||||
*/
|
||||
protected List spawnableCreatureList;
|
||||
|
||||
/**
|
||||
* Holds the classes of any aquatic creature that can be spawned in the water of
|
||||
* the biome.
|
||||
*/
|
||||
protected List spawnableWaterCreatureList;
|
||||
protected List spawnableCaveCreatureList;
|
||||
|
||||
/** Set to true if snow is enabled for this biome. */
|
||||
private boolean enableSnow;
|
||||
|
||||
/**
|
||||
* Is true (default) if the biome support rain (desert and nether can't have
|
||||
* rain)
|
||||
*/
|
||||
private boolean enableRain;
|
||||
|
||||
/** The id number to this biome, and its index in the biomeList array. */
|
||||
public final int biomeID;
|
||||
|
||||
/** The tree generator. */
|
||||
protected WorldGenTrees worldGeneratorTrees;
|
||||
|
||||
/** The big tree generator. */
|
||||
protected WorldGenBigTree worldGeneratorBigTree;
|
||||
|
||||
/** The forest generator. */
|
||||
protected WorldGenForest worldGeneratorForest;
|
||||
|
||||
/** The swamp tree generator. */
|
||||
protected WorldGenSwamp worldGeneratorSwamp;
|
||||
|
||||
protected BiomeGenBase(int par1) {
|
||||
this.topBlock = (byte) Block.grass.blockID;
|
||||
this.fillerBlock = (byte) Block.dirt.blockID;
|
||||
this.field_76754_C = 5169201;
|
||||
this.minHeight = 0.1F;
|
||||
this.maxHeight = 0.3F;
|
||||
this.temperature = 0.5F;
|
||||
this.rainfall = 0.5F;
|
||||
this.waterColorMultiplier = 16777215;
|
||||
this.spawnableMonsterList = new ArrayList();
|
||||
this.spawnableCreatureList = new ArrayList();
|
||||
this.spawnableWaterCreatureList = new ArrayList();
|
||||
this.spawnableCaveCreatureList = new ArrayList();
|
||||
this.enableRain = true;
|
||||
this.worldGeneratorTrees = new WorldGenTrees(false);
|
||||
this.worldGeneratorBigTree = new WorldGenBigTree(false);
|
||||
this.worldGeneratorForest = new WorldGenForest(false);
|
||||
this.worldGeneratorSwamp = new WorldGenSwamp();
|
||||
this.biomeID = par1;
|
||||
biomeList[par1] = this;
|
||||
this.theBiomeDecorator = this.createBiomeDecorator();
|
||||
this.spawnableCreatureList.add(new SpawnListEntry((w) -> new EntitySheep(w), 12, 4, 4));
|
||||
this.spawnableCreatureList.add(new SpawnListEntry((w) -> new EntityPig(w), 10, 4, 4));
|
||||
this.spawnableCreatureList.add(new SpawnListEntry((w) -> new EntityChicken(w), 10, 4, 4));
|
||||
this.spawnableCreatureList.add(new SpawnListEntry((w) -> new EntityCow(w), 8, 4, 4));
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntitySpider(w), 10, 4, 4));
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntityZombie(w), 10, 4, 4));
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntitySkeleton(w), 10, 4, 4));
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntityCreeper(w), 10, 4, 4));
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntitySlime(w), 10, 4, 4));
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntityEnderman(w), 1, 1, 4));
|
||||
this.spawnableWaterCreatureList.add(new SpawnListEntry((w) -> new EntitySquid(w), 10, 4, 4));
|
||||
this.spawnableCaveCreatureList.add(new SpawnListEntry((w) -> new EntityBat(w), 10, 8, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new BiomeDecorator for this BiomeGenBase
|
||||
*/
|
||||
protected BiomeDecorator createBiomeDecorator() {
|
||||
return new BiomeDecorator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the temperature and rainfall of this biome.
|
||||
*/
|
||||
private BiomeGenBase setTemperatureRainfall(float par1, float par2) {
|
||||
if (par1 > 0.1F && par1 < 0.2F) {
|
||||
throw new IllegalArgumentException("Please avoid temperatures in the range 0.1 - 0.2 because of snow");
|
||||
} else {
|
||||
this.temperature = par1;
|
||||
this.rainfall = par2;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum and maximum height of this biome. Seems to go from -2.0 to
|
||||
* 2.0.
|
||||
*/
|
||||
private BiomeGenBase setMinMaxHeight(float par1, float par2) {
|
||||
this.minHeight = par1;
|
||||
this.maxHeight = par2;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the rain for the biome.
|
||||
*/
|
||||
private BiomeGenBase setDisableRain() {
|
||||
this.enableRain = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a WorldGen appropriate for this biome.
|
||||
*/
|
||||
public WorldGenerator getRandomWorldGenForTrees(EaglercraftRandom par1Random) {
|
||||
return (WorldGenerator) (par1Random.nextInt(10) == 0 ? this.worldGeneratorBigTree : this.worldGeneratorTrees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a WorldGen appropriate for this biome.
|
||||
*/
|
||||
public WorldGenerator getRandomWorldGenForGrass(EaglercraftRandom par1Random) {
|
||||
return new WorldGenTallGrass(Block.tallGrass.blockID, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* sets enableSnow to true during biome initialization. returns BiomeGenBase.
|
||||
*/
|
||||
protected BiomeGenBase setEnableSnow() {
|
||||
this.enableSnow = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected BiomeGenBase setBiomeName(String par1Str) {
|
||||
this.biomeName = par1Str;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected BiomeGenBase func_76733_a(int par1) {
|
||||
this.field_76754_C = par1;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected BiomeGenBase setColor(int par1) {
|
||||
this.color = par1;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the correspondent list of the EnumCreatureType informed.
|
||||
*/
|
||||
public List getSpawnableList(EnumCreatureType par1EnumCreatureType) {
|
||||
return par1EnumCreatureType == EnumCreatureType.monster ? this.spawnableMonsterList
|
||||
: (par1EnumCreatureType == EnumCreatureType.creature ? this.spawnableCreatureList
|
||||
: (par1EnumCreatureType == EnumCreatureType.waterCreature ? this.spawnableWaterCreatureList
|
||||
: (par1EnumCreatureType == EnumCreatureType.ambient ? this.spawnableCaveCreatureList
|
||||
: null)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the biome have snowfall instead a normal rain.
|
||||
*/
|
||||
public boolean getEnableSnow() {
|
||||
return this.enableSnow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the biome supports lightning bolt spawn, either by have the
|
||||
* bolts enabled and have rain enabled.
|
||||
*/
|
||||
public boolean canSpawnLightningBolt() {
|
||||
return this.enableSnow ? false : this.enableRain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the rainfall level of the biome is extremely high
|
||||
*/
|
||||
public boolean isHighHumidity() {
|
||||
return this.rainfall > 0.85F;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the chance a creature has to spawn.
|
||||
*/
|
||||
public float getSpawningChance() {
|
||||
return 0.1F;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an integer representation of this biome's rainfall
|
||||
*/
|
||||
public final int getIntRainfall() {
|
||||
return (int) (this.rainfall * 65536.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an integer representation of this biome's temperature
|
||||
*/
|
||||
public final int getIntTemperature() {
|
||||
return (int) (this.temperature * 65536.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a floating point representation of this biome's temperature
|
||||
*/
|
||||
public final float getFloatTemperature() {
|
||||
return this.temperature;
|
||||
}
|
||||
|
||||
public void decorate(World par1World, EaglercraftRandom par2Random, int par3, int par4) {
|
||||
this.theBiomeDecorator.decorate(par1World, par2Random, par3, par4);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeGenBeach extends BiomeGenBase {
|
||||
public BiomeGenBeach(int par1) {
|
||||
super(par1);
|
||||
this.spawnableCreatureList.clear();
|
||||
this.topBlock = (byte) Block.sand.blockID;
|
||||
this.fillerBlock = (byte) Block.sand.blockID;
|
||||
this.theBiomeDecorator.treesPerChunk = -999;
|
||||
this.theBiomeDecorator.deadBushPerChunk = 0;
|
||||
this.theBiomeDecorator.reedsPerChunk = 0;
|
||||
this.theBiomeDecorator.cactiPerChunk = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.EaglercraftRandom;
|
||||
|
||||
public class BiomeGenDesert extends BiomeGenBase {
|
||||
public BiomeGenDesert(int par1) {
|
||||
super(par1);
|
||||
this.spawnableCreatureList.clear();
|
||||
this.topBlock = (byte) Block.sand.blockID;
|
||||
this.fillerBlock = (byte) Block.sand.blockID;
|
||||
this.theBiomeDecorator.treesPerChunk = -999;
|
||||
this.theBiomeDecorator.deadBushPerChunk = 2;
|
||||
this.theBiomeDecorator.reedsPerChunk = 50;
|
||||
this.theBiomeDecorator.cactiPerChunk = 10;
|
||||
}
|
||||
|
||||
public void decorate(World par1World, EaglercraftRandom par2Random, int par3, int par4) {
|
||||
super.decorate(par1World, par2Random, par3, par4);
|
||||
|
||||
if (par2Random.nextInt(1000) == 0) {
|
||||
int var5 = par3 + par2Random.nextInt(16) + 8;
|
||||
int var6 = par4 + par2Random.nextInt(16) + 8;
|
||||
WorldGenDesertWells var7 = new WorldGenDesertWells();
|
||||
var7.generate(par1World, par2Random, var5, par1World.getHeightValue(var5, var6) + 1, var6);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeGenEnd extends BiomeGenBase {
|
||||
public BiomeGenEnd(int par1) {
|
||||
super(par1);
|
||||
this.spawnableMonsterList.clear();
|
||||
this.spawnableCreatureList.clear();
|
||||
this.spawnableWaterCreatureList.clear();
|
||||
this.spawnableCaveCreatureList.clear();
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntityEnderman(w), 10, 4, 4));
|
||||
this.topBlock = (byte) Block.dirt.blockID;
|
||||
this.fillerBlock = (byte) Block.dirt.blockID;
|
||||
this.theBiomeDecorator = new BiomeEndDecorator(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.EaglercraftRandom;
|
||||
|
||||
public class BiomeGenForest extends BiomeGenBase {
|
||||
public BiomeGenForest(int par1) {
|
||||
super(par1);
|
||||
this.spawnableCreatureList.add(new SpawnListEntry((w) -> new EntityWolf(w), 5, 4, 4));
|
||||
this.theBiomeDecorator.treesPerChunk = 10;
|
||||
this.theBiomeDecorator.grassPerChunk = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a WorldGen appropriate for this biome.
|
||||
*/
|
||||
public WorldGenerator getRandomWorldGenForTrees(EaglercraftRandom par1Random) {
|
||||
return (WorldGenerator) (par1Random.nextInt(5) == 0 ? this.worldGeneratorForest
|
||||
: (par1Random.nextInt(10) == 0 ? this.worldGeneratorBigTree : this.worldGeneratorTrees));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeGenHell extends BiomeGenBase {
|
||||
public BiomeGenHell(int par1) {
|
||||
super(par1);
|
||||
this.spawnableMonsterList.clear();
|
||||
this.spawnableCreatureList.clear();
|
||||
this.spawnableWaterCreatureList.clear();
|
||||
this.spawnableCaveCreatureList.clear();
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntityGhast(w), 50, 4, 4));
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntityPigZombie(w), 100, 4, 4));
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntityMagmaCube(w), 1, 4, 4));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.EaglercraftRandom;
|
||||
|
||||
public class BiomeGenHills extends BiomeGenBase {
|
||||
private WorldGenerator theWorldGenerator;
|
||||
|
||||
protected BiomeGenHills(int par1) {
|
||||
super(par1);
|
||||
this.theWorldGenerator = new WorldGenMinable(Block.silverfish.blockID, 8);
|
||||
}
|
||||
|
||||
public void decorate(World par1World, EaglercraftRandom par2Random, int par3, int par4) {
|
||||
super.decorate(par1World, par2Random, par3, par4);
|
||||
int var5 = 3 + par2Random.nextInt(6);
|
||||
int var6;
|
||||
int var7;
|
||||
int var8;
|
||||
|
||||
for (var6 = 0; var6 < var5; ++var6) {
|
||||
var7 = par3 + par2Random.nextInt(16);
|
||||
var8 = par2Random.nextInt(28) + 4;
|
||||
int var9 = par4 + par2Random.nextInt(16);
|
||||
int var10 = par1World.getBlockId(var7, var8, var9);
|
||||
|
||||
if (var10 == Block.stone.blockID) {
|
||||
par1World.setBlock(var7, var8, var9, Block.oreEmerald.blockID, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var5 = 0; var5 < 7; ++var5) {
|
||||
var6 = par3 + par2Random.nextInt(16);
|
||||
var7 = par2Random.nextInt(64);
|
||||
var8 = par4 + par2Random.nextInt(16);
|
||||
this.theWorldGenerator.generate(par1World, par2Random, var6, var7, var8);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.EaglercraftRandom;
|
||||
|
||||
public class BiomeGenJungle extends BiomeGenBase {
|
||||
public BiomeGenJungle(int par1) {
|
||||
super(par1);
|
||||
this.theBiomeDecorator.treesPerChunk = 50;
|
||||
this.theBiomeDecorator.grassPerChunk = 25;
|
||||
this.theBiomeDecorator.flowersPerChunk = 4;
|
||||
this.spawnableMonsterList.add(new SpawnListEntry((w) -> new EntityOcelot(w), 2, 1, 1));
|
||||
this.spawnableCreatureList.add(new SpawnListEntry((w) -> new EntityChicken(w), 10, 4, 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a WorldGen appropriate for this biome.
|
||||
*/
|
||||
public WorldGenerator getRandomWorldGenForTrees(EaglercraftRandom par1Random) {
|
||||
return (WorldGenerator) (par1Random.nextInt(10) == 0 ? this.worldGeneratorBigTree
|
||||
: (par1Random.nextInt(2) == 0 ? new WorldGenShrub(3, 0)
|
||||
: (par1Random.nextInt(3) == 0 ? new WorldGenHugeTrees(false, 10 + par1Random.nextInt(20), 3, 3)
|
||||
: new WorldGenTrees(false, 4 + par1Random.nextInt(7), 3, 3, true))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a WorldGen appropriate for this biome.
|
||||
*/
|
||||
public WorldGenerator getRandomWorldGenForGrass(EaglercraftRandom par1Random) {
|
||||
return par1Random.nextInt(4) == 0 ? new WorldGenTallGrass(Block.tallGrass.blockID, 2)
|
||||
: new WorldGenTallGrass(Block.tallGrass.blockID, 1);
|
||||
}
|
||||
|
||||
public void decorate(World par1World, EaglercraftRandom par2Random, int par3, int par4) {
|
||||
super.decorate(par1World, par2Random, par3, par4);
|
||||
WorldGenVines var5 = new WorldGenVines();
|
||||
|
||||
for (int var6 = 0; var6 < 50; ++var6) {
|
||||
int var7 = par3 + par2Random.nextInt(16) + 8;
|
||||
byte var8 = 64;
|
||||
int var9 = par4 + par2Random.nextInt(16) + 8;
|
||||
var5.generate(par1World, par2Random, var7, var8, var9);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeGenMushroomIsland extends BiomeGenBase {
|
||||
public BiomeGenMushroomIsland(int par1) {
|
||||
super(par1);
|
||||
this.theBiomeDecorator.treesPerChunk = -100;
|
||||
this.theBiomeDecorator.flowersPerChunk = -100;
|
||||
this.theBiomeDecorator.grassPerChunk = -100;
|
||||
this.theBiomeDecorator.mushroomsPerChunk = 1;
|
||||
this.theBiomeDecorator.bigMushroomsPerChunk = 1;
|
||||
this.topBlock = (byte) Block.mycelium.blockID;
|
||||
this.spawnableMonsterList.clear();
|
||||
this.spawnableCreatureList.clear();
|
||||
this.spawnableWaterCreatureList.clear();
|
||||
this.spawnableCreatureList.add(new SpawnListEntry((w) -> new EntityMooshroom(w), 8, 4, 8));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeGenOcean extends BiomeGenBase {
|
||||
public BiomeGenOcean(int par1) {
|
||||
super(par1);
|
||||
this.spawnableCreatureList.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeGenPlains extends BiomeGenBase {
|
||||
protected BiomeGenPlains(int par1) {
|
||||
super(par1);
|
||||
this.theBiomeDecorator.treesPerChunk = -999;
|
||||
this.theBiomeDecorator.flowersPerChunk = 4;
|
||||
this.theBiomeDecorator.grassPerChunk = 10;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package net.minecraft.src;
|
||||
|
||||
public class BiomeGenRiver extends BiomeGenBase {
|
||||
public BiomeGenRiver(int par1) {
|
||||
super(par1);
|
||||
this.spawnableCreatureList.clear();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue