Start singleplayer, 16 errors

This commit is contained in:
catfoolyou 2025-03-19 12:42:52 -04:00
parent b49e35d003
commit d2d261301c
62 changed files with 7311 additions and 1292 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_18" default="true" project-jdk-name="corretto-18" project-jdk-type="JavaSDK" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-18" project-jdk-type="JavaSDK" />
</project>

View File

@ -10,7 +10,7 @@ sourceSets {
java {
srcDirs(
"src/main/java",
"src/teavm/java"
"src/lwjgl/java"
)
}
}
@ -25,10 +25,10 @@ tasks.withType(JavaCompile) {
options.compilerArgs << "-Xmaxerrs" << "1000"
}
//sourceSets.main.resources.srcDirs += 'src/lwjgl/java/javazoom/jl/decoder'
sourceSets.main.resources.srcDirs += 'src/lwjgl/java/javazoom/jl/decoder'
dependencies {
//implementation fileTree(dir: './lwjgl-rundir/', include: '*.jar')
implementation fileTree(dir: './lwjgl-rundir/', include: '*.jar')
teavm(teavm.libs.jso)
teavm(teavm.libs.jsoApis)

Binary file not shown.

Binary file not shown.

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,45 @@
package net.lax1dude.eaglercraft.sp;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.src.ILogAgent;
public class EAGLogAgent implements ILogAgent {
private final Logger logger = Logger.getLogger("IntegratedServer");
public Logger getServerLogger() {
return this.logger;
}
public void logInfo(String par1Str) {
this.logger.log(Level.INFO, par1Str);
}
public void logWarning(String par1Str) {
this.logger.log(Level.WARNING, par1Str);
}
public void logWarningFormatted(String par1Str, Object... par2ArrayOfObj) {
this.logger.log(Level.WARNING, par1Str, par2ArrayOfObj);
}
public void logWarningException(String par1Str, Throwable par2Throwable) {
this.logger.log(Level.WARNING, par1Str, par2Throwable);
}
public void logSevere(String par1Str) {
this.logger.log(Level.SEVERE, par1Str);
}
public void logSevereException(String par1Str, Throwable par2Throwable) {
this.logger.log(Level.SEVERE, par1Str, par2Throwable);
}
@Override
public void logFine(String var1) {
this.logger.log(Level.FINE, var1);
}
}

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().logWarning("Can\'t keep up! Did the system time change, or is the server overloaded? Skipping " + ((delta - 2000l) / 50l) + " ticks");
delta = 2000L;
this.timeOfLastWarning = ctm;
}
if (delta < 0L) {
this.getLogAgent().logWarning("Time ran backwards! Did the fucking system time change?");
delta = 0L;
}
if (this.worldServers[0].areAllPlayersAsleep()) {
this.tick();
++tpsCounter;
lastTick = SysUtil.steadyTimeMillis();
} else {
if (delta > 50l) {
delta -= 50L;
lastTick += 50l;
this.tick();
++tpsCounter;
}
}
}
public void setPaused(boolean p) {
paused = p;
if(!p) {
lastTick = SysUtil.steadyTimeMillis();
}
}
public boolean getPaused() {
return paused;
}
@Override
protected boolean startServer() throws IOException {
SkinsPlugin.reset();
//VoiceChatPlugin.reset();
this.loadAllWorlds(folderName, 0l, newWorldSettings);
this.lastTick = SysUtil.steadyTimeMillis();
return true;
}
@Override
public void stopServer() {
super.stopServer();
SkinsPlugin.reset();
//VoiceChatPlugin.reset();
}
@Override
public boolean canStructuresSpawn() {
return false;
}
@Override
public EnumGameType getGameType() {
return gamemode;
}
@Override
public int getDifficulty() {
return difficulty;
}
@Override
public boolean isHardcore() {
return false;
}
@Override
public boolean isDedicatedServer() {
return false;
}
@Override
public boolean isCommandBlockEnabled() {
return true;
}
@Override
public WorkerListenThread getNetworkThread() {
return listenThreadImpl;
}
@Override
public String shareToLAN(EnumGameType var1, boolean var2) {
return null;
}
@Override
public ILogAgent getLogAgent() {
return IntegratedServer.logger;
}
}

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.enableCommands();
}
}
break;
case IPCPacket03DeleteWorld.ID: {
tryStopServer();
IPCPacket03DeleteWorld pkt = (IPCPacket03DeleteWorld)packet;
if(SYS.VFS.deleteFiles("worlds/" + pkt.worldName + "/") <= 0) {
throwExceptionToClient("Failed to delete world!", new RuntimeException("VFS did not delete directory 'worlds/" + pkt.worldName + "' correctly"));
sendTaskFailed();
break;
}
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
if(worldsTxt != null) {
LinkedList<String> newWorlds = new LinkedList<>();
for(String str : worldsTxt) {
if(!str.equalsIgnoreCase(pkt.worldName)) {
newWorlds.add(str);
}
}
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", newWorlds));
}
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket03DeleteWorld.ID));
}
break;
case IPCPacket04RenameWorld.ID: {
tryStopServer();
IPCPacket04RenameWorld pkt = (IPCPacket04RenameWorld)packet;
if(SYS.VFS.renameFiles("worlds/" + pkt.worldOldName + "/", "worlds/" + pkt.worldNewName + "/", pkt.copy) <= 0) {
throwExceptionToClient("Failed to copy/rename server!", new RuntimeException("VFS did not copy/rename directory 'worlds/" + pkt.worldOldName + "' correctly"));
sendTaskFailed();
break;
}else {
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
LinkedList<String> newWorlds = new LinkedList<>();
if(worldsTxt != null) {
for(String str : worldsTxt) {
if(pkt.copy || !str.equalsIgnoreCase(pkt.worldOldName)) {
newWorlds.add(str);
}
}
}
newWorlds.add(pkt.worldNewName);
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", newWorlds));
VFile worldDat = new VFile("worlds", pkt.worldNewName, "level.dat");
if(worldDat.canRead()) {
NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(worldDat.getAllBytes());
worldDatNBT.getCompoundTag("Data").setString("LevelName", pkt.displayName);
worldDat.setAllBytes(CompressedStreamTools.compress(worldDatNBT));
}else {
throwExceptionToClient("Failed to copy/rename world!", new RuntimeException("Failed to change level.dat world '" + pkt.worldNewName + "' display name to '" + pkt.displayName + "' because level.dat was missing"));
sendTaskFailed();
break;
}
}
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket04RenameWorld.ID));
}
break;
case IPCPacket05RequestData.ID: {
IPCPacket05RequestData pkt = (IPCPacket05RequestData)packet;
if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_EAG) {
try {
sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterEPK.exportWorld(pkt.worldName)));
} catch (Throwable t) {
String realWorldName = pkt.worldName;
int i = realWorldName.lastIndexOf(new String(new char[] { (char)253, (char)233, (char)233 }));
if(i != -1) {
realWorldName = realWorldName.substring(0, i);
}
throwExceptionToClient("Failed to export world '" + realWorldName+ "' as EPK", t);
sendTaskFailed();
}
}else if(pkt.request == IPCPacket05RequestData.REQUEST_LEVEL_MCA) {
try {
sendIPCPacket(new IPCPacket09RequestResponse(WorldConverterMCA.exportWorld(pkt.worldName)));
} catch (Throwable t) {
throwExceptionToClient("Failed to export world '" + pkt.worldName+ "' as MCA", t);
sendTaskFailed();
}
}
}
break;
case IPCPacket06RenameWorldNBT.ID: {
IPCPacket06RenameWorldNBT pkt = (IPCPacket06RenameWorldNBT)packet;
if(isServerStopped()) {
VFile worldDat = new VFile("worlds", pkt.worldName, "level.dat");
if(worldDat.canRead()) {
NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(worldDat.getAllBytes());
worldDatNBT.getCompoundTag("Data").setString("LevelName", pkt.displayName);
worldDat.setAllBytes(CompressedStreamTools.compress(worldDatNBT));
}else {
throwExceptionToClient("Failed to rename world!", new RuntimeException("Failed to change level.dat world '" + pkt.worldName + "' display name to '" + pkt.displayName + "' because level.dat was missing"));
}
}else {
System.err.println("Client tried to rename a world '" + pkt.worldName + "' to have name '" + pkt.displayName + "' while the server is running");
sendTaskFailed();
}
}
break;
case IPCPacket07ImportWorld.ID: {
IPCPacket07ImportWorld pkt = (IPCPacket07ImportWorld)packet;
if(isServerStopped()) {
if(pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_EAG) {
try {
WorldConverterEPK.importWorld(pkt.worldData, pkt.worldName);
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket07ImportWorld.ID));
}catch(Throwable t) {
SYS.VFS.deleteFiles("worlds/" + VFSSaveHandler.worldNameToFolderName(pkt.worldName) + "/");
throwExceptionToClient("Failed to import world '" + pkt.worldName + "' as EPK", t);
sendTaskFailed();
}
}else if(pkt.worldFormat == IPCPacket07ImportWorld.WORLD_FORMAT_MCA) {
try {
WorldConverterMCA.importWorld(pkt.worldData, pkt.worldName);
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket07ImportWorld.ID));
}catch(Throwable t) {
SYS.VFS.deleteFiles("worlds/" + VFSSaveHandler.worldNameToFolderName(pkt.worldName) + "/");
throwExceptionToClient("Failed to import world '" + pkt.worldName + "' as MCA", t);
sendTaskFailed();
}
}else {
System.err.println("Client tried to import a world in an unknown format: 0x" + Integer.toHexString(pkt.worldFormat));
sendTaskFailed();
}
}else {
System.err.println("Client tried to import a world '" + pkt.worldName + "' while the server is running");
sendTaskFailed();
}
}
break;
case IPCPacket09RequestResponse.ID:
break;
case IPCPacket0ASetWorldDifficulty.ID: {
IPCPacket0ASetWorldDifficulty pkt = (IPCPacket0ASetWorldDifficulty)packet;
if(!isServerStopped()) {
currentProcess.setDifficultyForAllWorlds(pkt.difficulty);
}else {
System.err.println("Client tried to set difficulty '" + pkt.difficulty + "' while server was stopped");
sendTaskFailed();
}
}
break;
case IPCPacket0BPause.ID: {
IPCPacket0BPause pkt = (IPCPacket0BPause)packet;
if(!isServerStopped()) {
if(!pkt.pause && !currentProcess.getPaused()) {
currentProcess.saveAllWorlds(true);
}else {
currentProcess.setPaused(pkt.pause);
if(pkt.pause) {
currentProcess.saveAllWorlds(true);
}
}
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket0BPause.ID));
}else {
System.err.println("Client tried to " + (pkt.pause ? "pause" : "unpause") + " while server was stopped");
}
}
break;
case IPCPacket0CPlayerChannel.ID: {
IPCPacket0CPlayerChannel pkt = (IPCPacket0CPlayerChannel)packet;
if(!isServerStopped()) {
if(pkt.open) {
if(!currentProcess.getNetworkThread().openChannel(pkt.channel)) {
System.err.println("Client tried to open a duplicate channel '" + pkt.channel + "'");
}
}else {
if(!currentProcess.getNetworkThread().closeChannel(pkt.channel)) {
System.err.println("Client tried to close a null channel '" + pkt.channel + "'");
}
}
}else {
System.err.println("Client tried to " + (pkt.open ? "open" : "close") + " channel '" + pkt.channel + "' while server was stopped");
}
}
break;
case IPCPacket0EListWorlds.ID: {
if(isServerStopped()) {
String[] worlds = SYS.VFS.getFile("worlds.txt").getAllLines();
if(worlds == null || (worlds.length == 1 && worlds[0].trim().length() <= 0)) {
worlds = null;
}
if(worlds == null) {
sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, new LinkedList<NBTTagCompound>()));
break;
}
LinkedList<String> updatedList = new LinkedList<>();
LinkedList<NBTTagCompound> sendListNBT = new LinkedList<>();
boolean rewrite = false;
for(String w : worlds) {
byte[] dat = (new VFile("worlds", w, "level.dat")).getAllBytes();
if(dat != null) {
NBTTagCompound worldDatNBT;
try {
worldDatNBT = CompressedStreamTools.decompress(dat);
worldDatNBT.setString("folderName", w);
sendListNBT.add(worldDatNBT);
updatedList.add(w);
continue;
}catch(IOException e) {
// shit fuck
}
}
rewrite = true;
System.err.println("World level.dat for '" + w + "' was not found, attempting to delete 'worlds/" + w + "/*'");
if(SYS.VFS.deleteFiles("worlds/" + w) <= 0) {
System.err.println("No files were deleted in 'worlds/" + w + "/*', this may be corruption but '" + w + "' will still be removed from worlds.txt");
}
}
if(rewrite) {
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", updatedList));
}
sendIPCPacket(new IPCPacket16NBTList(IPCPacket16NBTList.WORLD_LIST, sendListNBT));
}else {
System.err.println("Client tried to list worlds while server was running");
sendTaskFailed();
}
}
break;
case IPCPacket0FListFiles.ID:
break;
case IPCPacket10FileRead.ID:
break;
case IPCPacket12FileWrite.ID:
break;
case IPCPacket13FileCopyMove.ID:
break;
case IPCPacket14StringList.ID: {
IPCPacket14StringList pkt = (IPCPacket14StringList)packet;
switch(pkt.opCode) {
case IPCPacket14StringList.LOCALE:
StringTranslate.init(pkt.stringList);
break;
case IPCPacket14StringList.STAT_GUID:
AchievementMap.init(pkt.stringList);
AchievementList.init();
break;
default:
System.err.println("Strange string list 0x" + Integer.toHexString(pkt.opCode) + " with length " + pkt.stringList.size() + " recieved");
break;
}
}
break;
case IPCPacket17ConfigureLAN.ID: {
IPCPacket17ConfigureLAN pkt = (IPCPacket17ConfigureLAN)packet;
//currentProcess.getConfigurationManager().configureLAN(pkt.gamemode, pkt.cheats, pkt.iceServers); // FIX THIS SHIT
}
break;
case IPCPacket18ClearPlayers.ID: {
SYS.VFS.deleteFiles("worlds/" + ((IPCPacket18ClearPlayers)packet).worldName + "/player");
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket18ClearPlayers.ID));
}
break;
default:
System.err.println("IPC packet type 0x" + Integer.toHexString(id) + " class '" + packet.getClass().getSimpleName() + "' was not handled");
sendTaskFailed();
break;
}
}catch(Throwable t) {
String str = "IPC packet 0x" + Integer.toHexString(id) + " class '" + packet.getClass().getSimpleName() + "' was not processed correctly";
System.err.println(str);
throwExceptionToClient(str, t);
sendTaskFailed();
}
continue;
}
}
long watchDog = SysUtil.steadyTimeMillis();
itr = cur.iterator();
int overflow = 0;
while(itr.hasNext()) {
PKT msg = itr.next();
if(!msg.channel.equals("IPC")) {
if(SysUtil.steadyTimeMillis() - watchDog > 500l) {
++overflow;
continue;
}
if(!msg.channel.startsWith("NET|") || currentProcess == null) {
//System.err.println("Unknown ICP channel: '" + msg.channel + "' passed " + msg.data.length + " bytes");
continue;
}
String u = msg.channel.substring(4);
currentProcess.getNetworkThread().recievePacket(u, msg.data);
}
}
if(overflow > 0) {
System.err.println("Async ICP queue is overloaded, server dropped " + overflow + " player packets");
}
}
@JSBody(params = { "ch", "dat" }, script = "postMessage({ ch: ch, dat : dat });")
private static native void sendWorkerPacket(String channel, ArrayBuffer arr);
public static void sendIPCPacket(IPCPacketBase pkt) {
byte[] serialized;
try {
serialized = IPCPacketManager.IPCSerialize(pkt);
} catch (IOException e) {
System.err.println("Could not serialize IPC packet 0x" + Integer.toHexString(pkt.id()) + " class '" + pkt.getClass().getSimpleName() + "'");
e.printStackTrace();
return;
}
sendWorkerPacket("IPC", TeaVMUtils.unwrapArrayBuffer(serialized));
}
public static void sendPlayerPacket(String channel, byte[] buf) {
//System.out.println("[Server][SEND][" + channel + "]: " + buf.length);
sendWorkerPacket("NET|" + channel, TeaVMUtils.unwrapArrayBuffer(buf));
}
private static boolean isRunning = false;
public static void halt() {
isRunning = false;
}
private static void mainLoop() {
processAsyncMessageQueue();
if(currentProcess != null) {
currentProcess.mainLoop();
if(currentProcess.isServerStopped()) {
sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacket01StopServer.ID));
currentProcess = null;
}
}else {
SysUtil.sleep(50);
}
}
@JSBody(params = { "wb" }, script = "onmessage = function(o) { wb(o.data.ch, o.data.dat); };")
private static native void registerPacketHandler(WorkerBinaryPacketHandler wb);
public static void main(String[] args) {
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,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,156 @@
package net.lax1dude.eaglercraft.sp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import net.lax1dude.eaglercraft.sp.ipc.IPCPacket0CPlayerChannel;
import net.minecraft.server.MinecraftServer;
import net.minecraft.src.INetworkManager;
import net.minecraft.src.NetHandler;
import net.minecraft.src.NetLoginHandler;
import net.minecraft.src.NetServerHandler;
import net.minecraft.src.Packet;
public class WorkerNetworkManager implements INetworkManager {
private NetHandler theNetHandler;
private MinecraftServer minecraftServer;
private String ipcChannel;
private boolean isAlive;
private WorkerListenThread listenThread;
private LinkedList<byte[]> frags = new LinkedList<>();
public WorkerNetworkManager(String ipcChannel, MinecraftServer srv, WorkerListenThread th) {
this.ipcChannel = ipcChannel;
this.theNetHandler = new NetLoginHandler(srv, this);
th.addPlayer(theNetHandler);
this.minecraftServer = srv;
this.isAlive = true;
this.listenThread = th;
}
@Override
public void setNetHandler(NetHandler var1) {
theNetHandler = var1;
listenThread.addPlayer(theNetHandler);
}
@Override
public void addToSendQueue(Packet var1) {
if(!isAlive) {
return;
}
try {
ByteArrayOutputStream bao = new ByteArrayOutputStream(var1.getPacketSize() + 1);
Packet.writePacket(var1, new DataOutputStream(bao));
IntegratedServer.sendPlayerPacket(ipcChannel, bao.toByteArray());
}catch(IOException e) {
System.err.println("Failed to serialize minecraft packet '" + var1.getPacketId() + "' for IPC channel 'NET|" + ipcChannel + "'");
e.printStackTrace();
return;
}
}
public void addToRecieveQueue(byte[] fragment) {
//System.out.println("[Server][READ][QUEUE][" + ipcChannel + "]: " + fragment.length);
if(!isAlive) {
return;
}
frags.add(fragment);
}
@Override
public void wakeThreads() {
// no
}
@Override
public void processReadPackets() {
while(frags.size() > 0) {
byte[] pktBytes = frags.remove(0);
try {
ByteArrayInputStream bai = new ByteArrayInputStream(pktBytes);
int pktId = bai.read();
if(pktId == -1) {
System.err.println("Recieved invalid '-1' packet");
continue;
}
Packet pkt = Packet.getNewPacket(minecraftServer.getLogAgent(), pktId);
if(pkt == null) {
System.err.println("Recieved invalid '" + pktId + "' packet");
continue;
}
pkt.readPacketData(new DataInputStream(bai));
//System.out.println("[Server][" + ipcChannel + "]: packet '" + pkt.getClass().getSimpleName() + "' recieved");
try {
pkt.processPacket(theNetHandler);
}catch(Throwable t) {
System.err.println("Could not process minecraft packet 0x" + Integer.toHexString(pkt.getPacketId()) + " class '" + pkt.getClass().getSimpleName() + "' on channel 'NET|" + ipcChannel + "'");
t.printStackTrace();
}
}catch(IOException ex) {
System.err.println("Could not deserialize a " + pktBytes.length + " byte long minecraft packet of type '" + (pktBytes.length <= 0 ? -1 : (int)(pktBytes[0] & 0xFF)) + "' on channel 'NET|" + ipcChannel + "'");
}
}
}
@Override
public void serverShutdown() {
if(isAlive) {
listenThread.closeChannel(ipcChannel);
IntegratedServer.sendIPCPacket(new IPCPacket0CPlayerChannel(ipcChannel, false));
}
if(theNetHandler != null && (theNetHandler instanceof NetServerHandler)) {
((NetServerHandler)theNetHandler).kickPlayerFromServer(null);
}
isAlive = false;
}
@Override
public int packetSize() { // why is this a thing
return 0;
}
@Override
public void networkShutdown(String var1, Object... var2) {
if(isAlive) {
listenThread.closeChannel(ipcChannel);
IntegratedServer.sendIPCPacket(new IPCPacket0CPlayerChannel(ipcChannel, false));
}
if(theNetHandler != null && (theNetHandler instanceof NetServerHandler)) {
((NetServerHandler)theNetHandler).kickPlayerFromServer(null);
}
isAlive = false;
}
@Override
public void closeConnections() {
}
@Override
public String getServerURI() {
return "None? I dont fucking know what a URI is";
}
public boolean equals(Object o) {
return (o instanceof WorkerNetworkManager) && ((WorkerNetworkManager)o).ipcChannel.equals(ipcChannel);
}
public int hashCode() {
return ipcChannel.hashCode();
}
}

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

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +1,34 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerMisc;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AchievementMap
{
public class AchievementMap {
/** Holds the singleton instance of AchievementMap. */
public static AchievementMap instance = new AchievementMap();
public static AchievementMap instance = null;
public static void init(List<String> guid) {
instance = new AchievementMap(guid);
StatList.initStats();
StatList.initBreakableStats();
}
/** Maps a achievement id with it's unique GUID. */
private Map guidMap = new HashMap();
private AchievementMap()
{
try {
String[] strs = EaglerMisc.bytesToLines(Minecraft.getMinecraft().texturePackList.getSelectedTexturePack().getResourceAsBytes("/achievement/map.txt"));
for(String str : strs) {
String[] var3 = str.split(",");
int var4 = Integer.parseInt(var3[0]);
this.guidMap.put(Integer.valueOf(var4), var3[1]);
}
} catch (Exception var5) {
var5.printStackTrace();
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));
public static String getGuid(int par0) {
return (String) instance.guidMap.get(Integer.valueOf(par0));
}
}
}

View File

@ -1,5 +1,6 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.minecraft.server.MinecraftServer;
import java.util.ArrayList;
@ -43,7 +44,7 @@ public class BiomeCache
this.cache.add(var5);
}
var5.lastAccessTime = MinecraftServer.getSystemTimeMillis();
var5.lastAccessTime = EaglerAdapter.steadyTimeMillis();
return var5;
}
@ -60,7 +61,7 @@ public class BiomeCache
*/
public void cleanupCache()
{
long var1 = MinecraftServer.getSystemTimeMillis();
long var1 = EaglerAdapter.steadyTimeMillis();
long var3 = var1 - this.lastCleanupTime;
if (var3 > 7500L || var3 < 0L)

View File

@ -385,6 +385,8 @@ public class Chunk
}
}
public static int totalBlockLightUpdates = 0;
/**
* Initiates the recalculation of both the block-light and sky-light for a given block inside a chunk.
*/
@ -495,6 +497,7 @@ public class Chunk
this.updateSkylightNeighborHeight(var6, var7, var12, var13);
}
++totalBlockLightUpdates;
this.isModified = true;
}
}

