Start singleplayer, 16 errors
This commit is contained in:
parent
b49e35d003
commit
d2d261301c
|
@ -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>
|
|
@ -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.
|
@ -0,0 +1,18 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class BooleanResult {
|
||||
|
||||
public static final BooleanResult TRUE = new BooleanResult(true);
|
||||
public static final BooleanResult FALSE = new BooleanResult(false);
|
||||
|
||||
public final boolean bool;
|
||||
|
||||
private BooleanResult(boolean b) {
|
||||
bool = b;
|
||||
}
|
||||
|
||||
public static BooleanResult _new(boolean b) {
|
||||
return b ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
public class CRC32 implements Checksum {
|
||||
private com.jcraft.jzlib.CRC32 impl = new com.jcraft.jzlib.CRC32();
|
||||
long tbytes;
|
||||
|
||||
@Override
|
||||
public long getValue() {
|
||||
return impl.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
impl.reset();
|
||||
tbytes = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(int val) {
|
||||
impl.update(new byte[] { (byte) val }, 0, 1);
|
||||
}
|
||||
|
||||
public void update(byte[] buf) {
|
||||
update(buf, 0, buf.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] buf, int off, int nbytes) {
|
||||
// avoid int overflow, check null buf
|
||||
if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) {
|
||||
impl.update(buf, off, nbytes);
|
||||
tbytes += nbytes;
|
||||
} else {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.minecraft.src.ILogAgent;
|
||||
|
||||
public class EAGLogAgent implements ILogAgent {
|
||||
|
||||
private final Logger logger = Logger.getLogger("IntegratedServer");
|
||||
|
||||
public Logger getServerLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
public void logInfo(String par1Str) {
|
||||
this.logger.log(Level.INFO, par1Str);
|
||||
}
|
||||
|
||||
public void logWarning(String par1Str) {
|
||||
this.logger.log(Level.WARNING, par1Str);
|
||||
}
|
||||
|
||||
public void logWarningFormatted(String par1Str, Object... par2ArrayOfObj) {
|
||||
this.logger.log(Level.WARNING, par1Str, par2ArrayOfObj);
|
||||
}
|
||||
|
||||
public void logWarningException(String par1Str, Throwable par2Throwable) {
|
||||
this.logger.log(Level.WARNING, par1Str, par2Throwable);
|
||||
}
|
||||
|
||||
public void logSevere(String par1Str) {
|
||||
this.logger.log(Level.SEVERE, par1Str);
|
||||
}
|
||||
|
||||
public void logSevereException(String par1Str, Throwable par2Throwable) {
|
||||
this.logger.log(Level.SEVERE, par1Str, par2Throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logFine(String var1) {
|
||||
this.logger.log(Level.FINE, var1);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.ipc.IPCPacket14StringList;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.EnumGameType;
|
||||
import net.minecraft.src.ILogAgent;
|
||||
import net.minecraft.src.WorldSettings;
|
||||
|
||||
public class EAGMinecraftServer extends MinecraftServer {
|
||||
|
||||
protected int difficulty;
|
||||
protected EnumGameType gamemode;
|
||||
protected long lastTick;
|
||||
protected WorkerListenThread listenThreadImpl;
|
||||
protected WorldSettings newWorldSettings;
|
||||
protected boolean paused;
|
||||
private int tpsCounter = 0;
|
||||
private int tpsMeasure = 0;
|
||||
private long tpsTimer = 0l;
|
||||
|
||||
public EAGMinecraftServer(String world, String owner, WorldSettings currentWorldSettings) {
|
||||
super(world);
|
||||
this.setServerOwner(owner);
|
||||
System.out.println("server owner: " + owner);
|
||||
this.setConfigurationManager(new EAGPlayerList(this));
|
||||
this.listenThreadImpl = new WorkerListenThread(this);
|
||||
this.newWorldSettings = currentWorldSettings;
|
||||
this.paused = false;
|
||||
}
|
||||
|
||||
public void setBaseServerProperties(int difficulty, EnumGameType gamemode) {
|
||||
this.difficulty = difficulty;
|
||||
this.gamemode = gamemode;
|
||||
this.setCanSpawnAnimals(true);
|
||||
this.setCanSpawnNPCs(true);
|
||||
this.setAllowPvp(true);
|
||||
this.setAllowFlight(true);
|
||||
}
|
||||
|
||||
public void mainLoop() {
|
||||
long ctm = SysUtil.steadyTimeMillis();
|
||||
|
||||
long elapsed = ctm - tpsTimer;
|
||||
if(elapsed >= 1000l) {
|
||||
tpsTimer = ctm;
|
||||
tpsMeasure = tpsCounter;
|
||||
IntegratedServer.sendIPCPacket(new IPCPacket14StringList(IPCPacket14StringList.SERVER_TPS, getTPSAndChunkBuffer(tpsMeasure)));
|
||||
tpsCounter = 0;
|
||||
}
|
||||
|
||||
if(paused && this.playersOnline.size() <= 1) {
|
||||
lastTick = ctm;
|
||||
return;
|
||||
}
|
||||
|
||||
long delta = ctm - lastTick;
|
||||
|
||||
if (delta > 2000L && ctm - this.timeOfLastWarning >= 15000L) {
|
||||
this.getLogAgent().logWarning("Can\'t keep up! Did the system time change, or is the server overloaded? Skipping " + ((delta - 2000l) / 50l) + " ticks");
|
||||
delta = 2000L;
|
||||
this.timeOfLastWarning = ctm;
|
||||
}
|
||||
|
||||
if (delta < 0L) {
|
||||
this.getLogAgent().logWarning("Time ran backwards! Did the fucking system time change?");
|
||||
delta = 0L;
|
||||
}
|
||||
|
||||
if (this.worldServers[0].areAllPlayersAsleep()) {
|
||||
this.tick();
|
||||
++tpsCounter;
|
||||
lastTick = SysUtil.steadyTimeMillis();
|
||||
} else {
|
||||
if (delta > 50l) {
|
||||
delta -= 50L;
|
||||
lastTick += 50l;
|
||||
this.tick();
|
||||
++tpsCounter;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setPaused(boolean p) {
|
||||
paused = p;
|
||||
if(!p) {
|
||||
lastTick = SysUtil.steadyTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getPaused() {
|
||||
return paused;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean startServer() throws IOException {
|
||||
SkinsPlugin.reset();
|
||||
//VoiceChatPlugin.reset();
|
||||
this.loadAllWorlds(folderName, 0l, newWorldSettings);
|
||||
this.lastTick = SysUtil.steadyTimeMillis();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopServer() {
|
||||
super.stopServer();
|
||||
SkinsPlugin.reset();
|
||||
//VoiceChatPlugin.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canStructuresSpawn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumGameType getGameType() {
|
||||
return gamemode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDifficulty() {
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHardcore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDedicatedServer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommandBlockEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkerListenThread getNetworkThread() {
|
||||
return listenThreadImpl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shareToLAN(EnumGameType var1, boolean var2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILogAgent getLogAgent() {
|
||||
return IntegratedServer.logger;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.EntityPlayerMP;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.ServerConfigurationManager;
|
||||
|
||||
public class EAGPlayerList extends ServerConfigurationManager {
|
||||
|
||||
private NBTTagCompound hostPlayerNBT = null;
|
||||
|
||||
public EAGPlayerList(MinecraftServer par1MinecraftServer) {
|
||||
super(par1MinecraftServer);
|
||||
this.viewDistance = 4;
|
||||
}
|
||||
|
||||
protected void writePlayerData(EntityPlayerMP par1EntityPlayerMP) {
|
||||
if (par1EntityPlayerMP.getCommandSenderName().equals(this.getServerInstance().getServerOwner())) {
|
||||
this.hostPlayerNBT = new NBTTagCompound();
|
||||
par1EntityPlayerMP.writeToNBT(hostPlayerNBT);
|
||||
}
|
||||
super.writePlayerData(par1EntityPlayerMP);
|
||||
}
|
||||
|
||||
public NBTTagCompound getHostPlayerData() {
|
||||
return this.hostPlayerNBT;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import com.jcraft.jzlib.CRC32;
|
||||
|
||||
public class EPK2Compiler {
|
||||
|
||||
private final ByteArrayOutputStream os;
|
||||
private final CRC32 checkSum = new CRC32();
|
||||
private int lengthIntegerOffset = 0;
|
||||
private int totalFileCount = 0;
|
||||
|
||||
public EPK2Compiler(String name, String owner, String type) {
|
||||
os = new ByteArrayOutputStream(0x200000);
|
||||
try {
|
||||
|
||||
os.write(new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)36,(byte)36}); // EAGPKG$$
|
||||
os.write(new byte[]{(byte)6,(byte)118,(byte)101,(byte)114,(byte)50,(byte)46,(byte)48}); // 6 + ver2.0
|
||||
Date d = new Date();
|
||||
|
||||
byte[] filename = (name + ".epk").getBytes(StandardCharsets.UTF_8);
|
||||
os.write(filename.length);
|
||||
os.write(filename);
|
||||
|
||||
byte[] comment = ("\n\n # Eagler EPK v2.0 (c) " + (new SimpleDateFormat("yyyy")).format(d) + " " +
|
||||
owner + "\n # export: on " + (new SimpleDateFormat("MM/dd/yyyy")).format(d) + " at " +
|
||||
(new SimpleDateFormat("hh:mm:ss aa")).format(d) + "\n\n # world name: " + name + "\n\n")
|
||||
.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
os.write((comment.length >> 8) & 255);
|
||||
os.write(comment.length & 255);
|
||||
os.write(comment);
|
||||
|
||||
writeLong(d.getTime(), os);
|
||||
|
||||
lengthIntegerOffset = os.size();
|
||||
os.write(new byte[]{(byte)255,(byte)255,(byte)255,(byte)255}); // this will be replaced with the file count
|
||||
|
||||
os.write('0'); // compression type: none
|
||||
|
||||
os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
|
||||
os.write(new byte[]{(byte)9,(byte)102,(byte)105,(byte)108,(byte)101,(byte)45,(byte)116,(byte)121,
|
||||
(byte)112,(byte)101}); // 9 + file-type
|
||||
|
||||
byte[] typeBytes = type.getBytes(StandardCharsets.UTF_8);
|
||||
writeInt(typeBytes.length, os);
|
||||
os.write(typeBytes); // write type
|
||||
os.write('>');
|
||||
|
||||
++totalFileCount;
|
||||
|
||||
os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
|
||||
os.write(new byte[]{(byte)10,(byte)119,(byte)111,(byte)114,(byte)108,(byte)100,(byte)45,(byte)110,
|
||||
(byte)97,(byte)109,(byte)101}); // 10 + world-name
|
||||
|
||||
byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
|
||||
writeInt(nameBytes.length, os);
|
||||
os.write(nameBytes); // write name
|
||||
os.write('>');
|
||||
|
||||
++totalFileCount;
|
||||
|
||||
os.write(new byte[]{(byte)72,(byte)69,(byte)65,(byte)68}); // HEAD
|
||||
os.write(new byte[]{(byte)11,(byte)119,(byte)111,(byte)114,(byte)108,(byte)100,(byte)45,(byte)111,
|
||||
(byte)119,(byte)110,(byte)101,(byte)114}); // 11 + world-owner
|
||||
|
||||
byte[] ownerBytes = owner.getBytes(StandardCharsets.UTF_8);
|
||||
writeInt(ownerBytes.length, os);
|
||||
os.write(ownerBytes); // write owner
|
||||
os.write('>');
|
||||
|
||||
++totalFileCount;
|
||||
|
||||
}catch(IOException ex) {
|
||||
throw new RuntimeException("This happened somehow", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void append(String name, byte[] dat) {
|
||||
try {
|
||||
|
||||
checkSum.reset();
|
||||
checkSum.update(dat, 0, dat.length);
|
||||
long sum = checkSum.getValue();
|
||||
|
||||
os.write(new byte[]{(byte)70,(byte)73,(byte)76,(byte)69}); // FILE
|
||||
|
||||
byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
|
||||
os.write(nameBytes.length);
|
||||
os.write(nameBytes);
|
||||
|
||||
writeInt(dat.length + 5, os);
|
||||
writeInt((int)sum, os);
|
||||
|
||||
os.write(dat);
|
||||
|
||||
os.write(':');
|
||||
os.write('>');
|
||||
|
||||
++totalFileCount;
|
||||
|
||||
}catch(IOException ex) {
|
||||
throw new RuntimeException("This happened somehow", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] complete() {
|
||||
try {
|
||||
|
||||
os.write(new byte[]{(byte)69,(byte)78,(byte)68,(byte)36}); // END$
|
||||
os.write(new byte[]{(byte)58,(byte)58,(byte)58,(byte)89,(byte)69,(byte)69,(byte)58,(byte)62}); // :::YEE:>
|
||||
|
||||
byte[] ret = os.toByteArray();
|
||||
|
||||
ret[lengthIntegerOffset] = (byte)((totalFileCount >> 24) & 0xFF);
|
||||
ret[lengthIntegerOffset + 1] = (byte)((totalFileCount >> 16) & 0xFF);
|
||||
ret[lengthIntegerOffset + 2] = (byte)((totalFileCount >> 8) & 0xFF);
|
||||
ret[lengthIntegerOffset + 3] = (byte)(totalFileCount & 0xFF);
|
||||
|
||||
return ret;
|
||||
|
||||
}catch(IOException ex) {
|
||||
throw new RuntimeException("This happened somehow", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeInt(int i, OutputStream os) throws IOException {
|
||||
os.write((i >> 24) & 0xFF);
|
||||
os.write((i >> 16) & 0xFF);
|
||||
os.write((i >> 8) & 0xFF);
|
||||
os.write(i & 0xFF);
|
||||
}
|
||||
|
||||
public static void writeLong(long i, OutputStream os) throws IOException {
|
||||
os.write((int)((i >> 56) & 0xFF));
|
||||
os.write((int)((i >> 48) & 0xFF));
|
||||
os.write((int)((i >> 40) & 0xFF));
|
||||
os.write((int)((i >> 32) & 0xFF));
|
||||
os.write((int)((i >> 24) & 0xFF));
|
||||
os.write((int)((i >> 16) & 0xFF));
|
||||
os.write((int)((i >> 8) & 0xFF));
|
||||
os.write((int)(i & 0xFF));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.jcraft.jzlib.CRC32;
|
||||
import com.jcraft.jzlib.GZIPInputStream;
|
||||
import com.jcraft.jzlib.InflaterInputStream;
|
||||
|
||||
public class EPKDecompiler {
|
||||
|
||||
public static class FileEntry {
|
||||
public final String type;
|
||||
public final String name;
|
||||
public final byte[] data;
|
||||
protected FileEntry(String type, String name, byte[] data) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
private ByteArrayInputStream in2;
|
||||
private DataInputStream in;
|
||||
private InputStream zis;
|
||||
private SHA1Digest dg;
|
||||
private CRC32 crc32;
|
||||
private int numFiles;
|
||||
private boolean isFinished = false;
|
||||
private boolean isOldFormat = false;
|
||||
|
||||
public EPKDecompiler(byte[] data) throws IOException {
|
||||
in2 = new ByteArrayInputStream(data);
|
||||
|
||||
byte[] header = new byte[8];
|
||||
in2.read(header);
|
||||
|
||||
if(Arrays.equals(header, new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)36,(byte)36})) {
|
||||
byte[] endCode = new byte[] { (byte)':', (byte)':', (byte)':', (byte)'Y',
|
||||
(byte)'E', (byte)'E', (byte)':', (byte)'>' };
|
||||
for(int i = 0; i < 8; ++i) {
|
||||
if(data[data.length - 8 + i] != endCode[i]) {
|
||||
throw new IOException("EPK file is missing EOF code (:::YEE:>)");
|
||||
}
|
||||
}
|
||||
in2 = new ByteArrayInputStream(data, 8, data.length - 16);
|
||||
initNew();
|
||||
}else if(Arrays.equals(header, new byte[]{(byte)69,(byte)65,(byte)71,(byte)80,(byte)75,(byte)71,(byte)33,(byte)33})) {
|
||||
initOld();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isOld() {
|
||||
return isOldFormat;
|
||||
}
|
||||
|
||||
public FileEntry readFile() throws IOException {
|
||||
if(!isOldFormat) {
|
||||
return readFileNew();
|
||||
}else {
|
||||
return readFileOld();
|
||||
}
|
||||
}
|
||||
|
||||
private void initNew() throws IOException {
|
||||
InputStream is = in2;
|
||||
|
||||
String vers = readASCII(is);
|
||||
if(!vers.startsWith("ver2.")) {
|
||||
throw new IOException("Unknown or invalid EPK version: " + vers);
|
||||
}
|
||||
|
||||
is.skip(is.read()); // skip filename
|
||||
is.skip(loadShort(is)); // skip comment
|
||||
is.skip(8); // skip millis date
|
||||
|
||||
numFiles = loadInt(is);
|
||||
|
||||
char compressionType = (char)is.read();
|
||||
|
||||
switch(compressionType) {
|
||||
case 'G':
|
||||
zis = new GZIPInputStream(is);
|
||||
break;
|
||||
case 'Z':
|
||||
zis = new InflaterInputStream(is);
|
||||
break;
|
||||
case '0':
|
||||
zis = is;
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Invalid or unsupported EPK compression: " + compressionType);
|
||||
}
|
||||
|
||||
crc32 = new CRC32();
|
||||
|
||||
}
|
||||
|
||||
private FileEntry readFileNew() throws IOException {
|
||||
if(isFinished) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] typeBytes = new byte[4];
|
||||
zis.read(typeBytes);
|
||||
String type = readASCII(typeBytes);
|
||||
|
||||
if(numFiles == 0) {
|
||||
if(!"END$".equals(type)) {
|
||||
throw new IOException("EPK file is missing END code (END$)");
|
||||
}
|
||||
isFinished = true;
|
||||
return null;
|
||||
}else {
|
||||
if("END$".equals(type)) {
|
||||
throw new IOException("Unexpected END when there are still " + numFiles + " files remaining");
|
||||
}else {
|
||||
String name = readASCII(zis);
|
||||
int len = loadInt(zis);
|
||||
byte[] data;
|
||||
|
||||
if("FILE".equals(type)) {
|
||||
if(len < 5) {
|
||||
throw new IOException("File '" + name + "' is incomplete (no crc)");
|
||||
}
|
||||
|
||||
int loadedCrc = loadInt(zis);
|
||||
|
||||
data = new byte[len - 5];
|
||||
zis.read(data);
|
||||
|
||||
crc32.reset();
|
||||
crc32.update(data, 0, data.length);
|
||||
if((int)crc32.getValue() != loadedCrc) {
|
||||
throw new IOException("File '" + name + "' has an invalid checksum");
|
||||
}
|
||||
|
||||
if(zis.read() != ':') {
|
||||
throw new IOException("File '" + name + "' is incomplete");
|
||||
}
|
||||
}else {
|
||||
data = new byte[len];
|
||||
zis.read(data);
|
||||
}
|
||||
|
||||
if(zis.read() != '>') {
|
||||
throw new IOException("Object '" + name + "' is incomplete");
|
||||
}
|
||||
|
||||
--numFiles;
|
||||
return new FileEntry(type, name, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int loadShort(InputStream is) throws IOException {
|
||||
return (is.read() << 8) | is.read();
|
||||
}
|
||||
|
||||
private static final int loadInt(InputStream is) throws IOException {
|
||||
return (is.read() << 24) | (is.read() << 16) | (is.read() << 8) | is.read();
|
||||
}
|
||||
|
||||
public static final String readASCII(byte[] bytesIn) throws IOException {
|
||||
char[] charIn = new char[bytesIn.length];
|
||||
for(int i = 0; i < bytesIn.length; ++i) {
|
||||
charIn[i] = (char)((int)bytesIn[i] & 0xFF);
|
||||
}
|
||||
return new String(charIn);
|
||||
}
|
||||
|
||||
private static final String readASCII(InputStream bytesIn) throws IOException {
|
||||
int len = bytesIn.read();
|
||||
char[] charIn = new char[len];
|
||||
for(int i = 0; i < len; ++i) {
|
||||
charIn[i] = (char)(bytesIn.read() & 0xFF);
|
||||
}
|
||||
return new String(charIn);
|
||||
}
|
||||
|
||||
private void initOld() throws IOException {
|
||||
isOldFormat = true;
|
||||
dg = new SHA1Digest();
|
||||
in = new DataInputStream(in2);
|
||||
in.readUTF();
|
||||
in = new DataInputStream(new InflaterInputStream(in2));
|
||||
}
|
||||
|
||||
private FileEntry readFileOld() throws IOException {
|
||||
if(isFinished) {
|
||||
return null;
|
||||
}
|
||||
String s = in.readUTF();
|
||||
if(s.equals(" end")) {
|
||||
isFinished = true;
|
||||
return null;
|
||||
}else if(!s.equals("<file>")) {
|
||||
throw new IOException("invalid epk file");
|
||||
}
|
||||
String path = in.readUTF();
|
||||
byte[] digest = new byte[20];
|
||||
byte[] digest2 = new byte[20];
|
||||
in.read(digest);
|
||||
int len = in.readInt();
|
||||
byte[] file = new byte[len];
|
||||
in.read(file);
|
||||
dg.update(file, 0, len); dg.doFinal(digest2, 0);
|
||||
if(!Arrays.equals(digest, digest2)) throw new IOException("invalid file hash for "+path);
|
||||
if(!"</file>".equals(in.readUTF())) throw new IOException("invalid epk file");
|
||||
return new FileEntry("FILE", path, file);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class EaglerUUID {
|
||||
|
||||
private final long mostSigBits;
|
||||
private final long leastSigBits;
|
||||
|
||||
private EaglerUUID(byte[] data) {
|
||||
long msb = 0;
|
||||
long lsb = 0;
|
||||
assert data.length == 16 : "data must be 16 bytes in length";
|
||||
for (int i=0; i<8; i++)
|
||||
msb = (msb << 8) | (data[i] & 0xff);
|
||||
for (int i=8; i<16; i++)
|
||||
lsb = (lsb << 8) | (data[i] & 0xff);
|
||||
this.mostSigBits = msb;
|
||||
this.leastSigBits = lsb;
|
||||
}
|
||||
|
||||
public EaglerUUID(long mostSigBits, long leastSigBits) {
|
||||
this.mostSigBits = mostSigBits;
|
||||
this.leastSigBits = leastSigBits;
|
||||
}
|
||||
|
||||
private static final EaglercraftRandom random = new EaglercraftRandom();
|
||||
|
||||
public static EaglerUUID randomUUID() {
|
||||
byte[] randomBytes = new byte[16];
|
||||
random.nextBytes(randomBytes);
|
||||
randomBytes[6] &= 0x0f; /* clear version */
|
||||
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new EaglerUUID(randomBytes);
|
||||
}
|
||||
|
||||
private static final MD5Digest yee = new MD5Digest();
|
||||
|
||||
public static EaglerUUID nameUUIDFromBytes(byte[] name) {
|
||||
yee.update(name, 0, name.length);
|
||||
byte[] md5Bytes = new byte[16];
|
||||
yee.doFinal(md5Bytes, 0);
|
||||
md5Bytes[6] &= 0x0f; /* clear version */
|
||||
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new EaglerUUID(md5Bytes);
|
||||
}
|
||||
|
||||
public static EaglerUUID fromString(String name) {
|
||||
String[] components = name.split("-");
|
||||
if (components.length != 5)
|
||||
throw new IllegalArgumentException("Invalid UUID string: "+name);
|
||||
for (int i=0; i<5; i++)
|
||||
components[i] = "0x"+components[i];
|
||||
|
||||
long mostSigBits = Long.decode(components[0]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[1]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[2]).longValue();
|
||||
|
||||
long leastSigBits = Long.decode(components[3]).longValue();
|
||||
leastSigBits <<= 48;
|
||||
leastSigBits |= Long.decode(components[4]).longValue();
|
||||
|
||||
return new EaglerUUID(mostSigBits, leastSigBits);
|
||||
}
|
||||
|
||||
public long getLeastSignificantBits() {
|
||||
return leastSigBits;
|
||||
}
|
||||
|
||||
public long getMostSignificantBits() {
|
||||
return mostSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version number associated with this {@code UUID}. The version
|
||||
* number describes how this {@code UUID} was generated.
|
||||
*
|
||||
* The version number has the following meaning:
|
||||
* <ul>
|
||||
* <li>1 Time-based UUID
|
||||
* <li>2 DCE security UUID
|
||||
* <li>3 Name-based UUID
|
||||
* <li>4 Randomly generated UUID
|
||||
* </ul>
|
||||
*
|
||||
* @return The version number of this {@code UUID}
|
||||
*/
|
||||
public int version() {
|
||||
// Version is bits masked by 0x000000000000F000 in MS long
|
||||
return (int)((mostSigBits >> 12) & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* The variant number associated with this {@code UUID}. The variant
|
||||
* number describes the layout of the {@code UUID}.
|
||||
*
|
||||
* The variant number has the following meaning:
|
||||
* <ul>
|
||||
* <li>0 Reserved for NCS backward compatibility
|
||||
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>
|
||||
* (Leach-Salz), used by this class
|
||||
* <li>6 Reserved, Microsoft Corporation backward compatibility
|
||||
* <li>7 Reserved for future definition
|
||||
* </ul>
|
||||
*
|
||||
* @return The variant number of this {@code UUID}
|
||||
*/
|
||||
public int variant() {
|
||||
// This field is composed of a varying number of bits.
|
||||
// 0 - - Reserved for NCS backward compatibility
|
||||
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||
// 1 1 1 Reserved for future definition.
|
||||
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62)))
|
||||
& (leastSigBits >> 63));
|
||||
}
|
||||
|
||||
public long timestamp() {
|
||||
if (version() != 1) {
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
|
||||
return (mostSigBits & 0x0FFFL) << 48
|
||||
| ((mostSigBits >> 16) & 0x0FFFFL) << 32
|
||||
| mostSigBits >>> 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* The clock sequence value associated with this UUID.
|
||||
*
|
||||
* <p> The 14 bit clock sequence value is constructed from the clock
|
||||
* sequence field of this UUID. The clock sequence field is used to
|
||||
* guarantee temporal uniqueness in a time-based UUID.
|
||||
*
|
||||
* <p> The {@code clockSequence} value is only meaningful in a time-based
|
||||
* UUID, which has version type 1. If this UUID is not a time-based UUID
|
||||
* then this method throws UnsupportedOperationException.
|
||||
*
|
||||
* @return The clock sequence of this {@code UUID}
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
* If this UUID is not a version 1 UUID
|
||||
*/
|
||||
public int clockSequence() {
|
||||
if (version() != 1) {
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
|
||||
return (int)((leastSigBits & 0x3FFF000000000000L) >>> 48);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return (digits(mostSigBits >> 32, 8) + "-" +
|
||||
digits(mostSigBits >> 16, 4) + "-" +
|
||||
digits(mostSigBits, 4) + "-" +
|
||||
digits(leastSigBits >> 48, 4) + "-" +
|
||||
digits(leastSigBits, 12));
|
||||
}
|
||||
|
||||
private static String digits(long val, int digits) {
|
||||
long hi = 1L << (digits * 4);
|
||||
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
long hilo = mostSigBits ^ leastSigBits;
|
||||
return ((int)(hilo >> 32)) ^ (int) hilo;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if ((null == obj) || !(obj instanceof EaglerUUID))
|
||||
return false;
|
||||
EaglerUUID id = (EaglerUUID)obj;
|
||||
return (mostSigBits == id.mostSigBits &&
|
||||
leastSigBits == id.leastSigBits);
|
||||
}
|
||||
|
||||
public int compareTo(EaglerUUID val) {
|
||||
return (this.mostSigBits < val.mostSigBits ? -1 :
|
||||
(this.mostSigBits > val.mostSigBits ? 1 :
|
||||
(this.leastSigBits < val.leastSigBits ? -1 :
|
||||
(this.leastSigBits > val.leastSigBits ? 1 :
|
||||
0))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class EaglercraftRandom {
|
||||
|
||||
private static final long multiplier = 0x5DEECE66DL;
|
||||
private static final long addend = 0xBL;
|
||||
private static final long mask = (1L << 48) - 1;
|
||||
|
||||
private static final double DOUBLE_UNIT = 0x1.0p-53;
|
||||
private long seed = 69;
|
||||
|
||||
public EaglercraftRandom() {
|
||||
this(System.nanoTime());
|
||||
}
|
||||
|
||||
public EaglercraftRandom(long seed) {
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
public void setSeed(long yeed) {
|
||||
seed = yeed;
|
||||
}
|
||||
|
||||
protected int next(int bits) {
|
||||
seed = (seed * multiplier + addend) & mask;
|
||||
return (int)(seed >>> (48 - bits));
|
||||
}
|
||||
|
||||
public void nextBytes(byte[] bytes) {
|
||||
for (int i = 0, len = bytes.length; i < len; )
|
||||
for (int rnd = nextInt(),
|
||||
n = Math.min(len - i, Integer.SIZE/Byte.SIZE);
|
||||
n-- > 0; rnd >>= Byte.SIZE)
|
||||
bytes[i++] = (byte)rnd;
|
||||
}
|
||||
public int nextInt() {
|
||||
return next(32);
|
||||
}
|
||||
public int nextInt(int bound) {
|
||||
int r = next(31);
|
||||
int m = bound - 1;
|
||||
if ((bound & m) == 0) // i.e., bound is a power of 2
|
||||
r = (int)((bound * (long)r) >> 31);
|
||||
else {
|
||||
for (int u = r;
|
||||
u - (r = u % bound) + m < 0;
|
||||
u = next(31))
|
||||
;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
public long nextLong() {
|
||||
return ((long)(next(32)) << 32) + next(32);
|
||||
}
|
||||
public boolean nextBoolean() {
|
||||
return next(1) != 0;
|
||||
}
|
||||
public float nextFloat() {
|
||||
return next(24) / ((float)(1 << 24));
|
||||
}
|
||||
public double nextDouble() {
|
||||
return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
|
||||
}
|
||||
private double nextNextGaussian;
|
||||
private boolean haveNextNextGaussian = false;
|
||||
public double nextGaussian() {
|
||||
// See Knuth, ACP, Section 3.4.1 Algorithm C.
|
||||
if (haveNextNextGaussian) {
|
||||
haveNextNextGaussian = false;
|
||||
return nextNextGaussian;
|
||||
} else {
|
||||
double v1, v2, s;
|
||||
do {
|
||||
v1 = 2 * nextDouble() - 1; // between -1 and 1
|
||||
v2 = 2 * nextDouble() - 1; // between -1 and 1
|
||||
s = v1 * v1 + v2 * v2;
|
||||
} while (s >= 1 || s == 0);
|
||||
double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
|
||||
nextNextGaussian = v2 * multiplier;
|
||||
haveNextNextGaussian = true;
|
||||
return v1 * multiplier;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
// note that there's a few things not implemented, but I don't care.
|
||||
|
||||
public class ExpiringSet<T> extends HashSet<T> {
|
||||
private final long expiration;
|
||||
private final ExpiringEvent<T> event;
|
||||
|
||||
private final Map<T, Long> timestamps = new HashMap<>();
|
||||
|
||||
public ExpiringSet(long expiration) {
|
||||
this.expiration = expiration;
|
||||
this.event = null;
|
||||
}
|
||||
|
||||
public ExpiringSet(long expiration, ExpiringEvent<T> event) {
|
||||
this.expiration = expiration;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public interface ExpiringEvent<T> {
|
||||
void onExpiration(T item);
|
||||
}
|
||||
|
||||
public void checkForExpirations() {
|
||||
Iterator<T> iterator = this.timestamps.keySet().iterator();
|
||||
long now = SysUtil.steadyTimeMillis();
|
||||
while (iterator.hasNext()) {
|
||||
T element = iterator.next();
|
||||
if (super.contains(element)) {
|
||||
if (this.timestamps.get(element) + this.expiration < now) {
|
||||
if (this.event != null)
|
||||
this.event.onExpiration(element);
|
||||
iterator.remove();
|
||||
super.remove(element);
|
||||
}
|
||||
} else {
|
||||
iterator.remove();
|
||||
super.remove(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(T o) {
|
||||
checkForExpirations();
|
||||
boolean success = super.add(o);
|
||||
if (success)
|
||||
timestamps.put(o, SysUtil.steadyTimeMillis());
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean remove(Object o) {
|
||||
checkForExpirations();
|
||||
boolean success = super.remove(o);
|
||||
if (success)
|
||||
timestamps.remove(o);
|
||||
return success;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.timestamps.clear();
|
||||
super.clear();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
checkForExpirations();
|
||||
return super.contains(o);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
/**
|
||||
* base implementation of MD4 family style digest as outlined in
|
||||
* "Handbook of Applied Cryptography", pages 344 - 347.
|
||||
*/
|
||||
public abstract class GeneralDigest {
|
||||
private byte[] xBuf;
|
||||
private int xBufOff;
|
||||
|
||||
private long byteCount;
|
||||
|
||||
/**
|
||||
* Standard constructor
|
||||
*/
|
||||
protected GeneralDigest()
|
||||
{
|
||||
xBuf = new byte[4];
|
||||
xBufOff = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor. We are using copy constructors in place
|
||||
* of the Object.clone() interface as this interface is not
|
||||
* supported by J2ME.
|
||||
*/
|
||||
protected GeneralDigest(GeneralDigest t)
|
||||
{
|
||||
xBuf = new byte[t.xBuf.length];
|
||||
System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
|
||||
|
||||
xBufOff = t.xBufOff;
|
||||
byteCount = t.byteCount;
|
||||
}
|
||||
|
||||
public void update(
|
||||
byte in)
|
||||
{
|
||||
xBuf[xBufOff++] = in;
|
||||
|
||||
if (xBufOff == xBuf.length)
|
||||
{
|
||||
processWord(xBuf, 0);
|
||||
xBufOff = 0;
|
||||
}
|
||||
|
||||
byteCount++;
|
||||
}
|
||||
|
||||
public void update(
|
||||
byte[] in,
|
||||
int inOff,
|
||||
int len)
|
||||
{
|
||||
//
|
||||
// fill the current word
|
||||
//
|
||||
while ((xBufOff != 0) && (len > 0))
|
||||
{
|
||||
update(in[inOff]);
|
||||
|
||||
inOff++;
|
||||
len--;
|
||||
}
|
||||
|
||||
//
|
||||
// process whole words.
|
||||
//
|
||||
while (len > xBuf.length)
|
||||
{
|
||||
processWord(in, inOff);
|
||||
|
||||
inOff += xBuf.length;
|
||||
len -= xBuf.length;
|
||||
byteCount += xBuf.length;
|
||||
}
|
||||
|
||||
//
|
||||
// load in the remainder.
|
||||
//
|
||||
while (len > 0)
|
||||
{
|
||||
update(in[inOff]);
|
||||
|
||||
inOff++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
public void finish()
|
||||
{
|
||||
long bitLength = (byteCount << 3);
|
||||
|
||||
//
|
||||
// add the pad bytes.
|
||||
//
|
||||
update((byte)128);
|
||||
|
||||
while (xBufOff != 0)
|
||||
{
|
||||
update((byte)0);
|
||||
}
|
||||
|
||||
processLength(bitLength);
|
||||
|
||||
processBlock();
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
byteCount = 0;
|
||||
|
||||
xBufOff = 0;
|
||||
for ( int i = 0; i < xBuf.length; i++ ) {
|
||||
xBuf[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void processWord(byte[] in, int inOff);
|
||||
|
||||
protected abstract void processLength(long bitLength);
|
||||
|
||||
protected abstract void processBlock();
|
||||
}
|
|
@ -0,0 +1,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
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
/**
|
||||
* implementation of MD5 as outlined in "Handbook of Applied Cryptography",
|
||||
* pages 346 - 347.
|
||||
*/
|
||||
public class MD5Digest extends GeneralDigest {
|
||||
private static final int DIGEST_LENGTH = 16;
|
||||
|
||||
private int H1, H2, H3, H4; // IV's
|
||||
|
||||
private int[] X = new int[16];
|
||||
private int xOff;
|
||||
|
||||
public String getAlgorithmName() {
|
||||
return "MD5";
|
||||
}
|
||||
|
||||
public int getDigestSize() {
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
protected void processWord(byte[] in, int inOff) {
|
||||
X[xOff++] = littleEndianToInt(in, inOff);
|
||||
|
||||
if (xOff == 16) {
|
||||
processBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private int littleEndianToInt(byte[] bs, int off) {
|
||||
int n = bs[off] & 0xff;
|
||||
n |= (bs[++off] & 0xff) << 8;
|
||||
n |= (bs[++off] & 0xff) << 16;
|
||||
n |= bs[++off] << 24;
|
||||
return n;
|
||||
}
|
||||
|
||||
protected void processLength(long bitLength) {
|
||||
if (xOff > 14) {
|
||||
processBlock();
|
||||
}
|
||||
|
||||
X[14] = (int) (bitLength & 0xffffffff);
|
||||
X[15] = (int) (bitLength >>> 32);
|
||||
}
|
||||
|
||||
public int doFinal(byte[] out, int outOff) {
|
||||
finish();
|
||||
|
||||
intToLittleEndian(H1, out, outOff);
|
||||
intToLittleEndian(H2, out, outOff + 4);
|
||||
intToLittleEndian(H3, out, outOff + 8);
|
||||
intToLittleEndian(H4, out, outOff + 12);
|
||||
|
||||
reset();
|
||||
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
private void intToLittleEndian(int n, byte[] bs, int off) {
|
||||
bs[off] = (byte) (n);
|
||||
bs[++off] = (byte) (n >>> 8);
|
||||
bs[++off] = (byte) (n >>> 16);
|
||||
bs[++off] = (byte) (n >>> 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the chaining variables to the IV values.
|
||||
*/
|
||||
public void reset() {
|
||||
super.reset();
|
||||
|
||||
H1 = 0x67452301;
|
||||
H2 = 0xefcdab89;
|
||||
H3 = 0x98badcfe;
|
||||
H4 = 0x10325476;
|
||||
|
||||
xOff = 0;
|
||||
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// round 1 left rotates
|
||||
//
|
||||
private static final int S11 = 7;
|
||||
private static final int S12 = 12;
|
||||
private static final int S13 = 17;
|
||||
private static final int S14 = 22;
|
||||
|
||||
//
|
||||
// round 2 left rotates
|
||||
//
|
||||
private static final int S21 = 5;
|
||||
private static final int S22 = 9;
|
||||
private static final int S23 = 14;
|
||||
private static final int S24 = 20;
|
||||
|
||||
//
|
||||
// round 3 left rotates
|
||||
//
|
||||
private static final int S31 = 4;
|
||||
private static final int S32 = 11;
|
||||
private static final int S33 = 16;
|
||||
private static final int S34 = 23;
|
||||
|
||||
//
|
||||
// round 4 left rotates
|
||||
//
|
||||
private static final int S41 = 6;
|
||||
private static final int S42 = 10;
|
||||
private static final int S43 = 15;
|
||||
private static final int S44 = 21;
|
||||
|
||||
/*
|
||||
* rotate int x left n bits.
|
||||
*/
|
||||
private int rotateLeft(int x, int n) {
|
||||
return (x << n) | (x >>> (32 - n));
|
||||
}
|
||||
|
||||
/*
|
||||
* F, G, H and I are the basic MD5 functions.
|
||||
*/
|
||||
private int F(int u, int v, int w) {
|
||||
return (u & v) | (~u & w);
|
||||
}
|
||||
|
||||
private int G(int u, int v, int w) {
|
||||
return (u & w) | (v & ~w);
|
||||
}
|
||||
|
||||
private int H(int u, int v, int w) {
|
||||
return u ^ v ^ w;
|
||||
}
|
||||
|
||||
private int K(int u, int v, int w) {
|
||||
return v ^ (u | ~w);
|
||||
}
|
||||
|
||||
protected void processBlock() {
|
||||
int a = H1;
|
||||
int b = H2;
|
||||
int c = H3;
|
||||
int d = H4;
|
||||
|
||||
//
|
||||
// Round 1 - F cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + F(b, c, d) + X[0] + 0xd76aa478, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[1] + 0xe8c7b756, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[2] + 0x242070db, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[3] + 0xc1bdceee, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[4] + 0xf57c0faf, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[5] + 0x4787c62a, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[6] + 0xa8304613, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[7] + 0xfd469501, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[8] + 0x698098d8, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[9] + 0x8b44f7af, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[10] + 0xffff5bb1, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[11] + 0x895cd7be, S14) + c;
|
||||
a = rotateLeft(a + F(b, c, d) + X[12] + 0x6b901122, S11) + b;
|
||||
d = rotateLeft(d + F(a, b, c) + X[13] + 0xfd987193, S12) + a;
|
||||
c = rotateLeft(c + F(d, a, b) + X[14] + 0xa679438e, S13) + d;
|
||||
b = rotateLeft(b + F(c, d, a) + X[15] + 0x49b40821, S14) + c;
|
||||
|
||||
//
|
||||
// Round 2 - G cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + G(b, c, d) + X[1] + 0xf61e2562, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[6] + 0xc040b340, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[11] + 0x265e5a51, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[0] + 0xe9b6c7aa, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[5] + 0xd62f105d, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[10] + 0x02441453, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[15] + 0xd8a1e681, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[4] + 0xe7d3fbc8, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[9] + 0x21e1cde6, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[14] + 0xc33707d6, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[3] + 0xf4d50d87, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[8] + 0x455a14ed, S24) + c;
|
||||
a = rotateLeft(a + G(b, c, d) + X[13] + 0xa9e3e905, S21) + b;
|
||||
d = rotateLeft(d + G(a, b, c) + X[2] + 0xfcefa3f8, S22) + a;
|
||||
c = rotateLeft(c + G(d, a, b) + X[7] + 0x676f02d9, S23) + d;
|
||||
b = rotateLeft(b + G(c, d, a) + X[12] + 0x8d2a4c8a, S24) + c;
|
||||
|
||||
//
|
||||
// Round 3 - H cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + H(b, c, d) + X[5] + 0xfffa3942, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[8] + 0x8771f681, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[11] + 0x6d9d6122, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[14] + 0xfde5380c, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[1] + 0xa4beea44, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[4] + 0x4bdecfa9, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[7] + 0xf6bb4b60, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[10] + 0xbebfbc70, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[13] + 0x289b7ec6, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[0] + 0xeaa127fa, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[3] + 0xd4ef3085, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[6] + 0x04881d05, S34) + c;
|
||||
a = rotateLeft(a + H(b, c, d) + X[9] + 0xd9d4d039, S31) + b;
|
||||
d = rotateLeft(d + H(a, b, c) + X[12] + 0xe6db99e5, S32) + a;
|
||||
c = rotateLeft(c + H(d, a, b) + X[15] + 0x1fa27cf8, S33) + d;
|
||||
b = rotateLeft(b + H(c, d, a) + X[2] + 0xc4ac5665, S34) + c;
|
||||
|
||||
//
|
||||
// Round 4 - K cycle, 16 times.
|
||||
//
|
||||
a = rotateLeft(a + K(b, c, d) + X[0] + 0xf4292244, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[7] + 0x432aff97, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[14] + 0xab9423a7, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[5] + 0xfc93a039, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[12] + 0x655b59c3, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[3] + 0x8f0ccc92, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[10] + 0xffeff47d, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[1] + 0x85845dd1, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[8] + 0x6fa87e4f, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[15] + 0xfe2ce6e0, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[6] + 0xa3014314, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[13] + 0x4e0811a1, S44) + c;
|
||||
a = rotateLeft(a + K(b, c, d) + X[4] + 0xf7537e82, S41) + b;
|
||||
d = rotateLeft(d + K(a, b, c) + X[11] + 0xbd3af235, S42) + a;
|
||||
c = rotateLeft(c + K(d, a, b) + X[2] + 0x2ad7d2bb, S43) + d;
|
||||
b = rotateLeft(b + K(c, d, a) + X[9] + 0xeb86d391, S44) + c;
|
||||
|
||||
H1 += a;
|
||||
H2 += b;
|
||||
H3 += c;
|
||||
H4 += d;
|
||||
|
||||
//
|
||||
// reset the offset and clean out the word buffer.
|
||||
//
|
||||
xOff = 0;
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSClass;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
import org.teavm.jso.workers.MessagePort;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
@JSClass
|
||||
public class MessageChannel implements JSObject {
|
||||
|
||||
@JSBody(params = { }, script = "return (typeof MessageChannel !== \"undefined\");")
|
||||
public static native boolean supported();
|
||||
|
||||
@JSProperty
|
||||
public native MessagePort getPort1();
|
||||
|
||||
@JSProperty
|
||||
public native MessagePort getPort2();
|
||||
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class NoCatchParse {
|
||||
|
||||
public static final int INT_EXCEPTION = Integer.MIN_VALUE;
|
||||
public static final float FLOAT_EXCEPTION = Float.NaN;
|
||||
public static final double DOUBLE_EXCEPTION = Double.NaN;
|
||||
|
||||
public static int parseInt(String s) {
|
||||
return parseInt(s, 10, false, INT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int radix) {
|
||||
return parseInt(s, radix, false, INT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int radix, boolean log) {
|
||||
return parseInt(s, radix, log, INT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int radix, boolean log, int exceptionResult) {
|
||||
if (s == null) {
|
||||
if (log) {
|
||||
System.err.println("parseInt: string was null");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
if (s.isEmpty()) {
|
||||
if (log) {
|
||||
System.err.println("parseInt: string was empty");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
|
||||
if (log) {
|
||||
System.err.println("parseInt: invalid radix '" + radix + "'");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
tryFail: {
|
||||
int result = 0;
|
||||
boolean negative = false;
|
||||
int i = 0, len = s.length();
|
||||
int limit = -Integer.MAX_VALUE;
|
||||
int multmin;
|
||||
int digit;
|
||||
|
||||
if (len > 0) {
|
||||
char firstChar = s.charAt(0);
|
||||
if (firstChar < '0') { // Possible leading "+" or "-"
|
||||
if (firstChar == '-') {
|
||||
negative = true;
|
||||
limit = Integer.MIN_VALUE;
|
||||
} else if (firstChar != '+')
|
||||
break tryFail;
|
||||
|
||||
if (len == 1)
|
||||
break tryFail;
|
||||
i++;
|
||||
}
|
||||
multmin = limit / radix;
|
||||
while (i < len) {
|
||||
// Accumulating negatively avoids surprises near MAX_VALUE
|
||||
digit = Character.digit(s.charAt(i++), radix);
|
||||
if (digit < 0 || result < multmin) {
|
||||
|
||||
break tryFail;
|
||||
}
|
||||
result *= radix;
|
||||
if (result < limit + digit) {
|
||||
break tryFail;
|
||||
}
|
||||
result -= digit;
|
||||
}
|
||||
} else {
|
||||
break tryFail;
|
||||
}
|
||||
int ret = negative ? result : -result;
|
||||
if (ret == exceptionResult) {
|
||||
System.err.println(
|
||||
"parseInt: number '" + s + "' was parsed successfully but it is equal to exceptionResult");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
if (log) {
|
||||
System.err.println("parseInt: cannot parse '" + s + "'");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
public static double parseDouble(String s) {
|
||||
return parseDouble(s, false, DOUBLE_EXCEPTION);
|
||||
}
|
||||
|
||||
public static double parseDouble(String s, boolean log) {
|
||||
return parseDouble(s, log, DOUBLE_EXCEPTION);
|
||||
}
|
||||
|
||||
public static double parseDouble(String s, boolean log, double exceptionResult) {
|
||||
if (s == null) {
|
||||
if (log) {
|
||||
System.err.println("parseDouble: string was null");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
if (s.isEmpty()) {
|
||||
if (log) {
|
||||
System.err.println("parseDouble: string was empty");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
tryFail: {
|
||||
int start = 0;
|
||||
int end = s.length();
|
||||
while (s.charAt(start) <= ' ') {
|
||||
if (++start == end) {
|
||||
break tryFail;
|
||||
}
|
||||
}
|
||||
while (s.charAt(end - 1) <= ' ') {
|
||||
--end;
|
||||
}
|
||||
|
||||
boolean negative = false;
|
||||
int index = start;
|
||||
if (s.charAt(index) == '-') {
|
||||
++index;
|
||||
negative = true;
|
||||
} else if (s.charAt(index) == '+') {
|
||||
++index;
|
||||
}
|
||||
if (index == end) {
|
||||
break tryFail;
|
||||
}
|
||||
char c = s.charAt(index);
|
||||
|
||||
long mantissa = 0;
|
||||
int exp = 0;
|
||||
boolean hasOneDigit = false;
|
||||
if (c != '.') {
|
||||
hasOneDigit = true;
|
||||
if (c < '0' || c > '9') {
|
||||
break tryFail;
|
||||
}
|
||||
while (index < end && s.charAt(index) == '0') {
|
||||
++index;
|
||||
}
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < Long.MAX_VALUE / 10 - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
} else {
|
||||
++exp;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
if (index < end && s.charAt(index) == '.') {
|
||||
++index;
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < Long.MAX_VALUE / 10 - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
--exp;
|
||||
}
|
||||
++index;
|
||||
hasOneDigit = true;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
break tryFail;
|
||||
}
|
||||
}
|
||||
if (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c != 'e' && c != 'E') {
|
||||
break tryFail;
|
||||
}
|
||||
++index;
|
||||
boolean negativeExp = false;
|
||||
if (index == end) {
|
||||
break tryFail;
|
||||
}
|
||||
if (s.charAt(index) == '-') {
|
||||
++index;
|
||||
negativeExp = true;
|
||||
} else if (s.charAt(index) == '+') {
|
||||
++index;
|
||||
}
|
||||
int numExp = 0;
|
||||
hasOneDigit = false;
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
numExp = 10 * numExp + (c - '0');
|
||||
hasOneDigit = true;
|
||||
++index;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
break tryFail;
|
||||
}
|
||||
if (negativeExp) {
|
||||
numExp = -numExp;
|
||||
}
|
||||
exp += numExp;
|
||||
}
|
||||
if (exp > 308 || exp == 308 && mantissa > 17976931348623157L) {
|
||||
return !negative ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
if (negative) {
|
||||
mantissa = -mantissa;
|
||||
}
|
||||
return mantissa * doubleDecimalExponent(exp);
|
||||
}
|
||||
if (log) {
|
||||
System.err.println("parseDouble: cannot parse '" + s + "'");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
public static double doubleDecimalExponent(int n) {
|
||||
double d;
|
||||
if (n < 0) {
|
||||
d = 0.1;
|
||||
n = -n;
|
||||
} else {
|
||||
d = 10;
|
||||
}
|
||||
double result = 1;
|
||||
while (n != 0) {
|
||||
if (n % 2 != 0) {
|
||||
result *= d;
|
||||
}
|
||||
d *= d;
|
||||
n /= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float parseFloat(String s) {
|
||||
return parseFloat(s, false, FLOAT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static float parseFloat(String s, boolean log) {
|
||||
return parseFloat(s, log, FLOAT_EXCEPTION);
|
||||
}
|
||||
|
||||
public static float parseFloat(String s, boolean log, float exceptionResult) {
|
||||
if (s == null) {
|
||||
if (log) {
|
||||
System.err.println("parseFloat: string was null");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
if (s.isEmpty()) {
|
||||
if (log) {
|
||||
System.err.println("parseFloat: string was empty");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
tryFail: {
|
||||
int start = 0;
|
||||
int end = s.length();
|
||||
while (s.charAt(start) <= ' ') {
|
||||
if (++start == end) {
|
||||
break tryFail;
|
||||
}
|
||||
}
|
||||
while (s.charAt(end - 1) <= ' ') {
|
||||
--end;
|
||||
}
|
||||
|
||||
boolean negative = false;
|
||||
int index = start;
|
||||
if (s.charAt(index) == '-') {
|
||||
++index;
|
||||
negative = true;
|
||||
} else if (s.charAt(index) == '+') {
|
||||
++index;
|
||||
}
|
||||
if (index == end) {
|
||||
break tryFail;
|
||||
}
|
||||
char c = s.charAt(index);
|
||||
|
||||
int mantissa = 0;
|
||||
int exp = 0;
|
||||
|
||||
boolean hasOneDigit = false;
|
||||
if (c != '.') {
|
||||
hasOneDigit = true;
|
||||
if (c < '0' || c > '9') {
|
||||
break tryFail;
|
||||
}
|
||||
|
||||
while (index < end && s.charAt(index) == '0') {
|
||||
++index;
|
||||
}
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < (Integer.MAX_VALUE / 10) - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
} else {
|
||||
++exp;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < end && s.charAt(index) == '.') {
|
||||
++index;
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < (Integer.MAX_VALUE / 10) - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
--exp;
|
||||
}
|
||||
++index;
|
||||
hasOneDigit = true;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
break tryFail;
|
||||
}
|
||||
}
|
||||
if (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c != 'e' && c != 'E') {
|
||||
break tryFail;
|
||||
}
|
||||
++index;
|
||||
boolean negativeExp = false;
|
||||
if (index == end) {
|
||||
break tryFail;
|
||||
}
|
||||
if (s.charAt(index) == '-') {
|
||||
++index;
|
||||
negativeExp = true;
|
||||
} else if (s.charAt(index) == '+') {
|
||||
++index;
|
||||
}
|
||||
int numExp = 0;
|
||||
hasOneDigit = false;
|
||||
while (index < end) {
|
||||
c = s.charAt(index);
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
numExp = 10 * numExp + (c - '0');
|
||||
hasOneDigit = true;
|
||||
++index;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
break tryFail;
|
||||
}
|
||||
if (negativeExp) {
|
||||
numExp = -numExp;
|
||||
}
|
||||
exp += numExp;
|
||||
}
|
||||
if (exp > 38 || exp == 38 && mantissa > 34028234) {
|
||||
return !negative ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
|
||||
}
|
||||
if (negative) {
|
||||
mantissa = -mantissa;
|
||||
}
|
||||
return mantissa * floatDecimalExponent(exp);
|
||||
}
|
||||
if (log) {
|
||||
System.err.println("parseFloat: cannot parse '" + s + "'");
|
||||
}
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
private static float floatDecimalExponent(int n) {
|
||||
double d;
|
||||
if (n < 0) {
|
||||
d = 0.1;
|
||||
n = -n;
|
||||
} else {
|
||||
d = 10;
|
||||
}
|
||||
double result = 1;
|
||||
while (n != 0) {
|
||||
if (n % 2 != 0) {
|
||||
result *= d;
|
||||
}
|
||||
d *= d;
|
||||
n /= 2;
|
||||
}
|
||||
return (float) result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2023-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class RandomAccessMemoryFile implements DataInput, DataOutput {
|
||||
|
||||
private byte[] buffer;
|
||||
private int length;
|
||||
private int pos;
|
||||
|
||||
public RandomAccessMemoryFile(byte[] initialBuffer, int initialLength) {
|
||||
this.buffer = initialBuffer;
|
||||
this.length = initialLength;
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
private void grow(int newMaxSize) {
|
||||
if (length < newMaxSize) {
|
||||
if (buffer.length < newMaxSize) {
|
||||
byte[] newBuffer = new byte[newMaxSize | 0x7FFFF];
|
||||
System.arraycopy(buffer, 0, newBuffer, 0, length);
|
||||
buffer = newBuffer;
|
||||
}
|
||||
length = newMaxSize;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getByteArray() {
|
||||
byte[] b = new byte[length];
|
||||
System.arraycopy(buffer, 0, b, 0, length);
|
||||
return b;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return (pos < length) ? (buffer[pos++] & 0xff) : -1;
|
||||
}
|
||||
|
||||
private int readBytes(byte b[], int off, int len) throws IOException {
|
||||
if (pos >= length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int avail = length - pos;
|
||||
if (len > avail) {
|
||||
len = avail;
|
||||
}
|
||||
if (len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
System.arraycopy(buffer, pos, b, off, len);
|
||||
pos += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
return readBytes(b, off, len);
|
||||
}
|
||||
|
||||
public int read(byte b[]) throws IOException {
|
||||
return readBytes(b, 0, b.length);
|
||||
}
|
||||
|
||||
public final void readFully(byte b[]) throws IOException {
|
||||
readFully(b, 0, b.length);
|
||||
}
|
||||
|
||||
public final void readFully(byte b[], int off, int len) throws IOException {
|
||||
int n = 0;
|
||||
do {
|
||||
int count = this.read(b, off + n, len - n);
|
||||
if (count < 0)
|
||||
throw new EOFException();
|
||||
n += count;
|
||||
} while (n < len);
|
||||
}
|
||||
|
||||
public int skipBytes(int n) throws IOException {
|
||||
int newpos;
|
||||
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
newpos = pos + n;
|
||||
if (newpos > length) {
|
||||
newpos = length;
|
||||
}
|
||||
seek(newpos);
|
||||
|
||||
return (int) (newpos - pos);
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
grow(pos + 1);
|
||||
buffer[pos] = (byte) b;
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
private void writeBytes(byte b[], int off, int len) throws IOException {
|
||||
grow(pos + len);
|
||||
System.arraycopy(b, off, buffer, pos, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
public void write(byte b[]) throws IOException {
|
||||
writeBytes(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
writeBytes(b, off, len);
|
||||
}
|
||||
|
||||
public void seek(int pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(int newLength) {
|
||||
grow(newLength);
|
||||
}
|
||||
|
||||
public final boolean readBoolean() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0)
|
||||
throw new EOFException();
|
||||
return (ch != 0);
|
||||
}
|
||||
|
||||
public final byte readByte() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0)
|
||||
throw new EOFException();
|
||||
return (byte) (ch);
|
||||
}
|
||||
|
||||
public final int readUnsignedByte() throws IOException {
|
||||
int ch = this.read();
|
||||
if (ch < 0)
|
||||
throw new EOFException();
|
||||
return ch;
|
||||
}
|
||||
|
||||
public final short readShort() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new EOFException();
|
||||
return (short) ((ch1 << 8) + (ch2 << 0));
|
||||
}
|
||||
|
||||
public final int readUnsignedShort() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new EOFException();
|
||||
return (ch1 << 8) + (ch2 << 0);
|
||||
}
|
||||
|
||||
public final char readChar() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new EOFException();
|
||||
return (char) ((ch1 << 8) + (ch2 << 0));
|
||||
}
|
||||
|
||||
public final int readInt() throws IOException {
|
||||
int ch1 = this.read();
|
||||
int ch2 = this.read();
|
||||
int ch3 = this.read();
|
||||
int ch4 = this.read();
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0)
|
||||
throw new EOFException();
|
||||
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
|
||||
}
|
||||
|
||||
public final long readLong() throws IOException {
|
||||
return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
public final float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
public final double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
public final String readLine() throws IOException {
|
||||
StringBuilder input = new StringBuilder();
|
||||
int c = -1;
|
||||
boolean eol = false;
|
||||
|
||||
while (!eol) {
|
||||
switch (c = read()) {
|
||||
case -1:
|
||||
case '\n':
|
||||
eol = true;
|
||||
break;
|
||||
case '\r':
|
||||
eol = true;
|
||||
int cur = pos;
|
||||
if ((read()) != '\n') {
|
||||
seek(cur);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
input.append((char) c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((c == -1) && (input.length() == 0)) {
|
||||
return null;
|
||||
}
|
||||
return input.toString();
|
||||
}
|
||||
|
||||
public final String readUTF() throws IOException {
|
||||
throw new IOException("TODO");
|
||||
}
|
||||
|
||||
public final void writeBoolean(boolean v) throws IOException {
|
||||
write(v ? 1 : 0);
|
||||
}
|
||||
|
||||
public final void writeByte(int v) throws IOException {
|
||||
write(v);
|
||||
}
|
||||
|
||||
public final void writeShort(int v) throws IOException {
|
||||
write((v >>> 8) & 0xFF);
|
||||
write((v >>> 0) & 0xFF);
|
||||
}
|
||||
|
||||
public final void writeChar(int v) throws IOException {
|
||||
write((v >>> 8) & 0xFF);
|
||||
write((v >>> 0) & 0xFF);
|
||||
}
|
||||
|
||||
public final void writeInt(int v) throws IOException {
|
||||
write((v >>> 24) & 0xFF);
|
||||
write((v >>> 16) & 0xFF);
|
||||
write((v >>> 8) & 0xFF);
|
||||
write((v >>> 0) & 0xFF);
|
||||
}
|
||||
|
||||
public final void writeLong(long v) throws IOException {
|
||||
write((int) (v >>> 56) & 0xFF);
|
||||
write((int) (v >>> 48) & 0xFF);
|
||||
write((int) (v >>> 40) & 0xFF);
|
||||
write((int) (v >>> 32) & 0xFF);
|
||||
write((int) (v >>> 24) & 0xFF);
|
||||
write((int) (v >>> 16) & 0xFF);
|
||||
write((int) (v >>> 8) & 0xFF);
|
||||
write((int) (v >>> 0) & 0xFF);
|
||||
}
|
||||
|
||||
public final void writeFloat(float v) throws IOException {
|
||||
writeInt(Float.floatToIntBits(v));
|
||||
}
|
||||
|
||||
public final void writeDouble(double v) throws IOException {
|
||||
writeLong(Double.doubleToLongBits(v));
|
||||
}
|
||||
|
||||
public final void writeBytes(String s) throws IOException {
|
||||
int len = s.length();
|
||||
byte[] b = new byte[len];
|
||||
s.getBytes(0, len, b, 0);
|
||||
writeBytes(b, 0, len);
|
||||
}
|
||||
|
||||
public final void writeChars(String s) throws IOException {
|
||||
int clen = s.length();
|
||||
int blen = 2 * clen;
|
||||
byte[] b = new byte[blen];
|
||||
char[] c = new char[clen];
|
||||
s.getChars(0, clen, c, 0);
|
||||
for (int i = 0, j = 0; i < clen; i++) {
|
||||
b[j++] = (byte) (c[i] >>> 8);
|
||||
b[j++] = (byte) (c[i] >>> 0);
|
||||
}
|
||||
writeBytes(b, 0, blen);
|
||||
}
|
||||
|
||||
public final void writeUTF(String str) throws IOException {
|
||||
throw new IOException("TODO");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
/**
|
||||
* implementation of SHA-1 as outlined in "Handbook of Applied Cryptography",
|
||||
* pages 346 - 349.
|
||||
*
|
||||
* It is interesting to ponder why the, apart from the extra IV, the other
|
||||
* difference here from MD5 is the "endienness" of the word processing!
|
||||
*/
|
||||
public class SHA1Digest extends GeneralDigest {
|
||||
private static final int DIGEST_LENGTH = 20;
|
||||
|
||||
private int H1, H2, H3, H4, H5;
|
||||
|
||||
private int[] X = new int[80];
|
||||
private int xOff;
|
||||
|
||||
/**
|
||||
* Standard constructor
|
||||
*/
|
||||
public SHA1Digest() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor. This will copy the state of the provided message digest.
|
||||
*/
|
||||
public SHA1Digest(SHA1Digest t) {
|
||||
super(t);
|
||||
|
||||
H1 = t.H1;
|
||||
H2 = t.H2;
|
||||
H3 = t.H3;
|
||||
H4 = t.H4;
|
||||
H5 = t.H5;
|
||||
|
||||
System.arraycopy(t.X, 0, X, 0, t.X.length);
|
||||
xOff = t.xOff;
|
||||
}
|
||||
|
||||
public String getAlgorithmName() {
|
||||
return "SHA-1";
|
||||
}
|
||||
|
||||
public int getDigestSize() {
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
protected void processWord(byte[] in, int inOff) {
|
||||
X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16) | ((in[inOff + 2] & 0xff) << 8)
|
||||
| ((in[inOff + 3] & 0xff));
|
||||
|
||||
if (xOff == 16) {
|
||||
processBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void unpackWord(int word, byte[] out, int outOff) {
|
||||
out[outOff] = (byte) (word >>> 24);
|
||||
out[outOff + 1] = (byte) (word >>> 16);
|
||||
out[outOff + 2] = (byte) (word >>> 8);
|
||||
out[outOff + 3] = (byte) word;
|
||||
}
|
||||
|
||||
protected void processLength(long bitLength) {
|
||||
if (xOff > 14) {
|
||||
processBlock();
|
||||
}
|
||||
|
||||
X[14] = (int) (bitLength >>> 32);
|
||||
X[15] = (int) (bitLength & 0xffffffff);
|
||||
}
|
||||
|
||||
public int doFinal(byte[] out, int outOff) {
|
||||
finish();
|
||||
|
||||
unpackWord(H1, out, outOff);
|
||||
unpackWord(H2, out, outOff + 4);
|
||||
unpackWord(H3, out, outOff + 8);
|
||||
unpackWord(H4, out, outOff + 12);
|
||||
unpackWord(H5, out, outOff + 16);
|
||||
|
||||
reset();
|
||||
|
||||
return DIGEST_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the chaining variables
|
||||
*/
|
||||
public void reset() {
|
||||
super.reset();
|
||||
|
||||
H1 = 0x67452301;
|
||||
H2 = 0xefcdab89;
|
||||
H3 = 0x98badcfe;
|
||||
H4 = 0x10325476;
|
||||
H5 = 0xc3d2e1f0;
|
||||
|
||||
xOff = 0;
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Additive constants
|
||||
//
|
||||
private static final int Y1 = 0x5a827999;
|
||||
private static final int Y2 = 0x6ed9eba1;
|
||||
private static final int Y3 = 0x8f1bbcdc;
|
||||
private static final int Y4 = 0xca62c1d6;
|
||||
|
||||
private int f(int u, int v, int w) {
|
||||
return ((u & v) | ((~u) & w));
|
||||
}
|
||||
|
||||
private int h(int u, int v, int w) {
|
||||
return (u ^ v ^ w);
|
||||
}
|
||||
|
||||
private int g(int u, int v, int w) {
|
||||
return ((u & v) | (u & w) | (v & w));
|
||||
}
|
||||
|
||||
private int rotateLeft(int x, int n) {
|
||||
return (x << n) | (x >>> (32 - n));
|
||||
}
|
||||
|
||||
protected void processBlock() {
|
||||
//
|
||||
// expand 16 word block into 80 word block.
|
||||
//
|
||||
for (int i = 16; i <= 79; i++) {
|
||||
X[i] = rotateLeft((X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]), 1);
|
||||
}
|
||||
|
||||
//
|
||||
// set up working variables.
|
||||
//
|
||||
int A = H1;
|
||||
int B = H2;
|
||||
int C = H3;
|
||||
int D = H4;
|
||||
int E = H5;
|
||||
|
||||
//
|
||||
// round 1
|
||||
//
|
||||
for (int j = 0; j <= 19; j++) {
|
||||
int t = rotateLeft(A, 5) + f(B, C, D) + E + X[j] + Y1;
|
||||
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotateLeft(B, 30);
|
||||
B = A;
|
||||
A = t;
|
||||
}
|
||||
|
||||
//
|
||||
// round 2
|
||||
//
|
||||
for (int j = 20; j <= 39; j++) {
|
||||
int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y2;
|
||||
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotateLeft(B, 30);
|
||||
B = A;
|
||||
A = t;
|
||||
}
|
||||
|
||||
//
|
||||
// round 3
|
||||
//
|
||||
for (int j = 40; j <= 59; j++) {
|
||||
int t = rotateLeft(A, 5) + g(B, C, D) + E + X[j] + Y3;
|
||||
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotateLeft(B, 30);
|
||||
B = A;
|
||||
A = t;
|
||||
}
|
||||
|
||||
//
|
||||
// round 4
|
||||
//
|
||||
for (int j = 60; j <= 79; j++) {
|
||||
int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y4;
|
||||
|
||||
E = D;
|
||||
D = C;
|
||||
C = rotateLeft(B, 30);
|
||||
B = A;
|
||||
A = t;
|
||||
}
|
||||
|
||||
H1 += A;
|
||||
H2 += B;
|
||||
H3 += C;
|
||||
H4 += D;
|
||||
H5 += E;
|
||||
|
||||
//
|
||||
// reset the offset and clean out the word buffer.
|
||||
//
|
||||
xOff = 0;
|
||||
for (int i = 0; i != X.length; i++) {
|
||||
X[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
import net.lax1dude.eaglercraft.sp.VirtualFilesystem.VFSHandle;
|
||||
|
||||
public class SYS {
|
||||
|
||||
public static final VirtualFilesystem VFS;
|
||||
|
||||
@JSBody(params = { }, script = "return eaglercraftServerOpts.worldDatabaseName;")
|
||||
private static native String getWorldDatabaseName();
|
||||
|
||||
static {
|
||||
|
||||
VFSHandle vh = VirtualFilesystem.openVFS("_net_lax1dude_eaglercraft_sp_VirtualFilesystem_1_5_2_" + getWorldDatabaseName());
|
||||
|
||||
if(vh.vfs == null) {
|
||||
System.err.println("Could not init filesystem!");
|
||||
IntegratedServer.throwExceptionToClient("Could not init filesystem!", new RuntimeException("VFSHandle.vfs was null"));
|
||||
}
|
||||
|
||||
VFS = vh.vfs;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.minecraft.src.EntityPlayerMP;
|
||||
import net.minecraft.src.Packet250CustomPayload;
|
||||
|
||||
public class SkinsPlugin {
|
||||
|
||||
private static final HashMap<String,byte[]> skinCollection = new HashMap<>();
|
||||
private static final HashMap<String,byte[]> capeCollection = new HashMap<>();
|
||||
private static final HashMap<String,Long> lastSkinLayerUpdate = new HashMap<>();
|
||||
|
||||
private static final int[] SKIN_DATA_SIZE = new int[] { 64*32*4, 64*64*4, -9, -9, 1, 64*64*4, -9 }; // 128 pixel skins crash clients
|
||||
private static final int[] CAPE_DATA_SIZE = new int[] { 32*32*4, -9, 1 };
|
||||
|
||||
public static boolean handleMessage(EntityPlayerMP player, Packet250CustomPayload payload) {
|
||||
if(payload.data.length > 0) {
|
||||
String user = player.username;
|
||||
byte[] msg = payload.data;
|
||||
try {
|
||||
if("EAG|MySkin".equals(payload.channel)) {
|
||||
if(!skinCollection.containsKey(user)) {
|
||||
int t = (int)msg[0] & 0xFF;
|
||||
if(t < SKIN_DATA_SIZE.length && msg.length == (SKIN_DATA_SIZE[t] + 1)) {
|
||||
skinCollection.put(user, msg);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if("EAG|MyCape".equals(payload.channel)) {
|
||||
if(!capeCollection.containsKey(user)) {
|
||||
int t = (int)msg[0] & 0xFF;
|
||||
if(t < CAPE_DATA_SIZE.length && msg.length == (CAPE_DATA_SIZE[t] + 2)) {
|
||||
capeCollection.put(user, msg);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if("EAG|FetchSkin".equals(payload.channel)) {
|
||||
if(msg.length > 2) {
|
||||
String fetch = new String(msg, 2, msg.length - 2, StandardCharsets.UTF_8);
|
||||
byte[] data;
|
||||
if((data = skinCollection.get(fetch)) != null) {
|
||||
byte[] conc = new byte[data.length + 2];
|
||||
conc[0] = msg[0]; conc[1] = msg[1]; //synchronization cookie
|
||||
System.arraycopy(data, 0, conc, 2, data.length);
|
||||
if((data = capeCollection.get(fetch)) != null) {
|
||||
byte[] conc2 = new byte[conc.length + data.length];
|
||||
System.arraycopy(conc, 0, conc2, 0, conc.length);
|
||||
System.arraycopy(data, 0, conc2, conc.length, data.length);
|
||||
conc = conc2;
|
||||
}
|
||||
player.playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|UserSkin", conc));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if("EAG|SkinLayers".equals(payload.channel)) {
|
||||
long millis = SysUtil.steadyTimeMillis();
|
||||
Long lsu = lastSkinLayerUpdate.get(user);
|
||||
if(lsu != null && millis - lsu < 700L) { // DoS protection
|
||||
return true;
|
||||
}
|
||||
lastSkinLayerUpdate.put(user, millis);
|
||||
byte[] data;
|
||||
if((data = capeCollection.get(user)) != null) {
|
||||
data[1] = msg[0];
|
||||
}else {
|
||||
data = new byte[] { (byte)2, msg[0], (byte)0 };
|
||||
capeCollection.put(user, data);
|
||||
}
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
DataOutputStream dd = new DataOutputStream(bao);
|
||||
dd.write(msg[0]);
|
||||
dd.writeUTF(user);
|
||||
byte[] bpacket = bao.toByteArray();
|
||||
for(Object o : player.mcServer.getConfigurationManager().playerEntityList) {
|
||||
EntityPlayerMP pl = (EntityPlayerMP) o;
|
||||
if(!pl.username.equals(user)) {
|
||||
pl.playerNetServerHandler.sendPacket(new Packet250CustomPayload("EAG|SkinLayers", bpacket));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
// hacker
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void handleDisconnect(EntityPlayerMP player) {
|
||||
skinCollection.remove(player.username);
|
||||
capeCollection.remove(player.username);
|
||||
lastSkinLayerUpdate.remove(player.username);
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
skinCollection.clear();
|
||||
capeCollection.clear();
|
||||
lastSkinLayerUpdate.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.browser.Window;
|
||||
import org.teavm.jso.core.JSString;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.dom.events.MessageEvent;
|
||||
import org.teavm.platform.Platform;
|
||||
import org.teavm.platform.PlatformRunnable;
|
||||
|
||||
public class SysUtil {
|
||||
|
||||
private static final JSObject steadyTimeFunc = getSteadyTimeFunc();
|
||||
|
||||
@JSBody(params = { }, script = "return ((typeof performance !== \"undefined\") && (typeof performance.now === \"function\"))"
|
||||
+ "? performance.now.bind(performance)"
|
||||
+ ": (function(epochStart){ return function() { return Date.now() - epochStart; }; })(Date.now());")
|
||||
private static native JSObject getSteadyTimeFunc();
|
||||
|
||||
@JSBody(params = { "steadyTimeFunc" }, script = "return steadyTimeFunc();")
|
||||
private static native double steadyTimeMillis0(JSObject steadyTimeFunc);
|
||||
|
||||
public static long steadyTimeMillis() {
|
||||
return (long)steadyTimeMillis0(steadyTimeFunc);
|
||||
}
|
||||
|
||||
public static long nanoTime() {
|
||||
return (long)(steadyTimeMillis0(steadyTimeFunc) * 1000000.0);
|
||||
}
|
||||
|
||||
@Async
|
||||
public static native void sleep(int millis);
|
||||
|
||||
private static void sleep(int millis, final AsyncCallback<Void> callback) {
|
||||
Platform.schedule(new DumbSleepHandler(callback), millis);
|
||||
}
|
||||
|
||||
private static class DumbSleepHandler implements PlatformRunnable {
|
||||
private final AsyncCallback<Void> callback;
|
||||
private DumbSleepHandler(AsyncCallback<Void> callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
callback.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasCheckedImmediateContinue = false;
|
||||
private static MessageChannel immediateContinueChannel = null;
|
||||
private static Runnable currentContinueHack = null;
|
||||
private static final JSString emptyJSString = JSString.valueOf("");
|
||||
|
||||
public static void immediateContinue() {
|
||||
if(!hasCheckedImmediateContinue) {
|
||||
hasCheckedImmediateContinue = true;
|
||||
checkImmediateContinueSupport();
|
||||
}
|
||||
if(immediateContinueChannel != null) {
|
||||
immediateContinueTeaVM();
|
||||
}else {
|
||||
sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Async
|
||||
private static native void immediateContinueTeaVM();
|
||||
|
||||
private static void immediateContinueTeaVM(final AsyncCallback<Void> cb) {
|
||||
if(currentContinueHack != null) {
|
||||
cb.error(new IllegalStateException("Worker thread is already waiting for an immediate continue callback!"));
|
||||
return;
|
||||
}
|
||||
currentContinueHack = () -> {
|
||||
cb.complete(null);
|
||||
};
|
||||
try {
|
||||
immediateContinueChannel.getPort2().postMessage(emptyJSString);
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Caught error posting immediate continue, using setTimeout instead");
|
||||
Window.setTimeout(() -> cb.complete(null), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkImmediateContinueSupport() {
|
||||
try {
|
||||
immediateContinueChannel = null;
|
||||
if(!MessageChannel.supported()) {
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to MessageChannel being unsupported");
|
||||
return;
|
||||
}
|
||||
immediateContinueChannel = new MessageChannel();
|
||||
immediateContinueChannel.getPort1().addEventListener("message", new EventListener<MessageEvent>() {
|
||||
@Override
|
||||
public void handleEvent(MessageEvent evt) {
|
||||
Runnable toRun = currentContinueHack;
|
||||
currentContinueHack = null;
|
||||
if(toRun != null) {
|
||||
toRun.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
immediateContinueChannel.getPort1().start();
|
||||
immediateContinueChannel.getPort2().start();
|
||||
final boolean[] checkMe = new boolean[1];
|
||||
checkMe[0] = false;
|
||||
currentContinueHack = () -> {
|
||||
checkMe[0] = true;
|
||||
};
|
||||
immediateContinueChannel.getPort2().postMessage(emptyJSString);
|
||||
if(checkMe[0]) {
|
||||
currentContinueHack = null;
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to actually continuing immediately");
|
||||
return;
|
||||
}
|
||||
sleep(10);
|
||||
currentContinueHack = null;
|
||||
if(!checkMe[0]) {
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to startup check failing");
|
||||
}
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Fast immediate continue will be disabled for server context due to exceptions");
|
||||
if(immediateContinueChannel != null) {
|
||||
safeShutdownChannel(immediateContinueChannel);
|
||||
}
|
||||
immediateContinueChannel = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void safeShutdownChannel(MessageChannel chan) {
|
||||
try {
|
||||
chan.getPort1().close();
|
||||
}catch(Throwable tt) {
|
||||
}
|
||||
try {
|
||||
chan.getPort2().close();
|
||||
}catch(Throwable tt) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.typedarrays.ArrayBufferView;
|
||||
import org.teavm.jso.typedarrays.Float32Array;
|
||||
import org.teavm.jso.typedarrays.Int16Array;
|
||||
import org.teavm.jso.typedarrays.Int32Array;
|
||||
import org.teavm.jso.typedarrays.Int8Array;
|
||||
import org.teavm.jso.typedarrays.Uint8Array;
|
||||
|
||||
public class TeaVMUtils {
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native Int8Array unwrapByteArray(byte[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class)
|
||||
public static native ArrayBuffer unwrapArrayBuffer(byte[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native ArrayBufferView unwrapArrayBufferView(byte[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class)
|
||||
public static native byte[] wrapByteArray(Int8Array buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class)
|
||||
public static native byte[] wrapByteArrayBuffer(ArrayBuffer buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native byte[] wrapByteArrayBufferView(ArrayBufferView buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapUnsignedTypedArray.class)
|
||||
public static native Uint8Array unwrapUnsignedByteArray(byte[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native byte[] wrapUnsignedByteArray(Uint8Array buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native Int32Array unwrapIntArray(int[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class)
|
||||
public static native ArrayBuffer unwrapArrayBuffer(int[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native ArrayBufferView unwrapArrayBufferView(int[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class)
|
||||
public static native int[] wrapIntArray(Int32Array buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class)
|
||||
public static native int[] wrapIntArrayBuffer(ArrayBuffer buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native int[] wrapIntArrayBufferView(ArrayBufferView buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native Float32Array unwrapFloatArray(float[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class)
|
||||
public static native ArrayBuffer unwrapArrayBuffer(float[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native ArrayBufferView unwrapArrayBufferView(float[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class)
|
||||
public static native float[] wrapFloatArray(Float32Array buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class)
|
||||
public static native float[] wrapFloatArrayBuffer(ArrayBuffer buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native float[] wrapFloatArrayBufferView(ArrayBufferView buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native Int16Array unwrapShortArray(short[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapArrayBuffer.class)
|
||||
public static native ArrayBuffer unwrapArrayBuffer(short[] buf);
|
||||
|
||||
@InjectedBy(TeaVMUtilsUnwrapGenerator.UnwrapTypedArray.class)
|
||||
public static native ArrayBufferView unwrapArrayBufferView(short[] buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapTypedArray.class)
|
||||
public static native short[] wrapShortArray(Int16Array buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBuffer.class)
|
||||
public static native short[] wrapShortArrayBuffer(ArrayBuffer buf);
|
||||
|
||||
@GeneratedBy(TeaVMUtilsUnwrapGenerator.WrapArrayBufferView.class)
|
||||
public static native short[] wrapShortArrayBuffer(ArrayBufferView buf);
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.spi.Generator;
|
||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||
import org.teavm.backend.javascript.spi.Injector;
|
||||
import org.teavm.backend.javascript.spi.InjectorContext;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class TeaVMUtilsUnwrapGenerator {
|
||||
|
||||
// WARNING: This code uses internal TeaVM APIs that may not have
|
||||
// been intended for end users of the compiler to program with
|
||||
|
||||
public static class UnwrapArrayBuffer implements Injector {
|
||||
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) {
|
||||
context.writeExpr(context.getArgument(0));
|
||||
context.getWriter().append(".data.buffer");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UnwrapTypedArray implements Injector {
|
||||
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) {
|
||||
context.writeExpr(context.getArgument(0));
|
||||
context.getWriter().append(".data");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WrapArrayBuffer implements Generator {
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) {
|
||||
String parName = context.getParameterName(1);
|
||||
switch (methodRef.getName()) {
|
||||
case "wrapByteArrayBuffer":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_bytecls").append(',').ws();
|
||||
writer.append("new Int8Array(").append(parName).append("))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapIntArrayBuffer":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_intcls").append(',').ws();
|
||||
writer.append("new Int32Array(").append(parName).append("))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapFloatArrayBuffer":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_floatcls").append(',').ws();
|
||||
writer.append("new Float32Array(").append(parName).append("))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapShortArrayBuffer":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_shortcls").append(',').ws();
|
||||
writer.append("new Int16Array(").append(parName).append("))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WrapArrayBufferView implements Generator {
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) {
|
||||
String parName = context.getParameterName(1);
|
||||
switch (methodRef.getName()) {
|
||||
case "wrapByteArrayBufferView":
|
||||
case "wrapUnsignedByteArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_bytecls").append(',').ws();
|
||||
writer.append("new Int8Array(").append(parName).append(".buffer))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapIntArrayBufferView":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_intcls").append(',').ws();
|
||||
writer.append("new Int32Array(").append(parName).append(".buffer))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapFloatArrayBufferView":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_floatcls").append(',').ws();
|
||||
writer.append("new Float32Array(").append(parName).append(".buffer))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapShortArrayBufferView":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_shortcls").append(',').ws();
|
||||
writer.append("new Int16Array(").append(parName).append(".buffer))").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WrapTypedArray implements Generator {
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) {
|
||||
String parName = context.getParameterName(1);
|
||||
switch (methodRef.getName()) {
|
||||
case "wrapByteArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_shortcls").append(',').ws();
|
||||
writer.append(parName).append(")").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapIntArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_intcls").append(',').ws();
|
||||
writer.append(parName).append(")").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapFloatArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_floatcls").append(',').ws();
|
||||
writer.append(parName).append(")").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
case "wrapShortArray":
|
||||
writer.append("return ").append(parName).ws().append('?').ws();
|
||||
writer.appendFunction("$rt_wrapArray").append('(').appendFunction("$rt_shortcls").append(',').ws();
|
||||
writer.append(parName).append(")").ws();
|
||||
writer.append(':').ws().append("null;").softNewLine();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UnwrapUnsignedTypedArray implements Injector {
|
||||
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) {
|
||||
context.getWriter().append("new Uint8Array(");
|
||||
context.writeExpr(context.getArgument(0));
|
||||
context.getWriter().append(".data.buffer)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.src.Chunk;
|
||||
import net.minecraft.src.ChunkCoordIntPair;
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.Entity;
|
||||
import net.minecraft.src.EntityList;
|
||||
import net.minecraft.src.ExtendedBlockStorage;
|
||||
import net.minecraft.src.IChunkLoader;
|
||||
import net.minecraft.src.MinecraftException;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.NBTTagList;
|
||||
import net.minecraft.src.NextTickListEntry;
|
||||
import net.minecraft.src.NibbleArray;
|
||||
import net.minecraft.src.TileEntity;
|
||||
import net.minecraft.src.World;
|
||||
|
||||
public class VFSChunkLoader implements IChunkLoader {
|
||||
|
||||
public final VFile chunkDirectory;
|
||||
|
||||
private static final String hex = "0123456789ABCDEF";
|
||||
|
||||
public static String getChunkPath(int x, int z) {
|
||||
int unsignedX = x + 1900000;
|
||||
int unsignedZ = z + 1900000;
|
||||
|
||||
char[] path = new char[12];
|
||||
for(int i = 5; i >= 0; --i) {
|
||||
path[i] = hex.charAt((unsignedX >> (i * 4)) & 0xF);
|
||||
path[i + 6] = hex.charAt((unsignedZ >> (i * 4)) & 0xF);
|
||||
}
|
||||
|
||||
return new String(path);
|
||||
}
|
||||
|
||||
public static ChunkCoordIntPair getChunkCoords(String filename) {
|
||||
String strX = filename.substring(0, 6);
|
||||
String strZ = filename.substring(6);
|
||||
|
||||
int retX = 0;
|
||||
int retZ = 0;
|
||||
|
||||
for(int i = 0; i < 6; ++i) {
|
||||
retX |= hex.indexOf(strX.charAt(i)) << (i << 2);
|
||||
retZ |= hex.indexOf(strZ.charAt(i)) << (i << 2);
|
||||
}
|
||||
|
||||
return new ChunkCoordIntPair(retX - 1900000, retZ - 1900000);
|
||||
}
|
||||
|
||||
public VFSChunkLoader(VFile chunkDirectory) {
|
||||
this.chunkDirectory = chunkDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk loadChunk(World var1, int var2, int var3) throws IOException {
|
||||
VFile file = new VFile(chunkDirectory, getChunkPath(var2, var3) + ".dat");
|
||||
byte[] bytes = file.getAllBytes();
|
||||
|
||||
if(bytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
NBTTagCompound nbt = CompressedStreamTools.decompress(bytes);
|
||||
nbt = nbt.getCompoundTag("Level");
|
||||
return readChunkFromNBT(var1, nbt, var2, var3);
|
||||
}catch(Throwable t) {
|
||||
file.delete();
|
||||
System.err.println("Corrupted chunk has been deleted: [" + var2 + ", " + var3 + "]");
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChunk(World var1, Chunk var2) throws MinecraftException, IOException {
|
||||
|
||||
NBTTagCompound chunkFile = new NBTTagCompound();
|
||||
this.writeChunkToNBT(var2, var1, chunkFile);
|
||||
|
||||
byte[] save;
|
||||
|
||||
try {
|
||||
NBTTagCompound chunkFileSave = new NBTTagCompound();
|
||||
chunkFileSave.setCompoundTag("Level", chunkFile);
|
||||
save = CompressedStreamTools.compressChunk(chunkFileSave);
|
||||
}catch(IOException e) {
|
||||
System.err.println("Corrupted chunk could not be serialized: [" + var2.xPosition + ", " + var2.zPosition + "]");
|
||||
return;
|
||||
}
|
||||
|
||||
VFile file = new VFile(chunkDirectory, getChunkPath(var2.xPosition, var2.zPosition) + ".dat");
|
||||
|
||||
if(!file.setAllBytes(save)) {
|
||||
System.err.println("Corrupted chunk could not be written: [" + var2.xPosition + ", " + var2.zPosition + "] to file \"" + file.toString() + "\")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveExtraChunkData(World var1, Chunk var2) {
|
||||
// ?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chunkTick() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveExtraData() {
|
||||
// unused
|
||||
}
|
||||
|
||||
private Chunk readChunkFromNBT(World par1World, NBTTagCompound par2NBTTagCompound, int x, int z) {
|
||||
int var3 = x; //par2NBTTagCompound.getInteger("xPos");
|
||||
int var4 = z; //par2NBTTagCompound.getInteger("zPos");
|
||||
Chunk var5 = new Chunk(par1World, var3, var4);
|
||||
var5.heightMap = par2NBTTagCompound.getIntArray("HeightMap");
|
||||
var5.isTerrainPopulated = par2NBTTagCompound.getBoolean("TerrainPopulated");
|
||||
NBTTagList var6 = par2NBTTagCompound.getTagList("Sections");
|
||||
byte var7 = 16;
|
||||
ExtendedBlockStorage[] var8 = new ExtendedBlockStorage[var7];
|
||||
boolean var9 = !par1World.provider.hasNoSky;
|
||||
|
||||
for (int var10 = 0; var10 < var6.tagCount(); ++var10) {
|
||||
NBTTagCompound var11 = (NBTTagCompound) var6.tagAt(var10);
|
||||
byte var12 = var11.getByte("Y");
|
||||
ExtendedBlockStorage var13 = new ExtendedBlockStorage(var12 << 4, var9);
|
||||
var13.setBlockLSBArray(var11.getByteArray("Blocks"));
|
||||
|
||||
if (var11.hasKey("Add")) {
|
||||
var13.setBlockMSBArray(new NibbleArray(var11.getByteArray("Add"), 4));
|
||||
}
|
||||
|
||||
var13.setBlockMetadataArray(new NibbleArray(var11.getByteArray("Data"), 4));
|
||||
var13.setBlocklightArray(new NibbleArray(var11.getByteArray("BlockLight"), 4));
|
||||
|
||||
if (var9) {
|
||||
var13.setSkylightArray(new NibbleArray(var11.getByteArray("SkyLight"), 4));
|
||||
}
|
||||
|
||||
var13.removeInvalidBlocks();
|
||||
var8[var12] = var13;
|
||||
}
|
||||
|
||||
var5.setStorageArrays(var8);
|
||||
|
||||
if (par2NBTTagCompound.hasKey("Biomes")) {
|
||||
var5.setBiomeArray(par2NBTTagCompound.getByteArray("Biomes"));
|
||||
}
|
||||
|
||||
NBTTagList var17 = par2NBTTagCompound.getTagList("Entities");
|
||||
|
||||
if (var17 != null) {
|
||||
for (int var18 = 0; var18 < var17.tagCount(); ++var18) {
|
||||
NBTTagCompound var20 = (NBTTagCompound) var17.tagAt(var18);
|
||||
Entity var22 = EntityList.createEntityFromNBT(var20, par1World);
|
||||
var5.hasEntities = true;
|
||||
|
||||
if (var22 != null) {
|
||||
var5.addEntity(var22);
|
||||
Entity var14 = var22;
|
||||
|
||||
for (NBTTagCompound var15 = var20; var15.hasKey("Riding"); var15 = var15.getCompoundTag("Riding")) {
|
||||
Entity var16 = EntityList.createEntityFromNBT(var15.getCompoundTag("Riding"), par1World);
|
||||
|
||||
if (var16 != null) {
|
||||
var5.addEntity(var16);
|
||||
var14.mountEntity(var16);
|
||||
}
|
||||
|
||||
var14 = var16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NBTTagList var19 = par2NBTTagCompound.getTagList("TileEntities");
|
||||
|
||||
if (var19 != null) {
|
||||
for (int var21 = 0; var21 < var19.tagCount(); ++var21) {
|
||||
NBTTagCompound var24 = (NBTTagCompound) var19.tagAt(var21);
|
||||
TileEntity var26 = TileEntity.createAndLoadEntity(var24);
|
||||
|
||||
if (var26 != null) {
|
||||
var5.addTileEntity(var26);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (par2NBTTagCompound.hasKey("TileTicks")) {
|
||||
NBTTagList var23 = par2NBTTagCompound.getTagList("TileTicks");
|
||||
|
||||
if (var23 != null) {
|
||||
for (int var25 = 0; var25 < var23.tagCount(); ++var25) {
|
||||
NBTTagCompound var27 = (NBTTagCompound) var23.tagAt(var25);
|
||||
par1World.scheduleBlockUpdateFromLoad(var27.getInteger("x"), var27.getInteger("y"),
|
||||
var27.getInteger("z"), var27.getInteger("i"), var27.getInteger("t"), var27.getInteger("p"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
|
||||
private void writeChunkToNBT(Chunk par1Chunk, World par2World, NBTTagCompound par3NBTTagCompound) {
|
||||
par3NBTTagCompound.setInteger("xPos", par1Chunk.xPosition);
|
||||
par3NBTTagCompound.setInteger("zPos", par1Chunk.zPosition);
|
||||
par3NBTTagCompound.setLong("LastUpdate", par2World.getTotalWorldTime());
|
||||
par3NBTTagCompound.setIntArray("HeightMap", par1Chunk.heightMap);
|
||||
par3NBTTagCompound.setBoolean("TerrainPopulated", par1Chunk.isTerrainPopulated);
|
||||
ExtendedBlockStorage[] var4 = par1Chunk.getBlockStorageArray();
|
||||
NBTTagList var5 = new NBTTagList("Sections");
|
||||
boolean var6 = !par2World.provider.hasNoSky;
|
||||
ExtendedBlockStorage[] var7 = var4;
|
||||
int var8 = var4.length;
|
||||
NBTTagCompound var11;
|
||||
|
||||
for (int var9 = 0; var9 < var8; ++var9) {
|
||||
ExtendedBlockStorage var10 = var7[var9];
|
||||
|
||||
if (var10 != null) {
|
||||
var11 = new NBTTagCompound();
|
||||
var11.setByte("Y", (byte) (var10.getYLocation() >> 4 & 255));
|
||||
var11.setByteArray("Blocks", var10.getBlockLSBArray());
|
||||
|
||||
if (var10.getBlockMSBArray() != null) {
|
||||
var11.setByteArray("Add", var10.getBlockMSBArray().data);
|
||||
}
|
||||
|
||||
var11.setByteArray("Data", var10.getMetadataArray().data);
|
||||
var11.setByteArray("BlockLight", var10.getBlocklightArray().data);
|
||||
|
||||
if (var6) {
|
||||
var11.setByteArray("SkyLight", var10.getSkylightArray().data);
|
||||
} else {
|
||||
var11.setByteArray("SkyLight", new byte[var10.getBlocklightArray().data.length]);
|
||||
}
|
||||
|
||||
var5.appendTag(var11);
|
||||
}
|
||||
}
|
||||
|
||||
par3NBTTagCompound.setTag("Sections", var5);
|
||||
par3NBTTagCompound.setByteArray("Biomes", par1Chunk.getBiomeArray());
|
||||
par1Chunk.hasEntities = false;
|
||||
NBTTagList var16 = new NBTTagList();
|
||||
Iterator var18;
|
||||
|
||||
for (var8 = 0; var8 < par1Chunk.entityLists.length; ++var8) {
|
||||
var18 = par1Chunk.entityLists[var8].iterator();
|
||||
|
||||
while (var18.hasNext()) {
|
||||
Entity var20 = (Entity) var18.next();
|
||||
var11 = new NBTTagCompound();
|
||||
|
||||
if (var20.addEntityID(var11)) {
|
||||
par1Chunk.hasEntities = true;
|
||||
var16.appendTag(var11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
par3NBTTagCompound.setTag("Entities", var16);
|
||||
NBTTagList var17 = new NBTTagList();
|
||||
var18 = par1Chunk.chunkTileEntityMap.values().iterator();
|
||||
|
||||
while (var18.hasNext()) {
|
||||
TileEntity var21 = (TileEntity) var18.next();
|
||||
var11 = new NBTTagCompound();
|
||||
var21.writeToNBT(var11);
|
||||
var17.appendTag(var11);
|
||||
}
|
||||
|
||||
par3NBTTagCompound.setTag("TileEntities", var17);
|
||||
List var19 = par2World.getPendingBlockUpdates(par1Chunk, false);
|
||||
|
||||
if (var19 != null) {
|
||||
long var22 = par2World.getTotalWorldTime();
|
||||
NBTTagList var12 = new NBTTagList();
|
||||
Iterator var13 = var19.iterator();
|
||||
|
||||
while (var13.hasNext()) {
|
||||
NextTickListEntry var14 = (NextTickListEntry) var13.next();
|
||||
NBTTagCompound var15 = new NBTTagCompound();
|
||||
var15.setInteger("i", var14.blockID);
|
||||
var15.setInteger("x", var14.xCoord);
|
||||
var15.setInteger("y", var14.yCoord);
|
||||
var15.setInteger("z", var14.zCoord);
|
||||
var15.setInteger("t", (int) (var14.scheduledTime - var22));
|
||||
var15.setInteger("p", var14.field_82754_f);
|
||||
var12.appendTag(var15);
|
||||
}
|
||||
|
||||
par3NBTTagCompound.setTag("TileTicks", var12);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public interface VFSIterator {
|
||||
|
||||
public static class BreakLoop extends RuntimeException {
|
||||
public BreakLoop() {
|
||||
super("iterator loop break request");
|
||||
}
|
||||
}
|
||||
|
||||
public default void end() {
|
||||
throw new BreakLoop();
|
||||
}
|
||||
|
||||
public void next(VIteratorFile entry);
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import net.minecraft.src.IProgressUpdate;
|
||||
import net.minecraft.src.ISaveFormat;
|
||||
import net.minecraft.src.ISaveHandler;
|
||||
|
||||
public class VFSSaveFormat implements ISaveFormat {
|
||||
|
||||
private VFSSaveHandler folder;
|
||||
|
||||
public VFSSaveFormat(VFSSaveHandler dir) {
|
||||
folder = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISaveHandler getSaveLoader(String var1, boolean var2) {
|
||||
return folder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushCache() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteWorldDirectory(String var1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOldMapFormat(String var1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean convertMapFormat(String var1, IProgressUpdate var2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.EntityPlayer;
|
||||
import net.minecraft.src.IChunkLoader;
|
||||
import net.minecraft.src.IPlayerFileData;
|
||||
import net.minecraft.src.ISaveHandler;
|
||||
import net.minecraft.src.MinecraftException;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.WorldInfo;
|
||||
import net.minecraft.src.WorldProvider;
|
||||
|
||||
public class VFSSaveHandler implements ISaveHandler, IPlayerFileData {
|
||||
|
||||
public final VFile worldDirectory;
|
||||
|
||||
private final HashMap<Integer, VFSChunkLoader> chunkLoaders = new HashMap<>();
|
||||
|
||||
public VFSSaveHandler(VFile worldDirectory) {
|
||||
this.worldDirectory = worldDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldInfo loadWorldInfo() {
|
||||
|
||||
byte[] level_dat_bin = (new VFile(worldDirectory, "level.dat")).getAllBytes();
|
||||
|
||||
if(level_dat_bin == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
NBTTagCompound level_dat = CompressedStreamTools.decompress(level_dat_bin);
|
||||
return new WorldInfo(level_dat.getCompoundTag("Data"));
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Could not parse level.dat!");
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSessionLock() throws MinecraftException {
|
||||
// no
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkLoader getChunkLoader(WorldProvider var1) {
|
||||
VFSChunkLoader loader = chunkLoaders.get(var1.dimensionId);
|
||||
|
||||
if(loader == null) {
|
||||
loader = new VFSChunkLoader(new VFile(worldDirectory, "level" + var1.dimensionId));
|
||||
chunkLoaders.put(var1.dimensionId, loader);
|
||||
}
|
||||
|
||||
return loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveWorldInfoWithPlayer(WorldInfo var1, NBTTagCompound var2) {
|
||||
NBTTagCompound var3 = var2 != null ? var1.cloneNBTCompound(var2) : var1.getNBTTagCompound();
|
||||
NBTTagCompound var4 = new NBTTagCompound();
|
||||
var4.setTag("Data", var3);
|
||||
|
||||
VFile level_dat = new VFile(worldDirectory, "level.dat");
|
||||
|
||||
byte[] compressed;
|
||||
|
||||
try {
|
||||
compressed = CompressedStreamTools.compress(var4);
|
||||
}catch(IOException e) {
|
||||
System.err.println("Could not serialize \"" + level_dat + "\"");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!level_dat.setAllBytes(compressed)) {
|
||||
System.err.println("Could not save \"" + level_dat + "\" to filesystem");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveWorldInfo(WorldInfo var1) {
|
||||
saveWorldInfoWithPlayer(var1, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPlayerFileData getPlayerNBTManager() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public VFile getMapFileFromName(String var1) {
|
||||
return new VFile(worldDirectory, "data", var1 + ".dat");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorldDirectoryName() {
|
||||
return worldDirectory.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writePlayerData(EntityPlayer var1) {
|
||||
NBTTagCompound var2 = new NBTTagCompound();
|
||||
var1.writeToNBT(var2);
|
||||
|
||||
byte[] bin;
|
||||
|
||||
try {
|
||||
bin = CompressedStreamTools.compress(var2);
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Could not serialize player data for \"" + var1.username + "\"");
|
||||
t.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
VFile playerData = new VFile(worldDirectory, "player", var1.username.toLowerCase() + ".dat");
|
||||
|
||||
if(!playerData.setAllBytes(bin)) {
|
||||
System.err.println("Could not write player data for \"" + var1.username + "\" to file \"" + playerData.toString() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound readPlayerData(EntityPlayer var1) {
|
||||
VFile playerData = new VFile(worldDirectory, "player", var1.username.toLowerCase() + ".dat");
|
||||
|
||||
NBTTagCompound ret = null;
|
||||
|
||||
byte[] playerBin = playerData.getAllBytes();
|
||||
if(playerBin != null) {
|
||||
try {
|
||||
ret = CompressedStreamTools.decompress(playerBin);
|
||||
var1.readFromNBT(ret);
|
||||
}catch(IOException e) {
|
||||
System.err.println("Could not deserialize player data for \"" + var1.username + "\"");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAvailablePlayerDat() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String worldNameToFolderName(String par1Str) {
|
||||
par1Str = par1Str.replaceAll("[\\./\"]", "_");
|
||||
|
||||
boolean shit = true;
|
||||
while(shit) {
|
||||
shit = (new VFile("worlds", par1Str, "level.dat")).exists();
|
||||
if(shit) {
|
||||
par1Str = par1Str + "_";
|
||||
}
|
||||
}
|
||||
|
||||
return par1Str;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
public class VFSTestClass {
|
||||
|
||||
public static void test(VirtualFilesystem vfs) {
|
||||
/*
|
||||
System.out.println("'test1' exists: " + vfs.getFile("test1").exists());
|
||||
System.out.println("'test1' chars: " + vfs.getFile("test1").getAllChars());
|
||||
System.out.println("'test2' chars: " + vfs.getFile("test2").getAllChars());
|
||||
System.out.println("'test3' chars: " + vfs.getFile("test3").getAllChars());
|
||||
System.out.println("'test2' exists: " + vfs.getFile("test2").exists());
|
||||
System.out.println("'test3' exists: " + vfs.getFile("test3").exists());
|
||||
|
||||
System.out.println("'test1' set chars 'test string 1': " + vfs.getFile("test1").setAllChars("test string 1"));
|
||||
System.out.println("'test2' set chars 'test string 2': " + vfs.getFile("test2").setAllChars("test string 2"));
|
||||
System.out.println("'test3' set chars 'test string 3': " + vfs.getFile("test3").setAllChars("test string 3"));
|
||||
|
||||
System.out.println("'test1' exists: " + vfs.getFile("test1").exists());
|
||||
System.out.println("'test2' exists: " + vfs.getFile("test2").exists());
|
||||
System.out.println("'test3' exists: " + vfs.getFile("test3").exists());
|
||||
|
||||
System.out.println("'test1' chars: " + vfs.getFile("test1").getAllChars());
|
||||
System.out.println("'test2' chars: " + vfs.getFile("test2").getAllChars());
|
||||
System.out.println("'test3' chars: " + vfs.getFile("test3").getAllChars());
|
||||
|
||||
System.out.println("'test3' delete: " + vfs.getFile("test3").delete());
|
||||
System.out.println("'test3' exists: " + vfs.getFile("test3").exists());
|
||||
|
||||
System.out.println("'test2' delete: " + vfs.getFile("test2").delete());
|
||||
System.out.println("'test2' chars: " + vfs.getFile("test2").getAllChars());
|
||||
|
||||
System.out.println("'test4' exists: " + vfs.getFile("test4").exists());
|
||||
System.out.println("'test1' to 'test4' rename: " + vfs.getFile("test1").rename("test4"));
|
||||
System.out.println("'test4' exists: " + vfs.getFile("test4").exists());
|
||||
System.out.println("'test4' chars: " + vfs.getFile("test4").getAllChars());
|
||||
System.out.println("'test1' exists: " + vfs.getFile("test1").exists());
|
||||
System.out.println("'test4' to 'test1' rename: " + vfs.getFile("test4").rename("test1"));
|
||||
System.out.println("'test4' exists: " + vfs.getFile("test4").exists());
|
||||
System.out.println("'test4' chars: " + vfs.getFile("test4").getAllChars());
|
||||
System.out.println("'test1' exists: " + vfs.getFile("test1").exists());
|
||||
System.out.println("'test1' chars: " + vfs.getFile("test1").getAllChars());
|
||||
|
||||
System.out.println("'test1' cache get chars: " + vfs.getFile("test1", true).getAllChars());
|
||||
System.out.println("'test1' cache exists: " + vfs.getFile("test1", true).exists());
|
||||
System.out.println("'test1' cache delete: " + vfs.getFile("test1", true).delete());
|
||||
System.out.println("'test1' cache exists: " + vfs.getFile("test1", true).exists());
|
||||
System.out.println("'test1' cache get chars: " + vfs.getFile("test1", true).getAllChars());
|
||||
|
||||
System.out.println("'test1' cache set chars 'test cache string 1': " + vfs.getFile("test1", true).setAllChars("test cache string 1"));
|
||||
System.out.println("'test2' cache set chars 'test cache string 2': " + vfs.getFile("test2", true).setAllChars("test cache string 2"));
|
||||
System.out.println("'test3' cache set chars 'test cache string 3': " + vfs.getFile("test3", true).setAllChars("test cache string 3"));
|
||||
|
||||
System.out.println("'test1' cache chars: " + vfs.getFile("test1").getAllChars());
|
||||
System.out.println("'test2' cache chars: " + vfs.getFile("test2").getAllChars());
|
||||
System.out.println("'test3' cache chars: " + vfs.getFile("test3").getAllChars());
|
||||
|
||||
System.out.println("'test1' cache copy chars: " + VirtualFilesystem.utf8(vfs.getFile("test1").getAllBytes(true)));
|
||||
System.out.println("'test2' cache copy chars: " + VirtualFilesystem.utf8(vfs.getFile("test2").getAllBytes(true)));
|
||||
System.out.println("'test3' cache copy chars: " + VirtualFilesystem.utf8(vfs.getFile("test3").getAllBytes(true)));
|
||||
*/
|
||||
|
||||
VFile f = new VFile("test1");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("/test1");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("/test2/");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("test2/");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("test2/teste");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("\\test2\\teste");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("\\test2\\teste\\..\\eag");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("test2", "teste", "eag");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile(f, "../", "test2", "teste", "eag");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile(f, "../../", "test2", ".", "eag");
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("you/eag", f);
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile(" you/ eag ", f);
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("\\yee\\", f);
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("\\yee\\", "yeeler", f, new VFile("yee"));
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile(f, new VFile("yee2"));
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("yee/deevler/", new VFile("yee2"));
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("yee/../../../../", new VFile("yee2"));
|
||||
System.out.println(f);
|
||||
|
||||
f = new VFile("yee/../../deevler../../", new VFile("yee2"));
|
||||
System.out.println(f);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class VFile {
|
||||
|
||||
public static final String pathSeperator = "/";
|
||||
public static final String[] altPathSeperator = new String[] { "\\" };
|
||||
|
||||
public static String normalizePath(String p) {
|
||||
for(int i = 0; i < altPathSeperator.length; ++i) {
|
||||
p = p.replace(altPathSeperator[i], pathSeperator);
|
||||
}
|
||||
if(p.startsWith(pathSeperator)) {
|
||||
p = p.substring(1);
|
||||
}
|
||||
if(p.endsWith(pathSeperator)) {
|
||||
p = p.substring(0, p.length() - pathSeperator.length());
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public static String[] splitPath(String p) {
|
||||
String[] pth = normalizePath(p).split(pathSeperator);
|
||||
for(int i = 0; i < pth.length; ++i) {
|
||||
pth[i] = pth[i].trim();
|
||||
}
|
||||
return pth;
|
||||
}
|
||||
|
||||
protected String path;
|
||||
|
||||
public static String createPath(Object... p) {
|
||||
ArrayList<String> r = new ArrayList<>();
|
||||
for(int i = 0; i < p.length; ++i) {
|
||||
if(p[i] == null) {
|
||||
continue;
|
||||
}
|
||||
String gg = p[i].toString();
|
||||
if(gg == null) {
|
||||
continue;
|
||||
}
|
||||
String[] parts = splitPath(gg);
|
||||
for(int j = 0; j < parts.length; ++j) {
|
||||
if(parts[j] == null || parts[j].equals(".")) {
|
||||
continue;
|
||||
}else if(parts[j].equals("..") && r.size() > 0) {
|
||||
int k = r.size() - 1;
|
||||
if(!r.get(k).equals("..")) {
|
||||
r.remove(k);
|
||||
}else {
|
||||
r.add("..");
|
||||
}
|
||||
}else {
|
||||
r.add(parts[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(r.size() > 0) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for(int i = 0; i < r.size(); ++i) {
|
||||
if(i > 0) {
|
||||
s.append(pathSeperator);
|
||||
}
|
||||
s.append(r.get(i));
|
||||
}
|
||||
return s.toString();
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public VFile(Object... p) {
|
||||
this.path = createPath(p);
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return isRelative() ? null : SYS.VFS.getFile(path).getInputStream();
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return isRelative() ? null : SYS.VFS.getFile(path).getOutputStream();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isRelative() {
|
||||
return path == null || path.contains("..");
|
||||
}
|
||||
|
||||
public boolean canRead() {
|
||||
return !isRelative() && SYS.VFS.fileExists(path);
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path.equals("unnamed") ? null : path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.lastIndexOf(pathSeperator);
|
||||
return i == -1 ? path : path.substring(i + 1);
|
||||
}
|
||||
|
||||
public boolean canWrite() {
|
||||
return !isRelative();
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? ".." : path.substring(0, i);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return path == null ? 0 : path.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return path != null && o != null && (o instanceof VFile) && path.equals(((VFile)o).path);
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return !isRelative() && SYS.VFS.fileExists(path);
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return !isRelative() && SYS.VFS.deleteFile(path);
|
||||
}
|
||||
|
||||
public boolean renameTo(String p, boolean copy) {
|
||||
if(!isRelative() && SYS.VFS.renameFile(path, p, copy)) {
|
||||
path = p;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return isRelative() ? -1 : SYS.VFS.getFile(path).getSize();
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
if(isRelative()) {
|
||||
throw new ArrayIndexOutOfBoundsException("File is relative");
|
||||
}
|
||||
SYS.VFS.getFile(path).getBytes(fileOffset, array, offset, length);
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
if(isRelative()) {
|
||||
throw new RuntimeException("File is relative");
|
||||
}
|
||||
SYS.VFS.getFile(path).setCacheEnabled();
|
||||
}
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllBytes();
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllChars();
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllLines();
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
if(isRelative()) {
|
||||
return null;
|
||||
}
|
||||
return SYS.VFS.getFile(path).getAllBytes(copy);
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllChars(bytes);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllBytes(bytes);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
if(isRelative()) {
|
||||
return false;
|
||||
}
|
||||
return SYS.VFS.getFile(path).setAllBytes(bytes, copy);
|
||||
}
|
||||
|
||||
public List<String> list() {
|
||||
if(isRelative()) {
|
||||
return Arrays.asList(path);
|
||||
}
|
||||
return SYS.VFS.listFiles(path);
|
||||
}
|
||||
|
||||
public int deleteAll() {
|
||||
return isRelative() ? 0 : SYS.VFS.deleteFiles(path);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.dom.events.Event;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.indexeddb.IDBCursor;
|
||||
import org.teavm.jso.indexeddb.IDBRequest;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.typedarrays.Uint8Array;
|
||||
|
||||
/**
|
||||
* Do not use an instance of this class outside of the VFSIterator.next() method
|
||||
*/
|
||||
public class VIteratorFile extends VFile {
|
||||
|
||||
static final VIteratorFile instance = new VIteratorFile();
|
||||
|
||||
private VIteratorFile() {
|
||||
super("");
|
||||
this.idx = -1;
|
||||
this.cur = null;
|
||||
this.vfs = null;
|
||||
}
|
||||
|
||||
private static class VirtualIteratorOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
private final VIteratorFile itr;
|
||||
|
||||
protected VirtualIteratorOutputStream(VIteratorFile itr) {
|
||||
this.itr = itr;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if(!itr.setAllBytes(super.toByteArray(), false)) {
|
||||
throw new IOException("Could not close stream and write to \"" + itr.path + "\" on VFS \"" + itr.vfs.database + "\" (the file was probably deleted)");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int idx;
|
||||
private IDBCursor cur;
|
||||
private VirtualFilesystem vfs;
|
||||
private boolean wasDeleted;
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));")
|
||||
private static native String readKey(JSObject k);
|
||||
|
||||
static VIteratorFile create(int idx, VirtualFilesystem vfs, IDBCursor cur) {
|
||||
String k = readKey(cur.getKey());
|
||||
if(k == null) {
|
||||
return null;
|
||||
}
|
||||
instance.update(idx, k, vfs, cur);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public VFile makeVFile() {
|
||||
return new VFile(path);
|
||||
}
|
||||
|
||||
private void update(int idx, String path, VirtualFilesystem vfs, IDBCursor cur) {
|
||||
this.idx = idx;
|
||||
this.path = path;
|
||||
this.vfs = vfs;
|
||||
this.cur = cur;
|
||||
this.wasDeleted = false;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return !wasDeleted ? new ByteArrayInputStream(getAllBytes()) : null;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return !wasDeleted ? new VirtualIteratorOutputStream(this) : null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isRelative() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canRead() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? path : path.substring(i + 1);
|
||||
}
|
||||
|
||||
public boolean canWrite() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
if(path == null) {
|
||||
return null;
|
||||
}
|
||||
int i = path.indexOf(pathSeperator);
|
||||
return i == -1 ? ".." : path.substring(0, i);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return path == null ? 0 : path.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return path != null && o != null && (o instanceof VFile) && path.equals(((VFile)o).path);
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return !wasDeleted;
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return wasDeleted = AsyncHandlers.awaitRequest(cur.delete()).bool;
|
||||
}
|
||||
|
||||
public boolean renameTo(String p) {
|
||||
byte[] data = getAllBytes();
|
||||
String op = path;
|
||||
path = p;
|
||||
if(!setAllBytes(data)) {
|
||||
path = op;
|
||||
return false;
|
||||
}
|
||||
path = op;
|
||||
if(!delete()) {
|
||||
return false;
|
||||
}
|
||||
path = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
throw new RuntimeException("Value of entry is missing");
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
throw new RuntimeException("Value of the fucking value of the entry is missing");
|
||||
}
|
||||
|
||||
return arr.getByteLength();
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
throw new ArrayIndexOutOfBoundsException("Value of entry is missing");
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
throw new ArrayIndexOutOfBoundsException("Value of the fucking value of the entry is missing");
|
||||
}
|
||||
|
||||
Uint8Array a = new Uint8Array(arr);
|
||||
|
||||
if(a.getLength() < fileOffset + length) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + path + "' size was "+a.getLength()+" but user tried to read index "+(fileOffset + length - 1));
|
||||
}
|
||||
|
||||
for(int i = 0; i < length; ++i) {
|
||||
array[i + offset] = (byte)a.get(i + fileOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
// no
|
||||
}
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);")
|
||||
private static native ArrayBuffer readRow(JSObject obj);
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
JSObject obj = cur.getValue();
|
||||
|
||||
if(obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayBuffer arr = readRow(obj);
|
||||
|
||||
if(arr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Uint8Array a = new Uint8Array(arr);
|
||||
int ii = a.getByteLength();
|
||||
|
||||
byte[] array = new byte[ii];
|
||||
for(int i = 0; i < ii; ++i) {
|
||||
array[i] = (byte)a.get(i);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
return VirtualFilesystem.utf8(getAllBytes());
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
return VirtualFilesystem.lines(VirtualFilesystem.utf8(getAllBytes()));
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
return getAllBytes();
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
return setAllBytes(VirtualFilesystem.utf8(bytes));
|
||||
}
|
||||
|
||||
public List<String> list() {
|
||||
throw new RuntimeException("Cannot perform list all in VFS callback");
|
||||
}
|
||||
|
||||
public int deleteAll() {
|
||||
throw new RuntimeException("Cannot perform delete all in VFS callback");
|
||||
}
|
||||
|
||||
@JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };")
|
||||
private static native JSObject writeRow(String name, ArrayBuffer data);
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
ArrayBuffer a = new ArrayBuffer(bytes.length);
|
||||
Uint8Array ar = new Uint8Array(a);
|
||||
ar.set(bytes);
|
||||
JSObject obj = writeRow(path, a);
|
||||
BooleanResult r = AsyncHandlers.awaitRequest(cur.update(obj));
|
||||
return r.bool;
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
return setAllBytes(bytes);
|
||||
}
|
||||
|
||||
public static class AsyncHandlers {
|
||||
|
||||
@Async
|
||||
public static native BooleanResult awaitRequest(IDBRequest r);
|
||||
|
||||
private static void awaitRequest(IDBRequest r, final AsyncCallback<BooleanResult> cb) {
|
||||
r.addEventListener("success", new EventListener<Event>() {
|
||||
@Override
|
||||
public void handleEvent(Event evt) {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.addEventListener("error", new EventListener<Event>() {
|
||||
@Override
|
||||
public void handleEvent(Event evt) {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,688 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.teavm.interop.Async;
|
||||
import org.teavm.interop.AsyncCallback;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.dom.events.EventListener;
|
||||
import org.teavm.jso.indexeddb.EventHandler;
|
||||
import org.teavm.jso.indexeddb.IDBCountRequest;
|
||||
import org.teavm.jso.indexeddb.IDBCursor;
|
||||
import org.teavm.jso.indexeddb.IDBCursorRequest;
|
||||
import org.teavm.jso.indexeddb.IDBDatabase;
|
||||
import org.teavm.jso.indexeddb.IDBFactory;
|
||||
import org.teavm.jso.indexeddb.IDBGetRequest;
|
||||
import org.teavm.jso.indexeddb.IDBObjectStoreParameters;
|
||||
import org.teavm.jso.indexeddb.IDBOpenDBRequest;
|
||||
import org.teavm.jso.indexeddb.IDBRequest;
|
||||
import org.teavm.jso.indexeddb.IDBTransaction;
|
||||
import org.teavm.jso.indexeddb.IDBVersionChangeEvent;
|
||||
import org.teavm.jso.typedarrays.ArrayBuffer;
|
||||
import org.teavm.jso.typedarrays.Int8Array;
|
||||
import org.teavm.jso.typedarrays.Uint8Array;
|
||||
|
||||
public class VirtualFilesystem {
|
||||
|
||||
protected static class VirtualOutputStream extends ByteArrayOutputStream {
|
||||
private final VFSFile file;
|
||||
|
||||
protected VirtualOutputStream(VFSFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if(!file.setAllBytes(super.toByteArray(), false)) {
|
||||
throw new IOException("Could not close stream and write to \"" + file.filePath + "\" on VFS \"" + file.virtualFilesystem.database + "\" (the file was probably deleted)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class VFSFile {
|
||||
|
||||
public final VirtualFilesystem virtualFilesystem;
|
||||
protected boolean cacheEnabled;
|
||||
protected String filePath;
|
||||
protected int fileSize = -1;
|
||||
protected boolean hasBeenDeleted = false;
|
||||
protected boolean hasBeenAccessed = false;
|
||||
protected boolean exists = false;
|
||||
|
||||
protected byte[] cache = null;
|
||||
protected long cacheHit;
|
||||
|
||||
protected VFSFile(VirtualFilesystem vfs, String filePath, boolean cacheEnabled) {
|
||||
this.virtualFilesystem = vfs;
|
||||
this.filePath = filePath;
|
||||
this.cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(cacheEnabled) {
|
||||
setCacheEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof VFSFile) && ((VFSFile)o).filePath.equals(filePath);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return filePath.hashCode();
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(fileSize < 0) {
|
||||
if(cacheEnabled) {
|
||||
byte[] b = getAllBytes(false);
|
||||
if(b != null) {
|
||||
fileSize = b.length;
|
||||
}
|
||||
}else {
|
||||
ArrayBuffer dat = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
if(dat != null) {
|
||||
fileSize = dat.getByteLength();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
byte[] dat = getAllBytes(false);
|
||||
if(dat == null) {
|
||||
return null;
|
||||
}
|
||||
return new ByteArrayInputStream(dat);
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return new VirtualOutputStream(this);
|
||||
}
|
||||
|
||||
public void getBytes(int fileOffset, byte[] array, int offset, int length) {
|
||||
if(hasBeenDeleted) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' has been deleted");
|
||||
}else if(hasBeenAccessed && !exists) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' does not exist");
|
||||
}
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(cacheEnabled && cache != null) {
|
||||
System.arraycopy(cache, fileOffset, array, offset, length);
|
||||
}else {
|
||||
ArrayBuffer aa = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
hasBeenAccessed = true;
|
||||
if(aa != null) {
|
||||
exists = true;
|
||||
}else {
|
||||
exists = false;
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' does not exist");
|
||||
}
|
||||
this.fileSize = aa.getByteLength();
|
||||
if(cacheEnabled) {
|
||||
cache = TeaVMUtils.wrapByteArrayBuffer(aa);
|
||||
}
|
||||
if(fileSize < fileOffset + length) {
|
||||
throw new ArrayIndexOutOfBoundsException("file '" + filePath + "' size was "+fileSize+" but user tried to read index "+(fileOffset + length - 1));
|
||||
}
|
||||
TeaVMUtils.unwrapByteArray(array).set(new Int8Array(aa, fileOffset, length), offset);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheEnabled() {
|
||||
if(!cacheEnabled && !hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
cache = getAllBytes(false);
|
||||
cacheEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getAllBytes() {
|
||||
return getAllBytes(false);
|
||||
}
|
||||
|
||||
public String getAllChars() {
|
||||
return utf8(getAllBytes(false));
|
||||
}
|
||||
|
||||
public String[] getAllLines() {
|
||||
return lines(getAllChars());
|
||||
}
|
||||
|
||||
public byte[] getAllBytes(boolean copy) {
|
||||
if(hasBeenDeleted || (hasBeenAccessed && !exists)) {
|
||||
return null;
|
||||
}
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(cacheEnabled && cache != null) {
|
||||
byte[] b = cache;
|
||||
if(copy) {
|
||||
b = new byte[cache.length];
|
||||
System.arraycopy(cache, 0, b, 0, cache.length);
|
||||
}
|
||||
return b;
|
||||
}else {
|
||||
hasBeenAccessed = true;
|
||||
ArrayBuffer b = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
if(b != null) {
|
||||
exists = true;
|
||||
}else {
|
||||
exists = false;
|
||||
return null;
|
||||
}
|
||||
this.fileSize = b.getByteLength();
|
||||
if(cacheEnabled) {
|
||||
if(copy) {
|
||||
cache = new byte[fileSize];
|
||||
TeaVMUtils.unwrapByteArray(cache).set(new Int8Array(b));
|
||||
}else {
|
||||
cache = TeaVMUtils.wrapByteArrayBuffer(b);
|
||||
}
|
||||
}
|
||||
return TeaVMUtils.wrapByteArrayBuffer(b);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setAllChars(String bytes) {
|
||||
return setAllBytes(utf8(bytes), true);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes) {
|
||||
return setAllBytes(bytes, true);
|
||||
}
|
||||
|
||||
public boolean setAllBytes(byte[] bytes, boolean copy) {
|
||||
if(hasBeenDeleted || bytes == null) {
|
||||
return false;
|
||||
}
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
this.fileSize = bytes.length;
|
||||
if(cacheEnabled) {
|
||||
byte[] copz = bytes;
|
||||
if(copy) {
|
||||
copz = new byte[bytes.length];
|
||||
System.arraycopy(bytes, 0, copz, 0, bytes.length);
|
||||
}
|
||||
cache = copz;
|
||||
return sync();
|
||||
}else {
|
||||
boolean s = AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, filePath,
|
||||
TeaVMUtils.unwrapArrayBuffer(bytes)).bool;
|
||||
hasBeenAccessed = true;
|
||||
exists = exists || s;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean sync() {
|
||||
if(cacheEnabled && cache != null && !hasBeenDeleted) {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
boolean tryWrite = AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, filePath,
|
||||
TeaVMUtils.unwrapArrayBuffer(cache)).bool;
|
||||
hasBeenAccessed = true;
|
||||
exists = exists || tryWrite;
|
||||
return tryWrite;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
if(!hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(!AsyncHandlers.deleteFile(virtualFilesystem.indexeddb, filePath).bool) {
|
||||
hasBeenAccessed = true;
|
||||
return false;
|
||||
}
|
||||
virtualFilesystem.fileMap.remove(filePath);
|
||||
hasBeenDeleted = true;
|
||||
hasBeenAccessed = true;
|
||||
exists = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean rename(String newName, boolean copy) {
|
||||
if(!hasBeenDeleted && !(hasBeenAccessed && !exists)) {
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
ArrayBuffer arr = AsyncHandlers.readWholeFile(virtualFilesystem.indexeddb, filePath);
|
||||
hasBeenAccessed = true;
|
||||
if(arr != null) {
|
||||
exists = true;
|
||||
if(!AsyncHandlers.writeWholeFile(virtualFilesystem.indexeddb, newName, arr).bool) {
|
||||
return false;
|
||||
}
|
||||
if(!copy && !AsyncHandlers.deleteFile(virtualFilesystem.indexeddb, filePath).bool) {
|
||||
return false;
|
||||
}
|
||||
}else {
|
||||
exists = false;
|
||||
}
|
||||
if(!copy) {
|
||||
virtualFilesystem.fileMap.remove(filePath);
|
||||
filePath = newName;
|
||||
virtualFilesystem.fileMap.put(newName, this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
if(hasBeenDeleted) {
|
||||
return false;
|
||||
}
|
||||
cacheHit = SysUtil.steadyTimeMillis();
|
||||
if(hasBeenAccessed) {
|
||||
return exists;
|
||||
}
|
||||
exists = AsyncHandlers.fileExists(virtualFilesystem.indexeddb, filePath).bool;
|
||||
hasBeenAccessed = true;
|
||||
return exists;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final HashMap<String, VFSFile> fileMap = new HashMap<>();
|
||||
|
||||
public final String database;
|
||||
private final IDBDatabase indexeddb;
|
||||
|
||||
public static class VFSHandle {
|
||||
|
||||
public final boolean failedInit;
|
||||
public final boolean failedLocked;
|
||||
public final String failedError;
|
||||
public final VirtualFilesystem vfs;
|
||||
|
||||
public VFSHandle(boolean init, boolean locked, String error, VirtualFilesystem db) {
|
||||
failedInit = init;
|
||||
failedLocked = locked;
|
||||
failedError = error;
|
||||
vfs = db;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if(failedInit) {
|
||||
return "IDBFactory threw an exception, IndexedDB is most likely not supported in this browser." + (failedError == null ? "" : "\n\n" + failedError);
|
||||
}
|
||||
if(failedLocked) {
|
||||
return "The filesystem requested is already in use on a different tab.";
|
||||
}
|
||||
if(failedError != null) {
|
||||
return "The IDBFactory.open() request failed, reason: " + failedError;
|
||||
}
|
||||
return "Virtual Filesystem Object: " + vfs.database;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static VFSHandle openVFS(String db) {
|
||||
DatabaseOpen evt = AsyncHandlers.openDB(db);
|
||||
if(evt.failedInit) {
|
||||
return new VFSHandle(true, false, evt.failedError, null);
|
||||
}
|
||||
if(evt.failedLocked) {
|
||||
return new VFSHandle(false, true, null, null);
|
||||
}
|
||||
if(evt.failedError != null) {
|
||||
return new VFSHandle(false, false, evt.failedError, null);
|
||||
}
|
||||
return new VFSHandle(false, false, null, new VirtualFilesystem(db, evt.database));
|
||||
}
|
||||
|
||||
private VirtualFilesystem(String db, IDBDatabase idb) {
|
||||
database = db;
|
||||
indexeddb = idb;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
indexeddb.close();
|
||||
}
|
||||
|
||||
public VFSFile getFile(String path) {
|
||||
return getFile(path, false);
|
||||
}
|
||||
|
||||
public VFSFile getFile(String path, boolean cache) {
|
||||
VFSFile f = fileMap.get(path);
|
||||
if(f == null) {
|
||||
fileMap.put(path, f = new VFSFile(this, path, cache));
|
||||
}else {
|
||||
if(cache) {
|
||||
f.setCacheEnabled();
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
public boolean renameFile(String oldName, String newName, boolean copy) {
|
||||
return getFile(oldName).rename(newName, copy);
|
||||
}
|
||||
|
||||
public boolean deleteFile(String path) {
|
||||
return getFile(path).delete();
|
||||
}
|
||||
|
||||
public boolean fileExists(String path) {
|
||||
return getFile(path).exists();
|
||||
}
|
||||
|
||||
public List<String> listFiles(String prefix) {
|
||||
final ArrayList<String> list = new ArrayList<>();
|
||||
AsyncHandlers.iterateFiles(indexeddb, this, prefix, false, (v) -> {
|
||||
list.add(v.getPath());
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<VFile> listVFiles(String prefix) {
|
||||
final ArrayList<VFile> list = new ArrayList<>();
|
||||
AsyncHandlers.iterateFiles(indexeddb, this, prefix, false, (v) -> {
|
||||
list.add(new VFile(v.getPath()));
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
public int deleteFiles(String prefix) {
|
||||
return AsyncHandlers.deleteFiles(indexeddb, prefix);
|
||||
}
|
||||
|
||||
public int iterateFiles(String prefix, boolean rw, VFSIterator itr) {
|
||||
return AsyncHandlers.iterateFiles(indexeddb, this, prefix, rw, itr);
|
||||
}
|
||||
|
||||
public int renameFiles(String oldPrefix, String newPrefix, boolean copy) {
|
||||
List<String> filesToCopy = listFiles(oldPrefix);
|
||||
int i = 0;
|
||||
for(String str : filesToCopy) {
|
||||
String f = VFile.createPath(newPrefix, str.substring(oldPrefix.length()));
|
||||
if(!renameFile(str, f, copy)) {
|
||||
System.err.println("Could not " + (copy ? "copy" : "rename") + " file \"" + str + "\" to \"" + f + "\" for some reason");
|
||||
}else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public void flushCache(long age) {
|
||||
long curr = SysUtil.steadyTimeMillis();
|
||||
Iterator<VFSFile> files = fileMap.values().iterator();
|
||||
while(files.hasNext()) {
|
||||
if(curr - files.next().cacheHit > age) {
|
||||
files.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DatabaseOpen {
|
||||
|
||||
protected final boolean failedInit;
|
||||
protected final boolean failedLocked;
|
||||
protected final String failedError;
|
||||
|
||||
protected final IDBDatabase database;
|
||||
|
||||
protected DatabaseOpen(boolean init, boolean locked, String error, IDBDatabase db) {
|
||||
failedInit = init;
|
||||
failedLocked = locked;
|
||||
failedError = error;
|
||||
database = db;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JSBody(script = "return ((typeof indexedDB) !== 'undefined') ? indexedDB : null;")
|
||||
protected static native IDBFactory createIDBFactory();
|
||||
|
||||
protected static class AsyncHandlers {
|
||||
|
||||
@Async
|
||||
protected static native DatabaseOpen openDB(String name);
|
||||
|
||||
private static void openDB(String name, final AsyncCallback<DatabaseOpen> cb) {
|
||||
IDBFactory i = createIDBFactory();
|
||||
if(i == null) {
|
||||
cb.complete(new DatabaseOpen(false, false, "window.indexedDB was null or undefined", null));
|
||||
return;
|
||||
}
|
||||
final IDBOpenDBRequest f = i.open(name, 1);
|
||||
f.setOnBlocked(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, true, null, null));
|
||||
}
|
||||
});
|
||||
f.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, false, null, f.getResult()));
|
||||
}
|
||||
});
|
||||
f.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(new DatabaseOpen(false, false, "open error", null));
|
||||
}
|
||||
});
|
||||
f.setOnUpgradeNeeded(new EventListener<IDBVersionChangeEvent>() {
|
||||
@Override
|
||||
public void handleEvent(IDBVersionChangeEvent evt) {
|
||||
f.getResult().createObjectStore("filesystem", IDBObjectStoreParameters.create().keyPath("path"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult deleteFile(IDBDatabase db, String name);
|
||||
|
||||
private static void deleteFile(IDBDatabase db, String name, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBRequest r = tx.objectStore("filesystem").delete(makeTheFuckingKeyWork(name));
|
||||
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return (typeof obj === 'undefined') ? null : ((typeof obj.data === 'undefined') ? null : obj.data);")
|
||||
protected static native ArrayBuffer readRow(JSObject obj);
|
||||
|
||||
@JSBody(params = { "obj" }, script = "return [obj];")
|
||||
private static native JSObject makeTheFuckingKeyWork(String k);
|
||||
|
||||
@Async
|
||||
protected static native ArrayBuffer readWholeFile(IDBDatabase db, String name);
|
||||
|
||||
private static void readWholeFile(IDBDatabase db, String name, final AsyncCallback<ArrayBuffer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readonly");
|
||||
final IDBGetRequest r = tx.objectStore("filesystem").get(makeTheFuckingKeyWork(name));
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(readRow(r.getResult()));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(null);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"string\") ? k : (((typeof k) === \"undefined\") ? null : (((typeof k[0]) === \"string\") ? k[0] : null));")
|
||||
private static native String readKey(JSObject k);
|
||||
|
||||
@JSBody(params = { "k" }, script = "return ((typeof k) === \"undefined\") ? null : (((typeof k.path) === \"undefined\") ? null : (((typeof k.path) === \"string\") ? k[0] : null));")
|
||||
private static native String readRowKey(JSObject r);
|
||||
|
||||
@Async
|
||||
protected static native Integer iterateFiles(IDBDatabase db, final VirtualFilesystem vfs, final String prefix, boolean rw, final VFSIterator itr);
|
||||
|
||||
private static void iterateFiles(IDBDatabase db, final VirtualFilesystem vfs, final String prefix, boolean rw, final VFSIterator itr, final AsyncCallback<Integer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", rw ? "readwrite" : "readonly");
|
||||
final IDBCursorRequest r = tx.objectStore("filesystem").openCursor();
|
||||
final int[] res = new int[1];
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
IDBCursor c = r.getResult();
|
||||
if(c == null || c.getKey() == null || c.getValue() == null) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
String k = readKey(c.getKey());
|
||||
if(k != null) {
|
||||
if(k.startsWith(prefix)) {
|
||||
int ci = res[0]++;
|
||||
try {
|
||||
itr.next(VIteratorFile.create(ci, vfs, c));
|
||||
}catch(VFSIterator.BreakLoop ex) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
c.doContinue();
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(res[0] > 0 ? res[0] : -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native Integer deleteFiles(IDBDatabase db, final String prefix);
|
||||
|
||||
private static void deleteFiles(IDBDatabase db, final String prefix, final AsyncCallback<Integer> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBCursorRequest r = tx.objectStore("filesystem").openCursor();
|
||||
final int[] res = new int[1];
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
IDBCursor c = r.getResult();
|
||||
if(c == null || c.getKey() == null || c.getValue() == null) {
|
||||
cb.complete(res[0]);
|
||||
return;
|
||||
}
|
||||
String k = readKey(c.getKey());
|
||||
if(k != null) {
|
||||
if(k.startsWith(prefix)) {
|
||||
c.delete();
|
||||
++res[0];
|
||||
}
|
||||
}
|
||||
c.doContinue();
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(res[0] > 0 ? res[0] : -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult fileExists(IDBDatabase db, String name);
|
||||
|
||||
private static void fileExists(IDBDatabase db, String name, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readonly");
|
||||
final IDBCountRequest r = tx.objectStore("filesystem").count(makeTheFuckingKeyWork(name));
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(r.getResult() > 0));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@JSBody(params = { "pat", "dat" }, script = "return { path: pat, data: dat };")
|
||||
protected static native JSObject writeRow(String name, ArrayBuffer data);
|
||||
|
||||
@Async
|
||||
protected static native BooleanResult writeWholeFile(IDBDatabase db, String name, ArrayBuffer data);
|
||||
|
||||
private static void writeWholeFile(IDBDatabase db, String name, ArrayBuffer data, final AsyncCallback<BooleanResult> cb) {
|
||||
IDBTransaction tx = db.transaction("filesystem", "readwrite");
|
||||
final IDBRequest r = tx.objectStore("filesystem").put(writeRow(name, data));
|
||||
|
||||
r.setOnSuccess(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(true));
|
||||
}
|
||||
});
|
||||
r.setOnError(new EventHandler() {
|
||||
@Override
|
||||
public void handleEvent() {
|
||||
cb.complete(BooleanResult._new(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static byte[] utf8(String str) {
|
||||
if(str == null) return null;
|
||||
return str.getBytes(Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
public static String utf8(byte[] str) {
|
||||
if(str == null) return null;
|
||||
return new String(str, Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
public static String CRLFtoLF(String str) {
|
||||
if(str == null) return null;
|
||||
str = str.indexOf('\r') != -1 ? str.replace("\r", "") : str;
|
||||
str = str.trim();
|
||||
if(str.endsWith("\n")) {
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
if(str.startsWith("\n")) {
|
||||
str = str.substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String[] lines(String str) {
|
||||
if(str == null) return null;
|
||||
return CRLFtoLF(str).split("\n");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.NetHandler;
|
||||
|
||||
public class WorkerListenThread {
|
||||
/** Reference to the MinecraftServer object. */
|
||||
private final MinecraftServer mcServer;
|
||||
private final HashSet<NetHandler> connections = new HashSet<>();
|
||||
private final HashMap<String, WorkerNetworkManager> channels = new HashMap<>();
|
||||
|
||||
/** Whether the network listener object is listening. */
|
||||
public volatile boolean isListening = false;
|
||||
|
||||
public WorkerListenThread(MinecraftServer par1MinecraftServer) {
|
||||
this.mcServer = par1MinecraftServer;
|
||||
this.isListening = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds this connection to the list of currently connected players
|
||||
*/
|
||||
public void addPlayer(NetHandler par1NetServerHandler) {
|
||||
System.out.println("[Server][ADDPLAYER][" + par1NetServerHandler.getClass().getSimpleName() + "]");
|
||||
this.connections.add(par1NetServerHandler);
|
||||
}
|
||||
|
||||
public void stopListening() {
|
||||
this.isListening = false;
|
||||
List<String> names = new ArrayList<>(channels.keySet());
|
||||
for(int i = 0, l = names.size(); i < l; ++i) {
|
||||
closeChannel(names.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean openChannel(String player) {
|
||||
System.out.println("[Server][OPENCHANNEL][" + player + "]");
|
||||
return channels.put(player, new WorkerNetworkManager(player, mcServer, this)) == null;
|
||||
}
|
||||
|
||||
public void recievePacket(String player, byte[] data) {
|
||||
WorkerNetworkManager channel = channels.get(player);
|
||||
if(channel == null) {
|
||||
return;
|
||||
}
|
||||
channel.addToRecieveQueue(data);
|
||||
}
|
||||
|
||||
public boolean closeChannel(String player) {
|
||||
System.out.println("[Server][CLOSECHANNEL][" + player + "]");
|
||||
WorkerNetworkManager channel = channels.get(player);
|
||||
if(channel == null) {
|
||||
return false;
|
||||
}
|
||||
channels.remove(player);
|
||||
channel.networkShutdown(null, null, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteDeadConnections() {
|
||||
Iterator<NetHandler> itr = this.connections.iterator();
|
||||
while (itr.hasNext()) {
|
||||
NetHandler handler = itr.next();
|
||||
if (handler.shouldBeRemoved()) {
|
||||
itr.remove();
|
||||
//System.out.println("[Client][REMOVEDEAD]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all incoming connections and packets
|
||||
*/
|
||||
public void handleNetworkListenThread() {
|
||||
|
||||
deleteDeadConnections();
|
||||
|
||||
List<NetHandler> conns = new ArrayList<>(this.connections);
|
||||
for (NetHandler var2 : conns) {
|
||||
var2.handlePackets();
|
||||
}
|
||||
|
||||
deleteDeadConnections();
|
||||
|
||||
}
|
||||
|
||||
public MinecraftServer getServer() {
|
||||
return this.mcServer;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import net.lax1dude.eaglercraft.sp.ipc.IPCPacket0CPlayerChannel;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.src.INetworkManager;
|
||||
import net.minecraft.src.NetHandler;
|
||||
import net.minecraft.src.NetLoginHandler;
|
||||
import net.minecraft.src.NetServerHandler;
|
||||
import net.minecraft.src.Packet;
|
||||
|
||||
public class WorkerNetworkManager implements INetworkManager {
|
||||
|
||||
private NetHandler theNetHandler;
|
||||
private MinecraftServer minecraftServer;
|
||||
private String ipcChannel;
|
||||
private boolean isAlive;
|
||||
private WorkerListenThread listenThread;
|
||||
|
||||
private LinkedList<byte[]> frags = new LinkedList<>();
|
||||
|
||||
public WorkerNetworkManager(String ipcChannel, MinecraftServer srv, WorkerListenThread th) {
|
||||
this.ipcChannel = ipcChannel;
|
||||
this.theNetHandler = new NetLoginHandler(srv, this);
|
||||
th.addPlayer(theNetHandler);
|
||||
this.minecraftServer = srv;
|
||||
this.isAlive = true;
|
||||
this.listenThread = th;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetHandler(NetHandler var1) {
|
||||
theNetHandler = var1;
|
||||
listenThread.addPlayer(theNetHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToSendQueue(Packet var1) {
|
||||
if(!isAlive) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream(var1.getPacketSize() + 1);
|
||||
Packet.writePacket(var1, new DataOutputStream(bao));
|
||||
IntegratedServer.sendPlayerPacket(ipcChannel, bao.toByteArray());
|
||||
}catch(IOException e) {
|
||||
System.err.println("Failed to serialize minecraft packet '" + var1.getPacketId() + "' for IPC channel 'NET|" + ipcChannel + "'");
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void addToRecieveQueue(byte[] fragment) {
|
||||
//System.out.println("[Server][READ][QUEUE][" + ipcChannel + "]: " + fragment.length);
|
||||
if(!isAlive) {
|
||||
return;
|
||||
}
|
||||
frags.add(fragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wakeThreads() {
|
||||
// no
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processReadPackets() {
|
||||
while(frags.size() > 0) {
|
||||
byte[] pktBytes = frags.remove(0);
|
||||
try {
|
||||
ByteArrayInputStream bai = new ByteArrayInputStream(pktBytes);
|
||||
int pktId = bai.read();
|
||||
|
||||
if(pktId == -1) {
|
||||
System.err.println("Recieved invalid '-1' packet");
|
||||
continue;
|
||||
}
|
||||
|
||||
Packet pkt = Packet.getNewPacket(minecraftServer.getLogAgent(), pktId);
|
||||
|
||||
if(pkt == null) {
|
||||
System.err.println("Recieved invalid '" + pktId + "' packet");
|
||||
continue;
|
||||
}
|
||||
|
||||
pkt.readPacketData(new DataInputStream(bai));
|
||||
|
||||
//System.out.println("[Server][" + ipcChannel + "]: packet '" + pkt.getClass().getSimpleName() + "' recieved");
|
||||
|
||||
try {
|
||||
pkt.processPacket(theNetHandler);
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Could not process minecraft packet 0x" + Integer.toHexString(pkt.getPacketId()) + " class '" + pkt.getClass().getSimpleName() + "' on channel 'NET|" + ipcChannel + "'");
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
}catch(IOException ex) {
|
||||
System.err.println("Could not deserialize a " + pktBytes.length + " byte long minecraft packet of type '" + (pktBytes.length <= 0 ? -1 : (int)(pktBytes[0] & 0xFF)) + "' on channel 'NET|" + ipcChannel + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serverShutdown() {
|
||||
if(isAlive) {
|
||||
listenThread.closeChannel(ipcChannel);
|
||||
IntegratedServer.sendIPCPacket(new IPCPacket0CPlayerChannel(ipcChannel, false));
|
||||
}
|
||||
if(theNetHandler != null && (theNetHandler instanceof NetServerHandler)) {
|
||||
((NetServerHandler)theNetHandler).kickPlayerFromServer(null);
|
||||
}
|
||||
isAlive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int packetSize() { // why is this a thing
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void networkShutdown(String var1, Object... var2) {
|
||||
if(isAlive) {
|
||||
listenThread.closeChannel(ipcChannel);
|
||||
IntegratedServer.sendIPCPacket(new IPCPacket0CPlayerChannel(ipcChannel, false));
|
||||
}
|
||||
if(theNetHandler != null && (theNetHandler instanceof NetServerHandler)) {
|
||||
((NetServerHandler)theNetHandler).kickPlayerFromServer(null);
|
||||
}
|
||||
isAlive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnections() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerURI() {
|
||||
return "None? I dont fucking know what a URI is";
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof WorkerNetworkManager) && ((WorkerNetworkManager)o).ipcChannel.equals(ipcChannel);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return ipcChannel.hashCode();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
|
||||
public class WorldConverterEPK {
|
||||
|
||||
public static void importWorld(byte[] archiveContents, String newName) throws IOException {
|
||||
String folder = VFSSaveHandler.worldNameToFolderName(newName);
|
||||
VFile dir = new VFile("worlds", folder);
|
||||
EPKDecompiler dc = new EPKDecompiler(archiveContents);
|
||||
EPKDecompiler.FileEntry f = null;
|
||||
int lastProgUpdate = 0;
|
||||
int prog = 0;
|
||||
boolean hasReadType = dc.isOld();
|
||||
while((f = dc.readFile()) != null) {
|
||||
byte[] b = f.data;
|
||||
if(!hasReadType) {
|
||||
if(f.type.equals("HEAD") && f.name.equals("file-type") && EPKDecompiler.readASCII(f.data).equals("epk/world152")) {
|
||||
hasReadType = true;
|
||||
continue;
|
||||
}else {
|
||||
throw new IOException("file does not contain a singleplayer 1.5.2 world!");
|
||||
}
|
||||
}
|
||||
if(f.type.equals("FILE")) {
|
||||
if(f.name.equals("level.dat")) {
|
||||
NBTTagCompound worldDatNBT = CompressedStreamTools.decompress(b);
|
||||
worldDatNBT.getCompoundTag("Data").setString("LevelName", newName);
|
||||
worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis());
|
||||
b = CompressedStreamTools.compress(worldDatNBT);
|
||||
}
|
||||
VFile ff = new VFile(dir, f.name);
|
||||
ff.setAllBytes(b);
|
||||
prog += b.length;
|
||||
if(prog - lastProgUpdate > 10000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.importing.0", prog);
|
||||
}
|
||||
}
|
||||
}
|
||||
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
if(worldsTxt == null || worldsTxt.length <= 0) {
|
||||
worldsTxt = new String[] { folder };
|
||||
}else {
|
||||
String[] tmp = worldsTxt;
|
||||
worldsTxt = new String[worldsTxt.length + 1];
|
||||
System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length);
|
||||
worldsTxt[worldsTxt.length - 1] = folder;
|
||||
}
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", worldsTxt));
|
||||
}
|
||||
|
||||
public static byte[] exportWorld(String worldName) {
|
||||
String realWorldName = worldName;
|
||||
String worldOwner = "UNKNOWN";
|
||||
int j = realWorldName.lastIndexOf(new String(new char[] { (char)253, (char)233, (char)233 }));
|
||||
if(j != -1) {
|
||||
worldOwner = realWorldName.substring(j + 3);
|
||||
realWorldName = realWorldName.substring(0, j);
|
||||
}
|
||||
final int[] bytesWritten = new int[1];
|
||||
final int[] lastUpdate = new int[1];
|
||||
String pfx = "worlds/" + realWorldName + "/";
|
||||
EPK2Compiler c = new EPK2Compiler(realWorldName, worldOwner, "epk/world152");
|
||||
SYS.VFS.iterateFiles(pfx, false, (i) -> {
|
||||
byte[] b = i.getAllBytes();
|
||||
c.append(i.path.substring(pfx.length()), b);
|
||||
bytesWritten[0] += b.length;
|
||||
if (bytesWritten[0] - lastUpdate[0] > 10000) {
|
||||
lastUpdate[0] = bytesWritten[0];
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.1", bytesWritten[0]);
|
||||
}
|
||||
});
|
||||
return c.complete();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
package net.lax1dude.eaglercraft.sp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import net.minecraft.src.CompressedStreamTools;
|
||||
import net.minecraft.src.NBTTagCompound;
|
||||
import net.minecraft.src.RegionFile;
|
||||
|
||||
public class WorldConverterMCA {
|
||||
|
||||
public static void importWorld(byte[] archiveContents, String newName) throws IOException {
|
||||
String folderName = newName.replaceAll("[\\./\"]", "_");
|
||||
VFile worldDir = new VFile("worlds", folderName);
|
||||
while((new VFile(worldDir, "level.dat")).exists() || (new VFile(worldDir, "level.dat_old")).exists()) {
|
||||
folderName += "_";
|
||||
worldDir = new VFile("worlds", folderName);
|
||||
}
|
||||
List<char[]> fileNames = new ArrayList<>();
|
||||
try(ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(archiveContents))) {
|
||||
ZipEntry folderNameFile = null;
|
||||
while((folderNameFile = zis.getNextEntry()) != null) {
|
||||
if (folderNameFile.getName().contains("__MACOSX/")) continue;
|
||||
if (folderNameFile.isDirectory()) continue;
|
||||
String lowerName = folderNameFile.getName().toLowerCase();
|
||||
if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr"))) continue;
|
||||
fileNames.add(folderNameFile.getName().toCharArray());
|
||||
}
|
||||
}
|
||||
final int[] i = new int[] { 0 };
|
||||
while(fileNames.get(0).length > i[0] && fileNames.stream().allMatch(w -> w[i[0]] == fileNames.get(0)[i[0]])) i[0]++;
|
||||
int folderPrefixOffset = i[0];
|
||||
try(ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(archiveContents))) {
|
||||
ZipEntry f = null;
|
||||
int lastProgUpdate = 0;
|
||||
int prog = 0;
|
||||
while ((f = zis.getNextEntry()) != null) {
|
||||
if (f.getName().contains("__MACOSX/")) continue;
|
||||
if (f.isDirectory()) continue;
|
||||
String lowerName = f.getName().toLowerCase();
|
||||
if (!(lowerName.endsWith(".dat") || lowerName.endsWith(".dat_old") || lowerName.endsWith(".mca") || lowerName.endsWith(".mcr") || lowerName.endsWith(".bmp"))) continue;
|
||||
byte[] b;
|
||||
int sz = (int)f.getSize();
|
||||
if(sz >= 0) {
|
||||
b = new byte[sz];
|
||||
int j = 0, k;
|
||||
while(j < b.length && (k = zis.read(b, j, b.length - j)) != -1) {
|
||||
j += k;
|
||||
}
|
||||
}else {
|
||||
b = inputStreamToBytesNoClose(zis);
|
||||
}
|
||||
String fileName = f.getName().substring(folderPrefixOffset);
|
||||
if (fileName.equals("level.dat") || fileName.equals("level.dat_old")) {
|
||||
NBTTagCompound worldDatNBT = CompressedStreamTools.readCompressed(new ByteArrayInputStream(b));
|
||||
worldDatNBT.getCompoundTag("Data").setString("LevelName", newName);
|
||||
worldDatNBT.getCompoundTag("Data").setLong("LastPlayed", System.currentTimeMillis());
|
||||
ByteArrayOutputStream bo = new ByteArrayOutputStream();
|
||||
CompressedStreamTools.writeCompressed(worldDatNBT, bo);
|
||||
b = bo.toByteArray();
|
||||
VFile ff = new VFile(worldDir, fileName);
|
||||
ff.setAllBytes(b);
|
||||
prog += b.length;
|
||||
} else if ((fileName.endsWith(".mcr") || fileName.endsWith(".mca")) && (fileName.startsWith("region/") || fileName.startsWith("DIM1/region/") || fileName.startsWith("DIM-1/region/"))) {
|
||||
VFile chunkFolder = new VFile(worldDir, fileName.startsWith("DIM1") ? "level1" : (fileName.startsWith("DIM-1") ? "level-1" : "level0"));
|
||||
RegionFile mca = new RegionFile(new RandomAccessMemoryFile(b, b.length));
|
||||
for(int j = 0; j < 32; ++j) {
|
||||
for(int k = 0; k < 32; ++k) {
|
||||
if(mca.isChunkSaved(j, k)) {
|
||||
NBTTagCompound chunkNBT;
|
||||
NBTTagCompound chunkLevel;
|
||||
try {
|
||||
chunkNBT = CompressedStreamTools.read(mca.getChunkDataInputStream(j, k));
|
||||
if(!chunkNBT.hasKey("Level")) {
|
||||
throw new IOException("Chunk is missing level data!");
|
||||
}
|
||||
chunkLevel = chunkNBT.getCompoundTag("Level");
|
||||
}catch(Throwable t) {
|
||||
System.err.println("Could not read chunk: " + j + ", " + k);
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
int chunkX = chunkLevel.getInteger("xPos");
|
||||
int chunkZ = chunkLevel.getInteger("zPos");
|
||||
VFile chunkOut = new VFile(chunkFolder, VFSChunkLoader.getChunkPath(chunkX, chunkZ) + ".dat");
|
||||
if(chunkOut.exists()) {
|
||||
System.err.println("Chunk already exists: " + chunkOut.getPath());
|
||||
continue;
|
||||
}
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
CompressedStreamTools.writeCompressed(chunkNBT, bao);
|
||||
b = bao.toByteArray();
|
||||
chunkOut.setAllBytes(b);
|
||||
prog += b.length;
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.importing.1", prog);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (fileName.startsWith("data/") || fileName.startsWith("players/")) {
|
||||
VFile ff = new VFile(worldDir, fileName);
|
||||
ff.setAllBytes(b);
|
||||
prog += b.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
String[] worldsTxt = SYS.VFS.getFile("worlds.txt").getAllLines();
|
||||
if(worldsTxt == null || worldsTxt.length <= 0 || (worldsTxt.length == 1 && worldsTxt[0].trim().length() <= 0)) {
|
||||
worldsTxt = new String[] { folderName };
|
||||
}else {
|
||||
String[] tmp = worldsTxt;
|
||||
worldsTxt = new String[worldsTxt.length + 1];
|
||||
System.arraycopy(tmp, 0, worldsTxt, 0, tmp.length);
|
||||
worldsTxt[worldsTxt.length - 1] = folderName;
|
||||
}
|
||||
SYS.VFS.getFile("worlds.txt").setAllChars(String.join("\n", worldsTxt));
|
||||
}
|
||||
|
||||
public static byte[] exportWorld(String folderName) throws IOException {
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
VFile worldFolder;
|
||||
try(ZipOutputStream zos = new ZipOutputStream(bao)) {
|
||||
zos.setComment("contains backup of world '" + folderName + "'");
|
||||
worldFolder = new VFile("worlds", folderName);
|
||||
VFile vf = new VFile(worldFolder, "level.dat");
|
||||
byte[] b;
|
||||
int lastProgUpdate = 0;
|
||||
int prog = 0;
|
||||
boolean safe = false;
|
||||
if(vf.exists()) {
|
||||
zos.putNextEntry(new ZipEntry(folderName + "/level.dat"));
|
||||
b = vf.getAllBytes();
|
||||
zos.write(b);
|
||||
prog += b.length;
|
||||
safe = true;
|
||||
}
|
||||
vf = new VFile(worldFolder, "level.dat_old");
|
||||
if(vf.exists()) {
|
||||
zos.putNextEntry(new ZipEntry(folderName + "/level.dat_old"));
|
||||
b = vf.getAllBytes();
|
||||
zos.write(b);
|
||||
prog += b.length;
|
||||
safe = true;
|
||||
}
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog);
|
||||
}
|
||||
String[] srcFolderNames = new String[] { "level0", "level-1", "level1" };
|
||||
String[] dstFolderNames = new String[] { "/region/", "/DIM-1/region/", "/DIM1/region/" };
|
||||
List<VFile> fileList;
|
||||
for(int i = 0; i < 3; ++i) {
|
||||
vf = new VFile(worldFolder, srcFolderNames[i]);
|
||||
fileList = SYS.VFS.listVFiles(vf.getPath());
|
||||
String regionFolder = folderName + dstFolderNames[i];
|
||||
Map<String,RegionFile> regionFiles = new HashMap<>();
|
||||
for(int k = 0, l = fileList.size(); k < l; ++k) {
|
||||
VFile chunkFile = fileList.get(k);
|
||||
NBTTagCompound chunkNBT;
|
||||
NBTTagCompound chunkLevel;
|
||||
try {
|
||||
b = chunkFile.getAllBytes();
|
||||
chunkNBT = CompressedStreamTools.readCompressed(new ByteArrayInputStream(b));
|
||||
if(!chunkNBT.hasKey("Level")) {
|
||||
throw new IOException("Chunk is missing level data!");
|
||||
}
|
||||
chunkLevel = chunkNBT.getCompoundTag("Level");
|
||||
}catch(IOException t) {
|
||||
System.err.println("Could not read chunk: " + chunkFile.getPath());
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
int chunkX = chunkLevel.getInteger("xPos");
|
||||
int chunkZ = chunkLevel.getInteger("zPos");
|
||||
String regionFileName = "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mca";
|
||||
RegionFile rf = regionFiles.get(regionFileName);
|
||||
if(rf == null) {
|
||||
rf = new RegionFile(new RandomAccessMemoryFile(new byte[65536], 0));
|
||||
regionFiles.put(regionFileName, rf);
|
||||
}
|
||||
try(DataOutputStream dos = rf.getChunkDataOutputStream(chunkX & 31, chunkZ & 31)) {
|
||||
CompressedStreamTools.write(chunkNBT, dos);
|
||||
}catch(IOException t) {
|
||||
System.err.println("Could not write chunk to " + regionFileName + ": " + chunkFile.getPath());
|
||||
t.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
prog += b.length;
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog);
|
||||
}
|
||||
}
|
||||
if(regionFiles.isEmpty()) {
|
||||
System.err.println("No region files were generated");
|
||||
continue;
|
||||
}
|
||||
for(Entry<String,RegionFile> etr : regionFiles.entrySet()) {
|
||||
String regionPath = regionFolder + etr.getKey();
|
||||
zos.putNextEntry(new ZipEntry(regionPath));
|
||||
zos.write(etr.getValue().getFile().getByteArray());
|
||||
}
|
||||
}
|
||||
fileList = SYS.VFS.listVFiles((new VFile(worldFolder, "data")).getPath());
|
||||
for(int k = 0, l = fileList.size(); k < l; ++k) {
|
||||
VFile dataFile = fileList.get(k);
|
||||
zos.putNextEntry(new ZipEntry(folderName + "/data/" + dataFile.getName()));
|
||||
b = dataFile.getAllBytes();
|
||||
zos.write(b);
|
||||
prog += b.length;
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog);
|
||||
}
|
||||
}
|
||||
fileList = SYS.VFS.listVFiles((new VFile(worldFolder, "players")).getPath());
|
||||
for(int k = 0, l = fileList.size(); k < l; ++k) {
|
||||
VFile dataFile = fileList.get(k);
|
||||
zos.putNextEntry(new ZipEntry(folderName + "/players/" + dataFile.getName()));
|
||||
b = dataFile.getAllBytes();
|
||||
zos.write(b);
|
||||
prog += b.length;
|
||||
if (prog - lastProgUpdate > 25000) {
|
||||
lastProgUpdate = prog;
|
||||
IntegratedServer.updateStatusString("selectWorld.progress.exporting.2", prog);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bao.toByteArray();
|
||||
}
|
||||
|
||||
private static byte[] inputStreamToBytesNoClose(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
|
||||
byte[] buf = new byte[1024];
|
||||
int i;
|
||||
while ((i = is.read(buf)) != -1) {
|
||||
os.write(buf, 0, i);
|
||||
}
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 + "%");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")));
|
||||
|
|
|
@ -19,5 +19,5 @@ public interface ICommandSender
|
|||
*/
|
||||
ChunkCoordinates getPlayerCoordinates();
|
||||
|
||||
World getEntityWorld();
|
||||
//World getEntityWorld();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue