sp-server

This commit is contained in:
catfoolyou 2025-02-06 18:02:51 -05:00
parent 353c5d7165
commit f4e3cbe2fe
1030 changed files with 123381 additions and 3 deletions

View File

@ -8,8 +8,11 @@ plugins {
sourceSets { sourceSets {
main { main {
java { java {
srcDir 'src/main/java' srcDirs(
srcDir 'src/teavm/java' "src/main/java",
"src/lwjgl/java",
"sp-server/src/ipc/java"
)
} }
} }
} }
@ -24,7 +27,7 @@ tasks.withType(JavaCompile) {
} }
dependencies { dependencies {
//implementation fileTree(dir: './lwjgl-rundir/', include: '*.jar') implementation fileTree(dir: './lwjgl-rundir/', include: '*.jar')
teavm(teavm.libs.jso) teavm(teavm.libs.jso)
teavm(teavm.libs.jsoApis) teavm(teavm.libs.jsoApis)

18
sp-server/.classpath Normal file
View File

@ -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>

23
sp-server/.project Normal file
View File

@ -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>

46
sp-server/build.gradle Normal file
View File

@ -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
}

View File

@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx2G -Xms2G

Binary file not shown.

View File

@ -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

185
sp-server/gradlew vendored Normal file
View File

@ -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" "$@"

89
sp-server/gradlew.bat vendored Normal file
View File

@ -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

12
sp-server/settings.gradle Normal file
View File

@ -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

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View 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&nbsp;RFC&nbsp;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))));
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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) {
}
}
}

View File

@ -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);
}

View File

@ -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)");
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
});
}
}
}

View File

@ -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");
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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 + "]";
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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));
}
}

View File

@ -0,0 +1,8 @@
package net.minecraft.src;
public class BiomeGenOcean extends BiomeGenBase {
public BiomeGenOcean(int par1) {
super(par1);
this.spawnableCreatureList.clear();
}
}

View File

@ -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;
}
}

View File

@ -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