View File

@ -338,4 +338,26 @@ public class ChunkProviderServer implements IChunkProvider
}
public void recreateStructures(int par1, int par2) {}
private int _r = 0;
private int _w = 0;
private int _g = 0;
public int statR() {
int r = _r;
_r = 0;
return r;
}
public int statW() {
int w = _w;
_w = 0;
return w;
}
public int statG() {
int g = _g;
_g = 0;
return g;
}
}

View File

@ -1,5 +1,6 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.minecraft.server.MinecraftServer;
import java.io.File;
@ -41,15 +42,15 @@ public class CommandDebug extends CommandBase
if (par2ArrayOfStr[0].equals("start"))
{
notifyAdmins(par1ICommandSender, "commands.debug.start", new Object[0]);
MinecraftServer.getServer().enableProfiling();
this.startTime = MinecraftServer.getSystemTimeMillis();
//MinecraftServer.getServer().enableProfiling();
this.startTime = EaglerAdapter.steadyTimeMillis();
this.startTicks = MinecraftServer.getServer().getTickCounter();
return;
}
if (par2ArrayOfStr[0].equals("stop"))
{
long var3 = MinecraftServer.getSystemTimeMillis();
long var3 = EaglerAdapter.steadyTimeMillis();
int var5 = MinecraftServer.getServer().getTickCounter();
long var6 = var3 - this.startTime;
int var8 = var5 - this.startTicks;

View File

@ -31,17 +31,6 @@ public class CommandDefaultGameMode extends CommandGameMode
protected void setGameType(EnumGameType par1EnumGameType)
{
MinecraftServer var2 = MinecraftServer.getServer();
var2.setGameType(par1EnumGameType);
EntityPlayerMP var4;
if (var2.getForceGamemode())
{
for (Iterator var3 = MinecraftServer.getServer().getConfigurationManager().playerEntityList.iterator(); var3.hasNext(); var4.fallDistance = 0.0F)
{
var4 = (EntityPlayerMP)var3.next();
var4.setGameType(par1EnumGameType);
}
}
MinecraftServer.getServer().setGameType(par1EnumGameType);
}
}

View File

@ -27,7 +27,7 @@ public class CommandSetPlayerTimeout extends CommandBase
if (par2ArrayOfStr.length == 1)
{
int var3 = parseIntWithMin(par1ICommandSender, par2ArrayOfStr[0], 0);
MinecraftServer.getServer().func_143006_e(var3);
//MinecraftServer.getServer().func_143006_e(var3);
notifyAdmins(par1ICommandSender, "commands.setidletimeout.success", new Object[] {Integer.valueOf(var3)});
}
else

View File

@ -13,8 +13,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import com.jcraft.jzlib.Deflater;
import com.jcraft.jzlib.GZIPInputStream;
import com.jcraft.jzlib.GZIPOutputStream;
public class CompressedStreamTools
{
@ -182,16 +184,12 @@ public class CompressedStreamTools
/**
* Reads from a CompressedStream.
*/
public static NBTTagCompound read(DataInput par0DataInput) throws IOException
{
public static NBTTagCompound read(DataInput par0DataInput) throws IOException {
NBTBase var1 = NBTBase.readNamedTag(par0DataInput);
if (var1 instanceof NBTTagCompound)
{
return (NBTTagCompound)var1;
}
else
{
if (var1 instanceof NBTTagCompound) {
return (NBTTagCompound) var1;
} else {
throw new IOException("Root tag must be a named compound tag");
}
}

View File

@ -1,5 +1,6 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.minecraft.server.MinecraftServer;
public class ConvertingProgressUpdate implements IProgressUpdate
@ -12,7 +13,7 @@ public class ConvertingProgressUpdate implements IProgressUpdate
public ConvertingProgressUpdate(MinecraftServer par1MinecraftServer)
{
this.mcServer = par1MinecraftServer;
this.field_96245_b = MinecraftServer.getSystemTimeMillis();
this.field_96245_b = EaglerAdapter.steadyTimeMillis();
}
/**
@ -25,9 +26,9 @@ public class ConvertingProgressUpdate implements IProgressUpdate
*/
public void setLoadingProgress(int par1)
{
if (MinecraftServer.getSystemTimeMillis() - this.field_96245_b >= 1000L)
if (EaglerAdapter.steadyTimeMillis() - this.field_96245_b >= 1000L)
{
this.field_96245_b = MinecraftServer.getSystemTimeMillis();
this.field_96245_b = EaglerAdapter.steadyTimeMillis();
this.mcServer.getLogAgent().logInfo("Converting... " + par1 + "%");
}
}

View File

@ -9,6 +9,8 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.minecraft.server.MinecraftServer;
public class EntityPlayerMP extends EntityPlayer implements ICrafting
@ -55,7 +57,7 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting
private int initialInvulnerability = 60;
/** must be between 3>x>15 (strictly between) */
private int renderDistance;
int renderDistance;
private int chatVisibility;
private boolean chatColours = true;
private long field_143005_bX = 0L;
@ -117,7 +119,7 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting
if (par1NBTTagCompound.hasKey("playerGameType"))
{
if (MinecraftServer.getServer().getForceGamemode())
if (true) // FIX THIS SHIT
{
this.theItemInWorldManager.setGameType(MinecraftServer.getServer().getGameType());
}
@ -234,10 +236,10 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting
}
}
if (this.field_143005_bX > 0L && this.mcServer.func_143007_ar() > 0 && MinecraftServer.getSystemTimeMillis() - this.field_143005_bX > (long)(this.mcServer.func_143007_ar() * 1000 * 60))
/*if (this.field_143005_bX > 0L && this.mcServer.func_143007_ar() > 0 && EaglerAdapter.steadyTimeMillis() - this.field_143005_bX > (long)(this.mcServer.func_143007_ar() * 1000 * 60))
{
this.playerNetServerHandler.kickPlayerFromServer("You have been idle for too long!");
}
}*/
}
public void onUpdateEntity()
@ -905,7 +907,10 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting
*/
public boolean canCommandSenderUseCommand(int par1, String par2Str)
{
return "seed".equals(par2Str) && !this.mcServer.isDedicatedServer() ? true : (!"tell".equals(par2Str) && !"help".equals(par2Str) && !"me".equals(par2Str) ? (this.mcServer.getConfigurationManager().isPlayerOpped(this.username) ? this.mcServer.func_110455_j() >= par1 : false) : true);
return "seed".equals(par2Str) && !this.mcServer.isDedicatedServer() ? true
: (!"tell".equals(par2Str) && !"help".equals(par2Str) && !"me".equals(par2Str)
? this.mcServer.getConfigurationManager().isPlayerOpped(this.username)
: true);
}
/**
@ -913,7 +918,7 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting
*/
public String getPlayerIP()
{
return null;
return "Cannot get IP over websocket";
}
public void updateClientInfo(Packet204ClientInfo par1Packet204ClientInfo)
@ -961,6 +966,6 @@ public class EntityPlayerMP extends EntityPlayer implements ICrafting
public void func_143004_u()
{
this.field_143005_bX = MinecraftServer.getSystemTimeMillis();
this.field_143005_bX = EaglerAdapter.steadyTimeMillis();
}
}

View File

@ -142,7 +142,7 @@ public class GuiMainMenu extends GuiScreen {
StringTranslate var2 = StringTranslate.getInstance();
int var4 = this.height / 4 + 48;
if(false) { // EaglerAdapter.isIntegratedServerAvailable()
if(EaglerAdapter.isIntegratedServerAvailable()) { // EaglerAdapter.isIntegratedServerAvailable()
this.buttonList.add(new GuiButton(1, this.width / 2 - 100, var4, var2.translateKey("menu.singleplayer")));
this.buttonList.add(new GuiButton(2, this.width / 2 - 100, var4 + 24 * 1, var2.translateKey("menu.multiplayer")));
this.buttonList.add(new GuiButton(3, this.width / 2 - 100, var4 + 24 * 2, var2.translateKey("menu.forkme")));

View File

@ -19,5 +19,5 @@ public interface ICommandSender
*/
ChunkCoordinates getPlayerCoordinates();
World getEntityWorld();
//World getEntityWorld();
}

View File

@ -12,12 +12,15 @@ public abstract class NetHandler
*/
public void handleMapChunk(Packet51MapChunk par1Packet51MapChunk) {}
/**
* Default handler called for packets that don't have their own handlers in NetClientHandler; currentlly does
* nothing.
*/
public boolean shouldBeRemoved() {
return true;
}
public void unexpectedPacket(Packet par1Packet) {}
public void handlePackets() {
}
public void handleErrorMessage(String par1Str, Object[] par2ArrayOfObj) {}
public void handleKickDisconnect(Packet255KickDisconnect par1Packet255KickDisconnect)

View File

@ -0,0 +1,242 @@
package net.minecraft.src;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import net.lax1dude.eaglercraft.sp.EaglercraftRandom;
import net.lax1dude.eaglercraft.sp.WorkerNetworkManager;
import net.minecraft.server.MinecraftServer;
public class NetLoginHandler extends NetHandler {
/** The Random object used to generate serverId hex strings. */
private static EaglercraftRandom rand = new EaglercraftRandom();
/** Reference to the MinecraftServer object. */
private final MinecraftServer mcServer;
public final WorkerNetworkManager myTCPConnection;
/**
* Returns if the login handler is finished and can be removed. It is set to
* true on either error or successful login.
*/
public boolean finishedProcessing = false;
/** While waiting to login, if this field ++'s to 600 it will kick you. */
private int loginTimer = 0;
private String clientUsername = null;
private volatile boolean field_72544_i = false;
private boolean field_92079_k = false;
private int hash = 0;
private static int hashBase = 69696969;
private int viewDistance = 2;
public NetLoginHandler(MinecraftServer par1MinecraftServer, WorkerNetworkManager par2Socket) {
this.mcServer = par1MinecraftServer;
this.myTCPConnection = par2Socket;
hash = ++hashBase;
}
public boolean shouldBeRemoved() {
return this.finishedProcessing;
}
/**
* Logs the user in if a login packet is found, otherwise keeps processing
* network packets unless the timeout has occurred.
*/
public void handlePackets() {
System.out.println("[Server][LOGIN][HANDLE][" + clientUsername + "]");
if (this.field_72544_i) {
this.initializePlayerConnection();
return;
}
if (this.loginTimer++ == 600) {
this.kickUser("Took too long to log in");
} else {
this.myTCPConnection.processReadPackets();
}
}
public boolean equals(Object o) {
return (o instanceof NetLoginHandler) && ((NetLoginHandler)o).hash == hash;
}
public int hashCode() {
return hash;
}
/**
* Disconnects the user with the given reason.
*/
public void kickUser(String par1Str) {
try {
this.mcServer.getLogAgent().logInfo("Disconnecting " + this.getUsernameAndAddress() + ": " + par1Str);
this.myTCPConnection.addToSendQueue(new Packet255KickDisconnect(par1Str));
this.myTCPConnection.serverShutdown();
this.finishedProcessing = true;
} catch (Exception var3) {
var3.printStackTrace();
}
}
public void handleClientProtocol(Packet2ClientProtocol par1Packet2ClientProtocol) {
this.clientUsername = par1Packet2ClientProtocol.getUsername();
int var2 = 64 << 3 - par1Packet2ClientProtocol.getViewDistance();
if(var2 > 400) {
var2 = 400;
}
var2 = (var2 >> 5) + 2;
this.viewDistance = var2;
System.out.println("[Server][HANDSHAKE][" + this.clientUsername + "]");
if (!this.clientUsername.equals(StringUtils.stripControlCodes(this.clientUsername))) {
this.kickUser("Invalid username!");
} else {
if (par1Packet2ClientProtocol.getProtocolVersion() != 61) {
if (par1Packet2ClientProtocol.getProtocolVersion() > 61) {
this.kickUser("Outdated server!");
} else {
this.kickUser("Outdated client!");
}
}else {
this.initializePlayerConnection();
}
}
}
public void handleClientCommand(Packet205ClientCommand par1Packet205ClientCommand) {
if (par1Packet205ClientCommand.forceRespawn == 0) {
if (this.field_92079_k) {
this.kickUser("Duplicate login");
return;
}
this.field_92079_k = true;
this.field_72544_i = true;
}
}
public void handleLogin(Packet1Login par1Packet1Login) {
}
/**
* on success the specified username is connected to the minecraftInstance,
* otherwise they are packet255'd
*/
public void initializePlayerConnection() {
String var1 = this.mcServer.getConfigurationManager().allowUserToConnect(this.clientUsername);
if (var1 != null) {
this.kickUser(var1);
} else {
EntityPlayerMP var2 = this.mcServer.getConfigurationManager().createPlayerForUser(this.clientUsername);
if (var2 != null) {
if (this.mcServer.getServerOwner().equals(this.clientUsername)) {
var2.renderDistance = this.viewDistance;
} else {
EntityPlayerMP fard = this.mcServer.getConfigurationManager().getPlayerForUsername(this.mcServer.getServerOwner());
int maxRenderDistance = fard == null ? 10 : (fard.renderDistance > 10 ? 10 : fard.renderDistance);
var2.renderDistance = this.viewDistance > maxRenderDistance ? maxRenderDistance : this.viewDistance;
}
this.mcServer.getConfigurationManager().initializeConnectionToPlayer(this.myTCPConnection, var2);
}else {
this.kickUser("Could not construct EntityPlayerMP for '" + var1 + "'");
}
}
this.finishedProcessing = true;
}
public void handleErrorMessage(String par1Str, Object[] par2ArrayOfObj) {
this.mcServer.getLogAgent().logInfo(this.getUsernameAndAddress() + " lost connection");
this.finishedProcessing = true;
}
/**
* Handle a server ping packet.
*/
public void handleServerPing(Packet254ServerPing par1Packet254ServerPing) {
try {
ServerConfigurationManager var2 = this.mcServer.getConfigurationManager();
String var3 = null;
if (par1Packet254ServerPing.readSuccessfully == 1) {
List var4 = Arrays.asList(new Serializable[] { Integer.valueOf(1), Integer.valueOf(61),
this.mcServer.getMinecraftVersion(), this.mcServer.getMOTD(),
Integer.valueOf(var2.getCurrentPlayerCount()), Integer.valueOf(var2.getMaxPlayers()) });
Object var6;
for (Iterator var5 = var4.iterator(); var5
.hasNext(); var3 = var3 + var6.toString().replaceAll("\u0000", "")) {
var6 = var5.next();
if (var3 == null) {
var3 = "\u00a7";
} else {
var3 = var3 + "\u0000";
}
}
} else {
var3 = this.mcServer.getMOTD() + "\u00a7" + var2.getCurrentPlayerCount() + "\u00a7"
+ var2.getMaxPlayers();
}
this.myTCPConnection.addToSendQueue(new Packet255KickDisconnect(var3));
this.myTCPConnection.serverShutdown();
this.finishedProcessing = true;
} catch (Exception var7) {
var7.printStackTrace();
}
}
/**
* Default handler called for packets that don't have their own handlers in
* NetServerHandler; kicks player from the server.
*/
public void unexpectedPacket(Packet par1Packet) {
this.kickUser("Protocol error");
}
public String getUsernameAndAddress() {
return this.clientUsername + "[EAG]";
}
/**
* determine if it is a server handler
*/
public boolean isServerHandler() {
return true;
}
/**
* Returns the server Id randomly generated by this login handler.
*/
static String getServerId(NetLoginHandler par0NetLoginHandler) {
return "you eagler";
}
/**
* Returns the reference to Minecraft Server.
*/
static MinecraftServer getLoginMinecraftServer(NetLoginHandler par0NetLoginHandler) {
return par0NetLoginHandler.mcServer;
}
/**
* Returns the connecting client username.
*/
static String getClientUsername(NetLoginHandler par0NetLoginHandler) {
return par0NetLoginHandler.clientUsername;
}
static boolean func_72531_a(NetLoginHandler par0NetLoginHandler, boolean par1) {
return par0NetLoginHandler.field_72544_i = par1;
}
}

View File

@ -1,5 +1,6 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.minecraft.server.MinecraftServer;
import java.io.*;
@ -25,7 +26,7 @@ public abstract class Packet
protected ILogAgent field_98193_m;
/** the system time in milliseconds when this packet was created. */
public final long creationTimeMillis = MinecraftServer.getSystemTimeMillis();
public final long creationTimeMillis = EaglerAdapter.steadyTimeMillis();
public static long receivedID;
public static long receivedSize;

View File

@ -4,74 +4,62 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class Packet2ClientProtocol extends Packet
{
public class Packet2ClientProtocol extends Packet {
private int protocolVersion;
private String username;
private String serverHost;
private int serverPort;
public Packet2ClientProtocol() {}
public Packet2ClientProtocol(int par1, String par2Str, String par3Str, int par4)
{
this.protocolVersion = par1;
this.username = par2Str;
this.serverHost = par3Str;
this.serverPort = par4;
}
private int viewDistance;
/**
* Abstract. Reads the raw packet data from the data stream.
*/
public void readPacketData(DataInput par1DataInput) throws IOException
{
this.protocolVersion = par1DataInput.readByte();
this.username = readString(par1DataInput, 16);
this.serverHost = readString(par1DataInput, 255);
this.serverPort = par1DataInput.readInt();
public void readPacketData(DataInput par1DataInputStream) throws IOException {
this.protocolVersion = par1DataInputStream.readByte();
this.username = readString(par1DataInputStream, 16);
this.serverHost = readString(par1DataInputStream, 255);
this.viewDistance = par1DataInputStream.readInt();
}
/**
* Abstract. Writes the raw packet data to the data stream.
*/
public void writePacketData(DataOutput par1DataOutput) throws IOException
{
par1DataOutput.writeByte(this.protocolVersion);
writeString(this.username, par1DataOutput);
writeString(this.serverHost, par1DataOutput);
par1DataOutput.writeInt(this.serverPort);
public void writePacketData(DataOutput par1DataOutputStream) throws IOException {
par1DataOutputStream.writeByte(this.protocolVersion);
writeString(this.username, par1DataOutputStream);
writeString(this.serverHost, par1DataOutputStream);
par1DataOutputStream.writeInt(this.viewDistance);
}
/**
* Passes this Packet on to the NetHandler for processing.
*/
public void processPacket(NetHandler par1NetHandler)
{
public void processPacket(NetHandler par1NetHandler) {
par1NetHandler.handleClientProtocol(this);
}
/**
* Abstract. Return the size of the packet (not counting the header).
*/
public int getPacketSize()
{
public int getPacketSize() {
return 3 + 2 * this.username.length();
}
/**
* Returns the protocol version.
*/
public int getProtocolVersion()
{
public int getProtocolVersion() {
return this.protocolVersion;
}
/**
* Returns the username.
*/
public String getUsername()
{
public String getUsername() {
return this.username;
}
}
public int getViewDistance() {
return this.viewDistance;
}
}

View File

@ -1,5 +1,7 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.lax1dude.eaglercraft.sp.RandomAccessMemoryFile;
import net.minecraft.server.MinecraftServer;
import java.io.*;
@ -11,8 +13,7 @@ import java.util.zip.InflaterInputStream;
public class RegionFile
{
private static final byte[] emptySector = new byte[4096];
private final File fileName;
private RandomAccessFile dataFile;
private RandomAccessMemoryFile dataFile;
private final int[] offsets = new int[1024];
private final int[] chunkTimestamps = new int[1024];
private ArrayList sectorFree;
@ -21,22 +22,16 @@ public class RegionFile
private int sizeDelta;
private long lastModified;
public RegionFile(File par1File)
public RegionFile(RandomAccessMemoryFile par1File)
{
this.fileName = par1File;
this.sizeDelta = 0;
try
{
if (par1File.exists())
{
this.lastModified = par1File.lastModified();
}
this.dataFile = new RandomAccessFile(par1File, "rw");
this.dataFile = par1File;
int var2;
if (this.dataFile.length() < 4096L)
if (this.dataFile.getLength() < 4096L)
{
for (var2 = 0; var2 < 1024; ++var2)
{
@ -51,15 +46,15 @@ public class RegionFile
this.sizeDelta += 8192;
}
if ((this.dataFile.length() & 4095L) != 0L)
if ((this.dataFile.getLength() & 4095L) != 0L)
{
for (var2 = 0; (long)var2 < (this.dataFile.length() & 4095L); ++var2)
for (var2 = 0; (long)var2 < (this.dataFile.getLength() & 4095L); ++var2)
{
this.dataFile.write(0);
}
}
var2 = (int)this.dataFile.length() / 4096;
var2 = (int)this.dataFile.getLength() / 4096;
this.sectorFree = new ArrayList(var2);
int var3;
@ -70,7 +65,7 @@ public class RegionFile
this.sectorFree.set(0, Boolean.valueOf(false));
this.sectorFree.set(1, Boolean.valueOf(false));
this.dataFile.seek(0L);
this.dataFile.seek(0);
int var4;
for (var3 = 0; var3 < 1024; ++var3)
@ -102,71 +97,50 @@ public class RegionFile
/**
* args: x, y - get uncompressed chunk stream from the region file
*/
public synchronized DataInputStream getChunkDataInputStream(int par1, int par2)
{
if (this.outOfBounds(par1, par2))
{
public synchronized DataInputStream getChunkDataInputStream(int par1, int par2) {
if (this.outOfBounds(par1, par2)) {
return null;
}
else
{
try
{
} else {
try {
int var3 = this.getOffset(par1, par2);
if (var3 == 0)
{
if (var3 == 0) {
return null;
}
else
{
} else {
int var4 = var3 >> 8;
int var5 = var3 & 255;
if (var4 + var5 > this.sectorFree.size())
{
if (var4 + var5 > this.sectorFree.size()) {
return null;
}
else
{
this.dataFile.seek((long)(var4 * 4096));
} else {
this.dataFile.seek(var4 * 4096);
int var6 = this.dataFile.readInt();
if (var6 > 4096 * var5)
{
if (var6 > 4096 * var5) {
return null;
}
else if (var6 <= 0)
{
} else if (var6 <= 0) {
return null;
}
else
{
} else {
byte var7 = this.dataFile.readByte();
byte[] var8;
if (var7 == 1)
{
if (var7 == 1) {
var8 = new byte[var6 - 1];
this.dataFile.read(var8);
return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(var8))));
}
else if (var7 == 2)
{
return new DataInputStream(
new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(var8))));
} else if (var7 == 2) {
var8 = new byte[var6 - 1];
this.dataFile.read(var8);
return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(var8))));
}
else
{
return new DataInputStream(new BufferedInputStream(
new InflaterInputStream(new ByteArrayInputStream(var8))));
} else {
return null;
}
}
}
}
}
catch (IOException var9)
{
} catch (IOException var9) {
return null;
}
}
@ -256,7 +230,7 @@ public class RegionFile
}
else
{
this.dataFile.seek(this.dataFile.length());
this.dataFile.seek(this.dataFile.getLength());
var6 = this.sectorFree.size();
for (var11 = 0; var11 < var8; ++var11)
@ -271,7 +245,7 @@ public class RegionFile
}
}
this.setChunkTimestamp(par1, par2, (int)(MinecraftServer.getSystemTimeMillis() / 1000L));
this.setChunkTimestamp(par1, par2, (int)(EaglerAdapter.steadyTimeMillis() / 1000L));
}
catch (IOException var12)
{
@ -284,7 +258,7 @@ public class RegionFile
*/
private void write(int par1, byte[] par2ArrayOfByte, int par3) throws IOException
{
this.dataFile.seek((long)(par1 * 4096));
this.dataFile.seek(par1 * 4096);
this.dataFile.writeInt(par3 + 1);
this.dataFile.writeByte(2);
this.dataFile.write(par2ArrayOfByte, 0, par3);
@ -320,7 +294,7 @@ public class RegionFile
private void setOffset(int par1, int par2, int par3) throws IOException
{
this.offsets[par1 + par2 * 32] = par3;
this.dataFile.seek((long)((par1 + par2 * 32) * 4));
this.dataFile.seek((par1 + par2 * 32) * 4);
this.dataFile.writeInt(par3);
}
@ -330,18 +304,29 @@ public class RegionFile
private void setChunkTimestamp(int par1, int par2, int par3) throws IOException
{
this.chunkTimestamps[par1 + par2 * 32] = par3;
this.dataFile.seek((long)(4096 + (par1 + par2 * 32) * 4));
this.dataFile.seek(4096 + (par1 + par2 * 32) * 4);
this.dataFile.writeInt(par3);
}
/**
* close this RegionFile and prevent further writes
*/
public void close() throws IOException
{
if (this.dataFile != null)
{
this.dataFile.close();
public RandomAccessMemoryFile getFile() {
return dataFile;
}
class ChunkBuffer extends ByteArrayOutputStream {
private int chunkX;
private int chunkZ;
public ChunkBuffer(int x, int z) {
super(8096);
this.chunkX = x;
this.chunkZ = z;
}
/**+
* close this RegionFile and prevent further writes
*/
public void close() throws IOException {
RegionFile.this.write(this.chunkX, this.chunkZ, this.buf, this.count);
}
}
}

View File

@ -1,5 +1,6 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.minecraft.server.MinecraftServer;
import java.io.*;
@ -16,7 +17,7 @@ public class SaveHandler implements ISaveHandler, IPlayerFileData
/**
* The time in milliseconds when this field was initialized. Stored in the session lock file.
*/
private final long initializationTime = MinecraftServer.getSystemTimeMillis();
private final long initializationTime = EaglerAdapter.steadyTimeMillis();
/** The directory name of the world */
private final String saveDirectoryName;

View File

@ -254,6 +254,19 @@ public abstract class ServerConfigurationManager
/**
* checks ban-lists, then white-lists, then space for the server. Returns null on success, or an error message
*/
public String allowUserToConnect(String par2Str) {
if(this.playerEntityList.size() >= this.maxPlayers) {
return "The server is full!";
}else {
for(EntityPlayerMP pp : (List<EntityPlayerMP>)this.playerEntityList) {
if(pp.username.equalsIgnoreCase(par2Str)) {
return "Someone with your username is already on this world";
}
}
return null;
}
}
public String allowUserToConnect(SocketAddress par1SocketAddress, String par2Str)
{
if (this.bannedPlayers.isBanned(par2Str))

View File

@ -2,6 +2,7 @@ package net.minecraft.src;
import java.io.IOException;
import java.util.IllegalFormatException;
import java.util.List;
import java.util.Properties;
import java.util.TreeMap;
@ -25,6 +26,10 @@ public class StringTranslate {
this.loadLanguageList();
}
public static void init(List<String> en_us) {
instance.loadLanguageList();
}
/**
* Return the StringTranslate singleton instance
*/

View File

@ -1,5 +1,6 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.lax1dude.eaglercraft.EaglercraftRandom;
import net.minecraft.server.MinecraftServer;
@ -4021,7 +4022,7 @@ public abstract class World implements IBlockAccess
{
if (this.getTotalWorldTime() % 600L == 0L)
{
this.theCalendar.setTimeInMillis(MinecraftServer.getSystemTimeMillis());
this.theCalendar.setTimeInMillis(EaglerAdapter.steadyTimeMillis());
}
return this.theCalendar;

View File

@ -1,5 +1,6 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglerAdapter;
import net.minecraft.server.MinecraftServer;
public class WorldInfo
@ -251,7 +252,7 @@ public class WorldInfo
par1NBTTagCompound.setLong("Time", this.totalTime);
par1NBTTagCompound.setLong("DayTime", this.worldTime);
par1NBTTagCompound.setLong("SizeOnDisk", this.sizeOnDisk);
par1NBTTagCompound.setLong("LastPlayed", MinecraftServer.getSystemTimeMillis());
par1NBTTagCompound.setLong("LastPlayed", EaglerAdapter.steadyTimeMillis());
par1NBTTagCompound.setString("LevelName", this.levelName);
par1NBTTagCompound.setInteger("version", this.saveVersion);
par1NBTTagCompound.setInteger("rainTime", this.rainTime);

View File

@ -1,6 +1,7 @@
package net.minecraft.src;
import net.lax1dude.eaglercraft.EaglercraftRandom;
import net.lax1dude.eaglercraft.sp.SysUtil;
import net.minecraft.server.MinecraftServer;
import java.util.*;
@ -45,6 +46,20 @@ public class WorldServer extends World
/** An IntHashMap of entity IDs (integers) to their Entity objects. */
private IntHashMap entityIdMap;
private int r = 0;
private int w = 0;
private int g = 0;
private int tu = 0;
private int lu = 0;
private int _r = 0;
private int _w = 0;
private int _g = 0;
private int _tu = 0;
private int _lu = 0;
private long rwgtuluTimer = 0l;
public WorldServer(MinecraftServer par1MinecraftServer, ISaveHandler par2ISaveHandler, String par3Str, int par4, WorldSettings par5WorldSettings, ILogAgent par7ILogAgent)
{
super(par2ISaveHandler, par3Str, par5WorldSettings, WorldProvider.getProviderForDimension(par4));
@ -133,6 +148,42 @@ public class WorldServer extends World
this.villageSiegeObj.tick();
this.worldTeleporter.removeStalePortalLocations(this.getTotalWorldTime());
this.sendAndApplyBlockEvents();
_r += this.theChunkProviderServer.statR();
_w += this.theChunkProviderServer.statW();
_g += this.theChunkProviderServer.statG();
_lu += Chunk.totalBlockLightUpdates;
Chunk.totalBlockLightUpdates = 0;
long millis = SysUtil.steadyTimeMillis();
if(millis - rwgtuluTimer >= 1000l) {
rwgtuluTimer = millis;
r = _r; _r = 0;
w = _w; _w = 0;
g = _g; _g = 0;
tu = _tu; _tu = 0;
lu = _lu; _lu = 0;
}
}
public int getR() {
return r;
}
public int getW() {
return w;
}
public int getG() {
return g;
}
public int getTU() {
return tu;
}
public int getLU() {
return lu;
}
/**